Zombie Escape Video Game

2024-04-15

A simple 3D first-person shooter made in Unity for a Game Design project.

I made this game as part of a Level 1 Award in Game Design and Development. I wanted the opportunity to push myself, and to test the skills I had already learnt, such as modelling in Blender, and C++. I followed the Build Your Own First Person Shooter in Unity guide by Wireframe, as a tutorial for getting starting in Unity. This project was done over eight days in order to be completed by the final deadline. As well as creating the game, I also had to document every step using screenshots.

Day 1

On the first day I created a character model and gave it simple movement so the player could move around using WASD. Then I added a jump ability. Thanks to built in components in Unity, this was as simple as adding Rigidbody physics to the player model and enabling gravity, then adding the code to allow the player to jump. Next I added the ability to look around with the mouse. Once the player could look around, I created an Empty object to act as a spawn point for bullets (this is what will become a weapon). I then created a bullet object, which takes the camera rotation on spawning, and travels in that direction for around 2 seconds, or until it hits something. Then I added a crosshair by placing a canvas on the screen. This is like a surface where we can place GUI and HUD elements for the player to see. Finally, I moved the Empty Weapon object to be a child of the camera. This now allows us to shoot in 3 dimensions, instead of on a flat plane.

Next I added a simple enemy. This started out as a Capsule object shaded green, and stayed that way until I had time to shade something fancier. I gave this object a Nav Mesh Agent, which is a component that controls the AI navigation for the Enemy. It’s target is constantly set as the Player, meaning its only goal in life is to chase the player. In order for the Nav Mesh Agent to work, it needs to have an attached Nav Mesh. To create this, I set the floor of the game as a static object, and then baked a Nav Mesh and attached it to the Nav Mesh Agent. This is where I had one of my first hiccups, as I attached the Nav Mesh Agent to the camera on accident, rather than the zombie. Fixing that solved the problem though. I then turned the Enemy in to a prefab, which means I can use it again and again in the scene without having to make it from scratch every time.

Then I added collisions, to allow the game to recognise when the Player and an Enemy collide. I also add collisions to the bullets from the gun, and add a health system to the Enemy. This means if they get hit by 3 bullets, they are removed from the game. To allow the Enemy to damage the Player, I gave them a script called SendDamage, which upon contact with a Player, sends the Player a message, telling them to deduct an amount from their health. I then gave the player a script that can respond to this message, as well as setting their initial health pool to 100. Then I added a text object to the canvas (which contains the crosshair from earlier), to display the health to the player.

Day 2

I started this day by creating a spawner for the enemy. This spawner is an Empty object that spawns a zombie and waits until it is killed. If its zombie is killed, it spawns another after a short delay. Then I make some minor adjustments to the navigation script for the zombie to ensure it can move when it spawns in.

Next up, I created a particle effect in Unity. This will be used to add extra flair to bullets hitting things. The particle itself needs a second camera to stop awkward clipping behaviour. The cameras need to be adjusted so that the main camera is not rendering any particle effects, and the secondary camera is only rendering particle effects. Then I go about changing all the particle settings to create an aesthetically pleasing particle effect. I also add a script to destroy the particle effect once it is done to minimise clutter. Then we can go into the script that registers bullet hits, and add spawning mechanics for a particle effect.

I also added some extra physics to the zombie navigation mesh to add some knockback, which sends the zombie backwards and disables its navigation for a short time.

Finally, I added a rounds system. This creates a round where every spawner is triggered to spawn a set amount of zombies. Once all the zombies in the wave are defeated, the game congratulates the player on completing a round, and then begins the next round. This took me ages to implement. I had a Null Reference Error calling on a line it shouldn’t be calling on. It took me ages to figure out that it was a different null variable triggering the error, and the error just seemed to be calling in the wrong place.

Day 3

The first thing I did this day is adding a Start Menu to the game. This involved creating a different scene to hold the Canvas for the start menu. The menu had both a Start and Exit button, and I gave each of these buttons functions using OnClick events. I then edited the build setting so that the main menu is the first scene in the build, and the actual game is the second scene.

I then spent the rest of the day modelling and texturing the enemy model for the game, details of which can be found here or below.

Day 4

I started this day animating the zombie in Blender. Again, details of this can be found below.

Once I had a textured and animated zombie, I imported this into Unity. When importing a Blender object into Blender, you have to extract the materials from the Blender object, which creates an empty Unity material. Then you reassign the texture image to the material, and then the model should be textured as it was in Blender. Next we can create an Empty object inside the Enemy object, and drag and drop the model into this Empty. Then we untick the Capsule to stop it from rendering.

The next thing I did was to extract the animation from the blend file, rename it to Walk Cycle and set it to loop. Then I created an animation controller for the zombie model, and set up the walk cycle.

I then began to create the level blocks for the game. One of the restrictions for the project was to use royalty free images. I found it hard to get suitable royalty free images to use as textures for level blocks, so I went for a walk around the neighbourhood, taking pictures of interesting walls and floors. I then created simple level assets and textured them in Blender. I could then import these into Blender in much the same way as the zombie. More details on the game assets I’ve made can be found here, or below.

Day 5

After making the level blocks, I then turned them all into usable prefabs. Each level block needed to be made static, allowing them to be registered by the navigation mesh. I also had to add a Mesh Collider to stop the player from going through the walls. The prefab allows me to drag and drop the walls as I wish into the game scene.

I then went about creating a map for the player to navigate around. I also added extra spawners to increase the amount of zombies in the level.

Next I got rid of the large directional light above the scene, and added a smaller weaker one with a blue tone to make the level darker. I also turned down the ambient lighting strength to make the overall scene darker. I also added some weak ambient occlusion to darken the shadows between objects and surfaces to make all the corners of the map darker and spookier.

