混合語言的遊戲開發系統架構
2010-02-23 01:27
Milo Yip
閱讀(18332)
評論(25)
編輯
收藏
舉報
用什麼程式語言來做軟體是一個大問題,思考了一個周末,現時想做一個混合語言的遊戲開發系統架構。暫時隻考慮三種程式語言: C++、C# 及Lua。以下首先分析這三種語言的特性,之後再提出一個系統架構科案。
三種語言的比較
C++
C++是一個strongly typed、static、multi-paradigm (procedural, object-oriented, meta-programming) 的語言。基本上是遊戲引擎的 de facto 語言,其實沒有什麼第二選擇。
優點
- 高移植性: 所有遊戲平台都提供C++ 工具(除了一些嵌入式系統,如隻提供C 或Java)
- 高效率: C++ 是通用進階語言中最高效的,無論是時間和空間上。
缺點
- 程式庫不足: C++ 的标準程式庫是「簡而清」的,其實這是優點也是缺點。因為C++ 本身可以在不同的應用層面及系統上,是以标準庫不可能加入如平台相關的GUI、thread/process等程式庫,或應用相關的image processing、encryption等功能。就是這樣,C++才會産生五花八門的第三方程式庫。經驗告訴我,要選擇、整合和維護第三方程式庫是不容易的。有時候不同的程式庫會在有相容的問題,也會做成不協調(命名、記憶體管理等等)、或是功能上不能完全滿足需求。解決方法隻有兩個──直接由程式庫的源代碼修改并嵌入系統裡、或放棄使用程式庫自己重做。
- 高難度: 使用C++ 的難度實在太高。其中一個原因是C++ 的高***度,你可以用不同的組合方式實作一個功能。另外是C++ 接近低階,很多細節要程式員非常小心去處理,例如資源管理、pointer等等。要編寫及維護高品質的C++ 程式是非常困難,看看剛畢業的新人加入團隊時要花多少時間及教育訓練就可見一二了。
- 編譯慢: C++ 源至C,采用#include "header" 這種傳統文字方式去引用其他功能。就算采用了pre-compiled header 或distributed build (如使用IncrediBuild 軟體),一個大規模的系統都需要很長的編釋時間。一個Edit-compile-run Cycle 所需要的時間成為開發速度的一個巨大因素。如有一些代碼需要Tweaking,例如一些視覺效果及遊戲内容,用C++ 去做的話實在會浪費太多時間在編釋上。
C#
C# 是為.Net 而設計、 strongly typed、static、object-oriented 的語言。 C# 的文法參考了C++的文法,但作了許多改善。
優點
- 高階的語言功能: C#有許多高階的功能如Garbage Collection、Reflection、Attribute、Serialization等等。在C++ 要做到同樣的功能是可以的,但沒有語言本身的支援寫出來的代碼很不美觀、亦難以維護。
- .Net 的程式庫: .Net 的程式庫可謂包羅萬有,由低階的OS 功能,到公用的Xml / Regular Expression,到應用層面的GUI / Web / Database 都有很好的支援。 API 的設計也做得很好(相對于Windows 上C/C++ 的超大量新舊API)。
- 編譯快: 從C# 編釋至Microsoft Intermediate Language (MSIL) 是很快的。相對于C++ 的#include "header" 方式,.Net 語言采用引用.Net assembly 的meta data來編釋,節省很多時間(包括C++ 編釋時讀取及生成檔案的大量I/O 時間)。
- 中難度: 相對寫C++ ,寫C# 的人可能會長壽一點(^_^)。 C#代碼較簡潔美觀、不用分header/implementation 檔(.h/.cpp)、不用那麼着緊資源管理(雖然還是應該注意的)、不用調試release版本時看assembly...
缺點
- 跨平台問題: Microsoft 暫時沒有提供跨平台的.Net,這是可以了解的。但理論上,.Net是應可以和Java 一樣做到跨平台。開源的Mono項目就是提供跨平台的.Net 方案,包括C#編繹器、Common Language Runtime (CLR) 和.Net Framework Class Library等。早前看過一些Mono 的資料,Mono 很成熟,也支援embedding (可以用C# 做Script Engine),但暫時不考慮這個方案,詳情看後文。
- 與其他程式庫連接配接問題: 雖然.Net Framework Class Library 已經有大量标準的程式庫可供使用。但遊戲軟體必須使用一些特别的程式庫或API,例如DirectX、OpenGL 等。 C#提供P/Invoke及COM Interop機制去使用這些程式庫,但有一定的限制。
Lua
Lua 是一個dynamic、weakly typed 的腳本語言。 Lua 被應用到許多商業遊戲當中,最為人熟悉的例子如Far cry/Crysis 用Lua 來做gameplay、World of Warcraft 用Lua 做使用者接口。這些例子中Lua 版本的API 也開放給玩家來做MOD 或Add-on。
優點
- 輕量: Lua Runtime 加上它的standard library 隻是100KB~200KB 左右。放在記憶體受限的系統也沒有問題(如可攜式遊戲console)。
- 執行快: 一般性而言, Lua 的速度比許多其他腳本語言快。
- 低難度: 由于是loosely typed,編寫腳本比較簡單及容易。
- 動态: Lua 可以用字串生成代碼的執行,也可以在執行期為物加入attribute 及method。這是Lua 與C++/C# 設計上的一個重大分别,但其實這是一個特性,很難說是優點或缺點。
缺點
- 文法: 擁有自成一格的文法,和C/C++/Java/C# 系列的語言很不同。未接觸過Lua 的程式設計人員要花一點時間學習。
- Object-oriented: Lua 語言是沒有制定OO 的支援。但它的設計令使用者可以自行制作一套OO 的系統,并加入一些syntactic sugar 幫助編寫OO 的代碼。
- Unicode: 和C/C++ 一樣,Lua 沒有特别支援Unicode (可能是因為完整的Unicode 支援可能需要很多代碼,使Lua 變大)。要修改Lua 的編釋器代碼,或使用Lua 的Mod 如Lua Plus。
分析
沒有一個程式語言适合做所有的任務。我把以上的資料編成一個簡單的表:

