Nebula2的腳本系統(tǒng)實(shí)現(xiàn)了一個(gè)面向C++的腳本接口, 它把腳本命令直接映射到了C++方法. 從技術(shù)角度來說, 這是一個(gè)簡(jiǎn)捷的思路, 但是對(duì)于需要把游戲邏輯和行為腳本化的關(guān)卡設(shè)計(jì)師來說, Nebula2的腳本系統(tǒng)太底層和透明了.
關(guān)卡邏輯腳本一般來說構(gòu)架于比C++接口更高級(jí)的層次上, 直接把腳本命令映射到C++方法會(huì)把腳本層次弄得錯(cuò)綜復(fù)雜. Bug甚至?xí)韧瑯拥腃++代碼更多, 因?yàn)槟_本語言一般缺少?gòu)?qiáng)類型檢查和”編譯時(shí)”的錯(cuò)誤檢測(cè), 所以在本應(yīng)在C++編譯時(shí)發(fā)現(xiàn)的Bug會(huì)在腳本運(yùn)行時(shí)才發(fā)現(xiàn)(這對(duì)于不同的腳本語言有所不同). 這是我們從Project Nomads中得出的經(jīng)驗(yàn), 它就是用Nebula2的腳本系統(tǒng)驅(qū)動(dòng)的.
所以教訓(xùn)就是: 把你的腳本架構(gòu)在一個(gè)正確的抽象層上, 并且: 把你的C++接口映射到一種腳本語言是沒有意義的, 因?yàn)槟菢幽悴蝗鐝囊婚_始直接用C++來做這些東西.
相應(yīng)的, 新的Nebula3腳本哲學(xué)為關(guān)卡設(shè)計(jì)師提供一些在”正確的抽象層”的(大多是限于特定應(yīng)用)積木. 當(dāng)然, “正解的抽象層” 很難來定義, 因?yàn)檫@要在靈活性跟易用性之間找到一個(gè)平衡( 例如, 一個(gè)”Pickup” 命令是不是應(yīng)該把角色移動(dòng)到拾取范圍內(nèi)呢? )
除了太底層以外, Nebula2的腳本系統(tǒng)也有一些其它的缺點(diǎn):
- C++方法必須遵循可以轉(zhuǎn)化為腳本的原則( 只有簡(jiǎn)單數(shù)據(jù)類型才可以做為參數(shù) )
- 給程序員帶來麻煩. 每個(gè)C++方法都需要額外的腳本接口代碼( 每個(gè)方法幾行 )
- 只有派生自nRoot的類可以腳本化
- 對(duì)象關(guān)聯(lián)到腳本系統(tǒng)( 思路簡(jiǎn)單, 但是增加的依賴性會(huì)使重構(gòu)非常困難 )
下面是Nebual3的底層腳本的大概:
- 腳本系統(tǒng)的基礎(chǔ)是Script::Command類
- Script::Command是一個(gè)完全腳本語言無關(guān)的, 它包含了一個(gè)命令名稱, 一些輸入?yún)?shù)的集合還有一些輸出參數(shù)的集合.
- 一個(gè)新的腳本命令通過派生Script::Comand類來創(chuàng)建, 腳本的C++功能代碼可以寫入子類的OnExecute()方法
- ScriptServer類是腳本系統(tǒng)中僅有一個(gè)腳本語言相關(guān)的類, 它會(huì)把Command對(duì)象注冊(cè)成新的腳本命令, 并且把命令參數(shù)在腳本語言和C-API之間做翻譯.
這個(gè)觀念比Nebula2更為簡(jiǎn)單, 最重要的是, 它不會(huì)跟Nebula3的其它部分交織在一起. 甚至可以通過改變一個(gè)#define來編譯一個(gè)沒有腳本支持的Nebula3.
當(dāng)然, 書寫腳本命令的C++代碼跟Nebula2一樣煩人, 這是NIDL的由來. NIDL的是全稱是”Nebula Interface Definition Language”. 基本思想是通過為腳本命令定義一個(gè)簡(jiǎn)單的XML schema并把XML描述編譯成派生了Script::Command的C++代碼, 來盡量減少書寫腳本命令的重復(fù)性工作.
對(duì)于一個(gè)腳本命令必不可少的信息有:
- 命令的名稱
- 輸入?yún)?shù)的類型和名稱
- 輸出參數(shù)的類型和名稱
- 對(duì)應(yīng)的C++代碼( 通常只有一行 )
還有一些非必須, 但是可以帶來便利性的信息:
- 關(guān)于命令的作用和每個(gè)參數(shù)的意義的描述, 這可以作為運(yùn)行時(shí)的幫助系統(tǒng)
- 一個(gè)唯一的FourCC(四字符碼), 可以更快的通過二進(jìn)制通道傳輸
大部分的腳本命令翻譯成了大約7行的XML-NIDL代碼. 這些XML文件再用”nidlc”NIDL編譯器工具編譯為C++代碼. 這個(gè)預(yù)處理是VisualStudio完全集成的, 所以使用NIDL文件不會(huì)為程序員代來任何困難.
為了減少亂七八糟的文件(編譯生成的), 相關(guān)的腳本命令被組織到一個(gè)叫作庫的集合中. 一個(gè)庫由一個(gè)單獨(dú)的NIDL-XML文件表示, 并且它只會(huì)被翻譯一個(gè)C++頭文件和一個(gè)C++源代碼文件. 腳本庫可以在程序啟動(dòng)時(shí)注冊(cè)到ScriptServer, 所以如果你的應(yīng)用程序不需要腳本訪問文件的話, 僅僅不注冊(cè)IO腳本庫就可以了. 這會(huì)減小可執(zhí)行文件的體積, 因?yàn)檫B接器會(huì)把沒有用到的腳本庫丟棄掉.
最后, Nebula3放棄了TCL作為標(biāo)準(zhǔn)的腳本語言, 而采用了運(yùn)行時(shí)代碼更加小巧的LUA. LUA已經(jīng)成為游戲腳本的準(zhǔn)規(guī)范, 這也使得尋找熟練的LUA關(guān)卡設(shè)計(jì)師更加容易.