Spawning Waves Of Enemies
So I have Been Using a simple spawner that just spawned one thing at a interval. Instead of a simple spawner I am going to be spawning waves. I want to spawn a total number of enemies over time. I want the spawner to spawn y number of enemies at a time wait and then spawn another y number of enemies. For example I want to have a total of 10 enemies spawning 2 enemies at a time. I want it to wait 3 seconds before spawning the next group of enemies. I want to be able to wait between spawning waves, I also want to add an option to wait for all the objects to be destroyed before starting the the next wave.
I need to define what a Wave is.
- Inherits from ScriptableObject and Implements ISpawner
- Total number of enemies in the wave, default to 3.
- Total number of enemies to spawn at a time, default to 1.
- Time between spawning each group of enemies, default to 5 seconds.
- If the Wave is Active (Spawn Routine has successfully been started)
- If the Wave still has enemies (The spawn object list still has objects in it or there are still objects left to spawn).
- Be responsible for Starting and Stopping the wave.
The while loop is easy to implement now that I have the logic in place.
Now that I have the wave defined I need to create the ScriptableObject Spawner.
- Contains an array of waves to spawn
- Has a cool down timer (wait between spawning waves)
- Has an option to wait for all objects to be destroyed before starting the next wave, and an option to use wait time between waves before starting next wave.
- Keeps track of the current wave
- Starts the next Wave Spawner if there is one (This allows for changing the wait for all destroyed objects settings for different waves).
- Responsible for starting and stopping all the waves. (ISpawner)
- Be responsible for destroying all the spawned objects from the waves currently in the screen. (ISpawner)
- Has an Update Coroutine that is started from the Start Method. Contains all the logic to control the way waves are spawned.
I had to add a Destroy All Objects Method to the Wave Class in order to implement the ISpawner Destroy All Objects.
Once the Logic for the Update while loop was in place it was easy to implement the logic for the spawner.
Using the New Wave Spawner
The game works but there are some issues. In order to track down what was happening I used Debug Log to ensure things are getting called and that I was getting the expected results in my logic.
The first issue is the way Unity handles Assets, in particular the way it handles serialization of ScriptableObjects and hot reloading. While in the Editor ScriptableObjects retain their Non-Serialized fields and properties between entering play mode. Which means once the value of a variable changes it is retained. This should not affect the Game once it is built but to ensure that it does not cause any unwanted bugs in the game and because I need thing to reset for testing in the editor I added an Awake Method to the Wave and Wave Spawner. I call the Wave Awake from the Wave Spawner. The Wave Spawner Awake Method will be automatically called by Unity, I also had to add a call the the Wave Spawner Awake in the beginning of the Wave Spawner Init method to ensure it was getting called while in the Editor.
The next issue is in the Wave. The Logic for Has Objects is a little off. The problem is that the spawned object list does not automatically remove items that are null.
- The game spawns a Game Object and adds it to the list.
- The Game Object gets Destroyed in the Game.
- The Spawned Object list now has a Item in the List that is null.
I added a way to remove all of the Destroyed Ob jets from the list to the end of the Wave Spawn Routine. I also Change the Destroy All Objects to make sure that the Objects got removed from the list as they are being destroyed.
The next issue is a little harder to track down. The Game Plays, everything is getting reset correctly, but now when the first Wave Spawner no longer has Objects the game gets stuck in the Set Current Wave loop.
- The Update Spawner gets Started.
- I am in the while loop for the current wave is active.
- Wait all Objects Destroyed is true.
- Once there are no more Objects in the current wave, I increase the current wave number to 1.
- Set Current Wave Gets Gets Called
- Current Wave Number is 1 and there is only one wave in the spawner.
- Set Wave never changes the current wave so the current wave is still active.
- I never exit the current wave being active loop, so the current wave keeps increasing.
To fix this I change the while loop condition so when there are no more waves it will exit the loop.
Display a Wave Spawner is just making use of the event that gets fired when the timer starts and the float complete percentage.
I created a Text UI and added the Wave Timer Display. I then add a Text UI as a child with A float Text Display Component.
For the animation I just created a non-looping animation that enables both the Text Components. I also created an animation that disabled both Text Components. The Disable Animation was set to the default animation. Made a transition between the two using the Is Display Bool Parameter.
Currently the Cool Down Timer only exposes a percentage complete time which is a number between 0 and 1. What I really want to display is the count down time. So I added a Float Reference Cool Down Time Left.
I then used this new Variable.
I also had to make sure that the timer got started before the timer event got fired in the Wave Spawner Behavior.
Wave Spawner Complete Event
I added a Game event that gets fired at the end of the Update Spawner method. If there is not a next wave spawner and there is a game event I want to wait until the current wave no longer has objects (I only get to this portion of code if this is the last wave in the spawner) I know that the wave spawner is complete and I can fire the complete event.
I am going to use this event in the Spawn Manager to stop spawing. While I am at it I am going to add an event for if the Player is Destroyed to Stop Spawning, right now the Wave Spawner will continue to spawn after the player is dead .
I also use the Game Event in the Game Manager, When the Wave Spawner Complete Event fires I want to be able to load a new scene by the scene index. Now I can add another level or a win screen. Right now I just go back to the Main Menu.
Wave Spawner’s using events.
Now I have Wave Spawner that uses events, I have a fully playable game.