I make games

BlastCat Main Menu

For the longest time, the menu in BlastCat was in essence a programmer art placeholder. After finding some inspiration from other games, I had a solid idea on what to do. I was imagining a rotating planet showing different the different themes/worlds.

The very first iteration of the new menu. Note the curvature, it’s achieved by custom UI component that deforms an image by an AnimationCurve. Check here for another example of this component. It turns out that with actual graphics (not placeholder) the distortion actually looks like crap, in this case anyway. I’m sure I’ll find a good use for this component one day.

Here’s the final result. It uses essentially the same animation as as above, but somewhat snappier. Also the different background layers parallax slightly when transitioning. The gradient background is just a simple plane with the vertex colors lerped to transition between colors.

Float precision

Float precision issues is something you always know is out there, but something you rarely need to pay any thought to. As you can guess, it’s an issue we ran into. When making a build, one of our puzzles involving a rotating platform would seemingly work, but not fire the event it was suppose to at the end of it’s movement. Not firing this event essentially locked the entire puzzle as the player was not able to rotate the platform into any of the states required to proceed.

After a lot of debugging and generally tearing our hair out, me and Kenth finally found the cause of the issue. To keep track of what rotation (and if it had reached its destination) the delta rotation was calculated and then added to a variable each frame, simply:

actualRotationDelta = Mathf.SmoothDampAngle(...) - m_rotatedDegrees;
... do rotation ...
m_rotatedDegrees += actualRotationDelta;

It turns out, in this particular case, the value calculated by Unitys SmoothDampAngle got below the Epsilon value at high framerates. What this means in practice is that the value got so small enough for it to more or less equal zero, making it completely useless.

The way we resolved this issue is to simply check if whether value of actualRotationDelta is less than the Epsilon and if we’re close to the rotation we’re rotating to. This is not a optimal solution but considering the time constraints we had it was the simplest to implement.

actualRotationDelta = Mathf.SmoothDampAngle(...) - m_rotatedDegrees;

if (actualRotationDelta < Mathf.Epsilon && Mathf.Abs(m_rotatedDegrees – targetRotation) < 0.001f)
… snap to target rotation …
… do rotation…

Lesson learned here is to prefer lerp, slerp and smoothstep functionality in the future. Sure, it’s slightly more hassle but you’ll completely steer clear of these sort of issues.

The Puzzle Manager

Learning from the technical aspects of implementing puzzles and level events in CoBots, I was determined to come up with a solution for implementing the puzzles that generally would be easier to work with. The system had to fulfill the following requirements:

  • Easy to visualize
    Quickly being able to grasp how things are tied together saves a lot of time in the long run and makes it a lot easier for someone else other than the initial author to grasp the system
  • Easily interchangeable prefabs
    When working in a team on a project of this scope and on this timescale, rapid iteration is key. You should take this into consideration and design your tools to allow for it. Being able to easily switch out an entire object does just that.
  • Self contained and environment-unaware prefabs
    What does that even mean? It ties into the point above, for the different puzzle components/prefabs to be easily interchangeable the different instances can’t be very specialized as it would force you to do a whole bunch of setup whenever you want to switch something out. It also mean that the prefabs themselves should not concern themselves with anything outside their own hierarchy.
  • Keep serialized references to a minimum
    This further ties into the “interchangeable prefabs” point, to make that process as simple as possible, it’s easier if the references to a object is kept to a minimum. Also, Unity inspector object references is more or less an accident waiting to happen, something will happen which will magically break the reference connection for no apparent reason.

After giving it some thought and while taking all of the requirements above into consideration, the puzzle system that I came up consists of these three parts:

  • PuzzleTrigger
    When activated, will send a predetermined event to the PuzzleManager. How it’s activated and what event is sent is determined by what sort of object it is. 
  • PuzzleManager
    Holds references to all of the PuzzleTriggers and PuzzleTargets. Will convey any events from PuzzleTrigger to all the registered PuzzleTargets.
  • PuzzleTarget
    Listens to and acts upon events set up in the editor.

The different events that can be sent across the ‘network’ is simply an enum, containing generic, common events (ON, MOVING, IDLE, etc).. One compromise had to be made when it comes to the PuzzleTarget, you choose per instance what event act as activation and/or deactivation. This is because you often would like several different things affecting the same object but do different things.

Here’s a simple example of using the system. The lever and the door does not know of each others existence, not even at run-time. The PuzzleManager holds all the references and makes sure the event triggered by pulling the lever reaches the door.


Here’s the most complex use-case we’ve got for the system so far. It centers around a rotating platform which can be moved by several different levers. Each lever corresponds to a different rotational state and depending on what state it’s in, different doors are open.


It’s actually somewhat more complex than that, the player needs to move a minecart across the platform so the PuzzleManagers also manages which pieces of rail are currently connected. It also disables all the levers while the platform is moving, re-enabling them once it stops moving.

With all the green and yellow lines, at a glance it looks rather confusing, but while working with it all makes sense and is still somewhat clear. Admittedly, this case is more complex than the system was designed for, so some improvements can certainly be made. For example, some objects could be grouped together under a common manager if they activate/deactivate under the same circumstances.

I’m quite happy with this system as it achieved all the goals we set and stayed well clear of problems we’ve had with setting up puzzles previously.