A new frontier of zombies!
Type: Practice prototype. Genre: First person/third person shooter. Engine: Unity.
Key Roles: Game design,level design and programming. Released: 29th/10th/2026. Last updated: 29th/10th/2026. Made by: Only myself (assets were provided to me from my instructor, Mixamo and free unity assets respectively). Learned from: Jason Weimann’s Mastery course - gamedev.
Finally, after some delays and a busy week of Melbourne games week, I have completed a more complex first-person/third person hybrid which will still be useful for not only creating shooters, but to actually hopefully create some exciting action-adventure games in future with some third person mechanics.
This tutorial from this course is crazy expensive, but luckily, I got it included in a bundle from the Humble bundle website which was a little less than $50 Aus.
Normally making zombie games is not my first choice, but it was a great opportunity to learn more advanced mechanics to use to create my own games. This will not be the last time I create a practice prototype with zombies.
WHAT WENT WELL?
• When I finished the tutorial, some of the mechanics were not well polished and compete. I finished them off by myself like...
- Particles deleting themselves in a certain amount of time.
- Player animating while sidestepping and backwards.
- Zombies can't damage Players when they're already dead.
- Blaster projectiles can damage and kill zombies.
- Adding nav mesh agents to zombies.
- Spawning zombie variations.
- Consistent fixed Update character movement for Player.
- More info down below.
• Certain sections like projectile and particle pooling, I could not do because it required me to complete another project which is making a top-down hack and slash from the same instructor. I decided not to start that tutorial because it is not exactly related to fps games and I didn't want to waste months doing it unless my next game has similar mechanics to that prototype. I managed to work around the missing supposed features in the project with no pooling without causing the finished project to have errors and crashes.
WHAT I LEARNED? –Player–
• Make a Timeline event play if the Player movement is not null and in the trigger collider.
• Learned how to make use of the animation weights between the Player pointing the gun and not pointing the gun. So if the Player is holding down the fire key, he/she will not only point the gun and shoot, but it will change its animation weight to a group of gun-point animations that play like “pistol walk”, “pistol sidestep”, and “pistol backward”, and etc. If the Player does not hold the fire key after firing, it counts a coroutine (unless it is pressed again) to change back to the first layer which is a group of Player animations that he/she is not pointing the gun.
• Made the Player draw weapon speed smoothly when firing a gun using a Ienumerator with a while loop.
• Created an aim mode which allows the Player to rotate and view with a cinemachine camera around the Player while RMB is held only in third person mode which is a mechanic very similar to Fallout 3. If the RMB is let go, the camera rotates back to the default camera position for the Player.
• While in Aim mode, the player will fire the weapon forward from the barrel. This is both for the Berrata and Blaster.
• Learned to do a complex equation for Players health to format two ints current health and max health into strings, then converting them to floats, divide them and assign the values into the health fillbar each time to update Player’s current health.
–Scriptable game objects–
• For the first time ever, I have created a Customizable editor script that can be used to customize the Scriptable game objects. In this prototype, I have made a simple Audio event which uses a public min and max serializable float values (2 separate scripts as one of them is a child of an attribute) to randomize sounds of volume, pitch, clips, distance and be part of a mixer. This is used for zombie and player hurt sounds, and gun shots.
• Serializable ensures it is seen in the inspector for scriptable gameobjects.
• Learned to create the scriptable object in the project menu “AudioEvent” by listing it in the “create” menu.
• Made the scriptable object turn off its properties if other game objects are multiply selected.
• Made the scriptable object play its given sound with its randomized pitch, volume, clips and distance in the project view without running the game in the editor.
• As mentioned before, I created a sub-menu in the create menu to create a simple audio scriptable game object. When it is created, it will feature ranged float variables for clips(array), min inbetween max volume, min in between max pitch, min in between max distance, audio mixer to play it on, and a play button to test the sound of its randomised min and max values in the editor without playing the game to hear each the sound played.
• Made the rect, label, font size min-0 to max-1 in position and other properties to be displayed, used and read respectively in code. It is an overload that is part of inheritance. This takes the results and forces it to be in an MinMax range attribute and will be casted and assigned to ranges.
• Made sure the min and max values can be updated and saved into the scriptable object part of the editor.
• Customizable Scripts that are not in the editor folder cannot be referenced.
–Events–
• Got a better understanding of how events including delegates are used since I built the Battle royale prototype. Instead of getting most references by code, I had the code be ready to listen to each other in many different types of scripts each with self-explanatory names to get the job done without any errors whatsoever. These events are used for…
- Changing weapons.
- Which hotkeys to change weapons.
- Particles fired.
- Player hurt audio.
- Player fire key and fire weapon.
- Weapon changed.
- Ammo changed during reloading.
- Zombie attacks.
- Ontookhit and OnTookDeath for the Player and zombies.
- Button to hold in aim mode (like in Fallout 3 and 4).
- and etc.
• If the weapon script object (not game object) is destroyed, it will clean itself up. This possibly reduces errors as part of the event. It's good for character swapping and possibly gun swapping.
–Level design–
• Game objects need colliders before using Occlusion culling, make sure their set to Occlude and Occluder static. Makes a huge difference for dungeons and castles!! Outdoors is less valuable.
• For short levels, we want to guide the Player with short pathways and not so big open space unless it's an open world game, you want to prevent them from going the wrong way to mess up with the game and experience.
• Remember with designing levels: everything that the Player sees from a close perspective (not outside the level) has to look good, don't worry about what the geo the Player sees outside the level.
--Zombies–
• Created a sub state machine in the editor for the Zombies to randomize three different attack animations with Random.Range when attacking close to the Player.
• Made a zombie AI move towards the Player slowly with root motion without using a public variable of speed and a navmesh agent. However, I found further testing that the zombie AI can walk through buildings ignoring the navmesh baked navigation and causing errors, so I gave them a navmesh agents with 0 speed (which they can still move) to fix this. I could have given the buildings a nav mesh obstacle, but I didn't want to waste a tedious amount of time doing so for every single building.
• The zombie finds the path with a bool and the navmesh while calculating the path to its destination along with the area mask. It lists the corners of the places to move to get to the destination (Player). It will have 0 for the current position, 1 in its destination position (Player). It then instantiates a cube at the next destination position for the enemy to go too. It repeats the process again when it needs to go to a new destination including the Player. It uses a Vector3 with path.corners (e.g Vector3 nextDestination = path.corners[1];).
• Made the zombie walk in a flat position with a Vector 3. So it does not go up on the Y axis for any reason. It is then normalised.
• Root motion can be useful in enemy AI to make it smoother and realistic, however it's not good with AI navmesh navigation.
–Player Shooting–
• How to change Player camera from first person to third person with camera positions without clipping through the Player character and gun. This also organises which prefabs will be seen in third person or first person such as the Player character and his/her guns in separate arrays. So far this has not caused any known issues with Player shooting. This only works when the player is alive.
• For the Blaster, it uses a ViewpointToRay part of the main camera to shoot in the middle of the screen. ViewportPointToRay creates a ray cast position of the screen of 0.5, 0.5.
• When the blaster projectile is fired in a direction, it normalises its direction and cuts the magnitude completely and gets it down to the max magnitude of vector 3 = 1. Basically shortens down into a direction and not distance.
• Used three cinemachine cameras for the camera controller…
- Freelook Camera: if aimMode is on: it will use this camera to view around the Player and the world while setting its priority to 100, if not held its set to 0 and recentres around the target “Player”. This is done only in third person mode.
- Fps Camera: Allows to rotate up and down while not in Aimmode and in fps mode.
- Third Person: “Follow” camera determines the smoothness and damping when following and aiming at the Players direction while in third person.
–Weopons–
• For Blaster projectile impact particles: Instead of using quaternion.Identity, we use Identity with collision.contacts. It stores the amount of contacts it collided with Instantiate (particlePrefab, transform.position, Quaternion.LookRotation(collision.contacts[0].normal)); and determines the best way to face the opposite direction on whichever it contacts with. • Protected is an access modifier that allows a variable or method to be accessed only within its own class and by classes that inherit from it. This is used for WeaponComponent to inherit from Weapon class and WeaponProjectileLauncher.
• Made an infinity symbol on Textmeshpro which represents that the Beretta has unlimited max ammo, but still needs to be reloaded. This is done in string.format.
• Made a Math.min for reloading the ammo clip from the ammo remaining not in clip, similar to what I learned from the Battle royale prototype.
• Made a while loop that when reload key is pressed (after firing obviously), the weapon reloads the clip 1 bullet at a time until it reaches max clip then the Player can begin firing again repeating the process again until there is no more ammo in not in clip or if unlimited ammo is turned on.
• Rigidbody Continuous dynamic: good for very fast instantiated game objects like blaster lasers.
- Interpolate: If it impacts something very fast and goes through a wall, it will still count as a hit on the wall, not past it. It does not miss the first collision.
–Other–
• Made a trigger mechanic that if the Player stepped into its collider, all of its collider’s child objects will be set active to true using a loop without using public transform variables.
• Made a mechanic that snaps the gameobject to the ground layer while ONLY in the unity editor (not build version) using #if UNITY_EDITOR and [ExecuteInEditMode] which uses a raycast to check for the ground. This is used for zombies and spawners.
• Sphere colliders are usually very efficient, super lightweight and easy to work with.
• For Animations, make sure to not only have " loop" and "loop pose", but to also have "Bake into pose" to stop the animation from wobbling. Good for walking animations, even from Mixamo.
• For TextMeshPro, shrinking the text map and only keeping numbers and letters you need can save up memory and project size.
• Cinemachine: Freelook camera is set to do an orbital view of the Player character like a car or any other target from any angle.
• To write on lines at the same time in C#: hold alt and drag down vertically and then you can type multiple lines at the same time.
• In the game view while playing the game: The stats of: 425k Tri’s and 745k vert’s…
- 745k verts without doing an optimisation is usually great for performance compared to two or three million verts. The game will have high framerates, so it will work fine in VR and most mobiles.
- Set pass calls (aka draw calls) of 9 insanely good too. On mobile you want to keep it under 100. For PC games try to keep it as low as you can.
• The camera's W&H seems to influence the frame rate including the Tris’s in scene likely due to rendering.
WHAT WENT WRONG, COULD I HAVE DONE BETTER?
• While in aim mode, I should have adjusted the two guns to be held straight, instead of them held diagonally from the players hands which are to be fired diagonally. I only found out at the last second, but I decided to let it go since it is a prototype, not an actual game.
• I couldn't getting Beretta bullet hole detail on walls due to the difficulty of the code and didn't do Mastery course’s hack and slash tutorial of pooling beforehand, the Hack and Slash is not my first priority at this moment, however later in development I will be looking more into pooling and alternate ways to make bullet holes which I have done before, but the code structure is what made it difficult to begin execute with.
• I couldn't get the blaster cylinder shaped projectiles facing the right way when fired due to the different structure of code, so I made them into spheres instead.
• Sometimes when firing the blaster in third person, it goes straight to the ground rather than its intended target the Player aims at. Little do I know the reason for this, but I need to do more third person shooter tutorials to further understand why it happens and reduce it from happening again.
• I don't think I made the zombie hit from gunshots very responsive; I should tweak with the animation transition to get a faster response for similar enemies in future.
• In this prototype, I used several scripts on each gameobject (mostly for Player and Zombies). I think I will tone down the number of scripts being made and used as possible for each gameobject.
• Sometimes the building meshes glitch while looking around the town. It must have been something to do with the Occlusion Culling, I could have played around the settings more, but I decided to let it go for now since it wasn't exactly important. I need a further understanding of the feature to reduce the glitchyness from happening in future.
OVERALL It was a good tutorial teaching me a different approach to build zombie AI and first-person shooters with some third-person shooter mechanics while combining them both together with some advanced features.
I haven't got the hang of making scriptable objects yet due to it being my first time doing so, but I definitely like to learn more of it when I get the chance.
I still have a ways to go and more to practice before I can add third-person shooters to the list of genres I can build for future indie games.

Comments