はじめに
スクリプト言語「Lua」を用いることで、ゲームにおけるキャラクターやカードのスキルといった実装が柔軟に出来るようになる、と何かの記事でみた気がします。個人的にカードゲームにおけるカードのスキル(アビリティ)を作ってみたいなあって思ってたりします。
そこで、ゲーム開発によく使われるUnityにLuaを組み込むのってどんな感じなんだろうかと思い、実際にやってみたことをまとめてみました!
開発環境
- MacBook Pro (15-inch, 2018)
- macOS Catalina
- Unity 2019.3.4f1 Personal
- MoonSharp
MoonSharp
今回UnityでLuaを使えるようにするプラグインとして、「MoonSharp」というものを使います。
こちらはUnityのAsset StoreからImportすることが可能となっています。
以下のように、ImportしたいUnityプロジェクトでAsset Storeを開き、「MoonSharp」と検索すると出てきます。こちらをImportします。
Importが完了すると、Unityプロジェクトにおいて、「Assets > Plugins > MoonSharp」というフォルダにMoonSharpのコード類が格納されます。
公式チュートリアルを試してみる
MoonSharpの公式が用意してくれているコードを参考にして試してみます。
以下のコードを用意しました。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MoonSharp.Interpreter;
public class MoonSharpTest : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
double result = MoonSharpFactorial();
Debug.Log("Lua execution result: " + result);
}
double MoonSharpFactorial()
{
string script = @"
-- defines a factorial function
function fact (n)
if (n == 0) then
return 1
else
return n*fact(n - 1)
end
end
return fact(5)";
DynValue res = Script.RunString(script);
return res.Number;
}
}
こちらのコードを、Unityプロジェクト内の空オブジェクトにアタッチして実行してみると、以下のように結果が出力されます。
コードの中身を見ていきます。
まずMoonSharpで定義されているクラス類を使うために、以下の名前空間を指定してあげる必要があります。
using MoonSharp.Interpreter;
次に、Start
メソッドで呼び出しているMoonSharpFactorial
メソッドに着目します。
double MoonSharpFactorial()
{
string script = @"
-- defines a factorial function
function fact (n)
if (n == 0) then
return 1
else
return n*fact(n - 1)
end
end
return fact(5)";
DynValue res = Script.RunString(script);
return res.Number;
}
このメソッド内の、script
という変数に格納している内容が、まさしくLuaのソースコードになります(文字列だけど)。関数を定義しているっぽいですよね。
そして、以下の行です。
DynValue res = Script.RunString(script);
ここはあまり深く理解できていないのですが、MoonSharpで用意されているScript
クラスというものがあって、そのRunString
メソッドを使い、引数に指定したLuaスクリプトコードを実行している、といった感じでしょうか。このScript
クラスは、後ほどインスタンス化して使う例もお見せしますが、この例のようにStaticクラスとして使うこともできるようです。
公式ソースコードを見てみると、Script
クラスには他にもLoadFile
とかDoFile
といったメソッドが用意されていて、外部のLuaスクリプトファイルを読み込んで中のソースコードを実行してくれるクラスなのかなという印象を持ちました!
外部Luaスクリプトの読み込み&実行
先ほどのチュートリアルの例は、Unity側のC#に直接Luaのコードを書き込んで使っていました。
今度は、外部に用意したLuaスクリプトファイルを読み込んで、そのスクリプトファイル内に記載されている関数を実行してみたいと思います。
以下のように、LuaスクリプトとC#コードを作成しました。
function fact (n)
if (n == 0) then
return 1
else
return n * fact(n - 1)
end
end
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MoonSharp.Interpreter;
public class MoonSharpBeginner : MonoBehaviour
{
private Script _script;
private void Awake()
{
_script = new Script();
_script.DoFile("sample.lua");
}
// Start is called before the first frame update
void Start()
{
DynValue result = _script.Call(_script.Globals["fact"], 5);
Debug.Log("Lua execution result: " + result.Number);
}
}
luaスクリプトをsample.lua.txt
という名前にして、Unityプロジェクトの「Assets > Resources > MoonSharp > Scripts」フォルダの下に配置します(luaスクリプトの拡張子がtxtになっているのは、Unity側で読み込めるファイルの種類として、luaをサポートしていないためです。たぶん。)
luaスクリプトは、かなりシンプルですよね。
関数が1つだけ用意してあって、それを外部から呼び出してあげるイメージです。
C#コードを見ていきます。
今回は、Script
クラスをnewでインスタンス化しています。これにより、このクラスでScript
インスタンスを保持することができます。生成したScript
インスタンスに対しては、Script
クラスに属するメソッドが使えるので早速DoFile
メソッドを使っています。
このメソッドは引数に指定したスクリプトファイル名(正確には「Assets > Resources > MoonSharp > Scripts」に配置されているluaスクリプト名)からそのスクリプトファイルを読み込む処理を行っています。これにより、sample.lua
というファイルを読み込んでいるというイメージですね。
Awake
メソッドで読み込ませたら、Start
メソッドにてスクリプトファイル内の関数を呼び出しています。
以下の箇所です。
DynValue result = _script.Call(_script.Globals["fact"], 5);
読み込んだスクリプトファイル内の関数を実行するには、Call
メソッドを使います。
fact
と指定しているのが分かるかと思いますが、これは今回作成したsample.lua
スクリプトのfact
関数を指しています。この関数は引数を1つ取るので、Call
メソッドの第二引数にその値を指定しています。
これにより、以下のスクリプトファイルのfact
関数が呼び出され、引数n
には5が入るというわけです。
function fact (n)
if (n == 0) then
return 1
else
return n * fact(n - 1)
end
end
C#コードを空オブジェクトにアタッチして実行すると、以下のように結果が出力されるかと思います。
まとめ
今回は、MoonSharpというOSSプラグインを用いて、Unity側でLuaスクリプトを使う方法を試してみました。
C#コード内に直接Luaスクリプトを実装して使うパターンや、外部LuaスクリプトをC#コード内から参照して扱うパターンを確認しました。
いずれにせよ、すごくシンプルで導入が楽であったと感じています。
実際にLuaスクリプトを使えば、ゲーム中に動的にスクリプトを呼び出して中に記載されている処理を実行することができるので、処理の動的な切替などが実現できそうですね。
Luaスクリプトを使ったゲームとしては、世界的人気ゲーム「World of Warcraft」や、「ファイナルファンタジーXIV」「ドラゴンクエストX」のようなメジャータイトルで採用されているそうです。(https://ja.wikipedia.org/wiki/Lua)
冒頭でもありましたが、特にゲームキャラクタやカードゲームにおけるカードの個々の複雑なスキルを実現するという文脈で、良く取り上げられる技術なのかなという印象があるので、今後もう少し調べてみようかなと思っています!