Part 5 - Enemy Projectiles
In this walkthrough, add the ability for an Enemy object to fire projectiles. This will be similar to how the Player fires projectiles, but there will be some refactoring along the way.
Basic Projectile Firing
Since the concept of projectiles already exists in the game, it won't be too difficult to allow the Enemy class to fire them. It will be very similar to the basic Player projectile firing, with some different values. Open up the Enemy.cs file to get started.
- Find the
Updatemethod on theEnemyclass - At the bottom of the
Updatemethod, create a newVector2object namedprojectilePosition - Set the
Xvalue ofprojectilePositionto be the currentXposition- This will make the projectile appear on the left side of the
Enemysprite
- This will make the projectile appear on the left side of the
- Set the
Yvalue of theprojectilePositionto be the currentYposition, plus half of the sprite height- This will make the projectile appear near the vertical middle of the
Enemysprite
- This will make the projectile appear near the vertical middle of the
- Under that, create another
Vector2object namedprojectileVelocity - Set the
Xvalue ofprojectileVelocityto-5.0fto make it move to the left - Set the
Yvalue ofprojectileVelocityto0.0fto keep it vertically constant - Call
root.FireProjectileand pass in the position and velocity to fire the projectile!
The code should look something like this:
Vector2 projectilePosition = new Vector2(position.X, position.Y + SpriteHeight / 2);
Vector2 projectileVelocity = new Vector2(-5.0f, 0.0f);
root.FireProjectile(projectilePosition, projectileVelocity);
Run the program to see what happens. The enemy can fire projectiles, but the problem is that they fire constantly! This is the same problem that occurred with the player's projectiles. It would be possible to simply copy the solution from the Player class, but there is actually a better way.
Defining the Timer Class
Since both the Player and the Enemy need a cool down period, it would make sense to create a separate utility that both classes can use: a Timer class. In fact, this type of utility could be used for many things in a game (animations, countdowns, etc)!
Setup
Start with the basic setup for the class.
- Create a new file named Timer.cs in the "src" folder
- Add
usingstatements forMicrosoft.Xna.Framework - Create a
namespacewrapper forArcadeFlyer2D - In the body of the
ArcadeFlyer2Dnamespace, define aclassnamedTimer
using Microsoft.Xna.Framework;
namespace ArcadeFlyer2D
{
class Timer
{
}
}
Fields and Properties
The fields and properties will be based on the cool down timer fields from the Player class:
projectileCoolDownTimeprojectileTimerprojectileTimerActive
Since the Timer could be used for more than just projectile cool downs, these can be generalized and adapted. Some of the values will need to be accessible, but some can be hidden.
- In the body of the
Timerclass, add a privatefloatfield namedtotalTime- This will represent the total duration for the timer
- Under that, add another private
floatfield namedtimer- This will track the actual current time for the timer
- Under that, create a new property named
Active, with an auto-implementedgetandprivate set- This way, the classes that use the timer can check whether the timer is currently active
private float totalTime;
private float timer;
public bool Active { get; private set; }
Constructor
Define a constructor to initialize new Timer objects.
- In the body of the
Timerclass, define a public constructor forTimerobjects - Add a
floatparameter namedtotalTimeto the constructor method signature - In the body of the constructor, set the
totalTimefield to thetotalTimeparameter value - Under that, set the
timerfield value to0.0f- This way the timer will start at zero
- Under that, set the
Activeproperty tofalse- The
Timerobject will not start until it is activated
- The
public Timer(float totalTime)
{
this.totalTime = totalTime;
this.timer = 0.0f;
this.Active = false;
}
StartTimer Method
The Timer class should have a way to kick off a new timer process. This will basically be copied from the Player class.
- In the body of the
Timerclass, define a new method namedStartTimer- It should have a
voidreturn type and no parameters
- It should have a
- In the body of the
StartTimermethod, set theActiveproperty totrue- This means time will be ticking!
- Under that, set the
timerfield value to0.0f- This resets the time on the timer
public void StartTimer()
{
Active = true;
timer = 0.0f;
}
Update Method
A Timer object should update with each new frame, incrementing the time and resetting as needed. This will basically be copied from the Player class.
- In the body of the
Timerclass, define a new method namedUpdate- It should have a
voidreturn type and take in aGameTimeparameter namedgameTime
- It should have a
- In the body of the
Updatemethod, create anifstatement - In the condition for the
ifstatement, check theActiveproperty to see if the timer is currently running - In the body of the
ifstatement, the timer is active, so increment thetimerfield by theTotalSeconds- Use
(float)gameTime.ElapsedGameTime.TotalSecondsto get the total seconds elapsed
- Use
- Under that, still in the body of the
ifstatement, create a newifstatement - In the condition for the new
ifstatement, check if thetimerfield value has surpassed thetotalTimefield value - In the body of the
ifstatement, the timer has completed, so set theActiveproperty totrue
public void Update(GameTime gameTime)
{
if (Active)
{
timer += (float)gameTime.ElapsedGameTime.TotalSeconds;
if (timer >= totalTime)
{
Active = false;
}
}
}
Timer Code
At the end of this section, the code in the Timer.cs file should look something like this:
using Microsoft.Xna.Framework;
namespace ArcadeFlyer2D
{
class Timer
{
private float totalTime;
private float timer;
public bool Active { get; private set; }
public Timer(float totalTime)
{
this.totalTime = totalTime;
this.timer = 0.0f;
this.Active = false;
}
public void StartTimer()
{
Active = true;
timer = 0.0f;
}
public void Update(GameTime gameTime)
{
if (Active)
{
timer += (float)gameTime.ElapsedGameTime.TotalSeconds;
if (timer >= totalTime)
{
Active = false;
}
}
}
}
}
Using the Timer Class
Now that the Timer class has been created, it's time to use it!
Refactoring the Player Class
First, refactor the Player class to use a Timer object instead of its own fields. Open up the Player.cs file to begin.
- In the body of the
Playerclass, remove theprojectileCoolDownTime,projectileTimer, andprojectileTimerActivefields - In place of the removed fields, add a new private
Timerfield namedprojectileCoolDown - In the
Playerconstructor, set theprojectileCoolDownfield to a newTimerobject, passing in0.5fto theTimerconstructor - In the body of the
HandleInputmethod, find theifstatement that checks if a projectile should be fired - Fix the condition of the
ifstatement so that it usesprojectileCoolDown.Activeinstead ofprojectileTimerActive - At the bottom of the body of that
ifstatement, remove theprojectileTimerActiveandprojectileTimerfield setting - In place of the removed statements, kick off a new timer with
projectileCoolDown.StartTimer() - In the
Updatemethod, remove the entirety of theif (projectileTimerActive)statement - Replace that
ifstatement with an update to theTimerobject:projectileCoolDown.Update(gameTime)
The Timer class allowed quite a bit of code to be removed from the Player class, which is great!
Updating the Enemy Class
Next, use another Timer object on the Enemy class to limit the number of projectiles fired. Open up the Enemy.cs file to begin.
- In the body of the
Enemyclass, add a new privateTimerfield namedprojectileCoolDown - In the
Enemyconstructor, set theprojectileCoolDownfield to a newTimerobject, passing in2.0fto theTimerconstructor - In the body of the
Updatemethod, find the projectile firing code that creates the twoVector2variables and calls theroot.FireProjectilemethod - Wrap that code in an
ifstatement - Make the condition for the
ifstatement check if the cool down timer is NOT currently active:!projectileCoolDown.Active - In the body of the
ifstatement, after a projectile has been fired, kick off a new timer withprojectileCoolDown.StartTimer() - Outside of the
ifstatement, update theTimerobject withprojectileCoolDown.Update(gameTime)
Using the Timer class made it easier to repeat the cool down functionality from the Player class! Run the game to see how the enemy fires projectiles at a slower rate.
Different Projectile Types
Functionally, the game is working as expected. However, it would be nice to have different projectiles for the player and the enemy. Luckily, this is not too difficult!
Loading an Enemy Projectile Image Asset
To create new enemy projectiles, it will be necessary to have a new image for the them! Use the following fireball image, or any other image:

