遊戲編程模式:前言(架構,性能和遊戲)(Part I)

        譯者的序:《Game Programming Patterns》是一本不錯的書,講了設計模式在遊戲編程領域中的靈活運用。由於寫得實在是太好了,遂觸發了本人翻譯的熱情。所以把本人的渣翻放在這裏做成系列博客,希望大家多多提出寶貴意見呀!下面正式開始了!

        今天講的是引言部分。引言部分有很多對此書進行說明的部分,就略過了,直接來說說Architecture, Performance and Games這一章。下面是本人的翻譯。有一些本人的註解就放在括號裏了,大家很容易找到的。

 

-----------------------------------------------------分割線---------------------------------------------------------------

 

        在我們一頭扎進成堆的模式之前,我覺得先跟各位看官說說“我是怎麼看待軟件架構的以及它是如何運用於遊戲中的”會比較有用。這也許會讓你們能夠更好地理解本書餘下的部分。至少,當你參與到一個關於設計模式和軟件架構多麼糟糕(或者給力)的辯論中時,這會給你一些武器來使用。

    注意我並沒有假設你會站在哪一邊。就像武器供應商一樣,我爲交戰雙方都出售貨物。

1、什麼是軟件架構?

        如果你完完整整地讀完了此書,你並不會學會3D圖形背後的線性代數或者是遊戲物理學背後的微積分。它並不會告訴你如何修剪你的AI搜索樹或者是在你的音頻重放中模擬一個房間的迴音。

    天哪!上面這一段話將會成爲本書的一個糟糕的廣告!

        實際上,此書是關於那些代碼之間的那些事的。它更多地是關於組織代碼的,而非寫代碼的。每個程序都有某種組織形式,即使它僅僅是“將所有的事情擠在main()函數中然後看看會發生什麼”,所以我覺得談談什麼可以造就好的組織形式會更加有意思。我們怎麼區分架構是好是壞呢?

        我對這個問題琢磨了差不多五年。當然,像你一樣,我對於好的設計有一種直覺。我們都承受過代碼庫的折磨,這折磨是如此之嚴重,以至於你可以希望做的最好的事情就是take them out back and put them out of their misery(譯者注:這句話不知道怎麼翻譯啊,求助!)。

    讓我們承認吧,我們大部分人對其中一些糟糕的代碼庫是負有責任的。

        一些幸運兒有相反的經歷,有機會與設計得很漂亮的代碼一起工作。這種代碼庫感覺就像是一個完美裝飾的奢華旅館,還有門房在熱切地期待着你的每一個怪念頭。這兩種代碼的區別是什麼?

 

什麼是好的軟件架構?

        對於我來說,好的設計意味着當我做出一個改變的時候,看起來就像是整個程序就是爲了變更而製造的。我可以用僅僅幾個供選擇的、可以完美嵌入的函數調用解決一個任務,不會在平靜的代碼表面上留下任何漣漪。

        這聽上去不錯,但是不太可行。“儘管去寫你的代碼,使得變更不會擾亂其平靜的表面。”是的。

        讓我把這件事稍微細分一下。第一個關鍵部分是“架構是關於變更的”。某人不得不修改代碼庫。如果沒人去碰這代碼——要麼是因爲它很完美,很完整,要麼是因爲它很爛,以至於沒人會用它來弄髒自己的編輯器——那麼其設計就無關緊要了。對於一個設計的好壞的量度是它應對變更的方便程度。如果沒有變更的話,那麼它就是一個從未離開起跑線的賽跑者。

   

