New Behavior using a State Machine
Objective: Create Unique Movement Behavior for the new enemy.
Last time we created a new enemy that can withstand multiple hits. Now we want to give it some unique behavior.
- It will move to some point on the middle of the screen, stop briefly, and fire a laser directly at the player
- Then it will rapidly move up or down to a new point, stop again, and fire directly at the player
There’s several different kinds of actions it could take: moving, firing, and waiting. And yet, we want to be able to do all these things when Act is called.
How can one Act method perform three different behaviors? The answer is to use a State Machine.
We want to set up a system where our new enemy can only be doing one thing at a time. We can store that as an enum MovingState variable, that can be Moving, Firing, or Waiting. This way we ensure our Act method will only ever do a single thing at a time.
We also want the ability to switch to a new state when certain conditions are met. We can make a helper method called ChangeState that does this, in order to ensure that the new state has everything properly set up. Normally we can use a switch statement that will do any necessary set up for other states, but in this case only movement needs some prior setup. We need to determine which waypoint to move to, and we do so in order (waypoint 0, 1, 2, 3, then back to 0, and it loops). We also determine how long it should take get there.
Now that we have the basics of the state machine in place, we just have to use it. As always, the Start method does some initial setup like creating the waypoints and positioning the enemy off the right edge of the screen. We also get the transform of the Player since we will need that to shoot lasers directly at them.
The DoMove method determines how far to move from the previous waypoint to the next waypoint smoothly, by using SmoothStep and linear interpolation. The more time has elapsed, the closer the enemy will get to the next waypoint. If the elapsed time is greater than the time to move, then we switch to the firing state.
DoFire will first wait until some amount of time has passed. Then, if the player still exists, we will fire a laser directly at them. We get the direction to fire as a Quaternion, based on the angle between the upwards direction and the player’s direction. After firing, we switch to the waiting state.
Finally, the DoWait method just waits some time and then switches back to the moving state. These three states will keep looping like this until either the player or the enemy is destroyed.
Now we can attach this behavior to the new enemy in place of its previous behavior script, and set up some necessary variables.
All done! The new smart enemy will rapidly move back and forth between the waypoints and try to shoot right at the player. Battling this kind of enemy will give the player a much larger challenge.