Time for action - structuring the game

  1. In the EnemyManager class, change the default value of the Active member from true to false:
    public bool Active = false;
    
  2. In the Game1.cs file, modify the declaration for the gameState variable to set the default to GameStates.TitleScreen:
    GameStates gameState = GameStates.TitleScreen;
    
  3. Right-click on the Fonts folder in the content project and select Add | New Item... Create a new SpriteFont object named Pericles14.spritefont.
  4. The XML file for the SpriteFont will open automatically. Change the<FontName> tag from Kooteny to Pericles.
  5. Add the following declarations to the Game1 class:
    SpriteFont pericles14;
    private float playerDeathDelayTime = 10f;
    private float playerDeathTimer = 0f;
    private float titleScreenTimer = 0f;
    private float titleScreenDelayTime = 1f;
    private int playerStartingLives = 3;
    private Vector2 playerStartLocation = new Vector2(390, 550);
    private Vector2 scoreLocation = new Vector2(20, 10);
    private Vector2 livesLocation = new Vector2(20, 25);
    
  6. Add the following line to the LoadContent() method of the Game1 class to load the Pericles sprite font that we created when we first built the project:
    pericles14 = Content.Load<SpriteFont>(@"FontsPericles14");
    
  7. Add the resetGame() helper function to the Game1 class:
    private void resetGame()
    {
    playerManager.playerSprite.Location = playerStartLocation;
    foreach (Sprite asteroid in asteroidManager.Asteroids)
    {
    asteroid.Location = new Vector2(-500, -500);
    }
    enemyManager.Enemies.Clear();
    enemyManager.Active = true;
    playerManager.PlayerShotManager.Shots.Clear();
    enemyManager.EnemyShotManager.Shots.Clear();
    playerManager.Destroyed = false;
    }
    
  8. In the Update() method of the Game1 class, replace the current case section for GameStates.TitleScreen with:
    case GameStates.TitleScreen:
    titleScreenTimer +=
    (float)gameTime.ElapsedGameTime.TotalSeconds;
    if (titleScreenTimer >= titleScreenDelayTime)
    {
    if ((Keyboard.GetState().IsKeyDown(Keys.Space)) ||
    (GamePad.GetState(PlayerIndex.One).Buttons.A ==
    ButtonState.Pressed))
    {
    playerManager.LivesRemaining = playerStartingLives;
    playerManager.PlayerScore = 0;
    resetGame();
    gameState = GameStates.Playing;
    }
    }
    break;
    
  9. Replace the case section for GameStates.Playing with:
    case GameStates.Playing:
    starField.Update(gameTime);
    asteroidManager.Update(gameTime);
    playerManager.Update(gameTime);
    enemyManager.Update(gameTime);
    explosionManager.Update(gameTime);
    collisionManager.CheckCollisions();
    if (playerManager.Destroyed)
    {
    playerDeathTimer = 0f;
    enemyManager.Active = false;
    playerManager.LivesRemaining--;
    if (playerManager.LivesRemaining < 0)
    {
    gameState = GameStates.GameOver;
    }
    else
    {
    gameState = GameStates.PlayerDead;
    }
    }
    break;
    
  10. Replace the case section for GameStates.PlayerDead with:
    case GameStates.PlayerDead:
    playerDeathTimer +=
    (float)gameTime.ElapsedGameTime.TotalSeconds;
    starField.Update(gameTime);
    asteroidManager.Update(gameTime);
    enemyManager.Update(gameTime);
    playerManager.PlayerShotManager.Update(gameTime);
    explosionManager.Update(gameTime);
    if (playerDeathTimer >= playerDeathDelayTime)
    {
    resetGame();
    gameState = GameStates.Playing;
    }
    break;
    
  11. Replace the case section for GameStates.GameOver with:
    case GameStates.GameOver:
    playerDeathTimer +=
    (float)gameTime.ElapsedGameTime.TotalSeconds;
    starField.Update(gameTime);
    asteroidManager.Update(gameTime);
    enemyManager.Update(gameTime);
    playerManager.PlayerShotManager.Update(gameTime);
    explosionManager.Update(gameTime);
    if (playerDeathTimer >= playerDeathDelayTime)
    {
    gameState = GameStates.TitleScreen;
    }
    break;
    

What just happened?

Currently, the EnemyManager starts generating enemies as soon as the game starts, even if the game is sitting at the title screen. It is set this way because, while we were building the parts of our game, we wanted to see the enemy ships spawn, move, and fire. Now that we are building the structure of our game, we need to disable the EnemyManager by default, waiting for the game to start to enable it.

The same setting change is needed for the default game state. Currently, for testing purposes, we have the gameState variable defaulting to GameStates.Playing, meaning that when the game is launched the title screen is bypassed in favour of going directly to the game. We change this here to begin our game in the right mode.

Next, we need to define a number of variables to control the flow and appearance of the game, starting with a SpriteFont object to hold the Pericles 14 point font we added to the project when we initially created it.

Our standard timer is implemented with playerDeathTimer and playerDeathDelayTime to control how long the game waits when the player has been killed before resetting and respawning the player.

The same timing mechanism utilizes titleScreenTimer and titleScreenDelayTime to ensure that the title screen does not accept input for the first second it is active. This way, if the player is still pressing a key when the game switches back to the title screen, a new game will not be immediately started.

When starting a new game, the player will begin with three lives, and whenever the player is spawned (either for a new game or after being destroyed), the player will start at a screen location of (390, 550).

The final two vectors, scoreLocation and livesLocation, point to the location on the screen where the player's score and remaining lives will be displayed.

The resetGame() method positions the player, moves each asteroid to an off-screen location (to prevent them from being too close to the player when the player spawns), and clears any active enemies and shots. It enables the EnemyManager and finally sets the playerManager.Destroyed value to false, indicating that the player is currently "alive".

The title screen update code waits for the timer to elapse, and then checks to see if either the Space bar on the keyboard or the A button on the GamePad has been pressed. When it has, the player's remaining lives and score are reset, and the resetGame() method is called. The game state is then switched to GameStates.Playing.

While in the Playing state, the code is similar to what we were using during testing, updating each of the different managers. The addition here is that, after all of the updates, the playerManager.Destroyed value is checked. If the player has been destroyed, the death timer is reset, the EnemyManager is disabled, and a life is deducted from the player's remaining total. If the life that was just lost was the player's last, the game state is set to GameOver. Otherwise, the state is set to PlayerDead.

When in the PlayerDead state, most of the managers continue to update as normal, so the star field continues to move, and game objects continue on their courses. The CollisionManager is not updated while the player is dead. When the delay timer has expired, the resetGame() method is called and the state is set back to Playing.

Finally, when the game is over, the playerDeathTimer will accumulate time until it has finished, and then the game will return to the title screen.

At the moment, running the game will not produce the expected results since we have not yet updated the case statements in the Draw() method to match the game state flow.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.160.14