Cape Guy

  • Games
    • Re-Entry
    • Ski Three
    • 80 Days on PC and Mac
  • Blog
  • About
    • About
    • Friends
  • Contact

Disabling Assembly Reload While in Play Mode

9 June 2018 By Ben

Edit: As of Unity 2018 There’s a setting built into the Unity editor which allows control over how it handles script changes while editing so the contents of this post are only relevant to users of previous versions of Unity.

Original Post:

Note: This post can be considered a ‘take 2’ of my previous No More Unity Hot-Reload post. While the solution has changed the description of the issue in that post is still valid.

So what’s the issue again?

If you edit your scripts while running the Unity Editor in play mode, it will recompile your scripts and then try to hot-reload the newly compiled assemblies (ie. switch over to the new code without leaving play mode).

In order to do this the editor will:

  • Serialize all the objects in the hierarchy
  • Switch the managed environment over to the new assemblies
  • Deserialize all the objects back into the hierarchy

This sounds great except anything which isn’t serialized will be lost (see the Unity serialization rules here). This includes any Dictionaries and non-serializable custom types, anything private that isn’t marked with the [SerializeField] attribute. Basically loads of stuff you almost certainly have in any non-toy project. That means that, in practice, after the reload you just get a lot of NullReferenceExceptions every time anything tries to use everything which wasn’t serialized in the hot-reload process.

I see, what can I do about it?

It is theoretically possible to handle these reloads in your code by handling all the null cases – regenerating the data which is essentially just cached data and always serializing everything which can’t be regenerated (and hiding the things you don’t want to see in the inspector using the [HideInInspector] attribute). This becomes a lot easier if you use the Odin Inspector and Serializer (which I’m using for Re-Entry and is really excellent!) as that replaces Unity’s built-in serialization and allows you to serialize everything you’d expect to be able to serialize in C# on your MonoBehaviours (and edit them in the inspector). Seriously, it’s awesome!

Though I do use Odin I still choose not to bother making hot-reload in play mode work. Handling all the special cases is a heavy burden on the developer and significantly increases code complexity. It ultimately provides a minor improvement on the already quick edit-compile-test cycle Unity has without it.

I’d recommend a lot less time spent configuring your project to use Assembly Definition Files to get the cycle even shorter is a much more efficient way to go. Supporting Hot-reload while in play mode just isn’t worth the time spent maintaining it.

Ok, so how can I disable assembly reload while in play mode?

Back in 2015, I wrote a post titled No More Unity Hot Reload in which I propose the solution of exiting play mode immediately if script compilation is detected. I used that happily for a couple of years but it still bothered me that there was the potential for ‘oh no, I just lost some play mode changes I really wanted‘ situations.

I did some more investigation and there is an API for locking the assemblies which in-turn prevents auto-reload.

Note: The LockReloadAssembly API already existed back in 2015 but I couldn’t seem to get it to work for this purpose back then.

So I’ve switched to a new editor script for handling this issue which uses that API. Its behaviour is a little different from the previous ExitPlayModeOnScriptCompile script.

In this version, if script changes are detected while in play mode the Unity editor will display the script updating spinning ring in the bottom right but it won’t actually do any compilation or reload until play mode is exited. The only downside of this is that you won’t see any compile errors until you exit play mode but I guess you can’t have everything!

Here’s the code: DisableScriptReloadInPlayMode.cs

 

Binary Sorting for ILists

6 February 2018 By Ben

I often find myself wanting to deal with sorted lists and, while C# and LINQ make this pretty easy to code, the result using built-in methods is often very inefficient. This is because they run in O(N) time for searching and O(N2) time for sorting as they just use simple loops over the data (what is that big-O notation all about?). LINQ also tends to be pretty allocation heavy (which is bad in Unity right now). What we really need is a simple set of methods which allow the use of binary search and sorting algorithms and perform those sorts in-place, avoiding allocations.

If you’re not sure why this matters, take a look at this sorting algorithm comparison visualisation:

There’s a similar issue with linear vs. binary searches. Though, you can only perform a binary search on sorted data so there can be a tradeoff there for dynamic data sets.

Basically, if you have more than a small amount of data to sort or search, using O(N2) sort algorithms in the wrong place is going to kill your performance. Even an O(N) search run in the wrong place(s) can become a major performance issue.

Now that you’re, hopefully, convinced it’s important… Good news – I’ve written some extensions which use the Quicksort & Binary Search algorithms!

How do I Get Them?

The files you need are here…

  • IListExtensions.cs – A small collection of basic extensions to IList
  • IListSortExtensions.cs – All the sorting magic

And you can also grab the tests if you like (make sure you put them in an editor only folder)

  • Editor/IListSortExtensions_Tests.cs

What Do They Do?

  • Add extension methods that let you efficiently sort and search any IList<T> in place, if necessary, and without allocations.
  • For IList<T> – You can use T‘s standard ordering, an IComparer<T> or a lambda sort predicate (Func<T, T, int>).  With the comparer or lambda predicate the return value should behave like the IComparer<T>.Compare method (more info here).
  • The standard ordering and lambda predicate BinarySearch and Sort implementations do not allocate anything. The IComparer<T> implementations make a single allocation to wrap the comparer with a lambda.
  • The following methods have all been implemented for all comparison types:
    • BinaryInsertSorted – Inserts a single member into an already sorted list. Finding the place to insert element can be done in worst case O(logN) however, as the cost of inserting an element into a random position in a list is worst case O(N) this method runs in worst case O(N) time. As such it is not recommended to sort a list using repeated calls to this method – use the Sort method for that purpose. This method is intended for the case where you build a sorted list but only have access to one new value at a time.
    • BinaryLowerBound – Returns the index of the first element in the sorted list which is greater than or equal to the given element. Assumes the list is ordered WRT the sort comparison. Runs in worst case O(logN).
    • BinaryUpperBound – Returns the index of the first element in the sorted list which is greater the given element. Assumes the list is ordered WRT the sort comparison. Runs in worst case O(logN).
    • BinaryFindLast – Returns the index of the last element in the sorted list for which the given predicate is true, -1 if the predicate is never false. Assumes the list is sorted WRT the sort predicate. ie. the predicate has at most one value switch for the list and it is from true to false. Runs in worst case O(logN).
    • BinaryFindFirst – Returns the index of the first element in the sorted list for which the given predicate is true, list.Count if the predicate is never false. Assumes the list is sorted WRT the given predicate. ie. the predicate has at most one value switch for the list and it is from false to true. Runs in worst case O(logN).
    • BinarySearch – Returns the index if a matching element in the list, if such an element exists. Otherwise returns list.Count. Runs in worst case O(logN).
    • IsSorted – Calcules if the list is already sorted WRT the provided sort comparison. Runs in exactly O(N).
    • Sorted – Returns a sorted copy of the list using the Sort method to actually perform the sort.
    • Sort – Sorts the list in place, without allocations. Sorting is performed using Quicksort and so Runs in worst case O(N2) though that is extremely rare and on average it runs in O(NlogN).

 

  • Bluesky
  • E-mail

Copyright © 2025 Cape Guy Ltd.