I’ve been getting increasngly annoyed with the whole design time / run time aspect of writing code recently. Content changed, stop, compile run, test. Game object behaviour not working right? Change code, compile run, test, iterate. The first thing was to get content loading dynamically. Using MonoGame’s Content Builder proved to be the solution here as I could define content outside of Visual Studio, monitor changes to content files, build it and reload dynamically. I knocked up a simple demo to prove the concept and luckily it worked 🙂

Next up was the game objects themselves. There were already excellent REPL tools available for other languages and I wanted something like jsFiddle, ShaderToy, etc for my XNA games so that I could hack around on the fly and see real time changes. Unity was able to load scripts into the game and I wanted a similar solution!

I started with Using Roslyn to build a simple C# interactive script engine and had some success changing code on the fly and getting updated results in a console window. Next up I found CS-Script which I managed to glean some ideas from. But, a chance tweet by @SharpDX led me to the Gemini Framework where the missing part of the jigsaw fell in to place. I’d never really worked out how to refresh the assembly with the new code, but there it was in one of Gemini’s modules.


var newAssembly = _codeCompiler.Compile(
new[] { CSharpSyntaxTree.ParseText(_helixView.TextEditor.Text) },
new[]
{
MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "mscorlib.dll")),
MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.dll")),
MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Core.dll")),
MetadataReference.CreateFromFile(Path.Combine(assemblyPath+ "\\WPF\\", "PresentationCore.dll")),
MetadataReference.CreateFromFile(typeof(IResult).Assembly.Location),
MetadataReference.CreateFromFile(typeof(AppBootstrapper).Assembly.Location),
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(Assembly.GetEntryAssembly().Location)
},
"GeminiDemoScript");


if (newAssembly != null)
{
_scripts.AddRange(newAssembly.GetTypes()
.Where(x => typeof(IDemoScript).IsAssignableFrom(x))
.Select(x => (IDemoScript)Activator.CreateInstance(x)));
}

Just create the objects in the new assembly that match a certain interface / signature. I created another little demo application and yeah, that one worked too – Game object behaviour can be edited on the fly 🙂 It needs some work to support multiple game objects from a single script but it’s a start.

Here’s a link to the demo project…

https://t.co/Ty3epjjHon

PS – I’m sure there are other resources I used that I’ve forgotten about now but I’ll add links to them if and when I remember.

Leave a Reply