この記事は、新歓ブログリレー2021 52日目の記事です。
はじめに
こんにちは。20Bの@Renardです。
普段何しているか書こうと思いましたが、インターネットしかしていません。この記事の中で最も力強く断言できます。それだけは。
さて、上に書いた薄い個人情報はどうでも良くて、本題に入ります。

これです
これUnityでeval(Wikipedia)みたいな事をしたいということです。
そして

ついでにこれも
やるぞ~~~~~~!!!!!!!!!!!
準備
まずUnityでプロジェクトを作ります。私の環境ではバージョン2021.1.2f1を使用しました。どのバージョンでも動くと思います。
どうやって実行中にコードを動かすのかについてですが、これは Mono.CSharp.Evaluator (Mono Documentation)クラスを使うことで実現できそうです。
では、さっそくUnityでスクリプトを書こう!となるのですが、Unityには標準では Mono.CSharpライブラリが含まれていないので、追加する必要があります。
.NET で有名なパッケージマネージャとしてはNuGetがありますが、実はUnity向けに NuGetForUnity (NuGetForUnity - github.com) というものがあります。(UnityはMonoで動作しているので全てのライブラリが動くわけではありませんが、今回使用するのはMono.CSharpライブラリだけですので大丈夫です)
しかし、これを使って Mono.CSharp ライブラリを追加しようとすると、

Installボタンを押しても全く反応せず、できません...(GitHubのIssueも見たのですが原因はよく分かりませんでした)
なので、手動で追加しましょう
- プロジェクトのAssetsフォルダにPluginsフォルダを作ります。
- NuGetのサイトからMono.CSharpをダウンロードします。
- ダウンロードしたmono.csharp.(バージョン).nupkgを解凍して、libファイル中にあるMono.CSharp.dllを/Assets/Pluginsフォルダに入れます。

以上で追加が完了しました。
実装
実装済みのリポジトリはここです。(前述の通りバージョン2021.1.2f1なので注意)
Evaluatorスクリプトを作成し、書きます
using UnityEngine;
public class Evaluator : MonoBehaviour
{
public Mono.CSharp.Evaluator evaluator;
void Start()
{
//Mono.CSharp.Evaluatorの設定
var settings = new Mono.CSharp.CompilerSettings();
foreach (var assembly in System.AppDomain.CurrentDomain.GetAssemblies())
{
if (assembly == null) continue;
settings.AssemblyReferences.Add(assembly.FullName);
}
var printer = new Mono.CSharp.ConsoleReportPrinter();
var ctx = new Mono.CSharp.CompilerContext(settings, printer);
evaluator = new Mono.CSharp.Evaluator(ctx);
//名前空間の指定
evaluator.Run("using UnityEngine;");
}
//後々呼び出せるようにEval関数を作っておく
public string Eval(string cmd)
{
object obj;
bool hasobj;
evaluator.Evaluate(cmd, out obj, out hasobj);
string result = hasobj ? obj.ToString() : "";
return result;
}
}
ここのリファレンスを見たりして書きました。
foreachの部分ではCompilerSettingsに予め使用するアセンブリの情報を入れています。
Eval関数については、Mono.CSharp.Evaluator.Evaluateが実行結果をオブジェクトとしてくれるので、それをString型に変換して渡しています。
これだけだと動作確認ができないので、簡易的なターミナルのようなものを作ります。
UI>InputFieldを作成し、良い感じに形と色を変えます。
私は色を真っ黒にし、横長くしました。

テキストの大きさ、色なども見やすく変えておきます。InputFieldの子オブジェクトのTextで色を変更できます。
Alignmentの上下を中央ぞろえに、VerticalOverflowをOverflowにするといい感じです。

また、実行結果を表示したいので、同じようにInputFieldを作ります。(さっき作ったターミナルを複製すると楽です。)
これも見やすいように形を変え、InputField>LineTypeをMultiLineNewlineにしておきましょう。こうすることで複数行に対応できます。

簡単なTerminalスクリプトを作ります。
using UnityEngine;
using UnityEngine.UI;
public class Terminal : MonoBehaviour
{
[SerializeField] Evaluator evaluator;
[SerializeField] InputField terminal;
[SerializeField] InputField log;
//コード実行&ログに表示
public void EvalCode()
{
string obj = evaluator.Eval(terminal.text);
if (obj == "") return;
log.text += obj + "\n";
}
}
後でエディタ上でアウトレット接続するために[SerializeField]で各変数を宣言し、EvalCodeでは実行結果を改行をつけてログのtextに追加しています。
さて、コードのアタッチをしていきます。
CreateEmptyしてEvaluatorという名前を付け(なんでもいいですが)、Evaluator.cs、Terminal.csをアタッチします。
後はさっき宣言したものに全てアウトレット接続します。

そして、Terminal(InputField)のOnSubmitにEvaluatorオブジェクトを追加し、ドロップダウンからTerminal>EvalCode()を選択します。
完成!!!!
色々やってみましょう

GameObject.Find()によってobj変数にオブジェクトを入れて、CubeのTransformの情報がログに流れる事やTlanslateメソッドがちゃんと動作していることが分かります。
ゲームを作る
早速これを使って(24時間くらいで)ゲームを作りました!!!!!!!!!!!!!!!!!!!!!!!!!!

コードを実行することでステージを攻略するゲームです
ゲームはここからダウンロードして遊べます
ここで紹介したEvaluate以外に、EvaluMono.CSharp.Evaluatorクラスを使ってエラーを吐き出させたり、補完機能などを実装しています。
また、ゲームを作る際の注意点として、ビルドする前に
[Edit]>[Project Settings]>[Player]>[PlayerSettings]>[Configuration] >[Scripting Runtime Version] >[.NET 4.x] を選択しないとMono.CSharp.dllがプラットフォームに対応しない場合があります。
これはダウンロードしたMono.CSharp.dllが.NET 4.xをターゲットにしている為なのかな、と思います。
余談
タイトルに「ゲームも作っちゃおう」と書きましたが、具体的な作り方についてはインターネットで検索(最初の伏線が回収されましたね)したり、本を読んだりして学ぶのが良いと思います!!!!すいません、全ての手順を解説しようとすると私が疲れて、紐みたいになってしまうので
また、私はUnity1Weekに向けて先程のゲームを作っていたのですが、テストとしてWebGLビルドをした際にEvalが機能しなくなって最悪でした。
それに気づいた時はショックでずっと「ワァ......!コレって.....『無理』ってコト!?」とちいかわの真似をして叫んでいました。そして解決方法が分かる前にこの記事の期限が来てしまい...だからexe方式でビルドしていたんです、先程のゲームを。
さようなら...(紐になる)
まとめ
皆さんもこれを使ってゲームを作ってみてはいかがでしょうか!
明日は新歓ブログリレー2021最終日!担当者は@Rozelinです!お楽しみに!