Home > 遊戲編程

遊戲編程 Archive

要做 Visual Studio 樣子的使用者介面? 用 DockPanel Suite 吧!

雖然引擎連1%完成度也沒有,今天開始做遊戲的編輯工具,或者應該說是做工具的原型,一切都當作是實驗吧。

DockPanel Suite 的 Sample Screenshot
(這是 DockPanel Suite 的 Sample,不是 Visual Studio 啊! )

之前已決定採用開源的 DockPanel Suite。它是一個 C# 開源的 .Net 程序庫,基本上做到 Visual Studio.Net 的視窗 docking 功能,包括 tabbed window、split window、float window、docking indicator、auto-hide、讀寫 XML 設定檔等等。我是看到 Cg Composer 的 DLL 才知道有這個 .Net 程序庫,後來也找不到更好的選擇。

Continue reading

關於表頭檔的 inline function

閱讀了猴子靈藥《表頭檔要不要?拿速度來換!》後,發覺所言甚是,表頭檔的問題許多時會被忽略。現在寫的代碼,除了考慮執行效率,也應該要考慮編譯效率。

關於在標頭檔寫 inline function 而增加編譯時間,本人有一點看法,就在此和大家分享。

首先,我們知道

Inline function 的目的是增加執行效率,但一般在 debug 版本是不會被展開的,只有在 Release mode 才會用到。

那麼,在編譯 debug 版本中使用 inline function 只會增加編譯的時間。基於這一點,我現時的”引擎” (不知何時才能算是真正的引擎,可以去除引號)的代碼 layout 就使用 macro 去解決這個問題。所有類別的代碼 layout 分為 3 個檔案: header (.h), inline (.inl) 和 implementation (.cpp)。例如:

Continue reading

完成 Win32上的OpenGL/Cg

星期六日都坐在家中做 OpenGL/Cg 的移植,比想像中遇到更多問題,但終於在三月最後一個晚上完成。這裡做一個簡單的回顧,希望大家遇到類似的問題時可以減少脫髮。

編譯期選擇圖像 API

之前曾經寫過一篇《跨平台的圖像渲染引擎》,提及使用 abstract factory pattern 或 conditional compilation (macro) 來為圖像 API 抽像化。後者的優點是效能較高 (不需使用 virtual function call),但缺點是代碼難看。為了改善後者的缺點,我想到了使用 inheritance 去避免在 class 的定義裡寫 macros。舉一個例子就會明白。

首先,利用 #include 後可以是一個 macro 的功能 (Visual C++ 裡只可以是一個 macro identifier,而 gcc 可以 expand macro),把平台相關的 header 和 implementation 檔放在同一個地方:

Continue reading

實時 Radiosity 系統 Enlighten

今天才第一次看到 Geomerics 的 Enlighten 新示範短片(在 GDC2008 發佈),感覺很震撼。雖然在 Radiosity 技術方面和他們一年前的 Tech Demo 的分別可能不大,但結合了 HDR、SSAO 等技術、展示了一些使用者介面即時調效多個光源的設定、人物受到間接光源的效果等等,做出來的整體效果完全不一樣。而且,它說明這技術已實際在 PS3 和 Xbox360 上應用 (雖然沒有影片為證),即是它是可以應用在現今的硬件之上,而不是一些只可期待而不能用的技術。

網站裡介紹了 Enlighten 的工作模式:

  1. 場境需要作一些 pre-processing,但只需要 gemoetry 資料,而不需要 texture、material、lighting 等資料。這和 Precomputed Radiance Transfer (PRT) 的需求一樣。
  2. 使用者的引擎要把場境的 Local Illumination 渲染至 texture space ,交由 Enlighten 計算 Radiosity。
  3. 結合 Radiosity 和 Local Illumination。

使用這種工作模式對美工特別有幫助,因為美工可以即時微調光照效果,而不需使用 Global Illumination 的 Renderer 去花幾十分鐘甚至幾小時去渲染一些只是靜態的 Light Map。我相信除了遊戲之外,電腦動畫及電影特效都應該可以應用這種技術。

要追技術感覺是越來越難。不過我要提醒自己,不要只追求個別的技術,現時需要的應該是做整個遊戲的經驗,gameplay 和相關的工具是我最弱的。

其他類似的技術:

《修改代碼的藝術》