你怎麼做出一個變更?

        在你可以更改代碼來增加一個新特性、修復一個bug或者是爲了其他什麼導致你打開你的編輯器的目的之前,你得要理解現有的代碼正在做什麼。當然,你不必瞭解整個程序,但是你需要將相關的部分載入到你的頭腦中。

    想想有點奇怪,這是一個光學文字辨識(Optical CharacterRecognition, OCR)過程。

       我們傾向於對這一步驟避而不談,但是這經常是編程過程中最耗時的部分。If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves.

        一旦你將所有的正確的相關文本放到你的大腦中,你會略微思索,並找出你的解決方案。這裏可能會有很多的來回往復,但是這一般是比較直接的。一旦你理解了問題以及與之有接觸的那部分代碼,實際寫代碼的時候有時就不值得一提了。

        你用你肉乎乎的手指在鍵盤上敲打了一會兒,直到屏幕上閃爍着正確的顏色的光,然後你就完事了,對吧?還沒呢!在你寫測試代碼並將代碼發出去以供代碼評審之前,你經常有一些清理工作要做。

    我是不是說了“測試”?哦,是的,我說了。對於某些遊戲代碼來說寫單元測試很難,但是代碼庫的很大一部分完全是可以測試的。

    我不會在這裏搞長篇大論(譯者注:I won't get on a soapbox here),但是我會讓你考慮做更多的自動化測試(automated testing),如果你還沒有做的話。相比於一次又一次手動地確認事情,難道你沒有更好的事情去做嗎?

        你往你的遊戲中又塞入了一些代碼,但是你不希望下一個人被你在源代碼中留下的褶皺所絆倒。除非變更很小,否則通常你需要對代碼進行重新組織,以使你的新代碼與程序的其他部分無縫地結合成整體。如果你做得對,下一個人將沒法判斷出哪一行代碼是什麼時候寫的。

        簡而言之,編程的流程圖有點如下圖所示:


    現在我想了想,這個循環沒有跳出這一事實是令人擔憂的。

 

解耦如何有幫助?

        雖然這不明顯,但是我覺得軟件架構的很大一部分是關於上面那個學習階段的。將代碼載入到神經元中的過程慢得令人痛苦,所以找到策略來減小代碼的體積是值得的。本書有一整節是關於解耦模式的,並且《設計模式》(Design Patterns)的一大塊跟這一概念有關。

        你可以用好幾種方式來定義“解耦”,但是我覺得如果兩塊代碼是耦合的,那麼這意味着你要理解其中之一,就必得理解另一個。如果你解耦了它們,那麼你可以獨立地理解其中之一了。這很棒,因爲如果只有一塊代碼與你的問題有關,那麼你就只需要將這一塊代碼載入到你的頭腦中,而非把另一半也載入進來。

        對我來說,這是軟件架構的一個關鍵目標:減少你在能夠有進展之前需要知道的知識的量。

        後面的階段當然也有戲份了。解耦的另一個定義是對一塊代碼做出的變更未必要求另一塊的變更。我們顯然必須要變更某些東西,但是我們耦合的東西越少,該變更對遊戲的其他部分產生的漣漪就越少。

 

2、以什麼代價?

        這聽上去很棒,對吧?對所有東西進行解耦,然後你就可以行雲流水地寫代碼了。每一個變更意味着只要觸碰一兩個可供選擇的函數,並且你可以在代碼的表面跳舞,不留下一絲陰影。

        這種感覺正是爲什麼人們對抽象(abstraction)、模塊化(modularity)、設計模式和軟件架構感到興奮的原因。與一個良好構架的程序一起工作是一個愉快的經歷,並且每個人都喜歡變得更加多產。好的架構對於生產效率能夠產生巨大影響。再怎麼強調其效果的深遠性都不爲過。

        但是,就像人生中的所有事情一樣,這並不是免費的。好的架構需要付出努力以及訓練。每當你做出一個變更或者實現一個特性的時候,你必須努力工作以使得其餘程序的其餘部分優雅地結合成一個整體。你必須在兩方面小心行事:一方面是好好地組織代碼,另一方面是在經歷過上千個構成開發週期的小小的變更後保持代碼爲有組織的狀態。

    上述的第二部分——維護你的設計——需要引起特別的重視。我見過許多程序一開始是優美的,但是隨着程序員們一遍又一遍地添加“僅僅一個小小的劈砍”而死於遍體鱗傷。

    就像園藝一樣,僅僅放入新的植物是不夠的。你必須除草、剪枝。

        你必須考慮程序的哪一部分應該被解耦出來,並且在這些地方做出抽象。同樣地,你得確定什麼地方需要加入擴展性(原文:you have to determine where extensibility should be enginnered in)以便可以更加容易地做出未來的變更。

        人們對此感到興奮。他們設想未來的開發人員(或者就是未來的他們自己)涉足於代碼庫中,然後發現它是可修整的(open-ended)、強大的、希望被擴展的。他們想象着統治他們所有人的一個遊戲引擎(The One Game Engine To Rule Them All)。

        但這就是事情開始變得棘手的地方。每當你增加一個抽象層級或者一個可以支持可擴展性的地方的時候,你推測你後來會需要這種靈活性。你正在往你的遊戲中增加代碼和複雜性,而這需要時間來開發、調試以及維護。

        如果你猜得對並在後來接觸到那部分代碼的話,那麼這種努力會得到補償。但是預測未來是困難的,而當該模塊性最終證明無用的話,那麼它就會很快地變得有害。畢竟,你需要處理更多的代碼了。

    有些人發明了術語YAGNI(是You aren't gonna need it的縮寫),作爲用於對抗推測自己未來會需要什麼的衝動的真言。

        當人們對此變得過於狂熱的時候,你會得到一個代碼庫,其結構已經繞得脫離控制了(whose architecture has spiraled out of control)。你會在所有的地方遇到接口和抽象。插件系統(plug-in systems)、抽象基類、虛方法巨多無比,還有各種各樣的可擴展點。

        這會讓你花上一輩子在所有這樣的腳手架間穿梭以追蹤到一些真正做了實事的代碼。當你需要做出一個變更的時候,當然,很可能有一個接口在那裏可以相助,但是希望你運氣好到能夠找到它。理論上,所有的這種解耦意味着你在可以對代碼進行擴展之前,只需要理解比較少的代碼就行了,但是抽象層本身最終會裝滿你的心理暫存磁盤(mental scratch disk)的。

        像這樣的代碼庫正是使得人們反對軟件架構、尤其是設計模式的原因。很容易深陷進代碼本身,以至於你忘卻了你正在努力地發售一個遊戲。可擴展性這首塞壬之歌吞沒了無數的開發者,他們花了數年時間來做一個“引擎”,卻並不知道這是一個用於什麼的引擎。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章