Home > Portfolio > [Equilibrium]

Equilibrium Game Prototype

Ideation & Set up

The first step of the process was an ideation phase where various types of duality was explored, resulting in 24 pairs such as “up and down” or “hot and cold”. At the next step, 33 verbs were brainstormed to explore options for actions in the game.

From here, the duality pairings were matched in various ways with the different verbs to explore what kind of solutions were possible. Three ideas started to emerge and the idea that presented the clearest path forward, considering the time restraint, was chosen. The idea was formulated as:

  • People drop dead and you have to collect their souls before time runs out.
  • You need to balance your soul box.
  • You need to feed the life orbs to keep the seesaw balance representation in equilibrium.

After this, a simple list of mechanics was constructed to make it clearer what needed to be implemented:

  • You can harvest souls.
  • They stay in your soul box.
  • There’s a seesaw status representation
  • “Life orbs” drop at certain times and you can deliver souls to these orbs

And lastly, as a part of the ideation phase, the scene for the game was set:

  • You’re in a city block
  • There are X number of people in the scene from the beginning
  • Life orbs drop randomly
  • When a person dies, you have Y seconds to get to them to collect their souls
  • You have Z seconds to deliver a soul to the life orb before it despawns

In the interest of time, the idea was conceptualized as a 2D platforming type of game, focusing on a smaller, side scrolling setup.

Development

Aspects of some key game objects.

Player Character

To implement the player character, the Unreal Engine 2D side scroller template was used as a reference and aspects not relevant to the game were emitted. The player character blueprint features the movement and action input, as well as the variables to keep track of player progress and the timer. This includes:

  • Moving left
  • Moving right
  • Jump
  • Climb
  • The ability to interact via an interaction interface
  • Variables tracking souls and orbs captured, lost, and collected as well as a function to calculate the status percentages for these aspects in the UI
  • An OnTick event to handle the count down and functionality to add end screen UI at 00:00
Below is the timer set up, based on this tutorial (link). It was altered to feature seconds and centiseconds instead of minutes and seconds. The idea behind this is that it will introduce more of a sense of urgency for the player.

The blueprint simply checks whether any of the losing conditions are met (are soul or orb percentage at zero?) If any of the losing conditions are met, a simply “Game Over” screen is added to the HUD. Otherwise, the timer counts down by 1 every centisecond unless the centisecond variable is at zero, then it subtracts one from the seconds variable.

Souls

The souls were implemented as a type of Character Class to ensure that it had the ability to move around. The Soul Object contains five events, not counting the BeginPlay event which only sets the characters Flipbook (animated sprite asset).

A box collision checks whether the player character was close enough to the soul to be harvested through OnComponentBegin or EndOverlap. This then sets the OverlappingSoul variable of this particular instance to ensure that the “correct” soul is harvested.

A “Die” event calls a function to randomize death time, i.e. the time from the Object instance being spawned to when it dies. This was set up to ensure that not all souls perish at the same time with to goal to implement a more organic feel. The event then utilizes the RandomizedDeathTime variable as a delay before it runs the death animation and finally sets the Dead Flipbook asset for the character, allowing the player to harvest the soul.

The “Despawn” event utilizes a similar function to the RandomizeDeathTime function and attached variable to create a similar organic sense of randomness for each individual soul. If the Despawn time runs out, the player character SoulsLost variable is incremented and the object is destroyed.

These two events, Die and Despawn, are checked on tick from the moment the IsSpawned bool is set to true (which, naturally, is set once the Soul Instance is spawned.)

The actual harvesting is controlled through the Interact event, referenced in the player character blueprint on the Interact input action. This event first ensures that there is an OverlappingSoul through a validated get, set at the overlap with the box collision. The event then ensures that the soul is de facto dead, increments the player character SoulsCaputered variable, clears the OverlappingSoul variable and lastly destroys the actor.

AI

The souls move at random in designated NavMesh areas through a simple three task behavior tree:

First, the FindTarget task ensures that the soul is not dead. If it is not dead, a random point is found and set as the new destination. A preexisting Engine MoveTo task is then executed, follwed by another preexisting task Wait for 3 +-1 seconds before the pattern repeats. Again, this along with the randomness was introduced to ensure a more “living” world (ironically). The movement of the character also feeds into the pace of the game, just like the use of centiseconds, to create more of a sense of urgency.

Life Orbs

The orbs have a similar set up to the souls with a few differences. First of all, the orbs do not die and therefore simply requires the Despawn functionality. For simplicity, this is then directly added to the tick. They are also considered “dead objects” and require no AI.

The Interact event for the orbs increments the player character SoulsDelivered variable and decrements the SoulsCaptured variable to keep the counter in the “soul box” where it needs to be.