自從看見書評有關一本名為《修改代碼的藝術》(Working Effectively with Legacy Code, by Michael C. Feathers)的書,感覺是工作上很有需要,就急忙走上 amazon訂了這個內地中譯本。昨天收到並讀了幾章,先說一些表面的,看完後再寫讀後感吧。

如同本書的介紹說:

如果不積極地修改、挽救、隨著時間流逝,所有軟件都會不可避免地漸漸變得複雜、難以理解,最終腐化、變質。因此,理解並修改已經寫好的代碼,是每一位程序員每天都要面對的工作,也是開發程序新特性的基楚。

這些話語都像在對我說的。工作上,每天也要去修改一個很大的 code base,看 source code 好像 reverse-engineering 一樣,看到一些代碼甚至會感到沮喪。或許這也是許多這行業的人的經驗吧,可是我卻是非常缺乏這種經驗,因為以前的我比較多是維護自己設計軟件,比較少去修改別人的軟件。

遺留代碼一般所想就是沒有人維護、時代久遠、甚至是很爛的代碼。但作者對遺留代碼的定義和一般所想的很不同:

對我來說,遺留代碼就是沒有編寫相應測試的代碼。

這不是對遺留代碼字面的解釋,而是一個深切的體會。從樂觀的角度看,這定義引申,遺留代碼並不是無藥可救的東西,只是沒有測試程式而已,只要耐心加入測試機制,就可以慢慢地挽救。

作者提供了一連串的修改程式技術,這些技術沒有正統的名稱,所以每個章節的名稱也很長,例如「第22章 要修改一個巨型方法,郤沒法為它編寫測試」。從目錄看到這章的名稱,又令我回想一個月前用了一整天去理解一個幾千行的函數的遭遇。但是,很諷刺地,在這一部份的最後來一章又叫「第24章 當你感到絕望時」。昨天面對代碼而感到無助時,便立刻看了這一章,頓時覺得,我還未到絕望的境地,心態一轉,難題也解決了。

今天讀到 Fake Object 和 Mock Object,開始明白 Edwin 做的 AMOP 有幾偉大。

要寫漂亮的代碼是以往的一個目標,今天覺得,要寫不變成遺留代碼的代碼,是更重要的。如何在遊戲編程中實踐編寫測試是我今天的疑問 (對於圖像、互動的測試等…..),希望看畢本書後有新的體會,再與大家分享。

查看執行文件內容的小工具 Sizer

Sizer 是我無意中發現的一個小工具,可以檢測 EXE 及 DLL 裡的內容,讓你知道裡面的函式和資料各使用了多少空間。

知道了空間的分佈有助優化檔案大小,並且可以找到一些不必要的 dependenices。

使用時,必須生成 PDB 檔,讓那個工具可以取得 symbols。

以下節錄了我現時做的 “引擎”的 DLL 的大小分佈 (佔大部份是 static-link 的 Lua 引擎):

Continue reading

使用 Custom Build Tool 執行 SWIG

今晚完成了近期的第三個目標,加入 Script Module 的 Binding (其實只是一個叫 Runtime 的類別),利用 C# 去執行之前的 Lua 的程序 (test1.lua)。

今早 Edwin 的回應啟發了一些想法,所以著手更改 SWIG 在 Visual Studio 的編譯方法。

現在採用了Visual Studio 的 Custom Build Rule。由於 Lua 和 C# 需要不同的規則,所以要兩個 file extensions,我設定 Lua 的 Swig Interface 檔案為 .li,C# 則是 .ci。

Lua

swig
 -c++
 -lua
 -o $(OutDir)/$(InputName)Lua.cxx
 "$(InputPath)"

C#

swig
 -c++
 -csharp
 -o $(OutDir)/$(InputName)Cs.cxx
 -outdir $(OutDir)
 -namespace Mil.$(InputName)
 -dllimport $(TargetName)
 "$(InputPath)"

生成的檔案會放置於 Debug 或 Release 的輸出目錄,解決之前兩個 configuration 會互相影響的問題。

兩個規則分別生成 $(OutDir)/$(InputName)Lua.cxx 和 $(OutDir)/$(InputName)Cs.cxx,這些檔案需要加入 Project 裡。但因為一個項目不應該同時加入 Debug 和 Release 版的這些檔案,我想到的方法是寫一個 .cpp 去 #include 這些檔案,例如:

