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).

 

Global Game Jam 2018

1 February 2018 By Ben

I’ve been working in the games industry for 15 years but hadn’t ever taken part in a game jam, even though the idea sounds really fun to me and I’ve always enjoyed hearing what other people got up to at them. So this year, when I saw that there was a Global Game Jam location at the Cambridge Makespace I decided to give it a go!

The jam has a theme each year, which the games should somehow be inspired by, though they are generally chosen to allow a large range of game ideas.

This year the theme was Transmission. The jam’s intro video gave lots of ideas of how the theme might be interpreted in the games produced. I found this jumped me straight past the blank page problem and I started having game ideas almost instantly!

I latched straight onto the idea of transmission delay, where a user’s actions are delayed in proportion to the distance of the action away from the player. I didn’t want to do anything too complicated, due to time constraints (and having never previously programmed anything of note in just 2 days!) so I restricted myself to a tile-based game. A local two-player territory control game seemed like quite a fun idea that I wanted to try out. Each player launches actions to try and take control of tiles. They can charge their actions up to take more tiles but this takes time and makes the action projectile move slower. The player with the most territory gains ‘resilience’ from the other player and the game continues until either player loses all their resilience.

So Cat in the Corner (AKA Cats vs. Unicorns) was born.

The design, programming and, not very good, art for the game were all done by me. Julian Surma did the EPIC soundtrack! The sound effects are from freesound.org and the font is Amatic SC.

I really enjoyed the 48 hour time limit with the knowledge that I’d just stop working on the game after that. I didn’t care about code quality – or any other type of quality for that matter! I just tried to bash out a prototype of my idea. There are definitely a few minor bugs in the final version, but fixing them wouldn’t be in the spirit of it :-). The whole experience also really reminded me what a great prototyping tool Unity is. It certainly helps that I’m familiar with it but I really relied on Unity’s  fast edit-test cycle to produce a game with so much going on  (it doesn’t look like much but there’s quite a lot of logic in there!)

I’m certainly keen to have another go at a game jam in the future. Perhaps getting myself into a team rather than going solo would be fun for next time too.

Designing Ski Three

10 October 2017 By Ben

In my last post I talked about how I decided to make a match-three-endless-runner hybrid. The next task was to work out how such a thing would work. I don’t know of any magic short-cuts here. My ‘strategy’ was mainly to think about it for a while!

Straight to the Source

The first thing I did was to identify what the important elements of each of my target genres were.

Common Match Three Mechanics

  • Grid or hexagon tiled play space
  • Multiple tile types
  • Swap tiles to edit a play space
  • Match lines of three to make them disappear
  • Tiles can only be swapped which lead to matches (non-matches are cancelled before play continues)
  • Board rearranges in some way after match to fill the space (e.g. everything drops down to fill the space and new tiles appear from the top)
  • Points are scored for matches, more points for longer lines
  • Combo bonus points are usually given for chains of matches within a single move (i.e. If the tiles fall down and a new match is formed) or if two lines are created in one swap
  • There are usually special tile types e.g. Wild cards (match with anything) or bombs (clear an area on the board when matched)
  • The objective varies a lot depending on the context. Often tile types are used to represent an action (Puzzle Quest), all of a certain type of non-spawning tile type must be removed (Candy Crush) or just a high-score must be achieved (Zoo Keeper)

Common Endless Runner Mechanics

Endless runners are less formulaic than match three but there are some common themes:

  • The game involves moving in a direction
  • The play area moves on continually and is not entirely within the control of the player
  • The player must overcome obstacles to keep moving with along with the play area
  • There is often a speed based risk-reward mechanic (e.g. Combos and speed in Alto’s Adventure)
  • Play generally continues indefinitely until some kind of failure condition occurs.

What Mechanics to use?

Looking at these lists, there are no doubt many ways that the two genres could be combined by picking different subsets from each list. Previous games like 10,000,000 and We Must Build a Boat have already combined the genres in a way but have opted, like Puzzle Quest does with adventure games, to have a Match Three sub-game which is used as a metaphor for the player performing an action such as fighting.

However, to truly combine the genres into one I wanted to make the endless runner be actually within the match three board so the two existed in the same play space. I decided that meant I needed a constantly moving board of tiles which could be somehow matched in lines.

Skiing down a mountain of tiles seemed like a natural fit. As the tiles would represent the terrain it didn’t make sense for new tiles to drop down the board when matches happened so I decided replacing them with snow tiles was a better option.

I considered how the player would be represented in the game for a while but couldn’t think of a nice way for the player to control the skiing character. In the end I opted for a skiing character with an automated pathfinding algorithm. I tried to make the path finding algorithm do the ‘expected’ thing as often as possible (note: this is often NOT the most optimal thing) but after play-testing decided that even that wasn’t enough and visualising the player’s path was also required.

