(mouseover/click to play GIF) Click here for full-sized version.
If you follow me on one or other social network, you might have seen me post this gif on April 1st.
Most readers were quick to suspect something, but what if I told you that it was more real than you might have thought?
This small post is about that.
"GMLive" stands for "GameMaker Live" and is a live-coding extension for GameMaker.
That is, it lets you edit some code, press Ctrl+S, and see the logic update inside the game, without having to recompile/restart it. Can save you countless hours when iterating on code.
With one-minute setup, cross-platform support, and additional features (like sprite/room/shader reloading), it is widely regarded as one of the more useful extensions you can get for GameMaker, let alone in that price bracket.
You get the idea.
(tooting my own horn just a little while being pulled off the stage)
Consequently, "GMLive for Unity" means "GameMaker Live for Unity", and it live-[re]loads your GameMaker code (straight from a GameMaker project, no less) into a Unity game.
Rest assured, there is no practical use case for this.
I also take the opportunity to gently poke fun at Unity's live recompilation - although very powerful, there's a number of caveats (sample), and it can be hard to make use of in an environment where a lot of code comes from assets (and may not be compliant in any way).
On the morning of April 1st someone messages me and asks if I'm going to do anything for April Fools on Russian GameMaker community that I moderate. They suggest to post that the language update is out, but I disagree, as that's evil - everyone are already waiting for that.
I consider what other options there are and recall that I wanted to do "GMLive for <not GameMaker>" for Apr01 at some point. As I already have Unity installed, I decide to go with that. I consider my options:
- Make a simple mockup "screenshot" like most people would
- Use system-wide macros to fake the process for a GIF nicely
(prepare a separate Unity snippet for each step of the process)
- Use my very own extension to embed a borderless GameMaker window into Unity and use the regular GMLive.
- Compile GMLive to C#! After all, it's written in Haxe. Shouldn't be too hard.
I guess it's fairly apparent what I went with
We did not go to the Cool Moon because it was easy. Or for any identifiable reason.
Haxe is a language with the primary feature of compiling to other languages.
The process of getting my code to compile for C# was fairly straightforward:
- I added conditional flags so that unrelated features (sprite/room/shader reloading) are not processed at all.
- C# does not have an exact equivalent of "reference to a function" type, so I had to replace a few things with System.Action<>/System.Func<>.
- Rewrote the stack-like structure used for VM from a GML-specific approach (array with a prefix element indicating depth) to a more regular one. In hindsight, a bit of a mistake, as the original one would still work fine as far as this GIF goes.
- Disabled VM instructions that I definitely wouldn't need to implement for the GIF.
Upon getting the code to run, it came to my attention that some things do not work,
so I fixed those:
- HTTP requests were not being recognized by GMLive-server.
As it turns out, my HTTP mini-server was not exactly spec-compliant and would not expect the client to flush the stream before they're done writing out the method+URL line, which Haxe-C# implementation of HTTP request did. Curiously, UnityWebRequest does not share this trait.
- I might have over-optimized the GML stack implementation slightly (e.g. instructions that had to pop-push would work with "top" item directly) and this backfired upon trying to translate it into a more regular format, costing me some minutes staring at the debugger.
- C# turned out to be more type-strict than I recalled - for instance, if you have a double boxed inside an object, you cannot unbox it straight into a float - you have to unbox it into a double and then cast it to a float.
- Furthermore, I ran into this issue with type inference working unusually, costing me some more minutes staring at the generated code and the debugger.
In process I also discover that Haxe's generated C# can be hard to look at:
Mostly you don't have to (since Haxe will generate preprocessor definitions to map back to Haxe code), but if something goes wrong with generation, it's an interesting time.
Generated code's tidiness is a kind of problem that can be dealt with
A little more tooting? No? Okay.
but it certainly takes some work. More so for a fully typed language.
Anyway, with all that out of the way, the code was working as intended, so I implemented the few functions needed for this and recorded the GIF.
Then, of course, we have a caveat: the GameMaker API.
In GameMaker, GMLive uses GameMaker's built-in functions directly, reasonably enough.
Unity does not have a conveniently named package of GameMaker-like functions, so I'd have to make those myself.
So I do... for what is needed for the GIF. Which is 5 constants, 3 functions, 3 global variables, and two instance variables total:
If we were to go by auto-completion file for GameMaker Studio 2.2.5, that's 1877 functions less than is needed for 100% API coverage.
With some work and cleanup, it can serve as a good base for a game-specific scripting language - just like how Nuclear Throne (API reference), Forager, and Rivals of Aether use variously customized bits of GMLive's (GML) VM code.
I have talked about specifics and advantages of having a fine-tuned custom language in past.
Perhaps a matter of time / someone having a budget and desiring a non-Lua scripting language.
Overall, this was a fun thing to do in a day.