Making the weapon itself
First thing we need to do will be making the weapon that will actually be launching those missiles, right? It’s a plain and simple contraption made of two FSM’s i called FiringSystem and FiringTimer. Firing system will handle the missile creation, and FiringTimer will take care of giving the weapon a rate of fire. It’s basically a countdown timer of cooldown between two missile salvos. It’s a really simple setup.
Firing System FSM
Here’s the graph of the system that controls missiles. Let’s get through the states one by one.
Idle and Stop firing states, triggered by Start and StopFiring global events (global event is an event that can be sent to this FSM from any other FSM) are empty. They are used to have the system in the state of waiting for the command from another FSM via global event, in this case, the FiringController (which there’s no need to cover here, it’s just an input system to detect the firing button has activated the FSM).
When the Fire global event has been received (dark gray rectangle, like Start and StopFiring), we will be getting the Countdown Timer Value from the FiringTimer every frame and compare that value via Float Compare action with 0 so we know when the weapon has cooled down. When the counter hits zero, we move to Get Fire Button state where the FSM will anxiously wait for you to mash that button to spawn missiles.
Ok, we’ve hit that button and moved to Missile Counter state. It is set to 0, and it will count the number of missiles we want to spawn. Once it gets to the predefined number, it will stop creating them and move on. Every time we enter the Missile Counter state, +1 is added to the counter variable and the missile is spawned in the connected SpawnMissile state. Immediately afterwards, we go back to Missile Counter state where we check the counter. In my case, when it hits 11 (so 10 missiles are created) we no longer move to SpawnMissile state but to Send Reset Firing Timer Event state. In that state, a global event is sent to the FiringTimer FSM to start counting down. Until the counter tells us it counted down to zero, we won’t be able to fire the weapon again.
Of course, before we get back to the state of waiting for the countdown timer to do its job, we need to reset the Missile Counter to 0 so we can use it again. It is simply done by setting the value of the INT variable Counter to 0 in the mid state called Reset Counter.
Fun part – Missiles!
Now that we created our firing system that happily churns out 10 missiles in a matter of frames, we need to know what to do with them, right? I won’t be covering the topic of playing the sound of the missile or dealing the damage/destroying itself when leaving the screen or making the particle system for the trail as those are really not the point of this blog post and are part of some other intertwined systems. Instead, we will focus on the movement.
Our missiles are made of two components, the bullet itself that contains VerticalTranslate and HorizontalTranslate FSM’s and only the transform component, and the child element that holds the SpriteRenderer.
The reason why the missile is split into two GameObjects is because we will be using translate movement instead of physics for missile movement so we are unable to use the movement vectors for sprite rotation and instead have to rely on a different technique.
Horizontal Translate FSM is straightforward and designed with only one purpose in mind – to control the horizontal movement of the missile.
There’s really not much to it. We will apply a random amount of acceleration per missile so we get the effect of some missile being slower/faster than the others. Bear in mind that you also need to define the starting Speed of the missile if you don’t want it to start at 0 speed. I opted for 1.75.
VerticalTranslate FSM is where things get fun. Start state is a random event in which it is decided by pure randomness whether the missile will start moving up or down.
Oh, this one decided to go up! We’ll just make it move up a bit (you can also randomize that value, which i’ll probably do, but not too much) by adding the float value to the Y axis in the translation.
We will wait for a random amount of time before staring to move down.
The principle is the same, we are applying a negative float value to the Y axis and translating the missile so it slowly straightens itself and then starts nosediving.
If the missile chose to go down instead of up in the Start state, the rest of the states are exactly the same, we just apply negative value to Y axis in the Move Down state to make it go down and then the positive value in the Reverse Move Up state to make it go up.
Now that we have our missile moving all around we need to take care of the visual representation. As noted before, we are not using physics for movement but a simple translate movement (which is equal to clicking on the game object, holding down the button and moving it around the screen) so we need to relate on a simple technique to point the missile towards the movement direction.
First we need to get the current missile position, i’ll be saving it in the LastFramePosition vector2 variable since it will be the last frame position in the next state.
All we need to do now is to rotate the GameObject towards the last frame position and adjust the rotation. Bear in mind that i’m using 0 degrees of the Rotation Offset since my missile sprite is rotated towards left, but if your missile sprite is pointing right as things usually are, you should set the Rotation Offset to 180 so it’s actually looking in the opposite direction of the last frame position.
The reason we are doing the things this way, with the parent in charge of movement and the child in charge of holding the sprite and rotating it is because the rotation of the parent object would cause the missile to not move properly since rotation affects the translation of the objects.
Here is the final result with particle system added for trail as well as damage detection and all the things that make the missile a missile.
If the images are too small, you can find the FSM templates in my GitHub repository