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

3、性能和速度

    還有另外一個對於軟件架構和抽象化的批評你有時候會聽到,尤其是在遊戲開發領域:就是它們對於遊戲的性能有害。很多使得你的代碼更加靈活的模式依賴於虛分派(virtual dispatch)、接口、指針、消息等等會增加運行時成本的機制。

    一個有趣的反例是C++中的模板。模板元編程(template metaprogramming)有時候可以在不增加運行時負擔的同時爲你提供接口的抽象性。

    這裏有很多種不同的靈活性(There's a spectrum of flexibility here)。當你寫代碼來調用某個類中一個具體的方法時,你在創作時間(author time)固定了這個類——你寫死了你調用的那個類。當你使用一個虛函數或者接口的時候,直到運行時runtime)才知道調用了哪個類。這個要靈活很多,但是蘊含了一些運行時的負擔。

    模板元編程則位於這二者之間。當模板被實例化時,你在編譯時compile time)來決定調用哪個類。

        這是有理由的。很多軟件架構就是關於將你的遊戲變得更加靈活的。這是關於讓改變軟件更加省力的。這意味着在程序中注入更少的假設。(That means encoding fewer assumptions in the program.)你使用接口,以便你的代碼適用於任何實現了該接口的類,而不僅僅是今天管用的那個類。你使用觀察者模式(observers)和消息發送(messaging)來讓遊戲的兩個部分互相對話,以便將來可以輕鬆地應用於三個或四個遊戲部分之間的對話。

        但是性能完全是關於假設的。在具體的限制條件之下才能夠很好地進行優化的實踐。我們能否有把握地假設我們永遠不會有超過256個敵人呢?如果是的話,好極了,我們可以將ID打包進單個字節中。我們會不會僅僅對於一個具體的類型來調用一個方法呢?是的話,好的,我們可以靜態地分派它,或者將它變成inline函數。所有的實體(entity)都是同一個類的對象嗎?好的,我們可以創建一個連續數組來儲存它們。

        儘管如此,這並不意味着靈活性是不好的。靈活性讓我們迅速地改變遊戲,而開發development)速度對於達到一個娛樂體驗而言是絕對重要的。沒有人,哪怕是Will Wright也好,可以在紙上弄出一個平衡性很好的遊戲設計方案。這需要迭代和試驗。

        你嘗試新主意並觀察它們給人的感受的速度越快,你能夠嘗試的東西就越多,你就越有可能找到給力的東東。甚至是在你找到正確的機制之後,你還是需要大量的時間來進行微調。一點點的不平衡就可以毀掉一個遊戲的樂趣。

        沒有簡單的回答。讓你的遊戲變得更靈活,以便你可以更快地開發出一個原型,這會帶來一些性能的成本。類似地,對你的代碼進行優化則會使其靈活性降低。

        但我的經驗是,讓一個有趣的遊戲運行變快,比讓一個運行快的遊戲變得有趣要難。一種妥協方案是,保持你的代碼是靈活的,直到設計方案穩定了,然後拆除掉一些抽象層以提升性能。

 

