Part 6 - Collisions
In this walkthrough, add the ability for the game to detect collisions from projectiles.
Adding a ProjectileType on the Projectile Class
In order to properly check for collisions, it will be necessary to know the type of each Projectile object. If a projectile is coming from the Player, it should check for collisions with Enemy objects. If a projectile is coming from an Enemy, it should check for collisions with the Player object.
Add the Field and Property
First, update the Projectile class with a new field and property. Open up the Projectile.cs class to get started.
- In the body of the
Projectileclass, type in "propfull" and press Enter- This code snippet creates a template for a new property
- For the type of the new field/property, enter
ProjectileType - Press Tab to go to the next part of the template; the field name
- For the field name, enter
projectileType - Press Tab again to move onto the property name, and enter
ProjectileType
The code should ultimately look something like this:
private ProjectileType projectileType;
public ProjectileType ProjectileType
{
get { return projectileType; }
set { projectileType = value; }
}
Add the Constructor Parameter
Now that there is a ProjectileType on the Projectile class, it needs to be populated!
- Find the
Projectileconstructor - In the method signature, add a
ProjectileTypeparameter namedprojectileType - In the body of the constructor, set the
this.projectileTypefield to the value of theprojectileTypeparameter - Open up the ArcadeFlyerGame.cs file, and find the
FireProjectilemethod - Update the call to the
Projectileconstructor, and pass in theprojectileTypeparameter value
At this point, there should be no errors in the code. Nothing will have changed, but the new ProjectileType property will make it possible to distinguish between projectiles in the game.
Sprite Collisions
At long last, it's time to check whether any of the objects in our game collide with each other. One structure for doing this is by modifying our Sprite class, to add a method to see if any other sprites collide with it.
Every Sprite has a member Rectangle named PositionRectangle. The Rectangle class has a method named Intersects(), which takes another Rectangle as a parameter, and returns a bool indicating if the two rectangles overlap. We can write a "wrapper" for this in our Sprite class, to tell us if a Sprite intersects with another Sprite.
- In the Sprite.cs file, inside the
Spriteclass, add a new method namedOverlaps - Make the
Overlapsmethod take aSpriteobject namedotherSpriteas the parameter - Make the
Overlapsmethod return aboolindicating if the two sprites overlap - The
Overlapsmethod should callIntersectson thePositionRectangleof this object, and pass in thePositionRectangleof theotherSpriteas the parameter toIntersects
public bool Overlaps(Sprite otherSprite)
{
bool doesOverlap = this.PositionRectangle.Intersects(otherSprite.PositionRectangle);
return doesOverlap;
}
Removing Projectile Objects On Collision
Now that it is possible to detect collisions between sprites, it's time to update the game. If an enemy projectile comes in contact with the player, that projectile should be removed. Similarly, if a player projectile comes in contact with an enemy, that projectile should be removed. For now, the collision will not have any other effect (that will come later).
for Loop Setup
In order to remove Projectile objects from the projectiles list, it will be necessary to loop through each object. However, using a traditional for loop or foreach loop will not work; when an object is removed from a List, it disrupts the flow of the loop. Instead, loop through the list backwards so that removed objects do not interfere with the current position in the list.
- Find the
Updatemethod on theArcadeFlyerGameclass - In the body of the
Updatemethod, remove theforeachloop that loops through theprojectileslist - Create a new
forloop structure - For the initialization, create a new
int ivariable and set it to theCountof theprojectileslist minus1 - For the condition, check if the
ivariable value is greater than or equal to0 - For the increment, decrease the value of the
ivariable by1usingi-- - In the body of the
forloop, define a newProjectilevariablep, and set it to the element in theprojectileslist ati - Under that, call the
Projectile'sUpdatemethod on thepvariable
for (int i = projectiles.Count - 1; i >= 0; i--)
{
Projectile p = projectiles[i];
p.Update();
}
for Loop Body
Now that the for loop will properly loop through each Projectile object, it's time to remove the ones that collide!
- Declare a new
boolvariable namedplayerProjectile - Set the value of the
playerProjectilevariable to check if the currentProjectileobjectphas aProjectileTypeofProjectileType.Player - Under that, create an
ifstatement - In the condition for the
ifstatement, check ifplayerProjectileis false AND there is a collision between theplayerand the current projectilep - In the body of the
ifstatement, remove the currentProjectileobjectpfrom theprojectileslist withprojectiles.Remove(p) - Under the
if, create anelse if - In the condition for the
else ifstatement, check ifplayerProjectileis true AND there is a collision between theenemyand the current projectilep - In the body of the
else if, remove the currentProjectileobjectpfrom theprojectileslist withprojectiles.Remove(p)
bool playerProjectile = p.ProjectileType == ProjectileType.Player;
if (!playerProjectile && player.Overlaps(p))
{
projectiles.Remove(p);
}
else if (playerProjectile && enemy.Overlaps(p))
{
projectiles.Remove(p);
}
Run the game, and verify that when the proper projectiles collide with the player or the enemy, they disappear! This is a stepping stone toward destroying enemies and tracking the player's life and score.
Final Code
The final code for this walkthrough is available on GitHub.