一個遊戲通常會由不同的人員制作,程式設計人員大概可以分為做Technology、Toolset、Gameplay等領域。Technology 指做遊戲引擎核心部份,或客制化第三方的遊戲引擎。Toolset 包括面向不同使用者的軟體工具,從Content pipeline (如彙入彙出檔案)、Asset Management、Level Editor及其他編輯工具等。而Gameplay 是指遊戲内容中的行為部份,可以分為遊戲的核心行為(如人物控制、戰鬥系統),及為個别人物及關卡編寫的行為(如NPC對話、AI、任務、場境中的trigger等等)。
基本上Technology 的部份需要高效、跨平台、和低階API連接配接。基本上隻可以選擇 C/C++。
Toolset 的部份需要許多GUI 的部份,也要因應使用者(美工、關卡設計、音效設計等)的要求迅速改變或加強功能。另外Toolset 要處理不同種類的檔案,現時常見會使用XML 作為中介的檔案(如COLLADA)。以上三種語言其實都可以使用來做toolset。用C++ 的話可以選擇一個GUI API 如Win32、MFC、WTL、wxWindows、Qt 等等、或使用自己開發的GUI API。但是用C++ 開發GUI 的時候,程式設計困難程度比較高,編釋時間亦長。這兩點可能不符合需要經常改變需求的Toolset。如果使用腳本語言來做GUI 的話,就需要一個好的程式庫連接配接及工具。許多時候這些腳本用的程式庫和原來的GUI 程式庫會有版本後滞的問題。是以,我暫時認為用C# 做大部份工具是比較好的選擇。 .Net 提供的GUI (Windows Form) 及XML、Regular Expression 等功能也支援得很好。
而 Gameplay 的部份是經常要改動的。除了純粹改動數值外,還要在行為的處理上改動。對于個别人物和關卡的程式設計可能由關卡設計師負責,他們的程式設計能力可能相對較低。腳本語言比較适合。有一些情況也可以用視覺化的腳本來表達。首先嘗試用 Lua 吧。
設計科案
以下兩張圖是現時對于混合語言的遊戲開發系統架構的設計。
遊戲執行期
遊戲開發期
兩張圖的橙色部份都是由Swig 生成的模組。
這個設計是基于以上的分析,把Technology、Toolset和Gameplay的部份用各自最适合的語言來實作。
為了跨平台的需求,在執行期的版本是沒有C#的部份,隻使用Lua 的輕量腳本Runtime。
C++ Engine 設定為執行期和開發期的共通部份,是以把需要用C++ 的Toolset 部份抽取了出來。
而在遊戲開發期中,希望可以在工具裡直接運作及調試遊戲,是以會同時有三個語言的執行環境。
混合語言的優點
- 各取所長: 透個組合各個語言的優點,可以增強開發的效率及最終的遊戲品質。
- 适合不同的開發者: 做Technology、Toolset 和Gameplay 的開發人員在技能方面有所不同。不同語言各自照顧他們的需要。
- 低藕合性(Coupling): 不同語言的中間有一個獨立的接口,使藕合性降低。例如同樣的Gameplay代碼可以在不同版本的引擎運作。
混合語言的缺點
- 更多知識: Technology 的程式設計人員應該需要學習這三種語言,及相關的連接配接事宜。
- 多個接口: 需要建立及維護多個接口。希望 Swig 可以減輕這個問題。
- 調試困難: 調試時誇越一個語言可能會增加調試的難度。
其他設計科案
在搜集資料的時候,看見可以把Mono 的CLR 嵌入程式中,就好像把C# 用來做腳本語言。連接配接的實作方式類似于P/Invoke。用Mono 作為Gameplay 的腳本引擎是很吸引的。在效能上,由于采用Just-in-Time (JIT) 把MSIL 翻譯至native code,執行速度一定會比直譯式的腳本快。 C#在文法上跟C++/Java相似,許多人會懂得使用。此外,Mono 利用CLR 的特性,還可以支援其他語言。
這個方案的其中一個問題是它不太Light-weight。或許可以删掉大部份的程式庫及JIT來改善。另一個想法是,透過另一種語言的結合,可以引證系統的可延展性。基于可延展性,在将來也可以考慮加入用Mono來做Scripting。是以暫時采用 Lua。
後記
原來隻是想記錄一些想法,卻發現寫這篇日記花了多個小時。希望除了作為一個記錄,也可以引發大家一起讨論,那麼也可能是值得的。
我後來使用這架構開發 Mil Engine,但在開發中,我放棄使用.Net 的 XML 庫,而采用C++的 rapidxml 開源庫。之後會介紹陸續介紹 Mil 和把它開源。
其實一般遊戲引擎都會使用腳本語言,本文旨在分析及設計。而使用.Net來做ToolSet并非主流商用引擎的選擇,但在Mil 中也證明是一個很好的選擇。
本文原來是繁體中文,在2008-02-22發表于http://miloyip.seezone.net/?p=7,本文經過修正。
- 分類 遊戲引擎