Active
member from true
to false:
public bool Active = false;
Game1.cs
file, modify the declaration for the gameState
variable to set the default to GameStates.TitleScreen:
GameStates gameState = GameStates.TitleScreen;
Fonts
folder in the content project and select Add | New Item... Create a new SpriteFont
object named Pericles14.spritefont
. SpriteFont
will open automatically. Change the<FontName>
tag from Kooteny
to Pericles
.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);
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");
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; }
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;
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;
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;
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;
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.
3.133.160.14