2008年2月29日星期五

《Milo的遊戲開發》搬家了!

因種種原因,本 Blog 現已搬到 http://miloyip.seezone.net/

感謝 seezone 提供網頁空間!

2008年2月27日星期三

Immediate Mode GUI

今晚主要在思考 GUI 的問題。因為之前看 mono 提及 Unity 這個引擎,便去看 Unity 對 GUI 的部份,參考它支持甚麼功能和如何跟 Script 結合。

一看之下,竟然是這種奇異的寫法:
function OnGUI () {
if (GUI.Button (Rect (10,10,150,100), "I am a button")) {
print ("You clicked the button!");
}
}
其原理是,Unity 在一個 frame 裡會執行這個函式最多兩次,第一次 GUI.Button() 會渲染那個按鈕,第二次是處理輸入,如果GUI.Button()被按下,就傳回 true。

後來再在網上找到了相關的討論及短片(我無看足全程),稱傳統的 GUI 系統為 Retained Mode (下簡稱RM),這種系統為 Immediate Mode (下簡稱IM)。

傳統的 RM 做法是建立 GUI 物件的 Hierarchy,之後當有輸入時就通知應用程式 (event-driven)。而 IM 就不需要建立物件,純粹以 procedural 的運行方式顯示及處理反饋。

乍看之下,IM 的函式非常簡單,亦不需要額外的記憶體,好像是一個很好的方案。但再想了一會,相對 RM 的做法好像沒有那麼好:
  1. 其實 IM 和 RM 都要用一定的內存。如果 RM 是用 Script 或描述檔案 (e.g. XML) 來產生 GUI 物件,那段 Script 或描述檔案 在產生 GUI 物件之後就可以釋放,而它所描述的訊息只是轉化為內存的物件吧。所以兩種方法也是包含同樣的資訊,只是形式不同。
  2. IM 的 GUI 是 Stateless 的。因此一些需要 state 的 GUI 物件需要由函式外傳入及取回,例如
    textFieldString = GUI.TextField (Rect (25, 25, 100, 30), textFieldString);
  3. IM 執行效率應比一般的 RM 低。渲染 GUI 是每個 frame 都要處理的,而發生輸入事件的頻率相對非常低。IM 需要每個 frame 運行 Script,而 RM 可以在 C++ 獨立處理 GUI Hierarchy 的渲染,並且處理輸入後才產生 event 通知 Script。但如果 IM 的代碼也是用 C++ 寫的話,情況可能會相反,因為所有 GUI call 都是 static binding,而一般 RM 的做法都需要 virtual function call (例如渲染的函式)。
  4. 要做 IM GUI 的編輯工具比較困難。可能要用 RM 的資料去生成 IM 的代碼,但這樣做的話,連 event handling 的代碼也要從 RM 的資料插進去 IM ,那麼,IM 就好像沒有意義了。這可能就是 Unity 沒有 GUI 編輯工具的原因。
  5. 和第 4 點相關,RM 把物件的狀態描述和行為分離,減低 coupling。而 IM 反而應為兩者結合在一起編程比較容易。這讓我想起用 C 寫 CGI 和用 XML/XSLT 做網頁的日子......
原來這種 IM 模式至少在 2005 年已有人提倡,我真的是孤陋寡聞。

P.S. 今天無代碼上的進展......

2008年2月25日星期一

Lua 的 Unit Testing

今天成功把 Lua 嵌入Engine (其實只是十數行代碼及設定連結等),之後開了一個新 C++ project 執行一個 Lua 檔案去測試 Engine 提供的介面。

在網上找了一會,選擇了 luaUnit 來做 Unit Testing。luaUnit 的好處是使用 Lua 的 "Reflection" (其實只是找 table 吧),自動找尋所有 test suites。使用起來真的很簡單。以下是我做的 unit test。

require('luaunit')

TestVector3 = {}

function TestVector3:setUp()
self.a = Math.Vector3(1, 2, 3)
self.b = Math.Vector3(4, 6, 8)
end

