Okay, this has been eating at me for weeks and I’m at my wits’ end. I use coroutines with some regularity in my game, but for this particular part, I’m needing to generate a coroutine on the fly and run it. I’ve already got some code generating and executing at runtime, but it needs to be a coroutine so I can do things like suspend while waiting for user’s input. Here is what I have so far.
using Microsoft.CSharp;
using UnityEngine;
using System;
using System.Collections;
using System.CodeDom.Compiler;
using System.Reflection;
public class RuntimeCompiler : MonoBehaviour {
CSharpCodeProvider provider = new CSharpCodeProvider();
public static RuntimeCompiler instance { get; private set; }
void Awake()
{
if(instance == null)
instance = this;
else
Debug.Log("There are multiple instances of the RuntimeCompiler engine in the scene.");
}
void OnDestroy()
{
instance = null;
}
public class MethodWrapper
{
System.Reflection.MethodInfo method;
public MethodWrapper(System.Reflection.MethodInfo method)
{
this.method = method;
}
public void doIt()
{
IEnumerable en = (IEnumerable)method.Invoke(null, null);
}
}
public System.Action InterpretScript(string script){
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
parameters.ReferencedAssemblies.Add ("System.dll");
parameters.ReferencedAssemblies.Add ("System.Core.dll");
//parameters.ReferencedAssemblies.Add ("System.Collections.dll");
parameters.ReferencedAssemblies.Add (typeof(Transform).Assembly.Location);
parameters.ReferencedAssemblies.Add (typeof(MapExplore).Assembly.Location);
CompilerResults results = provider.CompileAssemblyFromSource(parameters, GetCode(script));
if(!results.Errors.HasErrors){
var cls = results.CompiledAssembly.GetType("DynamicCode");
var method = cls.GetMethod("DynamicMethod", BindingFlags.Static | BindingFlags.Public);
return (new MethodWrapper(method)).doIt;
}else{
foreach (CompilerError error in results.Errors)
{
Debug.Log(error.ErrorText);
}
throw new System.Exception("Dunno Man");
//return null;
}
}
public string[] GetCode(string script)
{
return new string[]
{
@"using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class DynamicCode : MapShim
{
public static void DynamicMethod()
{
" + script + @"
}
}
"
};
}
}
public class MapShim : MonoBehaviour
{
//A bunch of methods to make accessing game variables easier
}
So far this all works just fine. “Script” is supplied externally in a string, which is then interpreted into code and run when I invoke it.
*My first disclaimer is that I’ve had help getting this code working, so I’m a bit lost when it comes to making adjustments. What I need to do is instead have “Script” compiled into a coroutine and then have the invoke trigger that instead. I keep getting problems with object references when I try to do it the way I know how, and I suspect I’m just not structuring something correctly. Any suggestions?