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 enemyfield and remove it - Instead, add a
private List<Enemy>field to theArcadeFlyerGameclass namedenemiesprivate List<Enemy> enemies; - In the
ArcadeFlyerGame()constructor, find where theenemyfield was set - Instead of doing this, initialize the
enemiesfield to a new empty list of typeEnemyenemies = new List<Enemy>(); - On the next line, add the original
new Enemyobject from before to theenemieslistenemies.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
Updatemethod, find theenemy.Update(gameTime)method call - Remove that code, and instead create a
foreachloop - Set the loop to iterate through each
Enemyobject in theenemieslist - In the body of the
foreachloop, callUpdateon the current enemy objectforeach (Enemy enemy in enemies) { enemy.Update(gameTime); } - In the
Drawmethod, find theenemy.Draw(gameTime, spriteBatch)method call - Remove that code, and instead create a
foreachloop - Set the loop to iterate through each
Enemyobject in theenemieslist - In the body of the
foreachloop, callDrawon 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
Updatemethod, find the collision detection for enemieselse if (playerProjectile && enemy.Overlaps(p)) { // ... - Cut the
&&and everything after it, so that theelse ifonly checks forplayerProjectileelse if (playerProjectile) { // ... - In the body of the
else if, create aforeachloop to loop through each enemyforeach (Enemy enemy in enemies) { } - In the body of the
foreachloop, create anifstatement 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
ifstatement, remove the current projectilepfrom theprojectileslistprojectiles.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
Updatemethod, find where it loops through theenemieslist - Update the
foreachloop, and make it aforloop instead - Make the
forloop loop through theenemieslist in reverse orderfor (int enemyIdx = enemies.Count - 1; enemyIdx >= 0; enemyIdx--) { } - In the body of the
forloop, create anEnemy enemyvariable and set it to the enemy at the current indexEnemy enemy = enemies[enemyIdx]; - Now, within the body of the
ifthat 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> enemiesfield, create aprivate Timerfield namedenemyCreationTimerprivate Timer enemyCreationTimer; - In the
ArcadeFlyerGame()constructor, set theenemyCreationTimerto a new3.0-second timerenemyCreationTimer = new Timer(3.0f); - Call the
StartTimermethod on the newly-createdenemyCreationTimerenemyCreationTimer.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
Updatemethod, and make some new lines at the bottom - Create an
ifstatement checking!enemyCreationTimer.Activeif (!enemyCreationTimer.Active) { } - In the body of the
ifstatement, create a newEnemyand add it to theenemieslist- Note that this is just like what happens when the first enemy is created in the constructor!
- Under that, still within the
ifbody, start the timer againenemyCreationTimer.StartTimer(); - Under the
ifstatement, call theUpdatemethod on theenemyCreationTimerenemyCreationTimer.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.