function TestVector3:tearDown()
self.a = nil
self.b = nil
end

function TestVector3:testComponent()
assertEquals(self.a.x, 1)
assertEquals(self.a.y, 2)
assertEquals(self.a.z, 3)
end

function TestVector3:testCopy()
local c = Math.Vector3(self.a);
assertEquals(c.x, 1)
assertEquals(c.y, 2)
assertEquals(c.z, 3)
end

function TestVector3:testNegation()
print(-self.a)
assertEquals(c.x, -1)
assertEquals(c.y, -2)
assertEquals(c.z, -3)
end

function TestVector3:testAdd()
local c = self.a + self.b
assertEquals(c.x, 5)
assertEquals(c.y, 8)
assertEquals(c.z, 11)
end

-- Other operators, skipped here

function TestVector3:testSquredLength()
assertEquals(self.a:GetSquaredLength(), 14)
end

function TestVector3:testLength()
assert(math.abs(self.a:GetLength() - math.sqrt(14)) < 0.00001)
end

function TestVector3:testNormalize()
local c = self.a:GetNormalized();
assert(math.abs(c:GetLength() - 1) < 0.00001)

local d = c * self.a:GetLength()
assert((d - self.a):GetLength() < 0.00001)

local e = Math.Vector3(self.a)
e:Normalize()
assertEquals(c, e)
end

function TestVector3:testDot()
assertEquals(40, Math.Vector3_Dot(self.a, self.b))
end

LuaUnit:run()

執行結果
>>>>>>>>> TestVector3
>>> TestVector3:testAdd
>>> TestVector3:testComponent
>>> TestVector3:testCopy
>>> TestVector3:testDivide
>>> TestVector3:testDot
>>> TestVector3:testEquality
>>> TestVector3:testInequality
>>> TestVector3:testLength
>>> TestVector3:testMultiply
>>> TestVector3:testNegation
Error in operator - expected 1..1 args, got 2
stack traceback:
.\luaunit.lua:288: in function <.\luaunit.lua:287>
[C]: ?
main.lua:29: in function 'testNegation'
[string "TestVector3:testNegation()"]:1: in main chunk
[C]: in function 'xpcall'
.\luaunit.lua:292: in function 'runTestMethod'
.\luaunit.lua:309: in function 'runTestMethodName'
.\luaunit.lua:337: in function 'runTestClassByName'
.\luaunit.lua:369: in function 'run'
main.lua:95: in main chunk
Failed
>>> TestVector3:testNormalize
>>> TestVector3:testSquredLength
>>> TestVector3:testSubtract

=========================================================
Failed tests:
-------------
>>> TestVector3:testNegation failed
Error in operator - expected 1..1 args, got 2
stack traceback:
.\luaunit.lua:288: in function <.\luaunit.lua:287>
[C]: ?
main.lua:29: in function 'testNegation'
[string "TestVector3:testNegation()"]:1: in main chunk
[C]: in function 'xpcall'
.\luaunit.lua:292: in function 'runTestMethod'
.\luaunit.lua:309: in function 'runTestMethodName'
.\luaunit.lua:337: in function 'runTestClassByName'
.\luaunit.lua:369: in function 'run'
main.lua:95: in main chunk

Success : 92% - 12 / 13

找到錯誤


Oh! 竟然有一個錯誤! (當然是debug了其他錯誤後還有一個不能解決,而不是第一次寫 Lua 就沒錯)

找了一會,發現有文章講述 Lua 5.1 在呼叫使用者定義的 unary minus operator (__unm) 時傳入兩個 self 的 operands。因此 SWIG 會發生問題。

原本只是想用來測試系統架構及嘗試寫 unit test,今次,又再次證實 unit testing 真的是很有用啊。又想起這句名言
「唔試過又點知佢work呢?」
後話

Vector3 在 Lua 的語意上是 reference type,c = a 是指兩個變數都是指著同一個物件。要寫 c = Math.Vector3(a) 才能做 copy。這可能會很麻煩而且容易出錯。Lua又好像不能 overload assignment operator。有無甚麼好提議呢?

