Post Mortem 2: The Systems (1)


This is the second part in a post mortem series. Read the first part here.

Part 2: The Systems (1)

Picture: The imaginary flying-boat as seen in the Unity editor. Originally I wanted to incorporate actual Casino games from the latest Synty Studios Casino pack, but time ran out so I decided to fill the last part with a cinema.

3rd Party Assets Used

I haven't used a lot of tools for the production of the game. The only but significant major toolkit I am using is Game Creator 1, which I got on sale from the Unity Asset Store some weeks ago. I decided to put it to the test and use it for my entry, even though this proved challenging at times. I'll get into some specific issues later. However, with its solid actions and conditions system, it was a good starting point to iterate quickly. Apart from that, I am only using Unity's own Post Processing Stack (again, with specific challenges) and an improved WebGL template for Unity.

The Room System

Knowing which room a player is in was one of the most important points I wanted to cover. I used simple cubes placed over the entire map to mark the boundaries of rooms, and since not every room is strictly rectangular but sometimes contains other elements like the cooling room or the cells on the cargo deck, I sometimes used multiple of such cubes for a single room. I deactivated the mesh renderer and changed the collider to "trigger" so that I could attach Game Creator triggers to it.

With this setup, I was able to attach "on tag enter" and "on tag exit" triggers to ensure that certain actions would be invoked. I used "tag" instead of "player" for a specific reason I will explain later below when it comes to Game Creator 1 specifics.

I also created three more elements:

  1. A "RoomSO" scriptable object which holds some information about the room, including name and type (public, restricted, private).
  2. A "Room" script which I attached to each cube. This script holds 1. a reference to the "RoomSO" relevant for the room, and 2. two lists of game objects to activate or deactivate on enter.
  3. A "RoomHolder" script which is attached to each character in the game. This script holds a reference to the "Room" component belonging to the room GO (GameObject) the character is currently in.

One of the specific reasons I introduced the "RoomSO" was to be able to easily tweak data. I was actually planning to add more attributes to the SO, like a type (crew room, passenger room,...) and capacity, to dynamically populate the ship, but I dropped this idea due to time constraints. One more huge advantage that turned out during further development was that I could use this SO to check whether two characters were in the same room: they may be in two different collider GOs but as long as these two GOs use the same RoomSO, they're in the same place.

Whenever a player enters or exits a collider with a "Room" script on it, I call OnEnterRoom and OnExitRoom methods on the script. The script also has public lists of GameObjects to activate or deactivate on enter which I populated right from the editor. That way, I could exchange the full, regular wall with a cut away version once a player enters a room. Here's how this looks like for the restroom:


The trigger not only calls a method on the Room script. It also sends a message to the invoker (a player or NPC) which have the RoomHolder script attached. So whenever any character navigates through the labyrinth of rooms, the triggering collider/room is stored in a RoomHolder variable.

I also stored some information in variables, like a "Kid1Room" GameObject pointing to the first kid's room, etc., to have them handy when needed. One specific instance here is when the player switches to another kid: in that case, I need to restore the original state of the room where the current kid is in and then activate any room changes for the other kid. In hindsight, after understanding Game Creator's concepts better, that was probably a bit too complicated, but it worked well for the jam.

Here's the system in action: On the first screen, kid "Martha" is about to enter the restroom. The current room in the bottom left is still "Lounge". 

Now she pushes just a little bit forward, opening the door and entering the new room collider for the "Restrooms" area:


I created the replaced wall elements with ProBuilder (using their experimental Boolean CSG feature) and later Blender, because the meshes generated with ProBuilder were quite complex and caused some flickering issues with the bloom post-processing filter.

I stored such a fully equipped Room game object as a prefab for easy reusability.

The Player Switch System

Game Creator made switching players really easy. YouTuber RVR has a good tutorial on the topic. In essence, after whatever trigger caused the change, one

  1. simply disables the old player's Controllable attribute and enables the new one's, and
  2. changes the camera target to the new player.

Unfortunately, things became a bit more complicated.

Problem 1: The camera target.

Initially I had set up my "Follow Camera" camera motor anchor and look at target to always be the active "Player". I added an offset to the anchor to add some distance and to move the camera up a bit, but since the target was still the player's origin, it always looked down a bit, which I didn't like. So I gave all players a camera target child object at the height of their head. With the anchor's Y offset at 2 and the target Y at 1, I had the camera perspective I wanted to have.

Problem 2: The "Player" reference.

Configuring the camera to look at the "Player", or configuring any condition or action to work with the "Player", revealed some much bigger problems. The reason is Game Creator's PlayerHook component which is attached to every player. It stores a reference to the Player GameObject in a static variable upon Awake so that other scripts referring to the "Player" could make use of it. But with three players in the scene, that meant only the last one awoken by Unity would be stored in this variable and behaviour which was meant to refer to the currently played player ended up referencing another one. It took me some time to figure this out! The video by RVR above doesn't mention it at all, but one person in the comments pointed it out.  Unfortunately I didn't think about looking into the comments and spent quite some time debugging Game Creator. As a consequence, I built a simple action "Change HookPlayer instance to..." which I invoke on every player change.

One of the side effects of this phase in development was that I changed the camera motor from referring the "Player" to two "Global Variables", "ActivePlayer" and "ActivePlayerCameraTarget". These two variables are set whenever the player switches.

The other side effect was that I gave my player GOs a "Player" tag and used "on tag enter" and "on tag exit" triggers on my rooms, as described above, instead of "on player enter". I had no time to change it back after I identified the original issue.

When the player changes, I also call a method "OnKidChange" on the newly active kid's Room component. This method calls "OnExitRoom" on the other two kids' Room components before it calls "OnEnterRoom" on the active kid's Room. That way, the room layout is properly restored whenever the player switches to another kid.

Finally, I had to build logic to check whether the activated kid was detained in the flying ship's own jail cell or not. If yes, the kid's state wouldn't change to "Controllable"; if no, the kid would also play a nice little gesture animation.

I created three dedicated GameObjects with corresponding kid change actions and then called these actions either from the corresponding key press trigger or from the UI button.

To be continued

I hope this gives some insight into the inner workings of The Clipper Kids. Next time I'll provide some detail on the interaction system and the behaviour of NPCs.

If you enjoyed this or if you have specific questions, please comment!

Leave a comment

Log in with itch.io to leave a comment.