MoonSharpでUnity側の処理の終了を待つ
MoonSharpでメッセージ流して待機するのにしばらく悩んだのでメモ。
DynValue.NewYieldReqを使った方法はうまくいかなかった。
何かうまい回避策があるのかもしれないが、現状はこれで良いことにしておく。
一応軽く動作チェックはしてあるけど、夜中に書いてるんでまだなんか落とし穴があったらごめんなさい
1秒毎にメッセージを流すサンプル
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UniRx; using UniRx.Async; using MoonSharp.Interpreter; public class ScriptTest : MonoBehaviour { private Script script; public void Start(){ InitLua(); this.script.DoString(@" function wrapUnityCoroutine(co) for i in co do coroutine.yield(0) end end function message(...) wrapUnityCoroutine(playMessage(...)) end function enterPub() message([[ よう、ずぶ濡れじゃねえか 今日はどこの狩場にいたんだ? ]], [[ このスープはサービスだ ]] ) end"); var coroutine = script.CreateCoroutine(script.Globals["enterPub"]); StartCoroutine(RunLuaCoroutine(coroutine)); } private void InitLua() { this.script = new Script(); this.script.Globals["playMessage"] = new CallbackFunction((Func<ScriptExecutionContext, CallbackArguments, DynValue>)PlayMessage); } private DynValue PlayMessage(ScriptExecutionContext executionContext, CallbackArguments args) { var arr = args.GetArray(); var en = PlayMessage(arr); // internalなのでMoonSharp側の修正が必要。 return MoonSharp.Interpreter.Interop.Converters.ClrToScriptConversions.EnumerationToDynValue(script, en); } private IEnumerable PlayMessage(DynValue[] args) { for (var i = 0; i < args.Length; i++) { var val = args[i]; var wait = true; var subject = PlayMessage(val.String); subject.Subscribe(_ => { wait = false; }); // yield return nullにするとEnumerableWrapperによって即座に次が呼ばれてしまうため、 // メッセージ流してる間busy loopのような状態になる。 // とりあえず0を返しておく。 while (wait){ yield return 0; } } } // メッセージ垂れ流して終わったら通知するやつのつもりで適当に時間を置く private IObservable<Unit> PlayMessage(string str) { var subject = new Subject<Unit>(); Observable.Timer(TimeSpan.FromSeconds(1)).Subscribe(_ => { Debug.Log(str); subject.OnNext(Unit.Default); }).AddTo(this); return subject; } private IEnumerator RunLuaCoroutine(DynValue func) { var coroutine = func.Coroutine; while (true) { if (coroutine.State == CoroutineState.Dead){ break; } yield return coroutine.Resume(); } } }