Next up I created a gun in Blender. I wanted this to add more realism to the game when shooting, rather than the bullets spawning out of nowhere. Once I had the gun model, I made it a child of the Weapon object, which itself is a child of the camera. This means the gun will always track with the camera.

I then began adding noises to the game. This involved getting free-to-use sounds off of the internet. I would then trim them down to size in Audacity, and then adding code to the relevant scripts to play noises when appropriate. Each object wanting to play a noise is required to have an Audio Source component in Unity.

In the sound craze, I added a gunshot noise to the gun, and grunting noises to the zombies when they get hit. I also added footsteps for when the player moves. This was an interesting script, as it took a list of footstep noises and played a random one at regular random intervals while the player was walking. This involved adjustments to the player movement code to register when the player was walking or not.

I then added an ammo system to the game. I gave the gun a magazine with 10 bullets in it, and around 300 spare ammo. If the player wants to fire the gun, the script checks if the gun has ammo. If it does not, the gun plays a clicking noise. If the gun has ammo, it shoots, but also reduces the amount of ammo in the gun by 1. The player can reload by pressing R. This tops up the bullets in the mag and takes them away from the spare ammo count. It also plays a sound.

Day 6

The first thing I did today was create a text object in Blender, and use it to add graffiti to the game.

Next I created a first aid kit in Blender, which could be used as a pickup to heal the player. I then created a pickup script, which will detect if the player collides with the pickup. If they do, the first aid kit is hidden until it re-spawns and the player is healed.

Once this was done I went in and made major changes to the way Player health was handled. I completely rewrote the script so that the player can take damage and heal. It also added a feedback system, consisting of a sound and a red flash, to let the player know they’ve taken damage.

I also added a simple script called LightControl. This allows the point lights on the pickups to be turned on and off, depending on when the first aid kit is visible. I also added sounds to the health pickup to make it more entertaining.

When testing the game, I ran into a problem where it was possible to reload after you had reached zero spare ammo, allowing you to have negative ammo. To fix this, I added a check to the reload script to see if there player has zero ammo. If they do, we cannot reload at all. If they have some ammo, but not enough to fill the mag, we just put all that spare ammo in the mag. Otherwise, They can just reload as normal. I also replaced the raw value with a constant variable representing the capacity of the mag. This just makes it easier to maintain the code.

Now that it was possible to run out of ammo, I added an ammo pickup. This functions in much the same way as the health pickup, but increase ammo instead of health.

Next I began adding doors. I made a simple door model in Blender and imported it into the game scene. Then I added a script called TriggerAction. This acts as a base class for any triggerable object. It is an abstract class, which means it must be inherited. Then I created a corresponding trigger volume for the door, which is just a cube shaped Collider object. The object must be set as a trigger volume so that the player can walk inside it.

Day 7

I then added a simple script to the Trigger volume. When an object enters the trigger, it checks to see if it’s a player. If it is, we call the a method that triggers all the Triggerable objects assigned to the trigger.

I then added a script to the door so that it will move when the corresponding trigger volume is entered. The moving is done as a coroutine so the door doesn’t have to call an Update() function every frame. I also added an eerie squeaky door noise to play when the door opens. This is all saved as a prefab, so that I can easily put doors in whenever I want them.

I then spent some time investigating adding a second floor to the game, but decided it was out of scope for the time I had left.

With the code I had so far, I started to work out what how I wanted the game to play out. I decided the exit door was going to be locked behind a set of door controls, which needed the power to be turned on to use. To this end, I modelled a door control panel for the player to activate doors, and an electrical box for the player to turn the power on.

Day 8

The first thing I did today was add some spooky music. This was controlled by a standalone AudioSource. I dropped the sound file I had into the clip box, and then checked the Loop box to get it to loop.

The next thing I did was a small amount of rearranging of the map. This was to improve the flow of the game and make it more progression based, rather than free-flowing. It also allowed me to hide objectives around the map.

I then modelled a key in Blender for the player to pick up. I added a script to the key to enable picking it up. Upon collision it sends a message to the player to add the key to their inventory. I then created a simple inventory for the player to keep track of items. I also added a method to check if an item was in the inventory.

I then created a door to the electrical room. This had a corresponding trigger volume. I wanted it to be locked, meaning the player needed to find the key to open the door. I created a new trigger script that has a condition attached to it, which checks the player inventory for the key. If it does, the door can open.

I then added some more signposts to the level to help the player navigate around the maze.

I also created a roller door to stand between the player and the exit trigger volume. I created a metal door noise using a garage door sound effect on repeat.

I also created a trigger volume for the electrical power box. I added a ItemPickup script to this volume to allow the player to pick up “power”. This was a rework of the key pickup script to be more multi-functional. I also refactored the ConditionalTrigger script to work for any item not just the key. This means either “key” or “power” can be used as a condition for a trigger.

I also added instructions to the main menu.

I modelled a strip light in Blender to add creepy lighting to the game. I imported this into the game and added a point light to it. I got a collection of flickering fluorescent tube noises, made the lights flicker and play a noise occasionally. I then turned this light into a prefab, so I could duplicate it across the level.

Next, I went ahead and made a script that ends the game when the player gets past the final door.

I did some more final tweaks and the game was ready to go

Conclusion

This was a huge undertaking. I’m so proud of myself for doing it in eight days. It was my first time using Unity and my biggest ever programming challenge in C#. I really enjoyed bringing my skills together from different hobbies, such as the extensive modelling in Blender. I really look forward to getting the time to sitting down and working on the skills I learnt in doing this.