Now that I have an animation system, why don't I use it to try and implement a new enemy type with minimal code? I'm going to implement an XY Zapper enemy. This is an enemy that periodically spams out a horizontal or vertical laser beam that instantly fries everything in it's path. The Zapper tracks the player's position and homes in on it, which makes it hard for the player to sit still in one place on the screen. This is good as it helps to stop the player from being lazy, sitting still and exploiting holes in the bullet patterns.
The collision for the firing phase is also animated, like this:
The nice thing here is that only one step of the animation actually has any collision map attached to it. This means that I can rely on the animation system to turn the collision on and off at the right time, without having to do anything extra in code.
Looks
The Zapper has two states: moving and firing. Moving is just a repeated animation of four steps:
The length of this animation (it's 230 frame, or just under four seconds long) determines the length of the move state for the zapper - I'll talk about what that means in a minute...
The firing animation is a little more interesting. It includes a short "charge up" phase to telegraph to the player that the Zapper is about to fire and a "charge down" phase afterwards. The beam is only actually fatal when it is bright red:
The collision for the firing phase is also animated, like this:
The nice thing here is that only one step of the animation actually has any collision map attached to it. This means that I can rely on the animation system to turn the collision on and off at the right time, without having to do anything extra in code.
Brains
Remember that the Zapper has two states: moving and firing.
In the moving state the algorithm for the Zapper's movement is pretty simple. The Zapper has a heading (the direction of travel) and a velocity. On each update, I change the heading and the velocity and then move the Zapper's position at velocity speed in heading direction. To get the new heading, I take the current heading and I move it slightly towards the direction to the player. This means that the Zapper is always turning to face the player. The velocity at which the Zapper moves is based on its the distance from the player - when the Zapper is very close to the player it moves slowly, and when it is farther away it moves faster. As with heading, the target velocity is calculated each update, and the current velocity is nudged slightly towards it.
In the firing state, the logic is even simpler - the Zapper just continues moving in the same direction (ie: it doesn't turn) and it slows to a halt.
The lengths of the states are controlled by the lengths of animations. When the moving animation ends, it automatically goes into the firing animation, and when that ends it goes back to the moving animation. The Zapper's update() function figures out which animation is currently active and does the appropriate type of update logic
The code for all of this behaviour is in the update() function for the Zapper object. Here's a version that's slightly simplified for readability:
In the moving state the algorithm for the Zapper's movement is pretty simple. The Zapper has a heading (the direction of travel) and a velocity. On each update, I change the heading and the velocity and then move the Zapper's position at velocity speed in heading direction. To get the new heading, I take the current heading and I move it slightly towards the direction to the player. This means that the Zapper is always turning to face the player. The velocity at which the Zapper moves is based on its the distance from the player - when the Zapper is very close to the player it moves slowly, and when it is farther away it moves faster. As with heading, the target velocity is calculated each update, and the current velocity is nudged slightly towards it.
In the firing state, the logic is even simpler - the Zapper just continues moving in the same direction (ie: it doesn't turn) and it slows to a halt.
The lengths of the states are controlled by the lengths of animations. When the moving animation ends, it automatically goes into the firing animation, and when that ends it goes back to the moving animation. The Zapper's update() function figures out which animation is currently active and does the appropriate type of update logic
Note that I only talked about the X Zapper here - the Y Zapper has exactly the same logic but the graphics are in a vertical orientation.
Wrapping Up
I added a new enemy type to the game. I used the animation system that I added in the last update to drive the states of the enemy, which made it easy to implement the control logic.
I also added and changed a few other small part of the code, and you can check out the whole code online. The playable version is also available online.
Next Time
The game currently feels a little aimless - sure, you can try to get a high score but when you have unlimited time it's no big challenge. One thing that I want to implement to make high score chasing more of a challenge is to put a time limit on the levels, so that's what I'll work on next.
I also want to be heading towards adding a level editor so that you can design your own levels. Soon I will start heading towards making that happen.
Comments
Post a Comment