MoonSharpによるUnityとLuaの連携

# はじめに

スクリプト言語「Lua」を用いることで、ゲームにおけるキャラクターやカードのスキルといった実装が柔軟に出来るようになる、と何かの記事でみた気がします。個人的にカードゲームにおけるカードのスキル（アビリティ）を作ってみたいなあって思ってたりします。

そこで、ゲーム開発によく使われるUnityにLuaを組み込むのってどんな感じなんだろうかと思い、実際にやってみたことをまとめてみました！

[Unity](https://unity.com/ja)
[Lua](https://www.lua.org/)

# 開発環境

- 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します。

![moonsharp_install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386899/1ce99e2d-cd0f-46b0-15b6-60cb63a1386c.png)


Importが完了すると、Unityプロジェクトにおいて、「Assets > Plugins > MoonSharp」というフォルダにMoonSharpのコード類が格納されます。

# 公式チュートリアルを試してみる

MoonSharpの公式が用意してくれているコードを参考にして試してみます。
以下のコードを用意しました。

```cs
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_tutorial.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386899/642479ee-253f-32ac-9d59-2c4aa5d9f3c2.png)

コードの中身を見ていきます。
まずMoonSharpで定義されているクラス類を使うために、以下の名前空間を指定してあげる必要があります。

```cs
using MoonSharp.Interpreter;
```

次に、`Start`メソッドで呼び出している`MoonSharpFactorial`メソッドに着目します。

```cs
    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のソースコードになります（文字列だけど）。関数を定義しているっぽいですよね。

そして、以下の行です。

```cs
DynValue res = Script.RunString(script);
```

ここはあまり深く理解できていないのですが、MoonSharpで用意されている`Script`クラスというものがあって、その`RunString`メソッドを使い、引数に指定したLuaスクリプトコードを実行している、といった感じでしょうか。この`Script`クラスは、後ほどインスタンス化して使う例もお見せしますが、この例のようにStaticクラスとして使うこともできるようです。

[公式ソースコード](https://github.com/moonsharp-devs/moonsharp/blob/master/src/MoonSharp.Interpreter/Script.cs)を見てみると、`Script`クラスには他にも`LoadFile`とか`DoFile`といったメソッドが用意されていて、外部のLuaスクリプトファイルを読み込んで中のソースコードを実行してくれるクラスなのかなという印象を持ちました！

# 外部Luaスクリプトの読み込み＆実行

先ほどのチュートリアルの例は、Unity側のC#に直接Luaのコードを書き込んで使っていました。

今度は、外部に用意したLuaスクリプトファイルを読み込んで、そのスクリプトファイル内に記載されている関数を実行してみたいと思います。

以下のように、LuaスクリプトとC#コードを作成しました。

```lua:luaスクリプト
function fact (n)
    if (n == 0) then
        return 1
    else
        return n * fact(n - 1)
    end
end
```

```cs:C#コード
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`メソッドにてスクリプトファイル内の関数を呼び出しています。
以下の箇所です。

```cs
DynValue result = _script.Call(_script.Globals["fact"], 5);
```

読み込んだスクリプトファイル内の関数を実行するには、`Call`メソッドを使います。
`fact`と指定しているのが分かるかと思いますが、これは今回作成した`sample.lua`スクリプトの`fact`関数を指しています。この関数は引数を1つ取るので、`Call`メソッドの第二引数にその値を指定しています。

これにより、以下のスクリプトファイルの`fact`関数が呼び出され、引数`n`には5が入るというわけです。

```lua
function fact (n)
    if (n == 0) then
        return 1
    else
        return n * fact(n - 1)
    end
end
```

C#コードを空オブジェクトにアタッチして実行すると、以下のように結果が出力されるかと思います。

![script_load.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386899/45521af0-a497-f69b-134a-6d7c878e8fd9.png)

# まとめ

今回は、MoonSharpというOSSプラグインを用いて、Unity側でLuaスクリプトを使う方法を試してみました。
C#コード内に直接Luaスクリプトを実装して使うパターンや、外部LuaスクリプトをC#コード内から参照して扱うパターンを確認しました。
いずれにせよ、すごくシンプルで導入が楽であったと感じています。

実際にLuaスクリプトを使えば、ゲーム中に動的にスクリプトを呼び出して中に記載されている処理を実行することができるので、処理の動的な切替などが実現できそうですね。

Luaスクリプトを使ったゲームとしては、世界的人気ゲーム「World of Warcraft」や、「ファイナルファンタジーXIV」「ドラゴンクエストX」のようなメジャータイトルで採用されているそうです。（https://ja.wikipedia.org/wiki/Lua）

冒頭でもありましたが、特にゲームキャラクタやカードゲームにおけるカードの個々の複雑なスキルを実現するという文脈で、良く取り上げられる技術なのかなという印象があるので、今後もう少し調べてみようかなと思っています！

# 参考資料

- [MoonSharp公式サイト](https://www.moonsharp.org/)
- [MoonSharp公式Gitリポジトリ](https://github.com/moonsharp-devs/moonsharp)