序 Introduction
遊戲設計模式 Game Programming Patterns
在五年級時,我和我的朋友被准許使用一間存放有幾臺非常破舊的TRS-80s的房間。 爲了鼓舞我們,一位老師給我們找了一些簡單的BASIC程序打印文檔。
電腦的磁帶驅動器已經壞掉了,所以每當我們想要運行代碼,就得小心地從頭開始輸入它們。 因此,我們更喜歡那些只有幾行長的程序:
10 PRINT "BOBBY IS RADICAL!!!"
20 GOTO 10
哪怕這樣,過程也充滿了困難。我們不知道如何編程,所以小小的語法錯誤對我們來說也是天險。 如果程序沒有工作,我們就得從頭再來一遍——這經常發生。
文檔的最後幾頁是個真正的怪物:一個佔據了幾頁篇幅的程序。 我們得花些時間才能鼓起勇氣去試一試,但它實在太誘人——它的標題是“地道與巨魔”。 我們不知道它能做什麼,但聽起來像是個遊戲,還有什麼比自己編個電腦遊戲更酷的嗎?
我們從來沒能讓它運行起來,一年以後,我們離開了那間教室。 (很久以後,當我真的學會了點BASIC,我意識到那只是個桌面遊戲角色生成器,而不是遊戲。) 但是命運的車輪已經開始轉動——自那時起,我就想要成爲一名遊戲程序員。
青少年時,我家有了一臺能運行QuickBASIC的Macintosh,之後THINK C也能在其上運行。 幾乎整個暑假我都在用它編遊戲。 自學緩慢而痛苦。 我能輕鬆地編寫並運行某些部分——地圖或者小謎題——但隨着程序代碼量的增長,這越來越難。
起初,挑戰之處僅僅在於讓程序成功運行。然後,是搞明白怎樣寫出內容超出我大腦容量的代碼。 我不再只閱讀關於“如何用C++編程”的書籍,而開始嘗試找那些講如何組織程序的書。
幾年過後,一位朋友給我一本書:《設計模式:可複用面向對象軟件的基礎》。 終於!這正是我從青年時期就在尋找的書。 我一口氣從頭讀到尾。雖然我仍然掙扎於自己的程序中,但看到別人也在掙扎並提出瞭解決方案是一種解脫。 我意識到手無寸鐵的我終於有件像樣的工具了。
在2001年,我獲得了夢想中的工作:EA的軟件工程師。 我等不及要看看真正的遊戲,還有專業人士是如何組織一切的。 像實況足球這樣的大型遊戲使用了什麼樣的架構?不同的系統是如何交互的?一套代碼庫是如何在多個平臺上運行的?
分析理解源代碼是種震顫的體驗。圖形,AI,動畫,視覺效果皆有傑出代碼。 有專家知道如何榨乾CPU的最後一個循環並好好使用。 那些我都不知道是否可行的事情,這些人在午飯前就能完成。
但是這些傑出代碼依賴的架構通常是事後設計。 他們太注重功能而忽視了架構。耦合充斥在模塊間。 新功能被塞到任何能塞進去的地方。 在夢想幻滅的我看來,這和其他程序員沒什麼不同, 如果他們閱讀過《設計模式》,最多也就用用單例。
當然,沒那麼糟。我曾幻想遊戲程序員坐在白板包圍的象牙塔裏,爲架構冷靜地討論上幾周。 而實際情況是,我看到的代碼是努力應對緊張截止期限的人趕工完成的。 他們已經竭盡全力,而且就像我慢慢意識到的那樣,他們全力以赴的結果通常很好。 我花在遊戲代碼上的時間越多,我越能發現藏在表面下的天才之處。
不幸的是,“藏”是普遍現象。 寶石埋在代碼中,但人們從未意識到它們的存在。 我看到同事重複尋找解決方案,而需要的示例代碼就埋在他們所用的代碼庫中。
這個問題正是這本書要解決的。 我挖出了遊戲代碼庫中能找到的設計模式,打磨然後在這裏展示它們,這樣可以節約時間用在發明新事物上,而非重新發明它們。
書店裏已有的書籍
書店裏已經有很多遊戲編程書籍了。爲什麼要再寫一本呢?
我看到的很多編程書籍可以歸爲這兩類:
-
特定領域的書籍。 這些關於細分領域的書籍帶你深入理解遊戲開發的某一特定層面。 它們會教授你3D圖形,實時渲染,物理模擬,人工智能,或者音頻播放。 那些很多程序員窮其一生研究的細分領域。
-
完整引擎的書籍。 另一個方向,還有書籍試圖包含遊戲引擎的各個部分。 它們傾向於構建特定種類遊戲的完整引擎,通常是3D FPS遊戲。
這兩種書我都喜歡,但我認爲它們並未覆蓋全部空間。 特定領域的書籍很少告訴你這些代碼如何與遊戲的其他部分打交道。 你擅長物理或者渲染,但是你知道怎麼將兩者優雅地組合嗎?
第二類書包含這些,但是我發現完整引擎的書籍通常過於整體,過於專注某類遊戲了。 特別是,隨着手遊和休閒遊戲的興起,我們正處於衆多遊戲類型欣欣向榮的時刻。 我們不再只是複製Quake了。如果你的遊戲與該類遊戲不同,那些介紹單一引擎的書就不那麼有用了。
相反,我在這裏做的更à la carte 。 每一章都是獨立的、可應用到代碼上的思路。 這樣,你可以用你認爲最好的方式組合這些思路,用到你的遊戲上去。
和設計模式的關聯
任何名字中有“模式”的編程書 都與Erich Gamma,Richard Helm,Ralph Johnson,和John Vlissides(通常被稱爲GoF)合著的經典書籍: 《設計模式:可複用面向對象軟件要素》相關。
稱這本書爲“遊戲編程模式”,我不是暗示GoF的模式不適用於遊戲編程。 相反:本書的重返設計模式一節包含了《設計模式》中的很多模式, 但強調了這些模式在遊戲編程中的特定使用。
同樣地,我認爲本書也適用於非遊戲軟件。 我可以依樣畫瓢稱本書爲《更多設計模式》,但是我認爲舉遊戲編程爲例子更爲契合。 你真的想要另一本介紹員工記錄和銀行賬戶的書嗎?
也就是說,雖然這裏介紹的模式在其他軟件上也很有用,但它們更合適於處理遊戲中常見的工程挑戰:
-
時間和順序通常是遊戲架構的核心部分。事物必須在正確的時間按正確的順序發生。
-
高度壓縮的開發週期,大量程序員需要能快速構建和迭代一系列不同的行爲,同時保證不煩擾他人,也不污染代碼庫。
-
在定義所有的行爲後,遊戲開始互動。怪物攻擊英雄,藥物相互混合,炸彈炸飛敵人或者友軍。 實現這些互動不能把代碼庫搞成一團亂麻。
-
最後,遊戲中性能很重要。 遊戲開發者處於一場榨乾平臺性能的競賽中。 節約CPU循環的技巧區分了A級百萬銷量遊戲和掉幀差評遊戲。
如何閱讀這本書
《遊戲編程模式》分爲三大塊。 第一部分介紹並劃分本書的框架。包含你現在閱讀的這章和下一章。
第二部分,重訪設計模式,複習了GoF書籍裏的很多模式。 在每一章中,我給出我對這個模式的看法,以及我認爲它和遊戲編程有什麼關係。
最後一部分是這本書最肥美的部分。 它展示了十三種我發現有用的模式。它們被分爲四類: 序列模式, 行爲模式, 解耦模式,和優化模式。
每種模式都使用固定的格式表述,這樣你可以將這本書當成引用,快速找到你需要的:
-
意圖 部分提供這個模式想要解決什麼問題的簡短介紹。 將它放在首位,這樣你可以快速翻閱,找到你現在需要的模式。
-
動機 部分描述了模式處理的問題示例。 不同於具體的算法,模式通常不針對某個特定問題。 不用示例教授模式,就像不用麪糰教授烘烤。動機部分提供了麪糰,而下部分會教你烘烤。
-
模式 部分將模式從示例中剝離出來。 如果你想要一段對模式的教科書式簡短介紹,那就是這部分了。 如果你已經熟悉了這種模式,想要確保你沒有拉下什麼,這部分也是很好的提示。
-
到目前爲止,模式只是用一兩個示例解釋。但是如何知道模式對你的問題有沒有用呢? 何時使用 部分提供了這個模式在何時使用何時不用的指導。 記住 部分指出了使用模式的結果和風險。
-
如果你像我一樣需要具體的例子來真正地理解某物,那麼示例代碼部分能讓你稱心如意。 它描述模式的一步步具體實現,來展現模式是如何工作的。
-
模式與算法不同的是它們是開放的。 每次你使用模式,可以用不同的方式實現。 下一部分設計決策,討論這些方式,告訴你應用模式時可供考慮的不同選項。
-
作爲結尾,這裏有參見部分展示了這一模式與其他模式的關聯,以及那些使用它的真實代碼。
關於示例代碼
這本書的示例代碼使用C++寫就,但這並不意味着這些模式只在C++中有用,或C++比其他語言更適合使用這些模式。 這些模式適用於幾乎每種編程語言,雖然有的模式假設編程語言有對象和類。
我選擇C++有幾個原因。首先,這是在遊戲製作中最流行的語言,是業界的通用語。 通常,C++基於的C語法也是Java,C#,JavaScript和其他很多語言的基礎。 哪怕你不懂C++,你也只需一點點努力就能理解這裏的示例代碼。
這本書的目標不是教會你C++。 示例代碼儘可能地簡單,不一定符合好的C++風格或規範。 示例代碼展示的是意圖,而不是代碼。
特別地,代碼沒用“現代的”——C++11或者更新的——標準。 沒有使用標準庫,很少使用模板。 它們是“糟糕”C++代碼,但我希望保持這樣,這樣那些使用C,Objective-C,Java和其他語言的人更容易理解它們。
爲了避免花費時間在你已經看過或者是與模式無關的代碼上,示例中省略了部分代碼。 如果是那樣,示例代碼中的省略號表明這裏隱藏了一些代碼。
假設有個函數,做了些工作然後返回值。 而用它作示例的模式只關心返回的值,而不是完成了什麼工作。那樣的話,示例代碼長得像這樣:
bool update()
{
// 做點工作……
return isDone();
}
接下來呢
設計模式在軟件開發過程中不斷地改變和擴展。 這本書繼續了GoF記錄分享設計模式的旅程,而這旅程也不會終於本書。
你是這段旅程的關鍵部分。改良(或者否決)了這本書中的模式,你就是爲軟件開發社區做貢獻。 如果你有任何建議,更正,或者任何反饋,保持聯絡!