4、壞代碼中的好東西

        這讓我想到這樣一件事:在有些時機和場合下,是要有不同風格的代碼的。本書的很多內容是關於編寫可維護的、乾淨的代碼的,所以我很忠誠地擁護用“正確的”方式去做事情,但是草率寫就的代碼(slapdash code)也是有價值的。

        編寫構架良好的代碼需要謹慎的考慮,並且這會轉換成時間成本。而且,在一個工程的一生中維護一個好的架構需要很大的努力。你得像一個好的露營者對待自己的營地那樣對待你的代碼庫:永遠要努力讓它處於比你發現它時更好一點的狀態(always try to leave ita little better than you found it)。

        如果你打算長期與這代碼打交道的話,這做法不錯。但是,就像我之前提到的那樣,遊戲設計需要很多試驗和探索。尤其是在早期的時候,你經常會寫你知道你將會扔掉的代碼。

        如果你僅僅想要知道某個玩法的主意是否管用,將它優美地構架起來意味着在你最終將其顯示在屏幕上並得到一些反饋之前要耗費掉更多的時間。如果它最終證明不管用的話,當你刪除它時,你用於將你的代碼變得優雅所花費的時間就完全浪費掉了。

        原型製造(prototyping)——將一些剛好足夠有功能的代碼隨意地放置在一起以回答一個設計問題——是一個完全正當的編程實踐。不過我得給你一個警告。如果你編寫了要丟棄的代碼,你必須確保你可以將其扔掉。我見過糟糕的經理一次又一次地玩下面的遊戲:

    老闆:“嘿,我們想出了這個主意,你得試試。只是一個原型而已,所以不要覺得你需要把它做得很對。你多快可以將其組裝起來?”

    開發者:“額,如果我走很多近路,不測試,不寫文檔,並且產生超多的BUG,我可以在幾天以內給你一個臨時的代碼。”

    老闆:“好極了!”

幾天以後……

    老闆:“嘿,那個原型不錯。你可以僅僅花費幾個小時將其清理清理,然後將它變成正式的?”

        你需要保證使用要扔掉的代碼的人理解這一點:即使這代碼看起來有點管用,但是它並不能被維護,並且必須被改寫。如果If there's a chance you'll end up having to keep it around, you may have to defensively write itwell. (譯者注:不會翻譯了呀……)

    有一個訣竅可以保證你的原型代碼不會被強制地變成正式代碼:用跟你的遊戲所使用的編程語言不同的語言來編寫它。這樣的話,在它進入你的遊戲之前,你就必須得改寫它了。

 

5、達成平衡

        現在場上表演的有這麼幾個勢力:

1、我們想要一個好看的架構,以便代碼在工程的整個生命中更容易理解。

2、我們想要快速的運行時性能。

3、我們想要把今天的特性迅速搞定。

    我覺得很有趣:這些都是關於某種速度的:我們長期的開發速度,遊戲的執行速度,和我們短期的開發速度。

        這些目標至少是部分矛盾的。好的架構在長期會提升產量,但是維護它意味着每一個更改都需要花費一點更多的努力來保持整潔。

        寫起來最快的實現很少是運行起來最快的。相反,優化需要花費很多的工程時間。一旦優化完成,那麼它會傾向於將代碼庫鈣化(calcify):高度優化的代碼是不靈活的,改變起來很難。

        總是有壓力來讓今天的工作今日完成,而把其他所有的事情放到明天去擔心。但是如果我們儘可能快地填充新特性的話,我們的代碼庫會變成由hacks,BUG和不一致性所組成的一團亂麻,而這會損壞我們將來的產出。

        這裏沒有簡單的答案,只有權衡。從我收到的電子郵件來看,這讓很多人沮喪。尤其是對那些僅僅想要做一個遊戲的新手來說,聽到這句話是令人生畏的:“沒有正確的答案,只有不同口味的錯誤答案。”

        但是對我來說,這是令人興奮的!看看任何人們獻出其生涯去掌握的領域吧,在中心位置,你總是會找到一個由糾結的約束條件組成的集合。畢竟,如果真的有簡單的答案的話,那麼每個人就會那樣去做了。你一週之內就可以掌握的領域最終是乏味的。你不會聽說有人在挖溝(ditch digging)領域中是出類拔萃的。

    也許你確實聽說過;我並沒有對這個類比進行過調查。就我所知,可能有熱切的挖溝業餘愛好者、挖溝慣例以及圍繞它的一個完整的亞文化。我怎麼能夠判斷呢?

        對於我來說,這與遊戲本身有很多共同之處。像國際象棋這樣的遊戲永遠不會被掌握,因爲所有的棋子都如此完美地實現了彼此間的平衡。這意味着你可以花上你的一輩子來探索可行的策略所組成的廣闊空間。一個設計得糟糕的遊戲會坍縮到被使用了一次又一次的必勝策略上,直到你厭倦了並退出遊戲。

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