2008年11月21日 星期五

遊戲中的腳本語言

這幾年,越來越覺得遊戲內的腳本語言(Script Language)是一個必要的東西。尤其是遊戲越做越大,越做越複雜,沒有個理想的腳本語言實在是沒辦法應付這樣的需求。

在以前那個一切都還很美好的時代,遊戲也很美好,也很簡單,我們不需要太複雜的腳本語言。簡單的一個文字檔,寫下NPC的ID,起點的位置,終點的位置,然後我們就可以控制NPC,讓他在場景裡從這邊晃到那邊,再從那邊晃回來。

沒有人會嫌這隻NPC很單調。

不過時代不同了。

大概在半年多一年以前,我們的遊戲有個需求,我們需要在人物技能的Tool tip上,顯示各種技能在攻擊與防禦上的影響,而這些影響會跟著人物屬性數值變化。遊戲企劃給了一些公式,大概都是把屬性數值拿來加減乘除算一算。

於是我們的程式設計師很認真的開始分析這些公式,希望可以找出規則,產生出個表格,然後用一個函式搞定所有技能影響的計算。

如果你也是這麼想,那就表示你對遊戲企劃的了解還不夠深。

這規則只有神仙才找得出來。

於是我們開始引入腳本語言。

過去我們曾經研究過lua。lua名氣不小,而且自從「魔獸世界」的UI系統與它結合並且利用它發展出一整套的Addons之後,名氣更大。不過,我們選擇的是Squirrel

Squirrel,松鼠。不知道作者為什麼要取這個名字,它的Script檔案,附檔名是nut,就是給松鼠吃的堅果。很幽默。

Squirrel的作者據說是Far Cry這個遊戲的程式設計師,他們在Far Cry這個遊戲中原本是使用lua,所以骨子裡的程式碼是跟lua很像的。

好,回到我們的技能Tool tip。

舉個例子,有一個攻擊技能,會根據人物的力量屬性計算傷害加成,公式是這樣:

傷害加成 = (力量數值) x (技能等級+5) / 100

遊戲程式可以很簡單的把這個公式寫進程式中。但是隔沒幾天,任性的遊戲企劃來改公式了,於是程式設計師把這一小段程式改寫。又過了幾天,覺得這個技能太強了,又改公式,程式設計師又動了一次刀....

然後是無窮迴圈。

把這些公式的計算,開放在腳本語言中做計算,讓企劃可以自行修改,我們就能夠擺脫任性企劃的糾纏。

程式中的寫法當然不同了。

上面的傷害加成公式,是寫在Script中,腳本語言提供一個函式給遊戲程式呼叫,傳回傷害加成計算的結果。

function GetDamageModify()
{
  return PlayerStr() * ( SkillLv() + 5 ) / 100;
}

PlayerStr(), SkillLv()是兩個由遊戲程式提供給腳本語言呼叫的函式,分別會傳回力量屬性數值以及技能等級。

這兩個函式是額外寫的,腳本語言沒有強大到可以直接呼叫遊戲程式中的函式。我們必須將提供給腳本語言呼叫的C/C++函式,用腳本語言所能理解的方式再包裝一次,腳本語言才能使用它們。

這個包裝的過程,lua跟Squirrel叫做Bind。包裝的方法很繁瑣,也不容易理解。所以,有人利用Template寫出一整套的Binding工具程式庫。lua有luabind, Squirrel有sqplus。

RegisterGlobal(SquirrelVM::GetVMPtr(),&GetPlayerStr,_T("PlayerStr"));
RegisterGlobal(SquirrelVM::GetVMPtr(),&GetSkillLevel,_T("SkillLv"));

這兩行是寫在C/C++遊戲程式中,利用sqplus工具,將 C/C++ 的 GetPlayerStr(), GetSkillLevel()兩個函式,登記給腳本語言呼叫,在腳本中的函式名稱分別是 PlayerStr() 與 SkillLv() 。

這樣套入之後,我們與遊戲企劃就開始分工了。程式設計師需要做的,就是定義與實做這些提供給腳本呼叫的函式,至於公式的內容,就讓遊戲企劃自己去處理。

============= 時間的分隔線 ===============

話說,幾個月之後,遊戲製作人決定要重新檢視一遍所有幾十個技能的設定,同時調整所有的計算公式....

程式設計師笑了....

3 則留言:

半路 提到...

原來你們選擇使用 Squirrel 呀。

我認為 Squirrel 的優點在於語法比較接近 C++,對遊戲程式設計者來說,比較易於學習。但我好奇的是,不知 Squirrel 與 Lua 在效能上的比較如何。

目前我還是繼續在使用 Lua,希望有機會能互相交流使用心得。 ^^

藍斯洛 提到...

我們並沒有實際去比較過效能,因為兩者在實作層面的做法是非常類似的,感覺上應該是差不多。
我們選用Squirrel的原因,主要是語法接進C++,另外就是Binding的工具,比起lua來,好多了。

FuMinG 提到...

Squirrel 輸lua 而且輸不小
因為lua 5.2 版後對暫存器有最佳化

不過作者有指出真正遊戲應用上
不會有這麼懸殊的例子

真正繁雜的工作是給C++處理
Squirrel只是串聯各模組的指揮官