余小華,鐘紹勇
(華南理工大學(xué) 廣州學(xué)院 計(jì)算機(jī)工程學(xué)院,廣東 廣州510800)
游戲引擎[1,2]的使用雖然為游戲開(kāi)發(fā)帶來(lái)了便利,但是同時(shí)也帶來(lái)了另外一些問(wèn)題,例如由于封裝帶來(lái)的靈活性不足。游戲引擎決定了游戲程序里面的一切規(guī)則、效果實(shí)現(xiàn)都是由游戲引擎提供。正因?yàn)橥耆蕾囉谟螒蛞娴膶?shí)現(xiàn),所以開(kāi)發(fā)者失去了對(duì)程序自主地進(jìn)行進(jìn)一步優(yōu)化的權(quán)利[3]。目前游戲引擎解決的思路在于兩點(diǎn):接口要統(tǒng)一、實(shí)現(xiàn)可修改[4]。因此,本文提出了一種組件化的Ostrich游戲開(kāi)發(fā)框架并進(jìn)行了研究,致力于設(shè)計(jì)一個(gè)基于 “組件式”的游戲開(kāi)發(fā)框架,用于解決傳統(tǒng)游戲引擎所帶來(lái)的問(wèn)題。
游戲引擎的研發(fā)是一個(gè)相當(dāng)巨大的軟件工程,而Ostrich游戲開(kāi)發(fā)框架則是致力于定義一套盡量兼容游戲引擎所有功能的接口標(biāo)準(zhǔn),因此Ostrich也是一個(gè)相當(dāng)大的軟件項(xiàng)目。鑒于篇幅所限,本文主要挑選了以下模塊進(jìn)行設(shè)計(jì),并實(shí)現(xiàn)了圖形引擎模塊:①加載器,用于加載Ostrich的組件,是Ostrich開(kāi)發(fā)框架的核心內(nèi)容;②數(shù)學(xué)庫(kù),提供常用的3D 數(shù)學(xué)運(yùn)算;③圖形渲染,提供了圖形渲染相關(guān)的功能接口;④渲染組件,用于實(shí)現(xiàn)圖形渲染接口集合的組件;⑤示例程序,用于展示通過(guò)Ostrich游戲開(kāi)發(fā)框架所開(kāi)發(fā)的程序。
通過(guò)以上的工作,本文將會(huì)展示Ostrich開(kāi)發(fā)框架如何簡(jiǎn)化游戲開(kāi)發(fā)的過(guò)程以及Ostrich的 “組件式”思想對(duì)于游戲開(kāi)發(fā)的意義。除了對(duì)以上內(nèi)容進(jìn)行研究和設(shè)計(jì)之外,為了更好對(duì)引擎整體架構(gòu)進(jìn)行優(yōu)化,本文亦會(huì)研究關(guān)于設(shè)計(jì)模式在引擎研發(fā)當(dāng)中的應(yīng)用。
在3D 游戲的開(kāi)發(fā)當(dāng)中,例如游戲場(chǎng)景的創(chuàng)建、渲染,對(duì)象之間的碰撞檢測(cè),動(dòng)畫的渲染等,都需要大量運(yùn)用到諸如向量、矩陣、線性變換、空間解析幾何、四元數(shù)等數(shù)學(xué)知識(shí)。限于篇幅所限,本文只介紹有關(guān)向量的知識(shí)。
在3D 數(shù)學(xué)里面,向量是兩個(gè)坐標(biāo)點(diǎn)的差值,具有方向和大小,所以只需要給定兩個(gè)點(diǎn)就能夠確定唯一的一個(gè)向量[2,9]。例如在三維空間上存在兩個(gè)點(diǎn)A 和B,他們的坐標(biāo)分別為A (x1,y1,z1),B (x2,y2,z2),那么從A 指向B的向量v可以表示為
v的長(zhǎng)度又稱為模,是一個(gè)非負(fù)的標(biāo)量,用于表示A到B之間的距離:
如果向量的長(zhǎng)度為0,則稱該向量為零向量,零向量是向量里面的一個(gè)特例,它沒(méi)有方向,或者可以說(shuō)它指向任何方向。然而在對(duì)向量的研究里面,有時(shí)候只需要關(guān)心它的方向而不需要關(guān)心它的大小,在這個(gè)情況下使用單位向量則非常方便。所謂單位向量,就是長(zhǎng)度為1 的向量,因此單位向量也被稱為標(biāo)準(zhǔn)化向量或者法線。對(duì)于一個(gè)非零的向量v,都能夠計(jì)算出一個(gè)與它方向相同的單位向量vnormal,這個(gè)計(jì)算的過(guò)程稱作向量的標(biāo)準(zhǔn)化
此外,向量和標(biāo)量一樣,也有其自身的運(yùn)算法則:
向量加法:v1+v2=(v1x+v2x,v1y+v2y,v1z+v2z)
向量標(biāo)量乘:k·v=(kvx,kvy,kvz),其中k為標(biāo)量
向量的標(biāo)量積:v1·v2=v1xv2x+v1yv2y+v1zv2z
向量積:v1×v2=(v1yv2z-v1zv2y,v1zv2x-v1xv2z,v1xv2y-v1yv2z)
每一條運(yùn)算法則,都有其對(duì)應(yīng)的幾何意義。加法法則代表向量的 “三角形法則”;向量的標(biāo)量乘代表向量在原有方向上的拉伸;向量的標(biāo)量積用來(lái)表示兩個(gè)向量的相似程度,結(jié)果越大表示兩個(gè)向量越相近,如果結(jié)果為0則代表兩個(gè)向量互相垂直;向量的叉乘積代表一個(gè)垂直于原來(lái)兩個(gè)向量所構(gòu)成平面的向量。
與游戲引擎相似,Ostrich需要對(duì)游戲引擎或其子模塊的功能進(jìn)行整理和封裝,形成一個(gè)更高層次的抽象接口集合,讓程序開(kāi)發(fā)人員能夠真正地按照一個(gè)統(tǒng)一的接口標(biāo)準(zhǔn)去進(jìn)行游戲開(kāi)發(fā),而不必要關(guān)心接口標(biāo)準(zhǔn)的實(shí)現(xiàn),如果需要對(duì)接口功能進(jìn)行優(yōu)化,則只需要針對(duì)接口所在的組件進(jìn)行優(yōu)化,無(wú)需要改動(dòng)應(yīng)用程序甚至重新編譯。由此可以看出,Ostrich的整體引擎架構(gòu)和游戲引擎相似,但是由于Ostrich采用了組件化的思想,因此模塊劃分的粒度不能太小,不能像游戲引擎那樣可以細(xì)分到例如場(chǎng)景管理、光照管理等模塊。因此經(jīng)過(guò)了初步的研究和分析,整個(gè)框架的基本架構(gòu)如圖1所示,圖1描述了整個(gè)游戲框架的初步架構(gòu),其中用于被組件實(shí)現(xiàn)的是中間的接口層 (ostrich interface)。不難看出,除了模塊的粒度和游戲引擎不同,Ostrich在整體架構(gòu)上沒(méi)有過(guò)多的分層,功能模塊之間大多數(shù)都是處于一個(gè)平行的關(guān)系。為了滿足OCP原則—— “對(duì)擴(kuò)展開(kāi)放,對(duì)更改封閉”[5-7],因此Ostrich 提供了Extension的高層模塊,用于通過(guò)腳本引擎或者擴(kuò)展器來(lái)擴(kuò)展或接合基于Ostrich所開(kāi)發(fā)的高層應(yīng)用,例如針對(duì)格斗游戲的打斗模塊、針對(duì)角色扮演游戲的對(duì)話管理模塊等。若高層模塊是基于Ostrich框架所開(kāi)發(fā)的,那么它將會(huì)和Ostrich有著相同的可移植性,但如果采用其它的API開(kāi)發(fā),可能移植性會(huì)有所影響。
圖1 Ostrich游戲開(kāi)發(fā)框架基本架構(gòu)
另外,Ostrich是基于C++語(yǔ)言所開(kāi)發(fā)的框架,與Java、.NET 等語(yǔ)言不同的地方在于,它生成的是本地代碼而不是托管代碼,因此它僅能編譯跨平臺(tái)而不能夠在運(yùn)行時(shí)跨平臺(tái)。因此Ostrich的應(yīng)用程序如果移植到其它平臺(tái)的話需要重新編譯,但無(wú)需要關(guān)心應(yīng)用程序的代碼的改動(dòng)。但是組件由于是獨(dú)立于Ostrich去實(shí)現(xiàn),采用的API(例如DirectX)可能不支持其它平臺(tái),因此在開(kāi)發(fā)時(shí)應(yīng)盡量采用平臺(tái)無(wú)關(guān)的API或者庫(kù)去實(shí)現(xiàn),例如STL、OpenGL等,或者可以通過(guò)使用C++的宏對(duì)不同操作系統(tǒng)識(shí)別,針對(duì)不同的系統(tǒng)采用不同的API進(jìn)行編譯,就可以解決這個(gè)問(wèn)題[8]。
圖2為基于Ostrich 所開(kāi)發(fā)的應(yīng)用程序的架構(gòu)。基于Ostrich的游戲程序在架構(gòu)上存在明顯的分層關(guān)系,從上到下分別是游戲應(yīng)用程序?qū)?(game application),Ostrich框架(interface),加載器 (loader),組件配置文件 (configuration file),框架組件 (component)。其中,Ostrich 框架在整個(gè)應(yīng)用程序的架構(gòu)里面充當(dāng)著一個(gè) “外觀模式” (facade),它為框架的實(shí)現(xiàn)提供了一個(gè)高層的抽象接口集合,這個(gè)接口集合使整個(gè)框架更加易用[9,10]。
2.2.1 數(shù)學(xué)庫(kù)模塊
圖2 應(yīng)用程序架構(gòu)
數(shù)學(xué)庫(kù)模塊主要為Ostrich框架提供基本的數(shù)學(xué)運(yùn)算。主要包括了通用數(shù)學(xué)運(yùn)算、矩陣運(yùn)算、向量運(yùn)算、角度的轉(zhuǎn)換和四元數(shù)運(yùn)算等。詳細(xì)的類的劃分見(jiàn)表1。
表1 數(shù)學(xué)庫(kù)模塊功能
2.2.2 加載器模塊
加載器是Ostrich開(kāi)發(fā)框架的核心組成部分,用于加載框架所使用的組件 (例如dll文件),類似于.NET 和Java語(yǔ)言里面的反射功能。將組件加載到程序當(dāng)中并且用于實(shí)現(xiàn)框架接口。具體的功能見(jiàn)表2。
表2 加載器模塊功能
2.2.3 圖形渲染模塊
圖形渲染模塊提供了游戲開(kāi)發(fā)框架里面所有關(guān)于圖形渲染內(nèi)容的接口。主要用于模型管理、場(chǎng)景管理、動(dòng)畫、特效等。圖形渲染模塊對(duì)應(yīng)的是游戲引擎當(dāng)中的圖形引擎模塊 (或者子系統(tǒng)),因此圖形渲染模塊也是具有相當(dāng)?shù)膹?fù)雜性。因此在此處完成的功能見(jiàn)表3。
表3 圖形渲染模塊功能
3.1.1 工廠生成類
工廠生成類 (ostModuleFactory)是Ostrich開(kāi)發(fā)框架里面加載器的重要組成部分。它的作用是對(duì)加載器整體操作流程進(jìn)行一個(gè)封裝,從配置信息的加載到模塊信息的載入提供了一個(gè)簡(jiǎn)單的操作。ostModuleFactory的結(jié)構(gòu)如圖3所示。從圖中可以看出,ostModuleFactory是一個(gè)抽象工廠模式和單件模式相結(jié)合的一個(gè)類,它既擔(dān)當(dāng)了Ostrich框架組件加載、生成的功能,同時(shí)也為了節(jié)約系統(tǒng)性能,禁止重復(fù)實(shí)例化工廠類。ostModuleFactory的功能見(jiàn)表4。
表4 ostModuleFactory的功能
前面說(shuō)到,ostModuleFactory封裝了加載器和配置信息管理的類,ostModuleFactory的工作流程實(shí)質(zhì)上就是它與其它兩個(gè)類的共同工作流程。圖4給出了ostModuleFactory的工作流程。
3.1.2 配置信息類
配置信息類是用于對(duì)配置文件的加載和讀取。它的結(jié)構(gòu)如圖5所示。
ostConfiguration和ostModuleFactory是一個(gè)組合的關(guān)系,ostConfiguration是工廠類的一個(gè)組成的部分,它為工廠類在實(shí)際執(zhí)行過(guò)程中提供了配置文件解釋的功能。ost-Configuration的功能見(jiàn)表5。
圖3 ostModuleFactory的結(jié)構(gòu)
圖4 ostModuleFactory工作流程的時(shí)序圖
圖5 ostConfiguration結(jié)構(gòu)
表5 ostConfiguration的功能
3.1.3 加載類
加載類ostLoader的作用在于負(fù)責(zé)整個(gè)加載器里面最核心的加載工作。與ostConfiguration一樣,它和ostModule-Factory構(gòu)成的是一個(gè)組合關(guān)系,它就是為工廠類加載功能的實(shí)際提供者。ostLoader的結(jié)構(gòu)如圖6所示。
圖6 ostLoader的結(jié)構(gòu)
加載類ostLoader主要提供的功能見(jiàn)表6。
表6 ostLoader提供的功能
ostLoader本身的實(shí)現(xiàn)采用了Windows的API函數(shù)。因此相比其它模塊,它的可移植性有所影響,在日后更新的版本里面將會(huì)完善這一問(wèn)題。
3.2.1 場(chǎng)景管理類
場(chǎng)景管理類是提供對(duì)三維世界場(chǎng)景進(jìn)行管理的類,其主要功能見(jiàn)表7。
表7 ostSceneManager功能
由于場(chǎng)景管理類是圖形引擎的一個(gè)重要組成模塊,因此它的函數(shù)全部都是純虛函數(shù),也就是這個(gè)類是一個(gè)純接口。不提供實(shí)質(zhì)的功能,只提供一系列功能的標(biāo)準(zhǔn)。
3.2.2 實(shí)體類
實(shí)體類用于創(chuàng)建及存放三維空間內(nèi)模型實(shí)體的信息,其包含功能復(fù)雜,目前僅能提供兩個(gè)抽象接口,功能見(jiàn)表8。
表8 ostEntity功能
這里加載的材質(zhì)腳本沒(méi)有固定的格式,選用不同的組件就會(huì)有不同的要求。如果采用Ogre引擎來(lái)實(shí)現(xiàn)Ostrich,那么這里的材質(zhì)腳本格式就是和Ogre的腳本一致。
3.2.3 光照類
光照類的作用在于為三維空間提供光源用以照明。經(jīng)過(guò)分析后暫時(shí)得到的功能見(jiàn)表9。
表9 ostLight的功能
3.2.4 鏡頭類
鏡頭類用于觀察三維空間,目前為止所設(shè)計(jì)到的功能見(jiàn)表10。
表10 ostCamera的功能
3.2.5 簡(jiǎn)單的圖形引擎組件實(shí)現(xiàn)
(1)編寫組件的必要步驟:在編寫組件之前,首先第一步需要確定組件的設(shè)備名和組件名。所謂的設(shè)備名就是指組件對(duì)應(yīng)Ostrich框架里面哪個(gè)模塊,是一個(gè)固定、唯一的名字,如果當(dāng)前編寫的組件的渲染組件,則采用GrapicRender作為設(shè)備名;而組件名則可以是一個(gè)自定義的名稱,例如可以改名叫 “MokeyRender”等等。然后確定了組件名后,就需要定義兩個(gè)函數(shù),它是一個(gè)出口函數(shù),它用于返回生成類的工廠函數(shù),而且命名必須是 “組件名_CREATOR ()”。而工廠函數(shù)的名字沒(méi)有限制,但是返回類型必須要是void*,而參數(shù)則需要根據(jù)實(shí)際情況,例如圖形渲染組件則需要引用框架的GRAPHICRENDER_ENUM 作為判斷依據(jù)的參數(shù)。最后,編寫配置文件,其格式如下:
之后在程序調(diào)用配置文件,加載器就會(huì)自動(dòng)加載模塊。
(2)組件具體的實(shí)現(xiàn)方法:組件的實(shí)現(xiàn)方法可以自行編寫,只需要符合Ostrich框架的接口要求就可以。組件程序引用Ostrich提供的頭文件之后,自行編寫類用于實(shí)現(xiàn)接口,然后編寫出口函數(shù)和工廠函數(shù),需要注意的是工廠函數(shù)需要符合指定組件的工廠函數(shù)的函數(shù)指針的規(guī)則。
Ostrich框架本身的設(shè)計(jì)就是為了游戲開(kāi)發(fā),因此還需要對(duì)框架是否能構(gòu)建游戲進(jìn)行測(cè)試。在本次測(cè)試中,我們將選擇OGRE圖形引擎作為渲染組件按照Ostrich的接口標(biāo)準(zhǔn)進(jìn)行封裝。并且編寫如圖7所示應(yīng)用程序。
編譯調(diào)試之后,運(yùn)行程序,得到如圖8的效果。
圖8所渲染出來(lái)的就是代碼中所寫到的兩個(gè)實(shí)體,并且不難看出兩個(gè)模型都照有全局光。因此可以看出,Ostrich框架確實(shí)能夠?qū)崿F(xiàn)組件化的架構(gòu)來(lái)搭建游戲程序。
圖7 渲染模擬
圖8 渲染模擬
本文通過(guò)對(duì)現(xiàn)有游戲引擎靈活性不足的問(wèn)題進(jìn)行了簡(jiǎn)要的分析之后,自主提出了一款基于組件式開(kāi)發(fā)思想的游戲開(kāi)發(fā)框架(Ostrich)用以解決游戲引擎所存在的這一缺陷。同時(shí),本文還對(duì)Ostrich游戲開(kāi)發(fā)框架的整體架構(gòu)進(jìn)行了詳細(xì)的介紹和分析,并且在此基礎(chǔ)上,實(shí)現(xiàn)了部分核心的功能。Ostrich框架相比一般的游戲引擎更加具有靈活性,但是與之作為代價(jià)的是,它并沒(méi)有像一般游戲引擎那樣配置好就能馬上使用,而是需要選擇一個(gè)組件或者自行開(kāi)發(fā)組件以用于實(shí)現(xiàn)接口。不過(guò)相對(duì)于開(kāi)發(fā)一個(gè)完整的游戲引擎或者游戲而言,實(shí)現(xiàn)一個(gè)Ostrich接口要簡(jiǎn)單得多。接下來(lái)需要繼續(xù)優(yōu)化Ostrich的移植性,盡量采用標(biāo)準(zhǔn)庫(kù)而非系統(tǒng)API,并且使用宏定義區(qū)分系統(tǒng)平臺(tái),確保多平臺(tái)的支持。
[1]GENG Weidong,CHEN Kai,LI Xin,Implementation of 3D game engine [M].Zhejiang:Zhejiang University Press,2008:1-2 (in Chinese).[耿衛(wèi)東,陳凱,李鑫.三維游戲引擎設(shè)計(jì)與實(shí)現(xiàn) [M].浙江:浙江大學(xué)出版社,2008:1-2.]
[2]Eva Hudlicka.Affective game engines:Motivation and requirements[C]//Proceedings of the 4th International Conference on Foundations of Digital Games.New York:ACM,2009:299-306.
[3]Akagi Y,F(xiàn)urukawa M,F(xiàn)ukumoto S,et al.Interactive 3D animation system based on touch interface and efficient creation tools[C]//IEEE International Conference on Multimedia and Expo,2013:1-7
[4]Donald Hearn,Pauline Baker M.Computer graphics with OpenGL [M].3rd ed.CAI Shijie,SONG Jiqiang,CAI Min,transl.Beijing:Publishing House of Electronics Industry,2010:54-55 (in Chinese).[Donald Hearn,Pauline Baker M.計(jì)算機(jī)圖形學(xué) [M].3 版.蔡士杰,宋繼強(qiáng),蔡敏,譯.北京:電子工業(yè)出版社,2010:54-55.]
[5]Eike FA,Steffen E,Peter C.The case for research in game engine architecture [C]//Proceedings of the Conference on Future Play:Research,Play,Share.New York:ACM,2008:228-231.
[6]LI Zhongwen,QIN Zhidong,WANG Quanyu,et al.Optimized genetic algorithm for shortest-path problem in game engine[J].Application Research of Computers,2014,31 (1):76-79 (in Chinese).[黎忠文,覃志東,王全宇,等.游戲引擎最短路徑搜索優(yōu)化遺傳算法設(shè)計(jì) [J].計(jì)算機(jī)應(yīng)用研究,2014,31 (1):76-79.]
[7]TANG Xianhui.Research and implementation of 3Dgame engine [D].Wuhan:Wuhan University of Technology,2010(in Chinese).[唐先輝.3D 游戲引擎的研究與實(shí)現(xiàn) [D].武漢:武漢理工大學(xué),2010.]
[8]Frederick WB,Rynson WH,Danny K.Game-on-demand:An online game engine based on geometry streaming [J].ACM Transactions on Multimedia Computing,Communications and Applications,2011,7 (3):118-131.
[9]Martin Dorta N,Sanchez Berriel I,Bravo M,et al.A 3Deducational mobile game to enhance student’s spatial skills[C]//IEEE 10th International Conference on Advanced Learning Technologies,2010:6-10.
[10]Peker GA,Can T.A design goal and design pattern based approach for development of game engines for mobile platforms[C]//Proceedings of the 16th International Conference on Computer Games. Washington:IEEE Computer Society,2011:114-120.