UnityでIronPythonを使う

#はじめに
ここ数か月、Unityで強化学習をしようとしていました。
その際、Pythonの資産とUnityの物理エンジンを使うためにIronPythonを導入することにしました。
これはその過程で得た知見のまとめです。
IronPythonの仕組みなどではなく、とりあえず早急にUnityでPythonを使いたいという人向けです

正直、どうして動いているのかわからないところのほうが多いです
それでも記事にしたのは、実際にUnityで使うことを説明した記事が少なく、僕がとても苦労したからです
間違いだらけかもしれませんが、指摘していただけたら嬉しいです

前回初めて書いた記事が記事と呼ぶのもはばかられるqiita界の粗大ごみだったので今回はできる限り丁寧に書いていきますよ頑張る。

#準備
##環境
今回使用したのは
windows10
unity 2018.2.14f1
IronPython 2.7.7
です



##導入
~~IronPythonのHPから最新のものをダウンロードします~~

2019/4/29修正
unityのデフォルトの.NETバージョンは3.5に設定されていますが、IronPythonでは2.7.8で.net 3.5、2.7.9では.net 4.0むけのライブラリが削除されました
なので、.NETのバージョンを変更したくない場合はIronPython 2.7.7を使用してください

https://ironpython.net

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/231859/c44071d6-125f-8685-4bbc-94e0c7d867da.png)

一番上の.msiでインストールすると楽です。
インストーラの指示に従ってインストールしましょう
場所はどこでもいいです

次にunityプロジェクトに、
Assets/Plugins
フォルダを作ります

フォルダを作ったら、IronPythonをインストールした場所に行きましょう

