#はじめに
ここ数か月、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を使用してください
一番上の.msiでインストールすると楽です。
インストーラの指示に従ってインストールしましょう
場所はどこでもいいです
次にunityプロジェクトに、
Assets/Plugins
フォルダを作ります
フォルダを作ったら、IronPythonをインストールした場所に行きましょう
こんな感じになっていると思います
この中の、
-/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#でスクリプトを書きます
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を書きます
import UnityEngine
from UnityEngine import *
UnityEngine.Debug.Log("Hello World")
注意すべき点は、.txtで保存することです
そうすることでUnityEditor上でTextAssetとして扱われます
あとはインスペクターで適当なGameObjectにつけたら完了です
#実際に使えるようにする
##インスタンスの共有
C#からPythonのコードを実行することはできました
しかし、実際に開発に使うにはまだ問題があります
その一つがスコープの問題です
現状、C#からPythonに引数を渡すことはできませんし、結果をC#に渡すこともできません
また、PythonからC#のインスタンスを参照したりすることもできません
なので、それを可能にしたいと思います
なお、この方法はこのスライドの内容をパクっています
http://www.zengeren.com/content/data/02/02_AC_IronPython.pdf
このスライドには、IronPythonの少し詳しい説明も載っているので、見てください
公式ドキュメント読むに越したことは無いです(それはそう)
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型の変数一つと、それをログに流す関数を一つ持ったクラスを定義します
var log = new Log();
log.debugLog();
this.scriptScope.SetVariable("scope", log);
log.debugLog();
この部分では、Logクラスのインスタンスをscopeという名前で、scriptScopeに追加しています
こうすることで、Python側から、Logクラスのlogという名前のインスタンスをscopeという名前で利用できるようになります
Pythonのコードも見てみましょう
import UnityEngine
from UnityEngine import *
UnityEngine.Debug.Log("Hello World")
scope.testInt = 2
scope.debugLog()
scopeという名前でlogを参照していることが分かります
4つのDebug.Logを上から順に1,2,3,4とします
1は、
var log = new Log();
log.debugLog(); //←ここで呼ばれた
this.scriptScope.SetVariable("scope", log);
log.debugLog();
testIntを変更していないので、初期化したときの0が入っています
2は
var log = new Log();
log.debugLog();
this.scriptScope.SetVariable("scope", log);
log.debugLog(); //←ここで呼ばれた
ここでは、まだpython.txtは実行されていないので、testIntに変更はありません
3は
import UnityEngine
from UnityEngine import *
UnityEngine.Debug.Log("Hello World") #←これ
scope.testInt = 2
scope.debugLog()
python.txtのDebug.Logです
4は
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に追加します
#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
ここと、このページから飛べるリポジトリにおいてあるワードファイルに詳しい説明があります
まあそういうこともあるので、この記事もできるだけアップデートをしていきたいです