此外,static member function 像 Dot() 好似不太好用。還是用 % 及 ^ 做 dot 及 cross product 吧。這兩個 operator Lua 都支持。

2008年2月24日星期日

混合語言的遊戲開發系統架構

用甚麼程式語言來做軟件是一個大問題,思考了一個周末,現時想做一個混合語言的遊戲開發系統架構。暫時只考慮三種程式語言: C++、C# 及 Lua。以下首先分析這三種語言的特性。

C++

C++是一個 strongly typed、static、multi-paradigm (procedural, object-oriented, meta-programming) 的語言。基本上是遊戲引擎的標準語言,所以其實沒有甚麼第二選擇。

優點
  • 高移植性: 所有遊戲平台都提供 C++ 工具 (除了一些嵌入式系統,如只提供 C 或 Java)
  • 高效率: C++ 是通用語言中最高效的,無論是時間和空間上。
缺點
  • 程式庫不足: C++ 的標準程式庫是「簡而清」的,其實這是優點也是缺點。因為 C++ 本身可以在不同的應用層面及系統上,所以標準庫不可能加入如平台相關的 GUI、thread/process等程式庫,或應用相關的 image processing、encryption等功能。就是這樣,C++才會產生五花八門的第三方程式庫。經驗告訴我,要選擇、整合和維護第三方程式庫是不容易的。有時候不同的程式庫會在有兼容的問題,也會做成不協調(命名、記憶體管理等等)、或是功能上不能完全滿足需求。解決方法只有兩個──直接由程式庫的源代碼修改並嵌入系統裡、或放棄使用程式庫自己重做。
  • 高難度: 使用 C++ 的難度實在太高。其中一個原因是 C++ 的高自由度,你可以用不同的組合方式實現一個功能。另外是 C++ 接近低階,很多細節要程式員非常小心去處理,例如資源管理、pointers等等。要編寫及維護高質量的 C++ 程式是非常困難,看看剛畢業的人加入團隊時要花多少時間及培訓就可見一二了。
  • 編譯慢: C++ 源至 C,採用 #inlcude "header" 這種傳統文字方式去引用其他功能。就算採用了 pre-compiled headers 或 distributed compilation (如使用 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 (e.g. DLL) 的資料來做編釋,節省很多時間 (包括 C++ 編釋時讀取及生成檔案的大量 I/O 時間)。
  • 中難度: 相對寫 C++ ,寫 C# 的人可能會長壽一點 (^_^)。C#代碼較簡潔美觀、不用分header/implementation 檔 (.h/.cpp)、不用那麼著緊資源管理 (雖然還是應該注意的)、不用調試release版本時看 assembly...
缺點
  • 跨平台問題: Microsoft 暫時沒有提供跨平台的 .Net,這是可以理解的。但理論上,.Net是應可以和 Java 一樣做到跨平台。開源的 MonoDotGNU 項目就是提供跨平台的 .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。

後記

原來只是想記錄一些想法,卻發現寫這篇日記花了多個小時。希望除了作為一個記錄,也可以引發大家一起討論,那麼也可能是值得的。

2008年2月22日星期五

Unreal 3 at GDC08

Tim Sweeney 在 GDC 介紹了 Unreal 3 的新功能。



Ambient Occlusion

因為說明了是Post-rendering Effect,即是Screen Space Ambient Occlusion (SSAO)。這並不算是很新的技術,已出版的遊戲Crysis也有這個技術。好處是不用修改現有的Rendering Pipeline,只要有Depth或Position buffer,再加上normal的buffer就可以模擬Ambient Occlusion。

Crowd Simulation

他說用了「Flocking Technology」做 Crowd Simulation。近畫面的人物移動時有一些會因為路面變窄而有停頓,但人與人之間有避免碰撞,行為做得不錯。而 Rendering 方面應該是一般的instancing,在xbox360許多遊戲也做到相同的效果。

