top of page

Bushy Man

Screenshot (120).png

Demo in progress

We are currently working on a demo which will hopefully be ready for release by November 2025.  See the links below for more info on this project.

  • X
  • Instagram

History

I was one of many gamers who grew up playing early 3D platformers such as Spyro the Dragon and Crash Bandicoot, which among other fifth-generation titles are fondly looked back on for their innovation and charm, as well as their unmistakable impact on 3D gaming.

As I later discovered, the original Spyro trilogy belonged to a long-forgotten class of videogames known as Collectathons, which also included classics such as Banjo Kazooie and Jak & Daxter: The Precursor Legacy.  While popular around the late 1990's when 3D game design was still in a highly experimental phase, the genre has all but faded from the mainstream.  There have however been efforts to revive the Collectathon genre for a modern audience with titles such as A Hat In Time and Yooka Laylee, and recently in 2024 a team of passionate developers led by Tim de Waal had begun work on a new title starring the heroic duo Bushy Man and Bag Boy.  Drawing inspiration from Banjo especially, this title is intended to retool the core of Collectathon game design to better fit modern sensibilities.

In early 2025, word of this project reached my attention.  Eager to join the action and help recapture the magic of a great era in gaming history, I volunteered as a programmer for the team, implementing a number of gameplay mechanics.

Clock Rotation Mechanic

The first mechanic I built for this game was a clock rotation script.  Another programmer had previously written a script for continuous rotation of an object on a custom axis.  I was tasked with making a 'clockified' version of this script which performs the same function, but which pauses rotation at fixed intervals to emulate the ticking of a clock.

To achieve this, I copied over the continuous rotation script and slowly made the changes necessary to achieve the clock effect.  The original script handled rotation in the Update() method, but I knew the rotation for the clock script would be handled much better in a coroutine due to periodic downtime.  As such, I made a coroutine which rotates the target object for a given tick time then yields for the remainder of a given tick interval.

As a result, the clock script uses the same fundamental serialised variables as the continuous rotation script, but with additional floating-point values to control the time duration of each tick and tick interval as described above.

Unfortunately, the designers encountered an issue when using my script for certain set pieces, discovering that the rotational magnitude becomes increasingly less accurate with a high rotation speed.  To resolve this, I replaced the rotation speed variable with a 'rotation per cycle' variable so the coroutine can predetermine the correct end angle before starting each new cycle.  This fixed the issue and ensured the object would always rotate correctly without losing accuracy.

Jump-Based Rotation Mechanic

Another feature I was asked to implement was a recreation of the red-blue panels in Super Mario 3D World, as shown below:

 

These panels rotate between 2 different states each time the player jumps.  Another programmer had attempted to produce a script for this, but the result was janky and often rotated incorrectly.

I created a new version of this script and rebuilt the intended function from scratch, creating 2 coroutines: one for forward rotation and one for backward rotation.  Each time the player jumps, the script would run the least recently active coroutine.

In the same way as the original blue-red panels, this rotation script had to transition between two opposite rotations angled 180 degrees apart, rotating clockwise one way and rotating anticlockwise the other way.  However, I needed to provide a fix in the case that a platform was rotating the wrong way for both transitions.  I achieved this simply by copying over the rotation axis customisation from the clock script, allowing the level designers to flip the local axis for any platforms rotating incorrectly.

While it mostly worked fine, one issue arose in the case that the player jumps before the panel finishes the current coroutine.  When this occurred, the platform would snap to the initial angle at the start of the new coroutine rather than simply starting the rotation from its current position.  To fix this, I used a common floating-point variable for both coroutines which represents the lerp value from one angle to the other, with 0 for the start angle and 1 for the end angle.  Now, the coroutines were identical except one slowly increases the lerp value to 1 while the other decreases it to 0.

State-Based Rotation Mechanic

Once I was finished with the jump-based rotation mechanic, I created a new script for making an object rotate back and forth along a given axis.  This was simple to achieve, as I was able to rework the jump rotation script by setting the coroutine on an endless loop with a boolean value representing the current state, swapping its value at the end of each cycle.