- Save a new image named EnemyFire.png in the "Content" folder
- Open up the Content.mgcb file in the MonoGame Pipeline Tool
- Click the "Add Existing Item" button
- Select the EnemyFire.png file
- Click the "Build" button
Now the "EnemyFire" asset should be loadable in the game!
Storing the Image in the ArcadeFlyerGame Class
To make two different types of projectiles, it will be necessary to load in the new image. Open up the ArcadeFlyerGame.cs file to get started.
- In the body of the
ArcadeFlyerGameclass, add a new privateTexture2Dfield namedenemyProjectileSpriteto hold the enemy projectile image:private Texture2D enemyProjectileSprite; - In the body of the
LoadContentmethod, useContent.Load<Texture2D>to load the "EnemyFire" image asset - Store the "EnemyFire" image in the
enemyProjectileSpritefield:enemyProjectileSprite = Content.Load<Texture2D>("EnemyFire");
Updating the FireProjectile Method Definition
Next, make the FireProjectile method use a different image depending on the source of the projectile.
- Find the
FireProjectilemethod in theArcadeFlyerGameclass - Add a
stringparameter namedprojectileTypeto the method signature - In the body of the
FireProjectilemethod, declare (but do not set) a newTexture2Dvariable namedprojectileImage- This will store the proper image for the projectile based on the type
- Create an
if/elsestatement under that - Set the condition of the
ifstatement to check if theprojectileTypeparameter is equal to"Player" - In the body of the
ifstatement, set theprojectileImagevariable toplayerProjectileSprite - In the body of the
else, set theprojectileImagevariable toenemyProjectileSprite - Update the call to the
Projectileconstructor so that it passes inprojectileImagefor the image
public void FireProjectile(Vector2 position, Vector2 velocity, string projectileType)
{
Texture2D projectileImage;
if (projectileType == "Player")
{
projectileImage = playerProjectileSprite;
}
else
{
projectileImage = enemyProjectileSprite;
}
Projectile firedProjectile = new Projectile(position, velocity, projectileImage);
projectiles.Add(firedProjectile);
}
Updating the FireProjectile Method Calls
At this point, the calls to FireProjectile are broken in both the Player class and the Enemy class. They need to pass in an extra parameter!
- In the Player.cs file, find the call to the
FireProjectilemethod - Add a third argument to the method call:
"Player" - In the Enemy.cs file, find the call to the
FireProjectilemethod - Add a third argument to the method call:
"Enemy"
Run the program, and the different types of projectiles should appear!
The ProjectileType Enumeration
The current code works, but there is one slight improvement to be made. Currently, since FireProjectile takes in a string for the projectile type, any text value could be passed. However, not every text value would make sense as a projectile type; there are only "Player" projectiles and "Enemy" projectiles. One way to constrain the possible values passed is to use an enumeration.
In C#, enumerations are value types that are defined by a set of named constants with symbolic meaning. They are quite useful because they can ensure that variables respect any constraints that are necessary. Create a ProjectileType enumeration to use in the FireProjectile method.
- Make a new file named ProjectileType.cs in the "src" folder
- In the new file, add a
namespacewrapper forArcadeFlyer2D - Within the body of the
namespace, define a newenumnamedProjectileType - In the body of the
ProjectileTypeenumeration, addPlayerandEnemy, separated by a comma (,)
namespace ArcadeFlyer2D
{
enum ProjectileType
{
Player,
Enemy
}
}
Updating the FireProjectile Code
Now that the ProjectileType enumeration exists, there is no need to use string values to represent a type of projectile! Update the FireProjectile code to use ProjectileType instead of string.
- In the ArcadeFlyerGame.cs file, find the
FireProjectilemethod - In the method signature, change the type of the
projectileTypeparameter fromstringtoProjectileType - In the body of the
FireProjectilemethod, find theifstatement checking if it is a player type - Update the
ifcondition so that it checks againstProjectileType.Playerinstead of"Player" - In the Player.cs, find the
Updatemethod - In the body of the
Updatemethod, update theroot.FireProjectilecall to pass inProjectileType.Playerinstead of"Player" - In the Enemy.cs, find the
Updatemethod - In the body of the
Updatemethod, update theroot.FireProjectilecall to pass inProjectileType.Enemyinstead of"Enemy"
Run the code again, and make sure everything works the same as it did before!
Final Code
The final code for this walkthrough is available on GitHub.