Idea
The original idea for this project was to make a 3D horror game where the player roams a village while zombies attack them. I had originally chosen 3D as we had two weeks but due to other tasks I had to do I was pressed for time. I changed the project to 2D and went for the same idea.
I wanted to have a set of enemies spawn in waves with every time the wave is done spawning an even harder wave. I wanted the player to roam the village switching weapons and getting stronger to fight off the increasing waves of enemies.
Movement
For the movement I wanted to keep it simple and go for a general eight way directional movement. i also wanted to add a dash to the game so the player could dodge quickly.
To start with the movement script I began by adding the variables to the script. I needed variables for the movements as well as the dashing. for the movement, I needed to set a speed and create the input variables.
For the dashing, I needed the variables for the speed and duration of the dash as well as the time between each dash. I also need a private float for the current speed of the player.
The last variables are the object and input variables needed for the code to work. First the dash key so when I press that key we can have the dash take place. Next is the rigid body which is where we are going to be doing the movement by setting the velocity.
In the start function, I set the rigidbody to the player's object rigidbody using the get component. Then I set the active move speed to the base speed. this is so the base speed is the walk speed, not the dash speed.
Before I add the movement in the fixed update I set the input floats to the input axes so that when the player presses a key the float value changes to either 1, 0, or -1.
In the fixed update I start by checking if the player is giving the game any inputs then if both inputs are pressed apply a speed limit so the player doesn't speed up on the diagonal. Then if only one or the other is pressed apply a new velocity based on the input times of the player's active speed.
If the player stops inputting then set the rigid body's velocity to 0 so the player tops moving.
For the dash, I had to use a system where we use time delta time to count down so the dash was on a cool down. First I checked for the input of the dash key. Then if the cooldown counter and dash counter were both less than or equal to zero, set the move speed to the dash speed and set the dash counter to the dash length.
After this, I check if the dash counter is greater than 0 if it is I set the dash counter down with time delta time. Then I check if the dash counter is less than zero I set the speed back to the regular speed and then set the dash cooldown counter to the dash cool down.
lastly, I check if the dash cooldown is greater than 0 if so I count down using time delta time.
Camera
When it comes to the camera, I wanted the player to be able to look at the mouse. this was a choice I made because it will allow the player to aim at the enemies in a fun and forgiving way. For this, I need to get the mouse position and rotate the player object towards that direction.
To start the camera script, I set the variables. First, I make the rigidbody, then the camera. Lastly, I get a vector for the mouse position.
Next, in the start and update, I set the rigid body to the player component's rigid body, then set the mouse position to the main camera screen to the world point using the mouse input.
Lastly, in the fixed update, I set vector two to the mouse position minus the player position. Using a minus in the situation gets the distance and draws a line in between the player and the mouse.
Then I set the angle to the math atan between both look directions minus an offset value. Then, I set the player's rotation to this new angle float.
Player Health
Next, I aimed to get the player's health sorted out. For this, I wanted a simple player health system where he has a health and a max health and if the health goes below zero it destroys the game object.
First, I get two ints for the health variables, and then I get a game object which will be the panel for the death. At the start, I set the player's health to the maximum health.
Finally, in the take damage function, we remove a damage float anytime the function is called and if the health is less than 0 destroys the player.
Down below is the death panel:
Shooting
First, in shooting, I will make the gun. The gun needs to shoot a bullet every time the player clicks and should aim for the player's mouse. Next, I will need a bullet script where I call the damage enemy script.
First, I create the references to the fire point and bullet prefab using a transform and game object variable. Next, I add the variable for the bullet force, which determines the speed of the bullet.
In the update function, I check for input from the fire button and if it's found call the shoot function.
Then in the shoot function, I first set a game object to an instantiated bullet prefabn at the fire points position and rotation. Next, I set a rigid body to the new instantiated bullet's rigid body. To this rigid body, I add a force from the fire point using the bullet force and force mode impulse.
lastly, I destroy the bullet after 30 seconds if it doesn't hit anything. I did this to ensure that there were not any unnecessary objects in the scene.
On the bullet script, I start by creating a game object variable that will be the on-hit effect. Then I add an integer that will handle the bullet damage.
Then, in the on collision enter function I create the hit effect game object using instantiate. Then I destroy the effect and the bullet. Lastly, I check if the bullet collided with the enemy-tagged object if so then I would get the enemy health and call the take damage function using the bullet damage.
Enemy Scripts
To start here is the script on the health i wanted the enemy health the same way as the players health with a max health and health value.
For the enemy's health, it is close to the same as the player's health. I get the ints for the health variables. At the start, I set the enemy's health to the maximum health.
Finally, in the take damage function, we remove a damage float anytime the function is called and if the health is less than 0 destroys the enemy. If the enemy is destroyed it removes it from the list of enemies spawned so the spawner doesn't complain and can start the next wave.
Is the contact damage that the base enemies will have.
For the enemy damage, we first get the basic variables. one for the damage and one for the player's health.
In the start, we get the player's health component from the scene. In the collision, we check if the enemy hits the player and then take damage to the player using the damage value.
Next i will go over the enemy AI. Both enemies use similar scripts with the spider having a slightly more complex script.
For the AI scripts, they both get variables for the player and the maximum distance they can be away from said player. They both have a speed with which they chase the player. The spider AI has some extra variables for the shooting cooldowns and for the retreat distance.
In the start, they both set the player object to the object tagged with the player tag. and the AI spider sets the time for the shooting.
For the movement, they all get the distance of the player and move depending on the value of the distance between the player and the enemy.
The ghost gets the distance between the player using vbector2.distance. Then I set the direction to the player minus the enemy position. Then using the direction normalized I see if it's less than the max distance and if it is then move the enemy toward the player's position.
Using the same method we check if the spider is close to the player if so then he chases the player. If he gets within the distance and the shooting distance he will fire at the player. If the player gets too close I simply move him towards the player with reverse speed making the enemy go back.
For the spider, I check if the time between shots is less than 0 then we can shoot if not we count down the time.
For the AI I used these tutorials one by MoreBBlakeyyy for the simple chase ai and one by BackThornProd links found below:
Lastly for the enemy I wanted to get the shooting done flowing along with the shooting enemy tutorial by BlackThornProd.
For the spider bullet, I added a variable for the player, player health, damage, and speed. Then at the start, I set the player to the player object, set the target to the player's position and then set the player health to the player's health component.
For the rest, I made three functions. One is simply to destroy the bullet this can be called anytime the bullet needs to be destroyed. Next, there is the collision function where if the collider is the player then take damage and if it either just destroys the bullet.
Lastly, there is the update where we set the position of the bullet to the position of the player using vector2 move towards. then if the location is equal to the target position then destroy the bullet.
Wave System
For the wave system I wanted a simply system that spawned in enemies when the first wave has been defeated. For the wave system I used this video on making a better spawning system by ThatOneUnityDev.
For the first part, I set the variables in the beginning. First, there are three lists, one for the list of possible enemies, one for the list of enemies to spawn in the wave, and one to hold the spawned enemies.
There are a few variables to handle the timing of the spawner. There are three floats which handle the wave timer and spawn timer as well as an integer for the wave duration. Finally, there are a few variables two int to handle the wave and wave value as well as an array of spawn locations.
For this code, I handle the actual wave spawning in the fixed update. To start, I check if the spawn timer is less than or equal to zero. If it is, I proceed to check if there are enemies to spawn. If there are enemies to spawn I set a new game object to an instantiate enemy of the enemy to spawn list at the spawn location. After this, I remove the enemy from the spawn list and add it to the spawned enemies list.
After that, I check if the spawn index item is less than or equal to the spawn location length, then if it is I increase the spawn index if it is not I set it to 0. If the enemies to span list is empty I set the timer of the wave to 0 stopping the wave. If the spawn timer is above 0 it will count down the timer until it's 0.
Lastly, I check if the wave timer and spawned enemy count are less than or equal to zero and if they are added to the current wave and generate a wave.
For the generating enemies wave, I first get a list of generated enemies to a new list. Next, I add a while loop where if the value of the wave is greater than zero to the generated enemy list has more than 50 entries we do a task. If this is the case, I set it to the enemy cost and enemy id the cost is equivalent to the difficulty of the enemy and the id is simply the amount. Then, if the wave value minus the random enemy cost is greater than zero add to the enemies and remove the enemy cost and if it's not break the loop. After this, we clear the enemies and set them to the newly generated enemies.
Lastly, for the script, there are two things left. One is the enemy class which holds the cost and enemy prefab. A generate wave function which sets the wave value to the current wave times ten and then generates enemies. Then it sets the spawn interval and wave timer.
Final Product
Here is the game and a video showing the gameplay.
Feedback
My Thoughts
Overall I am very happy with this game for four days' worth of progress I feel I got plenty of progress done and I am happy with my complete game. there are many features my game didn't get to include that I wanted to get in there.
First of all, I wanted to create all of my assets like the village and zombies but due to time, I ended up suing Kenny assets are assets for the game. I aimed to add player XP and varied weapons. The player would be able to switch weapons and loot corpses. I also wanted more enemy types and boss fights after certain waves.
The feedback helped shine some well-needed light on either missing features or bugs. A great amount of feedback mentioned adding the player and enemy health bars. I think ithis is a great idea to add, because you can not tell when you or the enemy will die.
Some said that there could be more enemies, which was my plan if I had more time. Someone said the enemies were finite. I tested the game out but couldn't get the same result. Every time I played the enemies would respawn as planned.