Part 4 - Player Projectiles
In this walkthrough, add the ability for the player to fire a projectile. From an architectural perspective, there will be a Projectile class that inherits from the Sprite class. The ArcadeFlyerGame class will keep track of a List of Projectile objects. It will create, update, and draw them as necessary. The Player class will be able to fire a projectile by communicating with the ArcadeFlyerGame class.
Loading a Projectile Image Asset
To create projectiles, it will be necessary to have an image for the them! Use the following fireball image, or any other image:

- Save a new image named PlayerFire.png in the "Content" folder
- Open up the Content.mgcb file in the MonoGame Pipeline Tool
- Click the "Add Existing Item" button
- Select the PlayerFire.png file
- Click the "Build" button
Now the "PlayerFire" asset should be loadable in the game!
Creating the Projectile Class
The next thing to do is create a class that represents a projectile.
Setup
Start with the basic setup for the class.
- Create a new file named Projectile.cs in the "src" folder
- Add
usingstatements forMicrosoft.Xna.FrameworkandMicrosoft.Xna.Framework.Graphics - Create a
namespacewrapper forArcadeFlyer2D - In the body of the
ArcadeFlyer2Dnamespace, define aclassnamedProjectile - Use a colon (
:) to make theProjectileclass inherit from theSpriteclass- This will provide a lot of the necessary functionality for projectiles out of the box!
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace ArcadeFlyer2D
{
class Projectile : Sprite
{
}
}
Velocity
The projectile will be moving through space, so it will need a velocity. In the body of the Projectile class, create a private Vector2 field for this purpose:
private Vector2 velocity;
Constructor
The constructor will be used to create Projectile objects.
- In the body of the
Projectileclass, create a public constructor - Add a
Vector2parameter:position - Add another
Vector2parameter:velocity - Add a
Texture2Dparameter:spriteImage - Use
: base()after the method signature to call the baseSpriteconstructor, passing in thepositionparameter value - In the body of the constructor, set the
velocityfield to thevelocityparameter value - Under that, set the
SpriteWidthproperty from theSpriteclass to32.0f - Under that, set the
SpriteImageproperty from theSpriteclass to thespriteImageparameter value
public Projectile(Vector2 position, Vector2 velocity, Texture2D spriteImage) : base(position)
{
this.velocity = velocity;
this.SpriteWidth = 32.0f;
this.SpriteImage = spriteImage;
}
An Update method
There should be a way for the Projectile objects to update from one frame to the next.
- In the body of the
Projectileclass, define a new public method namedUpdate- The method should have no parameters, and a return type of
void
- The method should have no parameters, and a return type of
- In the body of the
Updatemethod, increment thepositionvalue by thevelocity
public void Update()
{
position += velocity;
}
Projectile Code
At the end of this section, the code in the Projectile.cs file should look something like this:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace ArcadeFlyer2D
{
class Projectile : Sprite
{
private Vector2 velocity;
public Projectile(Vector2 position, Vector2 velocity, Texture2D spriteImage) : base(position)
{
this.velocity = velocity;
this.SpriteWidth = 32.0f;
this.SpriteImage = spriteImage;
}
public void Update()
{
position += velocity;
}
}
}
Tracking Projectiles in the Game
Now that Projectile objects can be represented in the game, it's time to use them! Open up the ArcadeFlyerGame.cs file, and introduce the idea of projectiles.
Fields
In order to track projectiles, the ArcadeFlyerGame class will need some new fields.
- At the top of the ArcadeFlyerGame.cs file, add a
usingstatement forSystem.Collections.Generic- This will allow the class to utilize the
Liststructure
- This will allow the class to utilize the
- In the body of the
ArcadeFlyerGameclass, add a new private field of typeList<Projectile>namedprojectiles - Under the
projectilesfield, add a new privateTexture2Dfield namedplayerProjectileSprite- This will store the image for the projectile
Initialization and Loading
Now that the fields exist, it's time to initialize them.
- At the bottom of the
ArcadeFlyerGameconstructor, set theprojectilesfield to a new empty list:projectiles = new List<Projectile>(); - In the
LoadContentmethod, useContent.Load<Texture2D>to load the "PlayerFire" image asset - Store the "PlayerFire" image in the
playerProjectileSpritefield:playerProjectileSprite = Content.Load<Texture2D>("PlayerFire");
Updating and Drawing
Each Projectile object in the game will need to be updated and drawn each frame.
- In the body of the
Updatemethod, create a newforeachloop - In the loop, cycle through each
Projectileobject in theprojectileslist - Call the
Projectile'sUpdatemethod on eachProjectileobject:foreach (Projectile p in projectiles) { p.Update(); } - In the body of the
Drawmethod, between thespriteBatch.BeginandspriteBatch.End, create a newforeachloop - In the loop, cycle through each
Projectileobject in theprojectileslist - Call the
Sprite'sDrawmethod on eachProjectileobject, passing in thegameTimeandspriteBatch:foreach (Projectile p in projectiles) { p.Draw(gameTime, spriteBatch); }
Firing
The ArcadeFlyerGame class should have the ability to fire a projectile. This will involve creating a new Projectile object, and adding it to the projectiles list.
- In the body of the
ArcadeFlyerGameclass, define a new public method namedFireProjectile - Make the return type of the
FireProjectilemethodvoid - Give the
FireProjectiletwoVector2parameters:positionandvelocity - In the body of the
FireProjectilemethod, define aProjectileobject namedfireProjectile - Set the
fireProjectilevariable to be a newProjectileobject- Pass in the
position,velocity, andplayerProjectileSpriteto theProjectileconstructor
- Pass in the
- Add the newly created
fireProjectileto theprojectileslist
public void FireProjectile(Vector2 position, Vector2 velocity)
{
Projectile firedProjectile = new Projectile(position, velocity, playerProjectileSprite);
projectiles.Add(firedProjectile);
}
Firing Projectiles from the Player Class
Now that game can properly handle Projectile objects, allow the Player to fire them! Open up the Player.cs file to get started. The Player should fire a projectile when the user presses the Space key.
- At the bottom of the
HandleInputmethod, create anifstatement - For the condition of the
ifstatement, check if the Space key is pressed - In the body of the
ifstatement, create a newVector2variable namedprojectilePosition- This will be where the projectile is created on the screen
- For the
Xvalue of the newVector2, set it to the currentXposition of the sprite, plus the total width of the sprite- This will make the projectile appear on the right side of the player sprite
- For the
Yvalue of the newVector2, set it to the currentYposition of the sprite, plus the sprite's height divided by2- This will make the projectile appear at the vertical middle of the player sprite
- Under that, create another new
Vector2variable namedprojectileVelocity - Set the
Xvalue for theprojectileVelocityto10.0f- This will make the projectile move to the right
- Set the
Yvalue for theprojectileVelocityto0.0f - Under that, use
root.FireProjectileto fire the projectile withprojectilePositionandprojectileVelocity
The code should look something like this:
if (currentKeyboardState.IsKeyDown(Keys.Space))
{
Vector2 projectilePosition = new Vector2(position.X + SpriteWidth, position.Y + SpriteHeight / 2);
Vector2 projectileVelocity = new Vector2(10.0f, 0.0f);
root.FireProjectile(projectilePosition, projectileVelocity);
}
Run the program and see how it works. The player can fire projectiles, which is great! The only problem is that there is no limit to the number of projectiles that can fire.
Timed Firing
To make the game a little more interesting, only allow the player to fire projectiles at a certain rate. This is possible using a timer mechanism.
Fields
There will be a few necessary fields to keep track of the cool down period for the projectile firing. Add the following private fields to the Player class:
- A
floatnamedprojectileCoolDownTimeset to0.5f- This is the total amount of time (in seconds) the timer will last
- A
floatnamedprojectileTimerset to0.0f- This is the current time (in seconds) for the timer
- A
boolnamedprojectileTimerActiveset tofalse- This keeps track of whether or not the player is currently cooling down
private float projectileCoolDownTime = 0.5f;
private float projectileTimer = 0.0f;
private bool projectileTimerActive = false;
Conditional Firing
Next, update the code so that a projectile can only fire if the player is not cooling down.
- In the body of the
HandleInputmethod, find theifstatement checking if Space is pressed - Update the condition so that it checks for BOTH the key press, AND whether the projectile timer is not currently active:
if (!projectileTimerActive && currentKeyboardState.IsKeyDown(Keys.Space)) - In the body of the
ifstatement, at the bottom, set theprojectileTimerActivefield totrue- This will kick off a new cool down process
- Under that, set the
projectileTimerfield to0.0f- This will reset the timer
Timer Updates
For this to work, the timer will have to be updated so that it properly counts down the time! This will require the use of the MonoGame GameTime structure. The GameTime structure allows developers to track how much time has passed from frame to frame. This is useful for anything in the game that involves time. Check out this article for more information.
- In the body of the
Updatemethod, at the bottom, create a newifstatement - For the
ifcondition, check ifprojectileTimerActiveis true- This means the timer must be updated
- In the body of the
ifstatement, increment the value of theprojectileTimerfield based on the total seconds fromgameTime - Under that, create another
ifstatement - In the new
ifstatement, check if theprojectileTimerhas reached the totalprojectileCoolDownTimethreshold- This means the time is up
- In the body of the new
ifstatement, setprojectileTimerActivetofalsebecause the timer has completed
if (projectileTimerActive)
{
projectileTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
if (projectileTimer >= projectileCoolDownTime)
{
projectileTimerActive = false;
}
}
Run the code again, and this time the player should only be able to fire a new projectile every half-second!
Final Code
The final code for this walkthrough is available on GitHub.