Plants vs Zombies in Unity using Bolt (Part 4)
Welcome to Part 4 of my “Plants vs Zombies in Unity using Bolt” tutorial. In the first part I tried to introduce you to Bolt and how Bolt works, we made the plants button UI, we made it so we know what grass tile we clicked and we made the new plant to follow the mouse cursor. In the second part we talked on how we load another scene additive, we talked about the pause menu, UI Manager and we even seen how we can use State Machines with Bolt. In the third part we talked about how to spend money to build plants, the money manager and some other stuff.
In this episode we are going to talk about:
- Health System (State Machine that we can add on anything that we want to have health)
- UI Gain/Logic Anim (I only show this in the video since I ended up not using it)
- Zombie Spawner
read more below
This is the Zombie state machine. We will start him by Walking. Whenever he will detect a Plant, we will put him into the Attacking State.
Ah, another thing that I will not show you here. We created an Animator for the zombie with 2 states, Walking and Attacking. We set up a Bool parameter “attacking” that we will trigger in the Bolt Attacking state of the zombie. That’s how we handle the animations. The Zombie also has a BoxCollider2D on him and an Rigidbody 2D that we set to be Kinematic. The float variables that the Zombie has are: Health 60 ; damage 2 ; attackSpeed 0.3 ; walkSpeed 0.01
Now let’s see both of those States.
In the Walking state we have an update where we Transform.Translate in order to move the Zombie. We get the variable walkSpeed and we Multiply it by -1, then we are feeding that into a new Vector 3. We ignore the Y and the Z axis since we don’t want him to change depth or go up/down. That -1 tells the Zombie to move from Right to Left.
Then we also have an On Trigger Enter 2D event. Whenever we collide with something the this event will output the object that we collided with. We have a Switch on String. In that Switch on String we check for the “Plants”. We take the Tag of the component that we just hit, and we check to see if that Tag = Plants. If it is, we trigger two events. “attacking” & “startAttacking”. The “startAttacking” event will also take an argument. That argument is the GameObject that we just hit.
Confused why we have the two events? Well, the attacking event triggers the transition from the Walking to the Attacking state. The “startAttacking” event will be used in our Attacking state. We basically wanted to get the GameObject that we hit… from this state to the other, so we can use it.
Now let’s look at the Attacking State Machine.
On Enter State & On Exit State – we change the state of our Animation. So our Zombie will walk/attack.
What’s more important happens in the Update state. We will constantly do a Null Check for the GameObject we just received (The Plant). That’s how we know if we killed the plant or not. Basically, if “Our Plant” = Null, it means the plant is dead so we trigger the event “walking” that will send our Zombie from this Attacking state, back to the Walking state.
If “Our Plant” = Not Null, it means that the plant is still alive, so we proceed by dealing some damage to it.
We are dealing damage to it by using a Wait Loop. The Wait Loop has a event called “break” that goes into the Break. We will use this as a safe check. Basically a Wait Loop happens in a separate layer for Bolt. It’s a coroutine that always runs. If we will later KILL our Zombie or Plant, that Coroutine will still run. So whenever something dies, and we want to destroy it, before that we will send an event called “break”. This goes on the Sunflower & on the Firingplant also. It’s something that I recently found so you don’t see this in the other parts of this tutorial.
Anyway, the Delay of the Wait Loop is our attackSpeed. When we Enter this Wait Loop we do this: We dealDamage (Using our Super Unit). That Super Unit requires 1 Float Input, where we basically send the damage of the zombie.
The Zombie will remain in this state as long as the Plant is still alive.
This is the dealDamage. This is what deals damage to whatever that TARGET is. The damage that it deals, is that DAMAGE float that we received. I know that you might be a bit confused… but think about this. Whenever we have to dealDamage, we need to know to whom … so we have that GameObject. We need to know how much damage to deal. We are also sending two Custom Event Triggers. “damage” and “receiveDamage”. We are sending those events to whomever is receiving damage.
This can be any object that we will create in the future. It can be the Zombie, the Plants, etc. So let’s see who uses those Triggers.
This is the healthSystem. It’s a State Machine that we can put on whatever object we want to have a Health system. Another thing that we have to do here is create a float variable called “health” on the object that we want to use this.
The Waiting system is just that. Waiting… and not doing anything. We can also “Heal” but in our “game”, we don’t do that. I just made it like this so you can have a better idea of a complete system. All you have to do to Heal is trigger an event that will go to the Healing state.
Now let’s talk about what we are actually using. From the Getting Hurt we can go back to the Waiting, or we can go to the Dying state.
The way this goes is like this. Every time we receive a “tick” (an event) of “damage”, we go to the Getting Hurt, we do that thing, then we go back to Wait. The Getting Hurt also has a check that sees if our Health is below or equal to 0, if it is, we don’t go back on Waiting, we go on the Dying state.
This is the Getting Hurt state. The receiveDamage has an Argument with the damage that it has to receive. In our past example, it’s the Zombie dealing damage to the plant. BUT! The beauty of this whole system is that, our Bullet from the Firingplant will also send a damage event to our Zombie which has this Health system, so our Zombie ca also receive damage from the bullet!
So, we Set Variable “health” to our “Current Health” where we Substract the “damage” that we just received.
Then we compare our new Health to see if it’s equal or less than 0. If it is not, we trigger the “waiting” event, if it is true, we go to the dying state.
What our Dying state does is pretty simple. Of course, if you would have a Dying animation for your monster/plant/zombie/etc you could trigger that here. But we just do a Null Check (just to be safe), we send a “break” Custom Event Trigger, and only THEN we Destroy ourselves.
So, let’s say that the Plant is going to die. We send that “break” event that is going to break whatever Wait Loops the plant would have had… and after that we destroy the plant.
The Bullet has an On Trigger Enter 2D. It has a Switch on String. This is going to check to see if the object we just hit has the Tag “Zombie”. If it has, we use the Super Unit called “dealDamage”. We send the variable damage, then we destroy the bullet.
Easy! As you can see! With this whole system is pretty straight forward and re-usable.
This is our Zombie Spawner. We placed this in the GAME_MANAGER. We have a Wait Loop that it’s going to run every X seconds. In our case … it says 0. You want to put it to a value on how often you want to spawn zombies. Every 4-5 seconds, etc.
Before I explain the rest, let’s look at our Spawners.
I created six empty game objects and I placed them under a parent called Spawners. Then I made sure to placed them nicely like that.
Now, what we want to do is every X seconds to do this:
- Pick one of those spawners at random
- We do that with the Random.Range function. We tell it to pick a number from 0 to “childCount” of our Spawner (in our case 6)
- Then we get that number and feed it into “Transform.GetChild”. This will give us a reference to that GameObject that we just randomly chose.
- We are then going to Instantiate a Zombie
- We are going to use our Super Unit called “setParent”.
- We will tell it to Set the Zombie as a Parent to that Spawner.
That’s all 🙂 Our Zombie will know how to walk from Right to Left … it will know how to stop and deal damage to whatever plant he finds… and he will also know how to die in case he get’s to much damage!
Anyway. Thank you guys for checking my really long tutorials. I hope they were useful to you!
This whole episode was also live streamed on my twitch. You can see the livestream here:
Thank You all!