[#7] Enemy Counter & Spawner

Next we are going to create a counter object that will manage the number of enemies on screen. It is good to create manager objects like these that many other smaller objects in the game can interface with to provide good structure and organization to the project and allow higher-level edits to be made with ease in one place, instead of changing multiple settings in various different game objects’ FSMs.

To do this, create a new empty Game Object in the Scene Hierarchy and name it enemyCounter.

Add an FSM to enemyCounter and name the starting state Setup for consistency.

Create two events in this FSM with the names “addEnemy” and “subtractEnemy”, and check the boxes next to the names to make them Global Events that can be accessed by any other object with an FSM in the game.

the correspondingly named global event to each one. You should end up with an FSM resembling the following image:

Add an “Int Add” action to the Adding Enemy state and set 1 to be added to a new Int Variable called enemyCount, which, as the name suggests, will be used to count the number of enemies in the scene. Adding 1 increases the count of enemies in the scene by 1.

Do exactly the same thing for the Subtracting Enemy state but this time set the value to add to be -1. This will decrease the count of the number of enemies in the scene by 1.

Still in the Subtracting Enemy state, click on the Variables tab and create a new variable called maxEnemies. Set this value to 20. We will use this later to limit the number of enemies on the screen at any given time to prevent lag within the game.

Make sure maxEnemies is set as an Int.

Next, create an empty Game Object in the Scene Hierarchy and name it “enemySpawner”. Also make sure that it is a child of PlayMaker Tutorial Game so that it will be included in the prefab we export to STYLY later.

Create a new FSM in enemySpawner and name the entry state “Setup” as shown in the image above. In this starting state create a new variable called maxEnemies. This should be named exactly the same as the maxEnemies variable in the the enemyCounter FSM. We will see why this is the case shortly.

Now, also in the same Setup state for the enemySpawner, create a new Get Fsm Variable action as shown in the picture below.

Specify the Game Object as enemyCounter by choosing Specify Game Object from the Game Object drop-down menu and dragging in the enemyCounter object from the Scene Hierarchy. Then store the value in maxEnemies, the variable we just created inside enemySpawner. The reason we are doing this is that STYLY does not support the use of global variables in playMaker. Global variables would normally be easily used to communicate information across multiple FSMs attached to different objects, and in this case we need a variable that functions like a global variable since both the enemy counter and enemy spawner need to know the max number of enemies. However, since we can’t use global variables we are forced to “hack” our way through by using the Get Fsm Variable action, which essentially copies the value of a variable in another object into one of exactly the same name in another object. In our case, the variable is named maxEnemies. We could have individually set two different variables and just changed both when tinkering with the max enemy number, however this enables us to most easily change the max enemy number since if we change it in one location only, it will change everywhere. This is a key reason why programmers use variables in the first place.

 

Next create a new Int variable called enemyCount. We will use this to count the number of enemies in the game at any given time.

Next, create a transition to a new state called Waiting upon completion of the Setup state, and then set this to transition to a state called “Checking Number of Enemies” upon the completion of that state.

In the Checking Number of Enemis state, add a new Get Fsm Variable action and specify the Game Object as enemyCounter, just like in the Setup state when we got the maxEnemies varaible from the enemyCounter object. This time however, we want to get the enemyCount variable, so set Store Value to be enemyCount. Now both the maxEnemies and enemyCount variables are shared between enemyCounter and enemySpawner.

 

Now create a new event called spawn enemy and add it to the Checking Number of Enemies state.

Once this is done, create an Int Compare action that compares enemyCount and maxEnemies, and if enemyCount is less (Integer 1 is less than Integer 2), trigger the spawn enemy event. Make sure that the “Every Frame” box is checked so the game is constantly checking up on how many enemies there are in the scene.

Next, add a transition to the Spawning Enemy state. When this is finished transition back to the Waiting state.

Now we are going to do some maths to spawn the enemies randomly on a circle.

 

First of all, create a new float variable called “radius” and set its value to 76. This is the radius of the circle whose circumference we want the enemies to spawn on.

Also make sure to set the enemySpawner’s position to (0,0,0) in the Transform component of the enemySpawner object in the Scene Hierarchy. This will center the circle at the middle of the arena so the enemies spawn equally far away from the player in all directions.

 

To choose a random point on a circle, we will use a bit of elementary trigonometry. If we consider the following circle:

We can easily figure out the x and y coordinates of the tip of the red arrow using sin and cosine (assuming the intersection of the red and blue arrows are at (0,0))

x = rcosθ

y = rsinθ

 

In terms of the circular arena in the game, we have the radius already, and we have a start position for the vector, which is (0,0), since that is where we set the enemySpawner to be located, so we can use exactly the same math to get a random point on the circle. All we have to do is choose a random angle θ, and for that θ we need to calculate the x and y coordinates as above. The steps taken to do this in playMaker are shown in the image below.

First we get a random float between 0 and 360 degrees and store it in a variable called randomTheta. This acts as our random variable. Then we get the cosine of this variable and store in a new variable called xCoord, making sure to check the Deg to Rad conversion box as well, as cosine in playMaker operates on angles in radians. Then we multiply xCoord’s value by radius, which we set to 76 earlier. Then, using the same randomTheta variable, we get its sine, making sure to convert to radians this time as well, and then do a similar float multiply by radius. Now we have the final x and y coordinates for a random enemy spawn. The final step is to store them in a vector variable called position, setting the x to xCoord, the z to yCoord, as in unity the z axis is in the horizontal plane. We have just used x and y for circle geometry as that is a common convention. Finally, set the y value of the vector to 1. This will place the enemies at exactly the right height so they look like they are standing on the stage.

 

Now add a Create Object action and set the object to appear at position, the random point on the circle stored as a vector. Leave the Game Object field blank for the moment – we will create a prefab for the enemy character and then return to fill this in later.

We also need to update the enemy counter. We have already created a global event called addEnemy in enemyCounter, and now we need to call this event from the enemySpawner object’s FSM to make sure the enemy count increments. Under enemySpawner, click on the Event Browser and then search for addEnemy. Then click Add Selected Event to FSM.

Next click on the Spawning Enemy state and add a Send Event action to send the addEnemy event to the enemyCounter object.

 

The counter and spawner system is now fully complete, aside from adding the enemy prefab later on. We now move on to creating the enemies.