2D Game Development with Unity: Zero Gravity

This is a project for a game jam, Miz Jam 1, I participated in 2020. It is the very first game I developed on my own with Unity. Unfortunately, due to my lack of familiarity with the framework, I could not submit this project in time. Nonetheless, I still managed to finish implementing the game afterwards. This blog will walk you through the development stages of the game. So buckle up, and let’s ride in Zero Gravity.

Try out Zero Gravity!

Here’s the magical portal to the game. Please make sure to use the browser on a computer.

Theme & Design

There was no specific theme for Miz Jam 1. The participants were only asked to make games using the assets provided by Kenny. Therefore, I had to set up my own theme for the game. In the beginning, I wanted to make a 2D platformer since there were tons of tutorials on that. However, this genre turned out to be too ordinary for me, and I wanted to create something unique. I played around with the different parameters I could tune for a 2D platformer, and then it suddenly occurred to me: why couldn’t I just remove the basic element of the platformer, gravity, and see how it goes? It turned out that this was an interesting idea to implement. I added a little momentum to the sprite so that it spun around my cursor. Now the question was, what should the players do? For that, I aimed for a mechanism that was within my skills at the time, which was Endless Runner. This genre did not need me to spend a lot of time on level design, and yet I could have the freedom to decide how the players could obtain scores. Furthermore, I would like the game to be a bit challenging for the players so they need to spend some time to get good at it. A nice way to reach this objective was through the following mechanism:

  • Objective: The player has to use the spinning sprites to collect hearts with a ball to maintain a decreasing health bar.

  • Challenge:

    • If the cursor gets too far from the sprite, it will spin faster; hence the player needs to point closer to the sprite to reduce its momentum.

    • Randomly generated obstacles will block the player from collecting hearts. And with time passes, more and more obstacles will be generated (up to a limit). If the player gets trapped in an obstacle, he dies.

    • The boundary of the scene is lethal. If the player hits it, then it’s an immediate game-over. This prevents the player from arbitrarily waving the cursor to hit the hearts with random chances.

  • Aid: The player can press and hold the control key to freeze the sprite at the spot. Once the control key is pressed and held, the stamina bar will decrease rapidly after a short amount of time. This allows the player to have more control over the sprite but not exploit it.

With this design, the game is ready for some coding.

Code Structure

Player

The player scripts define how to control the cursor and the sprite. The scripts include:

  • PlayerAction.cs: Act towards user input.
    • Set a time interval for tracing the cursor.
    • Detect whether the left control button is pressed and deplete stamina.
  • PlayerController.cs: Update the sprite's values (e.g. health) and set the its state accordingly.
  • PlayerSensor.cs: For collision detection and debugging.
  • PlayerState.cs: Define the states of the sprite and a finite state machine.

The player scripts are straight-forward.

System

Audio

The audio system includes the background music and the sound effect when the ball hits a heart. The background music is funky to give the game a touch of arcade, whereas the sound effect is to reinforce user feedback when a heart is hit by the ball.

There is a GameObject called BackgroundAudio and its only purpose is to loop through the background music in every scene continuously. One thing to note is that this object needs to be a singleton as show below:

...
public class AudioManager : MonoBehaviour
{
    public static AudioManager instance;
    private void Awake()
    {
        if (instance == null) instance = this;
        else { Destroy(gameObject); return; }
        ...
    }
}

If BackgroundAudio is not a singleton, whenever you switch to the main menu scene, a new GameObject will be created and the background music will overlay each other.

The sound effect is attach to the heart prefab and played once before the heart object is destroyed.

Map

This category includes every object in the environment besides the player: the ball, the hearts, the walls, and the obstacles.

The main script is MapGeneration.cs which defines how everything should be generated. The player enters the game surrounded by pre-generated lethal walls. At the same time, two timers, mapRegenerationTime and obstacleChanceIncreaseTime, are set. mapRegenerationTime is for refreshing the obstacles, and obstacleChanceIncreaseTime is for increasing the probability (up to a limit) of generating an obstacle. When it's nearly time to refresh, the obstacle-dissolving effect is shown. Then when mapRegenerationTime reaches 0 (or a smaller value), all obstacles are destroyed. Furthermore, in every Update() call, the target number of hearts is checked and generated. The interesting thing is that the target number of balls is also checked and generated, even if there is only one. This is because the ball can overlap with a wall and gets destroyed. In this case, a new ball should be generated at a random position close to the player where there are no other objects.

Ball Regeneration Demo

UI

The UI includes elements like the health bar, the cursor, and the scoreboard across different scenes.

Special Effects

Special effects are added to the player sprite and the environment to make the game more vibrant. Here we have:

  • Particle Effects:
    • The sprite leaves a green trail which lights up the background when moving around.
    • When the ball hits a heart, the heart emits particles to its surrounding.
  • Camera Effects: when a heart gets hit, the camera shakes a little to emphasise the feedback.
  • Material Effects:
    • Blink: when the walls are about to be generated, they flash at their positions, and the player can still pass through them.
    • Dissolve: when the walls are about to be destroyed, the dissolve effect is played.

Special Effects Demo

Potential Improvements

Zero Gravity is playable but could be more fun. The only gradual change of the game is the increase of obstacles and hearts which doesn’t affect the gameplay mechanics. An improvement would be adding special items to the environment from which the player gains more abilities. For example, there can be items to freeze the environment or destroys all obstacles. Moreover, the environment itself can also change like having a wormhole to teleport the player from one location to another.


Download the Source Code

Please make sure you use Unity 2019.4.9f1 release to import the package.

Or you can also pull from this repository.

Previous
Previous

Virtual Reality Game Development with Oculus API: VR Ninja