The final part of the ‘make it a game’ requirements are that there is some kind of win (or actually lose in the case of endless runners) condition. I experimented with a constant camera movement speed and losing if the player dropped off the back but it felt unnatural so in the end decided to just have the game end if the player crashes. Once I’d tried that, it seemed so appropriate I did wonder why I hadn’t just done that in the first place! But that’s the point of the iterative process in game development. Try things and see what works the best.

That is the core loop of Ski Three and it’s what I implemented in the initial prototype. I thought it was a fun base for the game.

The level in which Ski Three plays out is procedurally generated with trees, houses and rocks. The procedural generation algorithm ensures no matches are ever generated and also has a gradually increasing difficulty as well as difficulty waves (where the density of tiles increases and decreases quite a bit locally as the overall density increases gradually).

Layered Mechanics

With the core loop in place I was able to layer some mechanics on top of that.

One of the things I really love about Alto’s Adventure is the risk and reward nature of combos and speed. I tried to recreate that in Ski Three using a match three combo system. If you make chains of matches close together without moving any other tiles then your character gets faster. Your scores for each match are also multiplied by the combo multiplier. It’s a fairly simple mechanic but I find it makes the game a lot more interesting once you’re getting good at it. And some people are REALLY good at it. The overall high score currently stands at 2,405,100 – which is approximately 2.2 million better than my best ever score!

The second layered mechanic I added was global leaderboards. Though I get really frustrated with global leaderboards in general as I tend to just be the N thousandth or million’th (depending on the popularity of the game) best player in the leaderboard giving me a strong sense of my mediocracy at playing games – presumably not the intended emotion by the developers?

I decided to do things a little differently. The game doesn’t actually tell you where you are in the leaderboard, just what the best score is. That would be pretty rubbish if it only ever showed the best EVER score which is way out of reach for most people so instead it has several leaderboards – one for the last hour, one for the last day, one for the last week and one for best ever. So there can always be a challenge for the last period of time which naturally is the one just better than you are. Of course, occasionally the best score in the last hour is the best score ever, but that only lasts for an hour and then it’s back to sensible scores (usually just a few thousand). I think that the leaderboards are the best part of Ski Three and I’ll definitely consider how to use them less directly as Ski Three does in future games.

The final ‘mechanic’ which is really a concession to Free-To-Play is that there is an in-game token system and you can only play if you have tokens. Tokens can be earned by:

  1. Coming top in a leaderboard (ie. best score in last hour)
  2. Waiting a day
  3. Watching an in-game advert
  4. Buying a Ski Pass (which gives you infinite tokens)

And that’s it.

What would I like to have added?

I intended there to be one further mechanic in the game which had to be axed to make the game fit into the development time I had available. I think it would be an interesting bit of game design though, so I’ll mention it briefly here.

It’s the Yeti! Here’s Ara’s awesome concept art for him:

The Yeti design was to be a character who occasionally appears ahead of the character (they appear on a tile as the character skis down the mountain) and upon getting a clear path to the character will chase them. The player must use the tiles as obstacles to stop The Yeti from being able to get to the player, which means avoiding matches – which would make the tiles clear away, while still matching three to progress. I think it’s an interesting idea and would nicely turn the match three mechanic on its head.

Summary

Ski Three is certainly a simple game, but it’s well executed and could be considered minimalist design rather than simple :-). I’m proud of it as my first attempt at designing and building a game from scratch. It’s been fairly successful by my standards in terms of number of downloads (over 25,000 so far), however I was unwilling to follow the Free-To-Play norms to the extent that would create a profitable game. Most of those norms feel like exploiting the player to me (eg. selling in-game currency the player cannot understand the value of) or at least a hindrance to the player enjoying themselves (eg. blocking gameplay at key moments with pay walls) which seems contrary to the job of a game designer. So if you’re looking for a free-to-play game that doesn’t exploit you – play Ski Three. I can tell it certainly doesn’t exploit anyone because I’m not making any money from it!

Next Page »
  • E-mail
  • Facebook
  • LinkedIn
  • Twitter

Latest Tweets

  • Just finished ironing out a few glitches in #ReEntry's space-time continuum #IndieDev #gamedev https://t.co/jhT2CWPSQd 26 February 2019 10:31 pm
  • #SkiThree version 2.0 is out! Loads of improvements, and it's now Pay Once and Play! https://t.co/LiPMP4QtkI #madewithunity #unityawards 30 September 2018 10:41 am
  • I’ve been blogging about disabling @unity3d editor's Assembly Reload feature while in play mode again:… https://t.co/GmdgVVtH8h 12 June 2018 11:44 am

Copyright © 2025 Cape Guy Ltd.