Braid 作者 Jonathan Blow 日前在他目前正在開發中的 The Witness 開發部落格上,發表了一篇他在今年四月份回 UC Berkeley 對他的學弟妹們演講的內容。演講的核心議題在「如何有效率地進行遊戲程式開發」,特別是針對獨立遊戲相關的開發。原網址上可下載 audio track 與 slide,但是 Jon 在 comments 中提到不知為何影片不見了 … XD 所以 Q&A 時段有一個 The Witness 的 preview 很可惜大家只能用「聽」的 XD ..
原網址:http://the-witness.net/news/?p=1004
下面我針對 talk 內容做一個翻譯與整理,但是在進入正題之前,有些前題希望大家先瞭解,不然可能會覺得「他為什麼要講這些」。
- 整個演講內容是 Jon 長年累積經驗歸納的心得,裡面也有些許舉例,但是這些舉例也有其時空背景環境,請不要太過著重於他所舉的例子字面上的意義。(或是可以怪我整理的不通順 :~)
- Jon 演講的對象是一群 UC Berkeley 的 Computer Science 學生,這些學生的性質、經驗、能力導向也許與你、我不同。而 Jon 真正的目的是要讓學生瞭解「學術」與「生產/開發」間明確的差異。
- 這場 talk 真的非常、非常 focus 在「獨立遊戲開發」,簡單的說就是通常只有一個人寫程式的狀態。
※ ※ ※
我粗略將演講內容分為以下幾大段落:
- 個人開發經驗與背景交待。
- 關於程式效率最佳化 …
- 關於使用資料結構 …
- 到底要最佳化什麼?
- 關於泛用化程式設計 …
- 總結:對於獨立遊戲開發來說,什麼叫「好的程式設計師」?
- Q&A (preview The Witness 那段會跳過,只能用聽的已經夠囧了,用文字描述感覺更差 XD)
不過 Q&A 有不少學生問了我認為很重要的問題,也有很多學生在原網址回應問問題,會略作整理。
※ ※ ※
Jonathan Blow 本人從事遊戲程式開發長達 16 年,在這段時間中,實戰經驗的累積讓他對程式設計的「美學」定義產生不少改變,而很多都與他當年唸 Computer Science 時所認知的、老師所教導的,甚至一般程式開發上普遍認同的習慣或常識相悖離。
他在一開始先提到 Braid 當初開發的一些背景。Braid 從 2005 年開發到 2008 年上市歷時三年半,程式由一人撰寫,最後總共包括了 221 個 C++ source,238 個 C++ header,3 個 C source,20 個 HLSL source,約 12k 行的註解與 90k 行的程式碼,其中大約有 75k 行是完全新寫的,沒有使用任何「協助產生程式碼」的工具。The Witness 離目標發行時間可能還有將近一年,它的程式碼量也已經超過了 Braid 一些(但沒有到兩倍就是了),雖然有其他程式員幫忙,不過好像也只有一個人。另外,考量到很多程式會不斷反複修正,實際上當然是寫了更多東西(只是後來刪掉了)。
雖然單純看這些數字很難表達什麼很切實的東西,但不論從何種角度來說,這樣的程式大概已經遠遠超過一個 Computer Science 學生在學時期,或剛畢業的時候會碰到的規模。而 Jon 指出,統計調查的結果,程式員產量如果用秤斤兩的方式來算的話,每年每人約是 3250 行(雖然我不知道這個 study 資料哪來的,另外容易 google 到的敘述是程式員平均一天寫十行程式),當然我想這是指「有留下來的程式」。然而粗略的概算,也知道用這種速度來開發如同 Braid 一樣規模的遊戲程式的話,十幾年也寫不完。
就算完全不管品質的瘋狂亂寫,但也總要到「一定的堪用程度」,不可能無止境地用品質換速度;再考量 Braid 當初是在 Xbox Live Arcade 上推出的作品,Jon 也描述了在 Xbox 360 上開發作品一般人不太會知道的困難處:
- 512 MB RAM only 而且不能全用;它的 3 核 CPU 是 in-order 架構的;File System 因為安全性設計,速度也不快。
- 遊戲審核 fps drop 容忍範圍很小;不能有 crash,讀取時間也設限。
- "Soak Test",遊戲要跑滿整整 3 天(以 60fps 換算成 frame 數的話要跑超過 15M 幅),簡單說你每個 frame 有 4 Bytes 的 memory leak 的話就絕對無法通過遊戲審核。
簡單說,能通過審核程序的遊戲程式幾乎肯定是要有相當品質的。
別忘了,身為獨立遊戲開發者,遊戲設計、關卡設計、美術協調、音效音樂協調、商業營運、行銷推廣、財務管理沒有一項是可以漏掉不做的!「必須以極端的效率來完成事情」是最困難的挑戰。
※ ※ ※
第一個關於「程式效率最佳化」的議題,當然其實講的是老生常談了,不過不曉得有多少人在還在學生時期就有老師教過「未成年就這麼優是萬惡根源」,所以我想在此還是重複提醒一下大家 Jon 是在對一群還沒畢業的學生講話。我們也常聽過 80/20 法則被套用在這裡:八成的程式與最後的效率影響幾乎微乎其微,剩下兩成的程式則會影響八成的效率。Jon 提到的比例更極端些,Braid 中 90k 行的程式大約只有 6k 行是 performance-sensitive 的。所以不要去想「這邊怎麼寫跑起來會比較快」,平鋪直敘地寫過去就好,否則相當容易造成真正時間上的浪費。對於 Computer Science 出身的學生,也許不去做最佳化可能是很違反直覺的,但是要學會盡量克制。另一點是,針對效率最佳化的程式通常都比較難修改,或比較不直覺,若未來程式需要修改時,往往會造成麻煩。
※ ※ ※
延續第一個議題,學生在學校通常也被教導各種「資料結構」的使用時機,他的存取時間複雜度等等問題。Jon 說,這就已經隱含了「最佳化」的意義在裡面,而我們從前面知道最佳化通常是不好的,所以,使用「正確」的資料結構通常是不好的。不過我個人猜想這應該是在需要自行土炮資料結構的情況下,才會有這個 concern。譬如說確實看過不少例子是雖然用 C++ 寫引擎,但是在資料結構上都捨 STL 的不用,不過這已經超過本篇範疇,就先不談了。
Jon 在這段舉的例子是,他以前曾跑到 DOOM 的 mailing list 上說 DOOM 的場景資源管理的程式寫得不好,那一段程式在應該可以用 hash 或 b-tree based 資料結構的地方居然用 array,結果和 John Romero 互嗆了起來 XD 他事後回想,確實在這邊使用了比較高級的資料結構的話,只是花了更多時間寫、要花更長時間 compile、然後程式執行時會吃掉更多記憶體,不過實際上根本對效率沒有多大的影響,因為這個片段並不是整體程式執行效率的主導因素。Jon 也對學生提了一個觀念,就是當看到被公認為「高手」的人做了一些你覺得(技術上)「見面不如聞名」的事的時候,不妨多想想,也許你沒抓到重點或沒看到整體成果;況且,若對方有實績而自己沒有,似乎也該謙遜一點,畢竟很多臨場智慧,是沒實際走過一趟不知道的。
Jon 說他現在幾乎所有資料結構都是單純的 arrays of structs (in C/C++)。
※ ※ ※
開頭兩段其實主要都是在講與 optimization 有關的議題,那到底有沒有該最佳化的東西呢?
Jon 提出,比起最佳化程式執行的時間與耗用的記憶體空間,真的該最佳化的是「你在每個程式實作上所耗用的生命」,這才是真正有效益的最佳化考量。資料結構與複雜的演算法沒辦法最佳化你所耗費的生命,花時間在這些東西上面只會把更重要的事務(確實把東西做完)邊緣化而已;除非,你確定非這麼做不可,或是確定這麼做的效益非常非常明顯。
提到效益問題,Jon 很排斥一些實際上根本只是為了發表而發表的應用計算機科學(Applied Computer Science)的相關論文,他說幾乎所有這類論文都是不好的,往往提出了一個雖然有少許效益,但過度複雜的方法,無法被廣泛應用而且統計數字只挑好聽的來講。
※ ※ ※
再來提到泛用(generalized)程式設計,他認為「通常」泛用化的實作最終導致的結果會比幾個針對性、寫死的實作要來得差。譬如說未考量過到底真的有需求的是哪些功能/可能性,往往做出來的泛用性會包含了很多到最後也用不上的功能,中間摻雜了多餘的抽象層(abstraction),而且每當系統需求產生改變的時候,這樣的泛用設計反而比寫死的還難更動。他認為泛用系統的另一個缺點是,實際上真正的操作反而變得不明顯了。(講到這有點 Linus Torvalds 的感覺… )
在 Q&A 時 Jon 有提到,當然也有許多狀況下,泛用程式很明顯是優於各別寫死的系統,比起「不要太早最佳化」這點來說,泛用系統到底何時該用、何時不該用是一個比較難以明確界定的問題,他認為除了多寫多做、經驗累積之外沒有什麼直接的方法。
另外對於在既有程式上添加/撰寫新系統也必需格外小心,該考量的是如何刪除程式碼而能不影響任何程式功能,而非增加程式的複雜度;若把整個程式中的每個模組、類別、物件、函數等等當成一個節點,然後整個程式就是把節點連起來所組成的一張圖(這裡指 graph,不是 image)來看的話,每多一個節點也代表整張圖的連線複雜度,會以至少線性,至多平方關係的速率成長,也更易造成藕合。這也是《人月神話》(the Mythical Man-Month)中所提到當程式專案變大時它是如何越來越變得難以控制。
其實這對有一點程式開發經驗的人不難理解,而且應該也都聽過類似說法;不過 Jon 在此提到的例子是,有時候我們會傾向為了讓每一個 function/method 內容看起來盡量簡潔,所以常常為此重構(refactor)程式碼,抽出一些看起來讓 function 內容變亂的東西,把它整理成另一個 function,所以我們可能會把一個很長的 function 抽成好幾段,變成好幾個不同的小 function。但是 Jon 說,他寧可讓那個很長的 function 就長那個樣子,如果這整段程式確實只做一件事而且只出現在這裡的話。因為他不希望原本一目了然的操作,變成把實際操作的內容隱藏到其他地方去,就算那段程式可能長達一千行,那就一千行又如何呢?
(註:舉例用的程式片段在 Jon 的演講投影片第 28 頁,我這邊就不額外貼 code 了。)
※ ※ ※
所以到底對獨立遊戲開發來說,怎樣叫做一個好的程式員?
迅速、腳踏實地、會確實把事情做完,對於各種進階的程式技術與知識有廣泛的瞭解,但是只在這些技術真的非常非常有幫助時才去用它們。Jon 覺得當然各種程式設計的進階議題是一定要知道的,這確實是基礎,不過這不代表「有機會就該用上」;當人們學到一個新東西的時候,往往看到的只有這個新東西的好處,但是好處背後所隱含的一些不利因素,卻很難在當下就馬上有辦法衡量,而經驗上來說通常壞處都會大過好處。程式技術、軟體開發的方法、名詞琳瑯滿目,從「知道」到「真的瞭解」是漫長的過程,而且常常使人犯下「明知故犯」、「知行不一」的毛病。
※ ※ ※
後面 Q&A 有部份是針對 Braid 或 Jon 本人提的問題,和前面整篇文章的主旨關聯不大,整理起來不太順,包括有人問它 Humble Indie Bundle 和 Steam 比較的話哪個通路賣得比較好,或是 Braid 的一些設計理念,覺得 DLC 重不重要之類的。所以對於這幾點有興趣的話可以自行稍微聽一下演講錄音檔後半段 Q&A。
Q&A 中 Jon 有提到的幾個重點:
把握「現在夠好就好了」的原則很重要,不過,確實永遠都有某些片段你到最後非最佳化不可,譬如說對於 Braid 的話,就是「時間倒轉」系統的設計。印象沒錯的話 Jon 在 2010 年的 GDC 有講這個主題,我有機會會把這個主題略作研讀並寫寫筆記。
有人提問 Braid 會不會 open source,Jon 的回答是「也許」。不過他覺得現在網路上資源這麼多,而且開發的時空環境背景不同,就算釋出 Braid 的 source code 其實對大家沒有什麼幫助,除非有人想做和 Braid 幾乎一模一樣的遊戲;而且他又還找不到時間做這些額外的工作。
Jon 對 C++ 的看法是,業界很多人使用 C++ 是因為逼不得已,也有人問那 C# 與 XNA 呢?Jon 說就他瞭解絕大多數 XBLA 遊戲仍主要靠 C++ 來寫,因為審核規定實在太嚴格,C# 與 XNA 主要都被用在 XBLIG,這些是沒有技術審查的(他在此沒就現在的 PC Game 或其他 Mobile Game 部份做太多描述,在原網址回應中他也說他並不熟悉 Unity 之類的技術,他只說不對 Unity 做大改寫的話,大概也做不出 The Witness,原因我就不推敲了 :p)。
他和他認識的很多朋友其實只把 C++ 當成「可以在 struct 裡面放 function 的 C」;而 C++0x 他更是覺得太瘋狂了,雖然有少數功能確實是大家希望的改變。他提到很多語言本身擁有的功能、程式設計的技巧、程式架構上的準則,其實都是為了多人合作而存在的。如果你今天就是一個人寫程式,或最多兩、三個人,那些功能、技巧、架構其實都是多餘的,特別 indie game 的重點是如何用少數人在相對短的時間內完成一個中~大型的專案。Jon 在此講了一句話,我引用原句:
"Optimize the game development process to produce the highest quality game in a reasonable amount of time"
當然 quality 和 reasonable 這兩個字眼在缺乏背景設定的情況下,是很模糊的,但至少我們可以確定 the highest quality game 當然不等同於 the highest quality program(此指我們通常認知的「程式設計」)。
不要過早最佳化、不要過度設計、不要過度blahblah,這些字眼聽在一般寫程式的人耳裡是理所當然的,因為字眼上本來就存在過度兩個字,所以甚至連不寫程式的人都知道這不好。但是在整個 talk 中 Jon 很明顯是認為,對於 Computer Science 出身的學生,這些台詞必需要反複強調。
Jon 也指出他不論在 Braid 或 The Witness 中,沒用過超過三層的繼承樹;只在 Array 上使用 C++ Template(前面也提到他幾乎只用 arrays of structs 來當資料結構)。
另外,Jon 提到在 Braid 之前他也是有不少不怎麼成功的開發過程,而歸納起來當初設定的目標很多都是錯的,或規模與技術難度太高的,譬如說用電腦視覺的技術想衍生出一個好玩的遊戲,結果最後卻卡在處理技術問題上;也有製作過不少 3D 的遊戲,但是同樣很容易搞得太複雜而超過一己之力能掌握的範圍。Braid 之所以成功是因為它有辦法在一個大家已經非常熟悉的系統上,傳遞一個很不一樣的意念;而且光是操縱時間的系統在技術上就夠複雜了,其他「載體」應該越是簡單越好。(Jon 也強調他不是 Mario 迷,只是單純向 platformer 上最成功的作品表達敬意)
※ ※ ※
有個學生提問了很重要的問題,而且這和後來在原網址回應的一些內容有關,故一併整理在最後。
該學生問:「那為什麼現在學校都這樣在教程式設計?」
Jon 的回答是,第一,當然學校教的 Computer Science / Programming 基礎是必備的,但是要不要用,或用得有沒有真得有效是另一回事,不過若沒學過,也不可能去比較到底怎樣比較有效;第二,學校本來就不是在教「Production」的,「專科學校」與「大學院」的導向本來就是不同的(雖然目前遇到的狀況是產學斷層日益嚴重,而且專科學校與大學院的界定也越來越模糊,美國教育體系同樣面臨這個問題);第三,其實學校教的和 Jon 所講的內容並不真的衝突,這是兩個層次的事情:先學會規則,然後懂得忘掉規則(※)。只是學校往往連教完第一個層次都很勉強,遑論去教那種需要經驗累積才能歸納出來的結論。
在原網址回應的討論中,看得出來蠻多也是學生去發問的,譬如說「哪間學校比較好」、「這系這樣排課程對不對」,看了 Jon 的一些說明,原來美國也是很多一窩蜂的「我也有」學校,開很多 Game Design、Digital Media、blah blah 科系。比照台灣現況,好像有稍許寬心的感覺 XD?Jon 認為只有極其少數的學校值得推薦,保險起見還是從 Computer Science 或 Classical Art 等方面切入較好;遊戲的話可以自己找課餘時間做。(FYI,Jonathan Blow 在 UC Berkeley 是雙主修 Computer Science 和英文文學,不過據說沒畢業)
更詳細的討論可以定期回原網址查看。
※ ※ ※
最後還是再把警告標語拿出來唸一下 :p
- 整個演講內容是 Jon 長年累積經驗歸納的心得,裡面也有些許舉例,但是這些舉例也有其時空背景環境,請不要太過著重於他所舉的例子字面上的意義。(或是可以怪我整理的不通順 :~)
- Jon 演講的對象是一群 UC Berkeley 的 Computer Science 學生,這些學生的性質、經驗、能力導向也許與你、我不同。而 Jon 真正的目的是要讓學生瞭解「學術」與「生產/開發」間明確的差異。
- 這場 talk 真的非常、非常 focus 在「獨立遊戲開發」,簡單的說就是通常只有一個人寫程式的狀態。
如果您有親自去聽完演講錄音而與我解讀有些不同的話,歡迎提出討論,感謝!
(※)well,「先學會規則,然後懂得忘掉規則」這個解讀算是我濃縮原句自己掰出來的,因為聽到 Jon 在 Q&A 那段談話,我不得不想起大學時某教授講的話,雖然他實際上的作為是「大家連半套規則都還學不懂(因為他也不會教),就要大家忘掉規則」。