Lastly, there are three types of orbs which ignore (or do not ignore) two different custom Collision Object Channels. This is implemented to make sure that not all orbs end up on the same height (for example rooftops). This was designed to encourage player movement.

Spawners

The game features two types of Spawn Objects: one for orbs and one for souls. Their spawn is controlled through a custom Spawn event.

A RandomizeSpawnTime event creates a random delay in a float range to spread out the spawns. After this, a simple SpawnActorFromClass node spawns the object in the scene at the location of the spawner. Souls are spawned on the ground and orbs are spawned in the “air” above the camera view to introduce a bit of variety and enhance the sense of one being people and the other being an otherworldly object.

This Spawn event is then called on tick to make sure the objects keep spawning throughout the game.

Art & Animation

The art in the prototype mixes 2D vector graphics and pixel art. The static elements, such as the background and the houses (i.e. the platforms) utilizes vector graphics created in Adobe Illustrator as a variety of assets which are combined to get a more varied look for the environment.

The player character, souls, and orbs are created using pixel art. This approach was used partly to simplify the process and allowing for more simple animation work, but also to make a distinction between what is interactable and what is “static” or the background. This pixel art was created in Photoshop.

The level was first blocked out in the engine using simple blocks. The art was then created to allow for these different levels for the player to walk on. The levels are connected with ladders, which has a box collision that sets an IsLadder on OnComponentBeginOverlap. If IsLadder is true, the player can climb to new levels using the InputAxis Climb.

These various levels utilizes different meshes and the custom Collision Object Channels to work in tandem with the various Orb Spawners.

Cut elements & Changes

This section outlines a some elements which were cut or significally changed from the ideation phase.

Seesaw

The original plan was to use a type of Seesaw to communicate the balance in the world, which would create a quite literal visual representation of the equilibrium. However, the issue with this would be that the player could simply be passive in the game if the weight was heavier on one side than the other and wait for orbs or souls to despawn. To counter this issue, the “seesaw” was split up into two progress bars counting down as either Souls or Orbs were lost. The problem with this solution is that it simply creates a sense of “staying away from loss” rather than actually progressing or doing good.

This could potentially be solved by, for example, implementing more positive feedback loops using sound effects and/or visuals, or other mechanics further ticking up the progress bars.

Balancing the Soul Box

Another idea was to have some kind of inventory management, meaning that the player couldn’t collect as many Souls as they came across and simply carry them around. The upside with this solution would be that it could promote player movement as they’d need to move around the map to deliver the Souls to the Orbs rather than first just “hang around” and harvest as much as possible and then move to a new “segment” where they only deliver them. This is deemed to be a interesting area of exploration for future development.

Challenges & Areas of Improvement

Below are some thoughts on optimization, areas of improvement, and future work.

Scoping

The initial goal was to finish this during the Global Game Jam weekend but unfortunately this goal was not met. This was mainly due to the project being over-scoped. A more reasonable scope would have been to block out the level to create the basic level set up and game loop, which is the amount of work that was finished at the end of the weekend. Having one or two more people on the project could also be a solution to reach the goal on time in this particular project.

Climbing

An issue with the prototype in its current state is the climbing mechanic. As it is set up right now, the player continuously “auto-climbs” up the ladders after simply pushing the “W” or “Arrow up” key. This is not as intended, but rather the character should only climb up when the key is down. This functionality was successfully implemented, but caused another bug where the player character would constantly set in “falling” mode when on higher levels inhibiting moveability.

2D in 3D Scene

The current prototype is set up as a 2D game in a 3D scene while not really utilizing any of the upsides of a 3D environment. This is of course a very expensive set up with no upsides as it stands. The reason why it’s built like this was simply in the interest of time as the designer aimed to learn many new aspects during a short period of time, and having more experience working in 3D this solution was chosen simply to make layouting and programming easier. Should the prototype be rebuilt, it could favorably utilize the upsides of 3D, perspective cameras, and/or the lighting opportunities that comes with it or be rebuilt as a true 2D project to make it cheaper.

Hobby project

Equilibrium is a game where you aim to keep the balance between life and death in the quaint little town of Equilibrium. Navigate the town streets to collect souls and deliver them to the life orbs to keep the balance.

This project sprung out of the Global Game Jam 2022 and its theme “Duality”. The project had two main goals:

  • To create a game from scratch without starting from premade engine assets or templates

  • To finish a prototype from ideation to playable game loop

Software

unreal_logo
photoshop_logo
illustrator_logo

Details

Areas of Responsibility

Ideation, sketching, prototyping, engine implementation, Visual Scripting, game design, art asset creation.