Plants vs Zombies in Unity using Bolt (Part 2)
Welcome to Part 2 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 this episode we are going to talk about:
– HUD Scene (Pause Menu & Money Label)
– Loading another scene additive
– Grass Tile Clicked (We are actually going to “plant” the plant in that grass tile that we clicked
– We fix the Flower UI Clicked bug
– UI Manager
– State Machine of the Sunflower
read more below
HUD Scene (Pause Menu & Money Label)
The next step that I wanted to implement is the ability to see how much money we have. I created an APP VARIABLE of type INT and I gave it the value of 100. Keep in mind that Bolt supports multiple variable types. Graph Variables that are only available to that Graph, Object Variables that are only available to that Game Object … and in our case an App Variable that will be available across our whole app that is using multiple scenes. That’s why it’s an App Variable and not a Scene Variable. You can read more about variables in the Bolt Manual.
I created a new scene that only has a Canvas and a PAUSE_MENU_LOGIC gameobject that has the logic of the Pause Menu (you will see it later). I will not go into details about the actual Canvas creation, etc but basically we have two Text Fields that we positioned to always be in our top-left corner. I also used TextMesh Pro for this.
Let’s talk a bit about the State Machines. Bolt is an asset that can create both Flow Graphs and State Graphs. That is actually kind of great! I love to create State Machines and I love to think logically like that. One of my favorite assets is PlayMaker which is an amazing tool if you want to create state machines. However Playmaker is a heavy tool with lot’s of files and sometimes you might not need it. You might only want to create something small and this is why it’s amazing that Bolt supports this. You will see later how we used a State Machine for our Sunflower plant!
Now back on our Pause Menu. Our game can have to states: Playing & Paused so a state machine would be perfect for this.
When we are paused we Display the above GameObject (the Pause Menu) and we set the timeScale to 0. When we are playing we hide the Pause Menu and we set the timeScale to 1. Super easy! Let’s see how we did this.
There are a few things that you need to take into consideration when it comes to a State Machine. You can not be in more states at the same time. When you go from one state to another you will need an Transition (This is called an Event in Playmaker). Think of the Animator in Unity! That thing is also a State Machine!
Another thing to take into consideration is the transitions and how they flow. In the above State we can see that we can go from Playing to Paused but also from Paused to Playing. There are state machines that can be pretty complex with multiple states, multiple transitions and of course you might not be able to go from X state to Y state.
Anyway, let’s see the Paused state.
This is it! When we Enter State we set the timeScale to 0 and we SetActive our Pause-Menu. That’s all!! I will not show you the Resumed state but basically there we set the timeScale to 1 and we uncheck that value box hiding the Pause-Menu. I will also show you the Resume transition that goes from the Paused to the Playing state. I’m doing it like this because the Paused state is so simple that … is actually kind of the same with the Resume state.
This is a transition!! When one of those Events trigger, we will Trigger the Transition going from one state to another! We can trigger the Resume transition by clicking on the “ResumeButton”, this is the actual Resume Button in our Menu, but we can also trigger this by pressing the Escape key on our keyboard. Our Transition from Playing to Paused only has the “On Keyboard Input” event.
Anyway, enough about the Pause Menu. We will look into another State Machine when we talk about the Sunflower.
Loading Another Scene Additive
When we start our game and our Game Scene has loaded, we will trigger the Start event (this is basic Unity stuff). We will then use the SceneManager ability to LoadSceneAsync. We specify a scene name and a mode. There are 2 modes: Single & Additive. Single mode will just close our current scene, and load the HUD scene. Additive means that it will “add” the HUD scene among our Game Scene. So we will have two scenes open at the same time!
For now ignore the Wait Until node and look at the rest.
What we want to do here is this:
- We start our game and we want to load the HUD scene
- Then we want to get a reference of the “Money-Text”
- We want to update the “Money-Text” to show the current money that we have (remember that “money” int variable?)
So we would trigger the event called HUDLoaded in the Game Object “UI-Manager”. You will see the UI-Manager in a bit and this will make more sense.
Now this is the problem. Since that “Money-Text” is in another scene, we can’t really reference it with a drag & drop like you normally do stuff in Unity. So that HUDLoaded function actually has a “Find Game Object” with the name “Money-Text”. Here is what UI-Manager has:
At the Start of the game the UI-Manager is setting himself (Self) in that Variable. That way the GameManager has a reference to the UI-Manager in order to trigger the HUDLoaded event.
The actual HUDLoaded event, as you can see, is having that Find GameObject. He finds it and he sets the GameObject in the “moneyText” variable then it triggers the updateMoney event.
Now let’s get a bit back and thing about this. Immediately when we load our Game scene we will trigger the load of the HUD scene. If we don’t use that “Wait Until” node, what’s after that node will trigger IMMEDIATELY! This means that we are going to “Find” the Money-Text … BEFORE the HUD scene has loaded and such, that moneyText variable will not be set, triggering an avalanche of errors!
So that’s why we used the Wait Until node. We will “Wait Until” the scene “HUD” = isLoaded. Makes sense? When the isLoaded becomes True we are 100% sure that the HUD scene has been loaded and so we can trigger the HUDLoaded event, we can Find the Money-Text and we can updateMoney.
This is our updateMoney function. We use the TextMeshProUGUI … and we are trying to update the text. We are feeding it our moneyText variable (since now we have a reference to it). However we have a small problem. Our “money” variable is of type “INT” (integer). Our text requires a STRING. That’s why we use string.Concat. We take that int and kind of transforming it into a String so we can display our money in the UI.
This is what happens here:
- When we click the GrassTile we check to see if we already clicked a Plant Button
- If we clicked a Plant Button it means we want to build that plant
- We need to check to see if we already have a plant on that Tile
- If we don’t have a plant on that Tile we setParent
setParent is a Suepr Unity. I made it like that in order to make the flow a little bit smaller. The Super Unit has two inputs of type GameObject. We require the Parent, in our case the GrassTile that we clicked, and we require a Child in our case the Plant that we want to build. In the above graph you can see that use use the Transform.SetParent. That’s how we set the parent for our child. The next step is to move our plant to be at 0.0.0 in the localPosition. What this means is that we want to be sure that our plant sits in the middle of our grass tile. If we woudn’t have done that our plant could be anywhere in the scene but still be a child of our grass tile.
Now look back at the grassTileClicked event. After we parent our child 😀 we send an event to our plant called “planted”. You will see what this does a bit later. Then we set the plantToBuild to NULL. We want to set it to null because we already planted the plant! Then we do our rightClick function.
If you remember from our first tutorial, our rightClick function Destroys our Plant! We don’t want to destroy the plant that we already builded so that’s why we set it to Null.
This is how we fixed our small bug. The problem was that, let’s say that we clicked the Sunflower plant. Our plant was getting initialized and it was set to “Follow the Cursor”. We did this in the first part of this tutorial. Now, if we were going to click the Firingplant UI Button, or the Sunflower UI Button again, this whole event would trigger again, leaving our already initialized plant in that spot and initializing a new plant!
So what we do here is that we check to see if the “plantUIClicked” is true, if it is true it means that we already have an plant initialized and following a cursor, so we run the “rightClick” function that if you remember… destroys our initialized plant. Then we call the same Event again. When we reach that Brach, the “plantUIClicked” ias already set to False, because if you remember … we set the plantUIClicked to False in our rightClick event. Easy fix!
Sunflower State Machine
Now let’s talk about something that it’s cool! All our plants will have this State Machine:
We need to differentiate between when a plant is following our cursor (Unplanted) and when it’s Planted. Why? Well, when our plant is Unplanted and it’s following our cursor, the plant already exists in the scene. If it’s a Sunflower, it will already spawn suns. If it’s a Firingplant, it can already shoot the zombies. Those plants will be able to do this without being planted! One way to do this would have been with a Bool, something like “isPlanted?” … and then always check to see if it’s True or False.
But since Bolt supports State Machines, why not use a state?
So in our Unplated state we do this:
It looks a bit complicated I know. This is what I tried to do live in the video but I coudn’t. The moment I ended the video I found the solution 😀
LATER EDIT: Actually, you can do this super super simple. However I will let the above screenshot so you can see that Explose Color node to have a better idea how you can complicate things 😀
This is how you should do it:
Yea it’s that easy. Just use the SpriteRenderer.Color and click that White thing. You can edit the color directly so you don’t have to extract any colors, create new color, etc.
Normally there wasn’t anything in this state but I decided to add a little bit of flavor. What those nodes are doing there is setting the oppacity (Alpha) of our Plant to about 30%, so the plant is a bit faded away.
We are doing this right when we enter the Unplanted Node.
The Custom event that you are seeing as a Transition from the Unplanted > Planted is actually that “planted” event that we trigger during the GrassTileClicked. Remember how I said that we are going to see that later? Well that’s where it happens.
Then we get into the Planted state that looks like this:
When we enter the “Planted” state we Set the Bool (planted) to true. Since this is not a Unity tutorial I don’t want to get into to many details but basically our Sunflower plant has an Idle animation. So I created an Animator for it and an Idle Animation. When the plant is Unplanted the Sunflower is not animating, but when we plant it we set that Bool to True so we actually start to animate the Sunflower. Then we set the Color back to normal. What is important there is the Alpha. We put it back to “full strength” so our Sunflower is not faded anymore.
Then we get to the interesting parts of the Sunflower.
Normally the Sunflower has to spawn a Sun every 10 Seconds. We do this using this Wait Loop that is not available by default in Bolt. This node, among a couple of other very useful nods were created by JasonJonesLASM. Those nodes can be downloaded from here.
Those nodes came right at the right time! In order to spawn a plant we need to execute the same event every X seconds. With this Wait Loop we can do it!
Anyway, I will actually have to update this node some more since right now we are only displaying a DebugLog 🙂 You will see this in Part 3!
End of Part 2
So thanks for reading guys! I hope this tutorial was useful to you!
Don’t forget to check my Twitch Channel. I started streaming when I work in Unity!