After creating this script, I was asked to make a more complex version for set pieces such as a hammer blockade (shown below), which switches between 3 different angles and performs each rotation at a different speed.  To achieve this, I created a serialisable struct for storing the new angle, duration, and pause time for each rotation in the cycle.  The multi-state rotation script contains an array of instances of this struct (all customisable in the inspector), iterating through each instance in a continuous loop.

Movement Trajectories

After programming the majority of scripts described above, I felt that the rotation looked quite stiff at particular speeds.  At this point, all rotation I had implemented was being performed at a constant speed, and I decided that there needed to be a smoother alternative so that rotations could either start or end at rest.

My solution was a sort of emulation of the LeanTween engine for Unity, containing an assortment of basic spline curves.  I built a static class to encapsulate these functions, allowing my rotation scripts to derive points situated on any one of these.  Each new trajectory was formed by taking a particular segment of a standard sine or tangent function, then offsetting and rescaling the segment to pass through the points (0, 0) and (1, 1).

I modified the coroutines for all my rotation scripts to work out the correct rotation at each point in the cycle using the Quaternion.Lerp() method, using the derived point from a specific trajectory for the lerp value of each frame to create the respective animation.

trajectories.png

Rhythmic Platforming Mechanic

One last gameplay mechanic I was asked to implement was rhythm platforms which turn on/off based on a set pattern in sync with the musical beat.  This is yet another mechanic which appears in Super Mario 3D World, as shown below (0:55):

 

To achieve this, I wrote a script to manage a set of blocks using a serialised 'beats per minute' variable to control the tempo of the sequencer.  I also created a public struct representing each block to be managed by the sequencer: containing references to the block object and its 'off' version (a collision-less placeholder to be shown in place when it is in the 'off' state).  The struct also contains an array signifying the pattern for the respective block, with the sequence for that block looping each time it reached the end of the array.  This array originally contained integers representing the length of each 'run' (the number of consecutive beats the block would stay in one state before switching to the other state - a common data technique used for 2-bit images).  However, the designers found the run system too convoluted to work with, so I eschewed it in favour of a simple boolean array representing the state of the block at each beat.

Portal Effect

Prior to joining this project, I had been learning about using the Unity stencil buffer system to create interesting VFX such as the portal effect from the original Spyro trilogy, as shown below:

 

I offered to use my knowledge to recreate this effect for Bushy Man, allowing the player to view the skybox of another realm through the corresponding portal.

My technique was to build the portal using 2 identical shapes through which the skybox would be visible.  These used 2 different shaders: one which writes to the stencil buffer before any skyboxes are rendered (to encapsulate the portal skybox), and one which writes to the depth buffer before all other geometry is rendered (to block all other objects).  I also set the portal and skybox to a specific layer, then added object render features to the URP forward renderer asset we were using to render every layer (except the portal/skybox layer) only if the depth buffer value was 0, which would fail if a portal had written to it already.

 

The portal skybox takes the form of a concave sphere located in the same position as the portal itself.  For the effect to work, I had to edit the skybox shader to read from the stencil buffer to check that the corresponding portal has written to it.​  Unfortunately I ran into a setback: the skybox materials were using a built-in Unity shader which did not contain a physical file in the repository.  To resolve this, I had to locate a physical copy of the skybox shader online and load this into the project, then set all skybox materials to use this instead.  From there, I was able to add a stencil buffer reader to the new shader.

My original idea was for each portal (and matching skybox) to use a unique Ref value to ensure each skybox would only be visible through its own portal.  However, there were still issues when portals were overlapping, as each portal could still be viewed through other portals.  I first considered editing the portal shader to read from the depth buffer before writing to it, checking it was empty before rendering.  However, thinking this solution through was tricky.  I eventually succeeded by creating a script to routinely assign a Ref value to each portal (and skybox) based on its distance from the camera at each frame (giving the highest value to the closest portal), while setting the portal shader to render only if its Ref value was greater than the current stencil buffer value.  This ensured the closest portal would render unconditionally while blocking all others.

bottom of page