Fluid Surface Simulation

估計是由原來的水波模擬(e.g. FFT + noise)再加上可互動的height map。這應該是可以用簡化的 Navier-Stroke 方程在 GPU 做到,要儲存height及velocity map,再用 height map 生成 normal map。我諗會難點在於如何控制那張map的resolution及位置,對於大面積的水面可能要不斷不移這些map。

Soft Body Dynamics

這是由AGEIA物理引擎提供的,基本上是純粹功能整合。

Frature System

我諗為這是比較突出的部份,相信最困難是如何在場境編輯時設定。

總結

最後,一如以往,Unreal Engine 給予我的感覺是技術運用的很好的一個產品。雖然在技術上並不會很突破,但整合及工具方面都做的非常好。目前能與它比較的也許只有Cry Engine。

每次看到一些技術就會心癢,希望可以盡快做一個 test bed 出來去做不同的嘗試。

試驗SWIG (一) C++ 連接C#和Lua

今晚初次嘗試使用 SWIG (Simple Wrapper Interface Generator)。SWIG是一個能為C++程式生成各種Script語言Wrapper的工具。簡單地說,就是有了一個C++程式,用SWIG來連結這個程式和Script,使腳本語言可以呼叫C++的函數及類別等等。

SWIG支持大部份C++的功能,甚至template也能支持。同時可以生成十數種腳本語言的wrapper,包括我正在考慮使用的Lua、Python和C#等。

跟據我的目標,先做一個3D Vector的測試。以下是一部份C++代碼 (Vector3.h):

#pragma once
#include

struct Vector3 {
Vector3();
Vector3(const Vector3& v);
Vector3(float x, float y, float z);
~Vector3();

Vector3 operator+(const Vector3& v) const;
Vector3& operator=(const Vector3& v);
Vector3& operator+=(const Vector3& v);
Vector3& operator-=(const Vector3& v);
Vector3& operator*=(float rhs);
Vector3& operator/=(float rhs);

bool operator==(const Vector3& v) const;
bool operator!=(const Vector3& v) const;

float GetSquaredLength() const;
float GetLength() const;
Vector3 GetNormalized() const;
void Normalize();

float x, y, z;
};

inline Vector3::Vector3(const Vector3& v) : x(v.x), y(v.y), z(v.z) {
}

inline Vector3& Vector3::operator+=(const Vector3&amp; v) {
x += v.x;
y += v.y;
z += v.z;
return *this;
}

inline Vector3&amp; Vector3::operator*=(float rhs) {
x *= rhs;
y *= rhs;
z *= rhs;
return *this;
}
// ...


之後寫一個SWIG的interface檔案 (Math.i):

%module Math
%{
#include "Vector3.h"
%}

%rename(Add) Vector3::operator+;
%rename(AddEqual) Vector3::operator+=;
%rename(MultiplyEqual) Vector3::operator*=;
// ...

%include "Vector3.h"


最後幾行是因為,以我所知,SWIG對C#並不支持operator overloading。SWIG本身是支持operator overloading的,或許要深入研究一下SWIG的Wrapper/Proxy生成過程可不可以加入這功能。

C#的測試

做C#的時候,用swig -c++ -csharp -o Math_wrap.cxx Math.i,就會新成三個檔案,分別為用來做C++ Wrapper的Math_wrap.cxx、用來做C# Proxy的MathPInvoke.cs和Vector3.cs。

接著就把Math_wrap.cxx編成DLL,而MathPInvoke.cs和Vector3.cs就可以放到C#的Project裡使用。

剛看到GDC的XNA演講提及.Net的速度,就直接用來比較一下。使用了SWIG生成的Proxy,使用Vector3類別和C#寫的沒有分別:

