I'm sure you don't have to imagine this scenario. You make a small script change to quickly see results. Unity welcomes you with dreaded 'reloading script assembles' screen. After 10 seconds you're ready to get back into playmode. Now just few more clicks to get where you already were. Now testing and...
... it didn't work, there's another small change needed.
All day long...
I'll share a way with you to hot-reload those changes without both full-domain-reload and playmode reset so you can see results in ~500ms and iterate on your code insanely fast.
Issue at hand
Unity has a quite lengthy iteration loop, every time you make a script change it'll compile changes and perform full domain reload before you can test those changes.
Reload process is non trival considering complexities of projects and what happens under the hood.
With that in mind we're not going to have a 100% bullet proof solution but I figured if a fast solution can be made that works in most scenarios then that'd be a huge improvement for my workflow.
This is how Fast Script Reload came about - a tool that does exactly that. In this blog series we'll go into technical details how it works.
Now the issue is non trival - otherwise Unity would have solved it already. With that in mind I gave up on trying to go deep into their process.
Instead, I figured an out-of-the-box approach would be my only real shot.
So we want to:
- compile changed code
- load that up and
- make sure new code is used
- without triggerring domain reload
Getting any code compiled is trival, Unity nowdays uses csc.exe and we can simply feed the file into it and get a resulting library.
Loading that library at runtime is also straight forward and can be done via Assembly.Load
The only missing piece is how to get existing code to call modified one in loaded assembly.
Magic of IL (Intermediate Language) Rewriting
Few years ago I've started looking into IL Rewriting and build few solutions around that. Essentially it allows to bend compiled code to your will :). This opens many doors on what and how we can build. It even got a name - Aspect Oriented Programming.
If you're interested - Fody is great place to start.
I've had some good time with IL Rewritting and some time later I've found Harmony
It allows you to tweak IL and patch code but AFTER assembly has been loaded / at-runtime. This is absolutely mind blowing and used in countless mods.
Now at the core there's an incredibly clever piece of software engineering - MonoMod. It allows to patch existing method call via inserting a JMP (jump) IL instruction into methods. This way you effectively redirect the flow of that method to wherever you want.
That's the missing piece, now we can stich it all together and enjoy a hot-reload approach that goes around Unity full domain reload.
It's a very bare-bones approach that'll bring up more challenges as we go forward, we'll build on that to tackle remaining problems in next blog posts:
- if you cross-reference changed classes you'll get an error. This is down to technically having 2 classes with the same name, if you use them both - the compiler will complain. We'll look at ways to adjust code / or IL representation to make sure your compiled changes do not clash with each other
- new fields are not visible in editor and can not be tweaked
- those will start posing some more challenges and we'll build on those where appropriate
Have a look at github solution, it's a simple / naive approach to show how it can be done. It's this way on purpose so critical parts are standing out.
We'll be digging deeper into remaining chalanges and it'd be great to have you on the journey.
Please subscribe to mailing list. I'll send you a message when next piece of puzzle is broken down and described. I'll also keep GitHub repo updated - so you can simply grab that instead :)
Btw: Fast Script Reload - Ready Made Tool is available on the asset store
I've built Fast Script Reload to tackle those challenges, most of them are already solved and I'm constantly working on remaining ones.
It's a huge help and allows me to improve the tool quicker for all of us :)