Two Times Pi
Description:
Kaskade is a minimalist strategy puzzle game developed in Unity and available on Steam. Players construct paths through a network of conveyor belts to ensure deliveries reach the correct destination. Over time the network grows and players unlock purchase and upgrade abilities to better manage the growing complexity of the system they oversee.


The gameplay map is a two dimensional array of conveyor belts that dictate the movement of deliveries throughout the map. Each belt in the array can be rotated by the player to one of the four cardinal directions. Each belt can also be upgraded by the player with abilities like jump (sending a delivery over its direct neighbor), double jump, and compact (combining deliveries of different colours) etc.
​
Each belt needed to be able to receive a delivery when empty, request shipment of a delivery to its target neighbour, wait to ship that delivery and finally trigger the sending of the delivery. At the same time, the belts needed to queue up and then implement any player input that requested the belt change where it sends its deliveries and how (e.g. via a jump).

To my mind, a FSM seemed a good fit as an architecture for handling this process and so each belt in the network is an independent state machine for controlling deliveries.
​​
I can see an argument that a manager class that controls all of the belts in the network being easier to implement and debug. As a personal project it was nice to be able to try the challenge of creating a decentralized network instead.
​


The game needed a means to detect which belts belong to a path that traces back to a spawning belt and to trace paths through the array. To do this successfully, required accounting for factors like:
-
Paths through the array branch when they reach a switch belt. (The switch ability is an upgrade that a player can make to a belt and it sets the belt to send deliveries on to two different directions.)
-
Belts can be oriented in such a way that a path loops back on itself.
​
Below is the code used to trace paths through the array.
​
-
It starts from a given “starting” belt and then recursively explores connected belts to build a Path object (a tree like structure).
-
The method uses a depth counter (beltDepth) to limit recursion. It also maintains a set of visited belts to avoid infinite loops if a belt is encountered more than once (namely, in a loop).
-
The recursion stops when certain conditions are met, like reaching the end of the belt path or if the current belt has no valid target neighbour for further traversal.
-
The output is a Path object that encapsulates the current belt and a list of paths from all the connected belts, representing the entire route through the network from the starting point.


Here the gizmos represent a path through the network. Pink spheres are parent belts of the current belt. Green spheres are child belts of the current belt. Red spheres are the previous children of the current belt (the children before it was rotated). And the blue and yellow spheres are the children of a switch belt.



In the game, there are a variety of ways in which a delivery (a "Widget") can move around the map. For example, they can bet set to jump from one belt to another, following an arc between two belts and rotating 180 degrees around the axis perpendicular to the direction of travel.
​
During production the number of different movement types grew and I knew there was potential to add more. I decided to refactor how I handled movement, adopting the Strategy Pattern, making the code cleaner to manage and better designed for extension.
Below is the WidgetMover, IMover and JumpMove classes.
The Widget Mover Class holds a dictionary of enums (that represent different movement types: Jump, DoubleJump etc.) and implementations of an interface IMover that handle movement behaviour. Movement is requested (through MoveToTarget) and the dictionary is checked for the appropriate movement strategy, which if found, starts its coroutine. ​
​
The IMover class is the interface that each movement strategy must implement.
​
JumpMove is an example implementation of the IMover interface.


x


x


x

