Part 7 - Multiple Enemies
In this walkthrough, update the game so that there are multiple enemies, and they can all be destroyed. All changes for this part will take place in the ArcadeFlyerGame.cs file.
A List
of Enemy
Objects
The first thing to do is make sure the ArcadeFlyerGame
class is able to handle multiple enemies. Currently, there is one Enemy
field that creates the single enemy in the game. Update it so it uses a List<Enemy>
field instead. Note that this is an example of refactoring; the game should function the same, but it will be much easier to maintain and update!
Creating the List
Make the changes in the ArcadeFlyerGame.cs file.
- Find the
private Enemy enemy
field and remove it - Instead, add a
private List<Enemy>
field to theArcadeFlyerGame
class namedenemies
private List<Enemy> enemies;
- In the
ArcadeFlyerGame()
constructor, find where theenemy
field was set - Instead of doing this, initialize the
enemies
field to a new empty list of typeEnemy
enemies = new List<Enemy>();
- On the next line, add the original
new Enemy
object from before to theenemies
listenemies.Add(new Enemy(this, new Vector2(screenWidth, 0)));
Fixing the Update
and Draw
Methods
At this point, there should be some errors in the ArcadeFlyerGame.cs file. This is because the class still references the enemy
field. Fix the errors in the Update
and Draw
methods so that they properly handle the enemies
field instead.
- In the
Update
method, find theenemy.Update(gameTime)
method call - Remove that code, and instead create a
foreach
loop - Set the loop to iterate through each
Enemy
object in theenemies
list - In the body of the
foreach
loop, callUpdate
on the current enemy objectforeach (Enemy enemy in enemies) { enemy.Update(gameTime); }
- In the
Draw
method, find theenemy.Draw(gameTime, spriteBatch)
method call - Remove that code, and instead create a
foreach
loop - Set the loop to iterate through each
Enemy
object in theenemies
list - In the body of the
foreach
loop, callDraw
on the current enemy objectforeach (Enemy enemy in enemies) { enemy.Update(gameTime, spriteBatch); }
Fixing the Collision Detection
Previously, the game only needed to detect collisions for one enemy. Now, since there are multiple enemies, the code will have to handle collisions for all of them.
- In the
Update
method, find the collision detection for enemieselse if (playerProjectile && enemy.Overlaps(p)) { // ...
- Cut the
&&
and everything after it, so that theelse if
only checks forplayerProjectile
else if (playerProjectile) { // ...
- In the body of the
else if
, create aforeach
loop to loop through each enemyforeach (Enemy enemy in enemies) { }
- In the body of the
foreach
loop, create anif
statement checking if there is a collision between the current projectile and the current enemy- Note that this is the same condition that was cut from above
- In the body of the
if
statement, remove the current projectilep
from theprojectiles
listprojectiles.Remove(p);
Code
if (!playerProjectile && player.Overlaps(p))
{
projectiles.Remove(p);
}
else if (playerProjectile)
{
foreach (Enemy enemy in enemies)
{
if (enemy.Overlaps(p))
{
projectiles.Remove(p);
}
}
}
At this point, it should be possible to run the game again! It should function exactly the same as it did before, but now it will be possible to create multiple enemies.
Enemy Destruction
The next step is to destroy an enemy if a player projectile collides with it. Using the List<Enemy>
, this is as simple as removing the given enemy from the list.
- In the
Update
method, find where it loops through theenemies
list - Update the
foreach
loop, and make it afor
loop instead - Make the
for
loop loop through theenemies
list in reverse orderfor (int enemyIdx = enemies.Count - 1; enemyIdx >= 0; enemyIdx--) { }
- In the body of the
for
loop, create anEnemy enemy
variable and set it to the enemy at the current indexEnemy enemy = enemies[enemyIdx];
- Now, within the body of the
if
that checks for collisions, under where it removes the projectile, remove the current enemy from the listenemies.Remove(enemy);
Code
for (int enemyIdx = enemies.Count - 1; enemyIdx >= 0; enemyIdx--)
{
Enemy enemy = enemies[enemyIdx];
if (enemy.Overlaps(p))
{
projectiles.Remove(p);
enemies.Remove(enemy);
}
}
Run the game again. At this point, the enemy should disappear when a projectile collides with it! But having only one enemy is not very fun...
Enemy Creation
Finally, use a Timer
to add new Enemy
objects to the enemies
list on a regular basis. The goal will be to have a new enemy appear every three seconds.
Creating and Initializing the enemyCreationTimer
Field
Create a new Timer
field, and start the timer.
- Under the
private List<Enemy> enemies
field, create aprivate Timer
field namedenemyCreationTimer
private Timer enemyCreationTimer;
- In the
ArcadeFlyerGame()
constructor, set theenemyCreationTimer
to a new3.0
-second timerenemyCreationTimer = new Timer(3.0f);
- Call the
StartTimer
method on the newly-createdenemyCreationTimer
enemyCreationTimer.StartTimer();
Adding New Enemies
Now it's time to add some new enemies! A new enemy should be added every time the enemyCreationTimer
goes off (i.e. any time the enemyCreationTimer.Active
value is false).
- Find the
Update
method, and make some new lines at the bottom - Create an
if
statement checking!enemyCreationTimer.Active
if (!enemyCreationTimer.Active) { }
- In the body of the
if
statement, create a newEnemy
and add it to theenemies
list- Note that this is just like what happens when the first enemy is created in the constructor!
- Under that, still within the
if
body, start the timer againenemyCreationTimer.StartTimer();
- Under the
if
statement, call theUpdate
method on theenemyCreationTimer
enemyCreationTimer.Update(gameTimer);
Code
if (!enemyCreationTimer.Active)
{
enemies.Add(new Enemy(this, new Vector2(screenWidth, 0.0f)));
enemyCreationTimer.StartTimer();
}
enemyCreationTimer.Update(gameTime);
Run the game. At this point, enemies should be destroyed and regenerate every three seconds! Feel free to update the timer values to change the rates of enemy creation and projectile firing.
Final Code
The final code for this walkthrough is available on GitHub.