static void Main(string[] args)
{
Vector3 Position = new Vector3(0, 0, 0);
Vector3 Velocity = new Vector3(10000, 10000, 10000);
const float Friction = 0.9f;

const int count = 10000000;
System.DateTime start = System.DateTime.Now;

for (int i = 0; i < count; i++)
{
Position.AddEqual(Velocity);
Velocity.MultiplyEqual(Friction);
}

System.DateTime end = System.DateTime.Now;
System.Console.WriteLine((double)count / (end - start).TotalSeconds);
}


結果留到最後和Lua一起比較吧。

Lua的測試

之前的Math.i和其他檔案都不用改,直接執行swig -c++ -lua -o Math_wrap.cxx math.i,就會生成Math_wrap.cxx這個proxy。和C#不同,是不需要Lua的Proxy的。把這個連結成Math.dll後,在Lua只需一句Require("Math")就行了!這實在太簡單!(這麼簡單的功能是在Lua 5.1才有)

同樣的測試用Lua來寫:

require("Math")

local Position = Math.Vector3(0, 0, 0)
local Velocity = Math.Vector3(10000, 10000, 10000)
local Friction = 0.9

local count = 1000000

local start = os.clock()

for i = 1, count do
Position:AddEqual(Velocity)
Velocity:MultiplyEqual(Friction)
end

local finish = os.clock()
print(count / (finish - start))


以前看了Lua的書,但沒有寫過,發覺Lua寫起來也很簡單,而且SWIG做的Binding也不錯。最容易錯的地方就是把object:method(...)寫成object.method(...)。

結果和分析

結果是以每秒執行iteration次數計算,數字越大表示越好。






Caseiteration/secrelative performance
C++1,544,163x
C#519,3720.336x
Lua357,6530.232x
XNA on Xbox3603,380,0002.19x
這個比較是有數個問題。一個估計XNA的測試裡會有大量的Particle instances,但我的測試只用了一組local variables,多instance會導致cache miss,應該會慢許多,另外這亦需要用iterator或index去存取instances。另外是我的電腦是Core2 2.4G,應該比XBox360快。再者,XNA應該有使用SIMD指令做優化。

不論這個結果準確與否,在對比C++的結果似乎這個最簡單使用SWIG的Wrapper在速度上還存有改善空間。而C#比Lua快應該是基於它使用JIT,而Lua只是一個interpreter。但就這個例子而言,使用Script的速度能達到直接使用C++的二至三成,已經原全超出我的預期。

接下來我想試一試:
  • 使用C++/CLI做Binding代替SWIG/PInvoke,看看速度的差異。如果能把Vector3當成value type,速度應該會快reference type許多,建立物件的overhead亦會減低 (不需要GC)。如果成功,可以把一部份常用類別用C++/CLI人手編寫,其他就用SWIG。
  • 嘗試寫SIMD優化幾個operators,和XNA的結果比較。
  • 測試SWIG對類別、smart pointer、STL等。

2008年2月20日星期三

目標

最近認為絕大部遊戲內容是應該用Script來寫的,而.Net來做工具似乎是個很好的選擇。我把最近想做到的都列出來吧。

短期目標:
  1. 學習Script在遊戲的應用。
  2. 選擇一個Script Language及Binding方法 (大概是Lua和Swig)。
  3. 先寫個3D Vector的Binding吧。
  4. 用Script做Unit Test。
  5. 做Native和Script的Performance Test。
  6. 再試做它的 .Net Binding (用Swig 的PInvoke或 C++/CLI)。
中期目標:
  1. 做個簡單的Graphics API Wrapper。最近剛做過某Console的簡單wrapper,感覺視野有闊了一點。始終底層都是那些硬件,Graphics API的差異變得較不重要。
  2. 做個簡單的GUI系統。不知做了多少次,十幾年前(?!)做的都沒有screenshots了,不然對比一下它們可能會很有趣。
  3. 用.Net做GUI Editor,Script做Event Handling。
長期目標:
  1. 開發簡單的Game Engine和工具,先針對遊戲內容的製作。
  2. 開發一個遊戲。不一定要完整的,但要有Core gameplay。
這些目標絕對會隨時改變,尤其是得到你們的指導(疏擺...... ^_^)。