// MathCs.cpp
#include "stdafx.h"
#include "MathCs.cxx"

之後再把 Project 的 Include path 加入 “$(OutDir)”,那麼,就可以正確地編譯現時 Configuration 的 Swig 生成檔案。

後來再試驗檔案的 Additional Dependencies 屬性,設定單一檔案仍然不成功。之後我把 .li 和 .ci 裡 include 的檔案改名為 .lii 和 .cii,希望只要可設置 dependencies 為 *.lii 和 *.cii 就可以了,但都不成功。或許可以用 Pre-Build Event 去檢查這些檔案,如比輸出檔案新就 touch 那個 .li 或 .ci。不過未嘗試。

最後,還有一個問題是 C# 的 Proxy 需要編譯多個由 SWIG 生成 .cs 檔案。我找了一回才發現 C# 沒有 #include、也似乎沒有機制可以編譯 *.cs 等多個檔案。最後暫時用 C++ Project 的 Post-Build Event 把適當 Configuration 的檔案拷貝到 C# 的項目裡,再人手加入這些檔案。

copy $(OutDir)\*.cs ..\MilCs

Lua 的渲染測試

今日順利完成3月6日訂下的第二個目標,把昨天的 C# 測試程式用 C++ 及 Lua 實現。圖像輸出和昨天的測試程式一模一樣,就不上傳了。

C++ 負責創建視窗和呼叫 Device::Open(Hwnd),Lua 做渲染的部份。(其實在遊戲中是不會用 Lua 做這麼低階的工作,這裡只是當作測試 Lua Binding。)

除了編寫 Lua 的 SWIG interface 檔案外,今天主要的工作是考慮如何從 C++ 呼叫 Lua 的類別。發覺對 Lua 的語法還是很不熟識。

有興趣的話可以比較以下的C++/Lua代碼和昨天的C#代碼

Continue reading

實作簡單的材質

進度有點緩慢,今天才完成了 3月6日定下的第一個目標。

使用 blinn_bump_reflect.fx 的渲染

今天的學習進度:

  1. 設定 Matrices 及修正相關函式: 現時設定為 column-major。改正相關函式花了很多時間。
  2. 修改 ChamferBox: 由於 ChamferBox 只負責生成一個 Geometry 物件,所以把 constructor 改為一個 function。原本想嘗試做一個 non-member function,但是 SWIG 部份有問題,花了一兩個小時也找不到解決辦法,所以現在改為一個 static member function。
  3. 實作 Material: 使用 HLSL fx 檔案在建構一個 Material (在 Direct3D9 上而已,之後再考慮跨平台的設計)。利用 FX Composer 的 Blinn.fx 測試渲染。
  4. 加入 Texture: 因為 blinn.fx 需要 diffuse texture,又實作了一個 Texture 類別,和 Material 相似,也是從 constructor 讀入一個影像檔案。
  5. 測試 blinn_bump_reflect.fx: 因為這個 Effect 需要 Tangent 和 Binormal,所以實作了
    void GeometryBuilder::ComputeTangentSpace(int texcoordIndex)

    函式從三角形網格計算這些向量,當中參考了這個網站的實作

  6. 加入 CubeTexture: blinn_bump_reflect.fx 需要環境貼圖,因此加入這個類別。並重構 Texture,使 Texture 和 CubeTexture 繼函自 TextureBase。

Continue reading

實作Geometry Builder

geometrybuilder1.png

因為還未決定坐標系統,今天先實作 Geometry Builder。

首先闡述現時的設計:

  • Geometry 類別是一個渲染的單位,儲存平台相關的渲染資訊,例如在 Direct3D9 版本裡包括了 Vertex Declaration、Vertex Buffer(s)、Index Buffer 和 Draw-call 的參數。但它並不包含 World Transform、Lighting 及 Material 等資訊。
  • GeometryBuilder 是用來生成 Geometry 的輔助類別。Geometry Builder 的介面是一個平台無關的,只有 Build() 函式的實現才跟平台相關, Build()是把建立的平台無間資訊轉化為平台有關的 Geometry 內容。
  • Device 類別有一個 DrawGeometry() 函式用來渲染 Geometry 物件。

Continue reading

Home > 遊戲編程

Search
Feeds
Meta

Return to page top