目錄

  • 致謝
  • 序
    • 架構,性能和遊戲
  • 重訪設計模式
    • 命令模式
    • 享元模式
    • 觀察者模式
    • 原型模式
    • 單例模式
    • 狀態模式
  • 序列模式
    • 雙緩衝模式
    • 遊戲循環
    • 更新方法
  • 行爲模式
    • 字節碼
    • 子類沙箱
    • 類型對象
  • 解耦模式
    • 組件模式
    • 事件隊列
    • 服務定位器
  • 優化模式
    • 數據局部性
    • 髒標識模式
    • 對象池模式
    • 空間分區
← 上一章     § Contents   ≡ 首頁   下一章 →  

序 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 。 每一章都是獨立的、可應用到代碼上的思路。 這樣,你可以用你認爲最好的方式組合這些思路,用到你的遊戲上去。

另一個廣泛使用這種à la carte風格的例子是Game Programming Gems系列。

和設計模式的關聯

任何名字中有“模式”的編程書 都與Erich Gamma,Richard Helm,Ralph Johnson,和John Vlissides(通常被稱爲GoF)合著的經典書籍: 《設計模式:可複用面向對象軟件要素》相關。

《設計模式》也受到之前的書籍的啓發。 創建一種模式語言來描述問題的開放式解法, 這思路來自 A Pattern Language, 作者是Christopher Alexander (還有Sarah Ishikawa和Murray Silverstein).

他們的書是關於架構的(建築和牆那樣的真正的框架結構), 但他們希望其他人能使用相同的方法描述其他領域的解決方案。 《設計模式》正是是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記錄分享設計模式的旅程,而這旅程也不會終於本書。

你是這段旅程的關鍵部分。改良(或者否決)了這本書中的模式,你就是爲軟件開發社區做貢獻。 如果你有任何建議,更正,或者任何反饋,保持聯絡!

← 上一章     § Contents   ≡ 首頁   下一章 →  
© 2009-2015 Robert Nystrom