Starting To Feel Like a Real Game

Raymond Mills
7 min readOct 30, 2023

--

A quick recap of what we have done so far. From a day one prototype to a functional game.

Now that we have added a few art assets, and core functionality to our game, everything is starting to come together. This is really starting to feel like a game that people will enjoy mastering.

Recap

Simple Player Movement in Unity 2D

We started by giving ourselves control of the player by setting our WSAD and arrow keys to translate the transform of our player based on input.

You can follow along with my tutorials by starting with Simple Player Movement in Unity 2D.

Variables: The Building Blocks of Programming

I then discussed the necessity of variables in programming. Everything in your game is going to need variables to exist and interact with any other elements in your game. These are the building blocks of programming. Everything from health, to ammo, to inventory, to score and equipment, these are all controlled by variables.

You can learn more about variables in my article Variables: The Building Blocks of Programming.

Pseudo Code: Why?

In this article I went over pseudo code. What it is, why it is useful and when should you use it. This is the act of writing your code in plain english. Either as a note to yourself for what you are trying to accomplish, or if it is not clear what the function is doing at a quick glance. This is also useful to clarify certain functions for your dev team, for the same reasons. By add “//” to the beginning of any line, the following text will be ignored by the program. This can also help if you have code written that is not ready for testing or has not been implemented yet.

To read my article on pseudo code refer to Pseudo Code: Why?

Instantiating & Destroying GameObjects in Unity

In this article I added the enemy spawn routine as well as firing lasers using the Space Bar. Enemies are instantiated at random x coordinates, with the y coordinate fixed so they appear at the top of the screen. They then descend downward at a fixed speed and are respawned if they reach the bottom of the screen without being destroyed.

To see the code that made all this work see my article Instantiating & Destroying GameObjects in Unity.

The lasers are done similarly the enemy spawn routine. Only this time the laser is instantiated at the player location and travels up at a slightly higher speed. When reaching the top of the screen, the laser is then destroyed instead of being reused like the enemies.

Creating a Cooldown System

Before this point the laser will fire as fast as you can press the Space key. Here I show one way we can control the rate of fire by implementing a cooldown system. First, we added a few variables to the existing script for our laser behavior.

_canFire will be the ability to fire your laser and _fireRate will be the cooldown time before you can use _canFire again.

Using these variables we can now control how fast the player can shoot. If the Space is pressed again before the wait time is resolved the button will not fire a laser. In my code the lasers will fire continuously while the Space is held. This is still controlled by the same variables limiting the speed at which lasers are instantiated.

&& is a two boolean expression, it returns true if both expressions are true; otherwise, it returns false. Time.time is the amount of real time that has elapsed in this frame.
Now we can only fire lasers if _canfire = Time.time + _fireRate

Introduction to Physics in Unity:

This is where we add some physics to the objects in our game. We add collision to the objects with Collider2D components and give the enemies a Rigidbody2D component as well. At least one of the objects in a collision needs to have a rigidbody component. Since the enemy needs to behave differently if it collides with a player or a laser, it makes sense for the enemy to have this component.

OnCollisionEnter Vs. OnTriggerEnter — When to use them?

This article ellaborates a little deeper on OnCollisionEnter and OnTriggerEnter. It explains which to use in different situations. In the example, I am using OnTriggerEnter2D. Which triggers an action when a collision is detected between the two objects. The objects are able to pass through each other instead of bouncing of each other. In this case the objects are destroyed as part of the collision.

Script Communication in Unity: Using GetComponent

This is where I talk about script communication. In order for one object to interact with another object you need to communicate with the other objects script. Here we do this using GetComponent. First, we establish “damage” as a method that reduces the number of lives the player has remaining. This is done in the player script, where our lives are tracked. Our player starts with three lives. When the damage method is called, we reduce the number of lives by one until zero lives remain and the player is destroyed.

private Int _lives = 3 by default, every time the method is run _lives is given a new value.

Now we need to use the GetComponent command to access the player script when the enemy object collides with the player object. This example uses the tag “Player” as an indicator as to what object the enemy is colliding with. There is a different outcome if the tag is “laser” instead. If the object the enemy collides with is labeled with the tag “Player” then we will access the player script and run the Damage method within.

Here PlayerMovement is our player script, which will be accessed when “player” is used in our code. (player.Damage will call the Damage method within the PlayerBehavior script.)

Within the SpawnManager script there is a method that is triggered when player lives equals zero. This method tells the SpawnManager to stop instantiating enemies when the player is destroyed. Instead of accessing the SpawnManager script every time the player takes damage, I have the player script GetComponent to communicate in the void start method. This allows the communication to be accessed whenever it is needed instead of using GetComponent every time the Damage method is called. This can save the resources used to GetComponent which can cause your game to slow down if not used efficiently.

When lives equal 0 the player is destroyed and OnPlayerDeath is called from the SpawnManager script.

Coroutines with Unity!

This is where I introduce you to coroutines, specifically IEnumerators. It’s a bit more complicated than any of the previous methods. If you want a full walkthrough of writing and utilizing the code, see my other article on Coroutines with Unity! I use an IEnumerator in my SpawnManager to continuously spawn my enemies at set intervals. When OnPlayerDeath is called, the SpawnManager stops spawning enemies. This is done using a while loop. In pseudocode it says, while the player is alive spawn enemies, when the player is dead stop spawning enemies.

This is all built around the private bool _stopSpawning.

Containers: Keep It Tidy!

When the game is running and enemies are spawning, the hierarchy window can start to get a bit crowded and flooded with enemies. We use a container for the enemies to spawn into. This is shown in the hierarchy window as container name with a drop-down menu that can be collapsed to see more of the hierarchy window. To see how to make this work, read my article Containers: Keep It Tidy!

You can expand or collapse the container to see or hide the contents in the hierarchy window.

From Prototype to Work of Art

After replacing the primitive shapes that were serving as placeholders for the game objects with some 2D sprites and giving the game a background, we turn our prototype into a work of art. With core functionality and a few art assets, this is starting to feel like a real game.

Using two cubes, one for the player and one for the enemy, and a capsule for the laser, and assigning a basic material for color we have everything we need to build a functioning game.
2D sprites attached to your game objects turn this prototype into a full ready to ship title, almost.

Benefits of Prototyping Without Assets

My last article discusses the benefits of prototyping without assets. This one touches on the time it takes to create assets as well as the cost. By using primitive shapes as placeholders, the developers can focus on making a game that works instead of trying to work around existing elements. This allows the developers to have a little more creative freedom. Focusing more on the functionality than the aesthetics, the core of any good game. If an idea or project is scrapped, you will save a lot of time and money by not spending them on assets that are no longer needed.

My next article will cover Animating Sprites in Unity. I will show you how simple Unity makes it for us to add some animation sequences to our game.

--

--

Raymond Mills
Raymond Mills

Written by Raymond Mills

Unity Software Engineer, I am in an apprenticeship with GameDevHQ. I am taking the steps to become a professional Unity Developer.

No responses yet