![image.png](https://qiita-image-store.s3.amazonaws.com/0/231859/1fdef999-7cc5-11f7-9dd8-b5c07df64703.png)

こんな感じになっていると思います
この中の、
-/IronPython 2.7/Platforms/Net35
内にある、

IronPython.dll
IronPython.Modules.dll
Microsoft.Dinamic.dll
Microsoft.Scripting.dll
Microsoft.Scripting.Core.dll
Microsoft.Scripting.Metadata.dll

をコピーし、

-\Assets\Plugins

に貼り付けます

次に、エディター左上のFileから、BuildSettings　→　PlayerSettingsと進んでください

デフォルトではインスペクターと同じ場所に出てくる、PlayerSettingsの、OtherSettings　→　Configuration → API compatibilityを選択し、.NET 2.0に設定してください（デフォルトは.NET 2.0 subsetになっています）

これでもうIronPythonが使えるようになっているはずです！！！

早速試してみましょう

#とりあえず使ってみる
C#でスクリプトを書きます

```C#:test.cs
using System.Collections.Generic;
using UnityEngine;
using IronPython.Hosting;

public class test : MonoBehaviour {

    [SerializeField]
    TextAsset pythonScript;

	// Use this for initialization
	void Start () {
        var scriptCreator = new PythonScriptCreator(pythonScript);
        scriptCreator.CallScript();
	}
	
	// Update is called once per frame
	void Update () {
		
	}
}


public class PythonScriptCreator
{
　　//pythonのスクリプトを文字列としてそのまま持つ
　　//改行などもそのまま使える
    string script;

  Microsoft.Scripting.Hosting.ScriptEngine scriptEngine;
    Microsoft.Scripting.Hosting.ScriptScope scriptScope;
    Microsoft.Scripting.Hosting.ScriptSource scriptSource;

    public PythonScriptCreator(TextAsset PythonFile)
    {
　　　　//TextAssetからstringに変換
        this.script = PythonFile.text;

        this.scriptEngine = Python.CreateEngine();

        scriptEngine.Runtime.LoadAssembly(typeof(GameObject).Assembly);
        this.scriptScope = scriptEngine.CreateScope();

　　　　//stringを渡すとpythonスクリプトとして読み込んでくれる
        this.scriptSource = scriptEngine.CreateScriptSourceFromString(script);
    }

    public void CallScript()
    {
　　　  //実行
        this.scriptSource.Execute();
    }
}
```
こんな感じになります

次はPythonのHello Worldを書きます

```python.txt
import UnityEngine
from UnityEngine import *

UnityEngine.Debug.Log("Hello World")
```

注意すべき点は、.txtで保存することです
そうすることでUnityEditor上でTextAssetとして扱われます

あとはインスペクターで適当なGameObjectにつけたら完了です
![image.png](https://qiita-image-store.s3.amazonaws.com/0/231859/d27e9dcb-4c6d-82ac-ee15-770f24b56802.png)

実行すると、
![image.png](https://qiita-image-store.s3.amazonaws.com/0/231859/53095bd8-69d8-8181-072e-23f55120fbd0.png)
こんな感じで動いているのが分ると思います

#実際に使えるようにする
##インスタンスの共有
C#からPythonのコードを実行することはできました
しかし、実際に開発に使うにはまだ問題があります
その一つがスコープの問題です

現状、C#からPythonに引数を渡すことはできませんし、結果をC#に渡すこともできません
また、PythonからC#のインスタンスを参照したりすることもできません
なので、それを可能にしたいと思います
なお、この方法はこのスライドの内容をパクっています
http://www.zengeren.com/content/data/02/02_AC_IronPython.pdf
このスライドには、IronPythonの少し詳しい説明も載っているので、見てください
公式ドキュメント読むに越したことは無いです（それはそう）

```C#:test.cs
public class PythonScriptCreator
{
    string script;
    Microsoft.Scripting.Hosting.ScriptEngine scriptEngine;
    Microsoft.Scripting.Hosting.ScriptScope scriptScope;
    Microsoft.Scripting.Hosting.ScriptSource scriptSource;

    public PythonScriptCreator(TextAsset PythonFile)
    {
        this.script = PythonFile.text;
        this.scriptEngine = Python.CreateEngine();

        scriptEngine.Runtime.LoadAssembly(typeof(GameObject).Assembly);
        this.scriptScope = scriptEngine.CreateScope();
        this.scriptSource = scriptEngine.CreateScriptSourceFromString(script);



        var log = new Log();
        log.debugLog();
        this.scriptScope.SetVariable("scope", log);
        log.debugLog();
    }

    public void CallScript()
    {
        this.scriptSource.Execute(this.scriptScope);
    }
}

public class Log
{
    public int testInt = 0;

    public void debugLog()
    {
        Debug.Log("Debug Log " + testInt);
    }
}
```

先ほどのコードに少し、追加しました
int型の変数一つと、それをログに流す関数を一つ持ったクラスを定義します

```C#:test.cs

        var log = new Log();
        log.debugLog();
        this.scriptScope.SetVariable("scope", log);
        log.debugLog();
```

この部分では、Logクラスのインスタンスをscopeという名前で、scriptScopeに追加しています
こうすることで、Python側から、Logクラスのlogという名前のインスタンスをscopeという名前で利用できるようになります

Pythonのコードも見てみましょう

```python.txt
import UnityEngine
from UnityEngine import *

UnityEngine.Debug.Log("Hello World")

scope.testInt = 2

scope.debugLog()
```

scopeという名前でlogを参照していることが分かります

これを実行すると、
![image.png](https://qiita-image-store.s3.amazonaws.com/0/231859/f483bc19-08e7-90f1-7bd4-8e66fb505fc7.png)
こうなります


４つのDebug.Logを上から順に１，２，３，４とします

１は、


```C#:test.cs

        var log = new Log();
        log.debugLog();　　　//←ここで呼ばれた
        this.scriptScope.SetVariable("scope", log);
        log.debugLog();
```
testIntを変更していないので、初期化したときの0が入っています


２は


```C#:test.cs

        var log = new Log();
        log.debugLog();
        this.scriptScope.SetVariable("scope", log);
        log.debugLog(); //←ここで呼ばれた
```
ここでは、まだpython.txtは実行されていないので、testIntに変更はありません

３は

```python.txt
import UnityEngine
from UnityEngine import *

UnityEngine.Debug.Log("Hello World") #←これ

scope.testInt = 2

scope.debugLog()
```
python.txtのDebug.Logです

４は

```python.txt
import UnityEngine
from UnityEngine import *

UnityEngine.Debug.Log("Hello World")

scope.testInt = 2

scope.debugLog() #←これ
```

testIntが変更された後なので2が出力されます


このように、

```
Microsoft.Scripting.Hosting.ScriptScope.SetVariable(string name,Object instance)
```
でC#のインスタンスをPythonに渡せます

僕が使っていた時はこれが分らなかったので、全部jsonにして渡していました、地獄だった、、

##ライブラリを使えるようにする
今のところ、Python側では、SystemやUnityEngineなどのライブラリを使うことができます
他にも、importに書くだけで、System.CollectionsなどのC#のライブラリやUnity関連のライブラリは普通に使えます
つまり、C#で使えるものはPythonでも普通に使える、ということです
（ただし、ジェネリックは対応する表記が分らないので使えていないです。
つまり、GetComponent<>()などができないので、これもPythonだけでゲームが作るのが厳しい一因になっています）


しかし、Pythonの膨大な資産も使いたいところです


まず、プロジェクトフォルダ内にPythonのライブラリを入れるためのフォルダを作ります
僕は
-\Assets\PythonLibrary
としました

次に、IronPythonをインストールしたフォルダに行き、
-\IronPython 2.7\Lib
をコピーして、先ほど作ったフォルダに貼り付けます

Libの中身だけをPythonLibrary内に貼り付けても構いません

次に、python.txtに追加します

```python.txt
#Unity、C#などのライブラリのインポート
import UnityEngine
from UnityEngine import *

#Pythonのライブラリのインポート
import sys
sys.path.append('-\Assets\PythonLibrary\Lib')

#例
import random
```
こんな感じです
当然、先にPythonのライブラリをインポートしても構いません

また、自作のライブラリなども、-\PythonLibrary\Lib内に入れておけば同様にインポートされます

これでPythonのライブラリも自由に使えるようになったかと思います


#まとめ
##感想
UnityのC#からIronPythonをつかってPythonのスクリプトを動かすことができました。
これによって、豊富に提供されているPythonのライブラリがUnityでも使えるようになりました
最近Unityばゲーム以外にも研究用のソフトウェアの開発などにも利用されています
Pythonが使えることでもっとできることの幅が増えれば楽しいなと思います

僕が高校の課題研究でIronPythonを使おうとしたときは変数も共有できず、StreamLineが飛び交っていましたし、Pythonのライブラリも導入できずに結局あまり意味がなくなってしまった、という悲しい結果になってしまいました
やり方を書いてあるだけの記事でどこがバグの温床になっているかもわからないですが、この記事が誰かの役に立てればいいな、などと思いあがっています
最初にも書きましたが間違っているところがあれば教えていただけると幸いです


##今後
~~今回は、ScriptEngineをはじめ、おまじないが多かったことが記事を出すか迷ってた原因の一つです。原因としては、Microsoft.Scripting.dllなどの使っているライブラリのドキュメントがどこを探しても見つからない、ということがあります。
明らかにマイクロソフトが作っているんですが、MSDNなどを漁っても見つけられなかったんですよね
他にも実際に使う上での注意点なども書いていきたいと思ってはいます。思っているだけですが~~
普通に見つけてしまったのでゆっくり読んでいきます。
https://docs.microsoft.com/ja-jp/dotnet/framework/reflection-and-codedom/dynamic-language-runtime-overview
ここと、このページから飛べるリポジトリにおいてあるワードファイルに詳しい説明があります

まあそういうこともあるので、この記事もできるだけアップデートをしていきたいです