這兩天,有關(guān)游戲引擎腳本系統(tǒng)的預(yù)研還在繼續(xù)。在google上找了一些資料來學(xué)習(xí),目前網(wǎng)絡(luò)上這部分的資料是比較少的,主要還是在gamedev上有一些相關(guān)的主題。泡了兩天的鳥文論壇,做了一打的摘錄。現(xiàn)在整理一下思緒,感覺腳本系統(tǒng)的輪廓已經(jīng)比較清晰了,但還有很多細節(jié)需要去了解,后續(xù)會對這些細節(jié)再慢慢研究道來。
腳本的基本特性
1、從語法的角度來說需要支持到順序、條件、循環(huán),并且最好語法接近某種成熟語言(比如C),這樣有利于學(xué)習(xí)&使用;
2、性能,性能,性能。要在實時的游戲里面發(fā)揮作用,性能無異是貫穿始終的話題。
語法功能解決方案
1、可以選擇一門成熟語言作為借鑒;
2、刪掉不需要的Keyword,裁剪語言元素。特別對于C語言,完全去掉有關(guān)函數(shù)聲明等部分可以大大降低parse&excute的復(fù)雜性。
腳本庫
1、腳本語言的函數(shù)庫比腳本語言的語法更加重要,這個說法的出發(fā)點仍然是性能。在上一篇文章中,我有提到“冗余”的概念,就是這句話很好的體現(xiàn)。盡量將復(fù)雜的函數(shù)功能封裝在游戲引擎本身,而不要在腳本中編寫這些函數(shù),甚至可以在腳本中不提供用戶自定義函數(shù)的功能。
2、提供給用戶(腳本編寫者)完善的庫函數(shù)集,從環(huán)境配置命令到信息查詢等等。用戶用這些函數(shù)來控制游戲環(huán)境,腳本語法只是提供分支&循環(huán)控制。
事件響應(yīng)
1、上篇提到“事件”(event)指的是所有外設(shè)輸入的統(tǒng)稱,這種說法是不全面的。除了外設(shè)輸入,事件還可以由游戲引擎內(nèi)部來觸發(fā),比如timer事件,這里做一個小小的更正。
2、事件包含一些參數(shù),參數(shù)的內(nèi)容是“哪些Object要響應(yīng)”&“事件類型”。
3、一個腳本可以調(diào)用其他腳本,實現(xiàn)腳本之間的通信。
4、事件,可作為腳本系統(tǒng)&游戲引擎的連接,也就是說,游戲引擎只有在處理事件的時候才會調(diào)用腳本系統(tǒng),而腳本系統(tǒng)的唯一任務(wù),就是響應(yīng)事件。
5、再次強調(diào)一下,事件不是只能有游戲引擎內(nèi)部的object來觸發(fā),還有timer、startUp、sceneEnd等等。
腳本運行模型
1、每個腳本都可以“掛”到任何數(shù)量的object上去;
2、每個腳本都可以看作是獨立的虛擬機,擁有自己獨立的變量、堆棧&運行點(execution pointer),這一點我自己有保留的想法,出于性能原因,可能獨立虛擬機的概念難以實施,需要進一步尋找變通的方法。
訪問控制
1、上篇提到腳本需要能讀取&改變游戲引擎內(nèi)部object的屬性,這里需要注意一個問題:讓腳本直接訪問object的屬性是很危險的事情,出于defensive coding的考慮,個人認為不應(yīng)該讓腳本直接地來訪問內(nèi)部數(shù)據(jù),而應(yīng)該提供庫函數(shù),通過庫函數(shù)來對內(nèi)部數(shù)據(jù)進行訪問,這樣做的好處是:將邊界檢查等等安全性工作加入了進來,并且讓腳本編寫者在編寫腳本的時候不用對安全性進行考慮;壞處是:肯定效率是有所降低的。(或者還有更好的辦法?想想。。。)
庫函數(shù)
1、庫函數(shù)的實質(zhì)是C/C++(引擎編寫語言)的函數(shù)指針,對于腳本系統(tǒng)來說它們是可見的全局symbols。這里引入兩個小話題:一是在引擎初始化的時候,需要注意建立這些函數(shù)的入口地址表,讓引擎在需要調(diào)用的時候有表可查;二是,java呢?有沒有類似函數(shù)指針的東西?(這里屬于實現(xiàn)階段的問題,我只是提出,沒有去研究)
2、腳本運行的時候,如果遇到調(diào)用庫函數(shù),則跳轉(zhuǎn)到native C/C++代碼去。這里存在運行環(huán)境切換的問題,從腳本語言的堆棧里彈出參數(shù),傳給native C/C++函數(shù)來執(zhí)行,執(zhí)行結(jié)束以后,返回結(jié)果壓入腳本語言的堆棧中。如果把游戲引擎看做虛擬機,那么腳本系統(tǒng)就是嵌套在這個虛擬機里面的虛擬機,^_^,拗口哦,不知道您明白了沒有?
====================================================================================================
以上討論的,是運行與游戲引擎部分的腳本系統(tǒng)。以下的,是有關(guān)腳本本身的一些討論,這個應(yīng)該歸屬于游戲制作工具的一部分。
====================================================================================================
編譯
1、當(dāng)然,這個步驟叫“解釋”,與C語言一類編譯式語言有所區(qū)分的話更加準確。但由于在做法上跟C是類似的,所以也可以叫“編譯”。
2、兩個階段:一是把腳本源代碼拆成語言元素,token,不知道這個用哪個中文表達好,編譯原理學(xué)了都忘了;二是把所有token組織起來,組成基本命令序列。
3、什么“基本命令序列”?剛才不是說把腳本看過虛擬機嗎?這個“基本命令”就相當(dāng)于這個虛擬機CPU的機器指令或匯編指令。也就是對于這個虛擬機來說的可執(zhí)行代碼。
“匯編指令”
1、為了簡單,就把腳本這個虛擬機設(shè)計成單堆棧的吧。
2、基本的命令包括:push,pop,go,stop,callfunc,add,asign等等。
舉個小例子吧:比如寫了如下的腳本(語法上近C)
a = 1 + 2;
通過編譯,變化成如下的“可執(zhí)行序列”:
push 1
push 2
pop // store 1
pop // store 2
add // store result
push 3 // push result
pop // store 3
pop // var name a
asign // a = 3
這里只是示意,可能與實際實現(xiàn)有出入。
這篇略顯枯燥了,聊點輕松的。一直在強調(diào)腳本語言的性能問題,來看看 各種語言性能比較 吧。LUA好像在腳本語言里面排名靠前呵,我正好也下載了它的源碼,并在VC上編譯好了。在Python&LUA之間猶豫了一下,還是選擇LUA吧,比較小,簡單,Python目前已經(jīng)做得很復(fù)雜了。再說偶的腳本系統(tǒng)里面目前不需要提供自定義函數(shù)機制,也不需要OO滴,看看幾個月后能不能作出自己的腳本語言來?到時候再跟Python等等一較高低!:)
好戲在后頭哦,下期開始實際以LUA為例子研究腳本系統(tǒng)了。。