<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                > 作者:Richard Shimooka, David White > 譯者:謝路云 > 狀態:完成 > 原文鏈接:[http://www.aosabook.org/en/wesnoth.html](http://box.kancloud.cn/2015-08-20_55d5891d81d46.png) ![enter image description here](http://box.kancloud.cn/2015-08-20_55d5891c0de66.jpg) 編程往往被簡單地看作一種解決問題的行為,開發者根據需求編碼得到一個解決方案。對代碼優美程度的判斷一般來自于技術實現上的優雅或者效率,而這本書(《開源軟件架構》)中的項目就是它們之中的杰出例子。除了計算,代碼還對公眾的生活產生了深遠的影響。它能夠激勵人們參與并創造新的事物。但不幸的是,大家在參與各種項目時仍然會遇到很高的門檻。 大多數編程語言都需要相當的技術專業知識才能使用,這對許多人來說遙不可及。此外,讓所有人都能編程不僅從技術上是困難的,而且對于許多項目也沒有必要。這樣做并不一定能夠得到簡潔的代碼或是聰明的解決方案。提高項目的參與程度需要開發者在項目和程序的設計時有遠見,而這經常是和正常的編程習慣相違背的。再者,大多數項目的核心都是一組熟練的高水平專業程序員。他們并不需要外部資源的幫助。因此,項目的可參與性就變成了可有可無的東西,甚至從沒有被考慮過。 我們的項目“韋諾之戰”試圖從源頭上解決這個問題。它是一個基于GPL2許可證的在開源模式下開發的回合制奇幻戰略游戲,它相當成功,截至這篇文章發表時已經被下載了超過四百萬次。雖然這個數據很可觀,但我們認為這個項目真正的出彩之處在于其開發模式凝聚了一大批能力水準各異的志愿者。 提高可參與性并不是我們開發者設立的一個模糊的目標,而是被視為這個項目成敗的關鍵。開源意味著韋諾之戰不可能立刻就吸引來大量高質量的開發者。吸引更多掌握不同技能的貢獻者參與項目才能保證項目的長久活力。 我們的開發者從第一個迭代起就開始為擴大項目的參與程度而努力。這不可避免的會對項目架構的各個方面都產生一些影響。項目中的大部分決策在制定的過程中也都會考慮到這個目標。本章會深入地講解我們的項目,尤其是我們在擴大項目參與度方面所進行的努力。 本章的第一部分概括了項目的代碼,包括編程語言、依賴和架構。第二部分將集中介紹韋諾之戰獨特的數據存儲語言,叫做“韋諾標記語言”(WML)。這部分將說明WML的功能,特別是對于游戲單位的影響。之后介紹的是多人游戲的實現以及一些外圍項目。章節的最后會給出一些我們在架構和拓展項目參與度方面觀察到的結論。 ## 25.1項目概況 韋諾之戰的核心引擎是用C++寫的,現在總共約20萬行代碼。這只是游戲的核心引擎,不包含任何游戲內容,約占整個代碼庫的一半。我們的程序接受由一種叫做“韋諾標記預言”(WML)的獨特的數據語言所定義的游戲內容。游戲在發布時還包含約25萬行WML代碼。這個比例在項目中還在不斷升高。隨著項目的成熟,硬編碼在C++中的游戲內容已經越來越多地被重寫為WML所定義的操作。圖25.1給出了項目的大致結構。綠色的部分是韋諾之戰的開發者們維護的,而白色的部分則是由外部的參與者們維護的。 ![enter image description here](http://box.kancloud.cn/2015-08-20_55d5891d81d46.png) 圖25.1:項目架構 總體而言,我們項目在大多數情況下都會盡量將依賴最小化以最大化應用程序的可移植性。這么做的間接好處是降低了程序的復雜度,并且開發者也無需再學習大量第三方API之間的瑣碎區別。但同時,謹慎的使用一些第三方庫也能達到相同的效果。例如,韋諾之戰在處理視頻、I/O和事件中使用了SDL庫。選擇它是因為它使用方便且提供了一個跨平臺的公共I/O接口。這使得韋諾之戰能夠被移植到許多平臺,而無需為不同的平臺專門編寫API。當然,這也是有代價的,那就是很難再利用一些平臺專有的特性。韋諾之戰還使用了SDL附帶的一系列類庫: * SDL_Mixer:音頻和聲音 * SDL_Image:加載PNG等格式的圖片 * SDL_Net:網絡I/O 此外,韋諾之戰還使用一些其他的庫: * Boost:各種高級C++特性 * Pango和Cairo:渲染各種字體 * zlib:壓縮 * Python和Lua:腳本支持 * GNU gettext:國際化 在韋諾之戰的引擎中,WML對象——即用字符串表示的含有子節點的字典——的使用是無處不在的。WML節點可以構造許多對象,而這些對象也可以被序列化為WML節點。引擎的一些部分將數據保存在WML字典的格式中并直接翻譯它們,而不是將其解析為C++的數據結構。 韋諾之戰中有若干個重要的子系統,它們大多數都盡可能地減少外部的依賴。這種高隔離度的結構也有助于提高項目的可參與性。對某個部分感興趣的人可以工作在其特定領域的代碼中,他們提交的修改不會涉及到項目的其他部分。主要子系統包括: * WML的預處理器和解析器 * 抽象了底層庫和系統調用的基礎I/O模塊:視頻、音頻和網絡 * GUI模塊,包括按鈕、下拉列表、菜單等控件的實現 * 顯示模塊,渲染游戲地圖、單位、動畫等等 * 人工智能(AI)模塊 * 尋路模塊,包含了許多處理六邊型游戲地圖的工具函數 * 地圖生成模塊,用于生成各種隨機地圖 同時,不同的模塊控制著游戲流程中的不同部分: * titlescreen模塊:控制游戲的主菜單畫面的顯示 * storyline模塊:顯示游戲中的對話、場景切換等的序列 * lobby模塊:在多人游戲服務器上處理游戲的顯示和創建 * "play game"模塊:控制整個游戲的主模塊 主模塊和顯示模塊是韋諾之戰中最大的模塊。它們的功能定義是最模糊的,因為它們的功能總是在變化,很難清晰地界定。因此,這兩個模塊曾多次有變成“上帝對象”的危險——變成一個無法定義其行為的龐然大物。所以,它們的代碼會被定期審查并將任何可以獨立為模塊的部分分離出來。 項目中還有一些附加功能,但它們和主項目是分開的。這包括用于多人網絡游戲的多人游戲服務器,以及一個內容服務器。用戶可以將他們的進度等信息上傳到公共內容服務器并相互分享。兩者都是用C++編寫的。 ## 25.2.韋諾標記語言 作為一個可擴展的游戲引擎,韋諾之戰使用了一種簡單的數據語言來保存和加載所有的游戲數據。最初我們準備使用XML,但后來大家希望使用一種對非技術用戶更友好且對可視化的數據的描述更加靈活的語言。因此,我們開發了自己的數據語言,叫做“韋諾標記語言”(WML)。我們在設計時就盡量降低了它的技術難度,希望即使連學習Python和HTML都感到困難的人也能看懂WML文件。韋諾之戰的所有游戲數據都存儲在WML中,包括單位的定義、戰役、劇情、圖形界面以及其他游戲邏輯方面的配置。 WML和XML的基本性質相同,都有元素和屬性,但是它不支持元素內的文本(text)。WML中的屬性可以直接用一個字典式的字符串到字符串的映射表示,而程序邏輯會負責翻譯這些屬性。下面這個簡單的例子是游戲中的“精靈戰士”(Elvish Fighter)這個單位的部分定義: ~~~ [unit_type] id=Elvish Fighter name= _ "Elvish Fighter" race=elf image="units/elves-wood/fighter.png" profile="portraits/elves/fighter.png" hitpoints=33 movement_type=woodland movement=5 experience=40 level=1 alignment=neutral advances_to=Elvish Captain,Elvish Hero cost=14 usage=fighter {LESS_NIMBLE_ELF} [attack] name=sword description=_"sword" icon=attacks/sword-elven.png type=blade range=melee damage=5 number=4 [/attack] [/unit_type] ~~~ 由于國際化對于韋諾之戰十分重要,所以WML直接支持國際化:含有下劃線前綴的屬性值是可翻譯的。所有可翻譯的字符串都會在解析時被GNU?`gettext`轉換為適當語言的字符串。 韋諾之戰選擇將引擎所需的所有主要的游戲數據記錄在一份WML文檔(document)中,而非多份文檔。這樣程序就只需要一個全局變量來表示這份文檔,并且在游戲加載時所有內容都會被加載,例如,所有單位的定義都可以在`units`元素的所有name屬性為`unit_type`的子元素中找到。 盡管所有數據都是被存儲在一份WML文檔中的,但將它們都保存在同一個文件中則是不明智的。韋諾之戰的預處理器會在解析之前遍歷所有WML文件。它允許一個文件包含另一個文件甚至另一個目錄的內容。例如: ~~~ {gui/default/window/} ~~~ 將會包含`gui/default/window/`下的所有`.cfg`文件。 由于WML可能會變得非常冗長,預處理器還允許定義宏來壓縮文件的長度。例如,精靈戰士的定義中調用了宏`{LESS_NIMBLE_ELF}`,這個宏的作用是在特定條件下使某些精靈單位不再敏捷,例如當他們在森林中靜止不動的時候。 ~~~ #define LESS_NIMBLE_ELF [defense] forest=40 [/defense] #enddef ~~~ 這個設計的優點在于游戲引擎無需了解WML文檔是如何被分解到多個文件中的。如何將游戲數據分割安排到不同的文件和目錄中去是WML文檔的作者們的職責。 當引擎加載WML文檔的時候,它會根據眾多游戲設置定義一些預處理器所使用的符號。例如,韋諾之戰中的戰役的難度是可設置的,每個難度設置都定義了一個不同的預處理器符號。調節難度的一種常用手段是改變分配給對手的資源量(用“金”表示)。為了簡化難度設置,我們定義了一個WML宏: ~~~ #define GOLD EASY_AMOUNT NORMAL_AMOUNT HARD_AMOUNT #ifdef EASY gold={EASY_AMOUNT} #endif #ifdef NORMAL gold={NORMAL_AMOUNT} #endif #ifdef HARD gold={HARD_AMOUNT} #endif #enddef ~~~ 例如,這個宏的參數可以是`{GOLD 50 100 200}`。在計算機對手的定義中調用這個宏就可以根據難度級別定義它所擁有的金錢。 因為WML的處理是依賴于外部條件的,所以如果韋諾之戰的引擎在執行期間發現WML文檔所需的任何符號發生了改變的話,整個WML文檔都會被重新加載并處理。例如,當用戶啟動游戲時,WML文檔就會被加載并得到已知的戰役和其他游戲內容。但是,如果用戶選擇了某個特定的難度——比如簡單——來開始某個戰役,那么整個WML文檔都會根據新定義的EASY符號被重新加載。 這種設計的方便之處在于單個文檔就能夠包含所有的游戲數據,并且可以通過各種符號配置該WML文檔。但是作為一個成功的項目,韋諾之戰中的內容越來越多,其中包括許多可下載的內容——這些內容最終會被插入到這棵文檔樹中,這意味著這份WML文檔的大小會是若干MB。它已經成為了韋諾之戰的一個性能問題。在某些計算機上,加載文檔就可能耗時一分鐘。只要需要重新加載文檔,游戲就會出現延遲。此外,它也消耗了大量的內存。我們也采取了一些應對措施,例如每個戰役都會定義一個唯一的符號。因此可以用`#ifdef`來定義僅有這個戰役才會用到的資源,這樣只有在這個戰役啟動的時候這些資源才會被加載。 此外,韋諾之戰使用了一套緩存系統來保存,有一組定義好的鍵對應被處理過的WML文檔。這個緩存系統會檢查所有WML文件的時間戳,如果任意一個文件發生了改變,就重新生成文檔。 ## 25.3.韋諾之戰中的單位 韋諾之戰的主角是其中的各種單位。一名精靈戰士和精靈薩滿可能會遭遇一個巨魔武士和獸人苦工。所有單元都有相同的基本行為能力,但許多單位還有自己的特殊技能,能夠改變正常的游戲流程。例如,巨魔單位在每一輪中都能恢復一些生命值,精靈薩滿能夠用糾纏根須降低敵人的移動速度,而樹人在森林中是隱身的。 在引擎中表示這些的最佳方式是什么?最容易想到的方式是在C++定義一個基類unit,然后從中派生出其他類型的單位。例如,可以從`unit`類中派生出一個`wose_unit`類,而`unit`中可以有一個返回false的虛函數`bool is_invisible() const`。`wose_unit`可以重載這個函數并在單位處于森林中的時候返回true。 如果游戲中的角色不多,這種方法是行得通的。但不幸的是韋諾之戰相當龐大,而這種方式并不是那么容易擴展。如果在這種方式下有人希望添加一種新的單位,他就需要在游戲中添加一個新的C++類。另外,這種方式無法組合各種技能。例如,如果我們需要一種既能回血,又能減速,還能在森林中隱身的單位該怎么辦?你將不得不寫一個全新的類而又復制其他類之中的代碼。 韋諾之戰的單位系統完全沒有使用繼承。相反,它使用一個`unit`類來表示各種單位,一個`unit_type`類來表示某一類型的單位所共有的特征。`unit`類會引用相應的類型對象。所有`unit_type`對象都被保存在一個全局字典中,和主WML文檔一同被加載。 單位的類型指的是該單位所擁有的一系列技能。例如,巨魔的“回春”的技能可以每回合都恢復一些生命值。而蜥蜴散兵的“穿插”技能使之能夠穿過敵人的兵線。技能的識別是引擎的一部分——例如,尋路算法會檢查一個單位是否設置了“穿插”標志來判斷他是否能夠自由地穿過敵人的兵線。用這種方式添加的新單位能夠擁有游戲引擎實現的所有技能的任意組合,只需要在WML中定義即可。當然,不修改引擎是不可能添加全新的技能和單位的基本行為的。 此外,韋諾之戰中的每個單位都可能有多種攻擊方式。比如精靈弓箭手,遠可以用弓,近可以用劍。每種攻擊的性質和所造成的傷害都是不同的。表示攻擊行為的是`attack_type`類。每個`unit_type`類的實例都會引用一個可用的`attack_types`列表。 為了讓每個單位都更有個性,韋諾之戰賦予了他們“特質”。每個單位在被征招的時候都會被賦予從一個列表中隨機選出的兩種特質。例如,一個“力量型”的單位在近戰中造成的傷害會更多,而一個“智力型”的單位“升級”所需的經驗值則更少。同時,作戰單位在游戲中可以拾取一些裝備來變得更加強大,比如單位在撿起了一把劍之后造成的傷害會更多。為了實現特質和裝備,韋諾之戰的引擎可以根據WML的定義修改單位的性質。某些類型的攻擊甚至也是可以修改的。例如,“力量型”特質賦予了單位更強大的近戰攻擊力,但是不會影響他的遠程攻擊力。 實現單位行為的完全WML可配置化是一個理想化的目標。因此,探究為什么韋諾之戰最終沒能達到這個目標也是有意義的。如果需要能夠任意定義單位的行為,WML就需要變得更加靈活。它必須從一個數據描述語言擴展為一個羽翼豐滿的成熟編程語言,但它也會變得令人望而卻步,嚇跑許多乘興而來的貢獻者。 韋諾之戰的AI是用C++編寫的,它了解游戲中的所有技能。它會最大限度靈活運用單位的回春、隱身等等不同技能。即使單位的技能是可以用WML創建的,也很難寫出足夠成熟的AI來學習并運用新的技能。實現一個AI無法利用的技能是無法令人滿意的。同樣,在WML中實現技能,但卻需要修改AI的C++代碼才能夠運用這項技能也是糟糕的。因此,用WML定義單位而將技能硬編碼在引擎中是最適合韋諾之戰需求的折中選擇。 ## 25.4.韋諾之戰的多人游戲 ![enter image description here](http://box.kancloud.cn/2015-08-20_55d5891ec857a.jpg) 我們用盡可能簡單的方式實現了韋諾之戰的多人游戲。服務器會盡力阻止惡意的攻擊,但并沒有在預防作弊上下功夫。韋諾之戰的一局游戲中所進行的任何動作——移動單位、攻擊敵人、招募單位等等——都能被保存為一個WML節點。例如,移動一個單位的一條命令所保存得到的WML節點是這樣的: ~~~ [move] x="11,11,10,9,8,7" y="6,7,7,8,8,9" [/move] ~~~ 它保存的是單位在玩家的命令下的行進路線。服務器在游戲中會執行接收到的任意類似的WML命令。這種設計的實用之處在于,只要保存了游戲的初始狀態和隨后的所有命令就等于保存了一場完整的游戲錄像。重放游戲既能幫助玩家觀察其他玩家的玩法,也能幫助我們調試一些錯誤報告。 我們希望社區的多人游戲的主旋律是友好、休閑的氣氛。與其在技術上和那些整天鉆研如何破解反作弊系統的反社會分子做斗爭,韋諾之戰基本沒有反作弊。在分析了其他多人游戲之后我們認為,競爭性的排名系統是這種反社會行為的根源,而在服務器上刻意忽略這種功能則將大大降低人們的作弊動機。此外,社區的管理員也在積極地鼓勵參與游戲的玩家互相建立良好的關系。這大大促進了友誼第一比賽第二的社區氛圍。這些努力是非常成功的,到目前為止那些嘗試惡意攻擊游戲的人都被社區孤立了。 韋諾之戰的多人游戲的實現是一個典型的C/S架構。服務端程序wesnothd會接受客戶端的連接,并向客戶端發送當前游戲的一份列表。韋諾之戰會向玩家顯示一個游戲大廳,玩家在其中可以選擇加入他人的游戲或是創建一個新的游戲并等待他人的加入。當所有玩家都進入游戲且游戲開始之后,韋諾之戰的客戶端會根據玩家的行動生成WML命令。這些命令會被發送到服務器,而服務器則會將它們轉發到游戲中的所有其他客戶端。服務器的任務僅僅是轉發而已。重放系統用于在其他客戶端上執行WML命令。由于韋諾之戰是一個回合制游戲,所有網絡通信使用的協議都是TCP/IP。 服務器還支持觀看者觀看游戲。觀看者可以加入一個進行中的游戲,服務器會將游戲的初始狀態和游戲的命令歷史全部發送給觀看者。這使得新的觀看者可以趕上游戲的進度。觀看者可以看到游戲的進程,但不會立即到達游戲的當前狀態——命令歷史可以快進,但仍然需要時間。另一種方法是令某個客戶端用WML描述一個游戲當前狀態的快照并將它發送給新的觀看者,但隨著觀看者的增多這種方法會增加游戲客戶端的負擔,而且只需讓多個觀看者同時加入一個游戲就可以達到“拒絕服務”攻擊的效果。 當然,由于韋諾之戰的客戶端相互之間不會共享任何游戲狀態,只會發送命令,所以游戲規則的一致性很重要。游戲服務器會根據版本將游戲分區,只有客戶端版本相同的玩家才能在一起游戲。如果有人的客戶端版本和其他玩家不同步,所有玩家都會立即收到警告。這也是一種有效的反作弊手段。雖然玩家想通過修改客戶端來作弊并不難,但任何版本差異都會被立即通報給所有玩家并由玩家們自己來解決這個問題。 ## 25.5.總結 我們認為韋諾之戰項目的優秀之處在于所有人都能參與編碼。為了實現這一目標,項目經常在代碼的優雅方面作出妥協。值得一提的是雖然項目中的許多優秀程序員在見到WML的低效語法時都會皺眉頭,但這種妥協正是項目的成功之源。今天,韋諾之戰的最大財富就是用戶創建的數百個戰役和時代,而這些用戶大都只有很少甚至沒有任何編程經驗。此外,它也激勵了許多人將編程作為一種職業,而將這個項目作為一種學習工具。這種成就是許多其他項目所無法比擬的。 讀者從韋諾之戰項目的工作中能夠學到的重要一課應該是如何去幫助那些不熟練的程序員。要認識到參與者在實際編碼和磨練技能時所遇到的困難并不容易。例如,某些人可能想為項目作出貢獻,但卻不具備任何編程技能。類似emacs或vim這樣的專業編輯器有著顯著的學習曲線,只會讓人們灰心喪氣。因此WML的設計初衷就是任何人都能用一個簡單的文本編輯器打開WML文件并作出貢獻。 但是,增強代碼庫的可參與性并不簡單。并沒有什么硬性和便捷的規則能夠降低代碼庫的門檻。相反,它需要照顧到方方面面的平衡,否則可能會對社區產生負面的后果,這在項目對依賴的處理上表現得很明顯。在某些情況下,依賴會提高項目參與的門檻,但在另一些情況下也可能使參與項目更容易。具體情況需要具體分析。 我們也不應該夸大韋諾之戰所取得的成功。這個項目擁有一些其他項目無法輕易復制的優點。降低編寫代碼的難度并吸引廣泛的公眾參與的成果也部分來自于項目的規則。作為一個開源項目,韋諾之戰在這方面有一定的優勢。在法律上,GNU許可證允許任何人打開代碼文件,學習其中的原理并進行修改。這種文化鼓勵所有人去實驗、學習并分享,但并不一定適合于其他項目。盡管如此,我們希望這個項目中的閃光點幫助了所有開發者并讓他們體會到了編程的美妙之處。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看