/// <summary> /// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input, and playing audio. /// </summary> /// <param name="gameTime">Provides a snapshot of timing values.</param> protected override void Update(GameTime gameTime) { //if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) // Exit(); // TODO: Add your update logic here kbState = Keyboard.GetState(); mState = Mouse.GetState(); #region Game States FSM - Update() portion switch (gameState) { case (GameStates.StartScreen): { // "Deactivate" the player. playerState = PlayerStates.Idle; // Use idle scrolling. scrollingState = ScrollingStates.IdleScrolling; startScreen.StartButton.Update(mState, prevMState); startScreen.LeaderboardButton.Update(mState, prevMState); startScreen.QuitButton.Update(mState, prevMState); // Giving the buttons on the start screen their functionality if (startScreen.StartButton.IsClicked) { // Perform necessary data resets here, instead of on the // pregame screen itself so the code only runs once. if (ResetAction != null) { ResetAction(); } // Initialise the map for the current run. // This Reset() method's signature does not match that // of the event handler's wallManager.ResetGame(wall); gameState = GameStates.Pregame; } if (startScreen.LeaderboardButton.IsClicked) { gameState = GameStates.Leaderboard; } if (startScreen.QuitButton.IsClicked) { Exit(); } break; } case (GameStates.Leaderboard): { // Should not need to do anything other than check for // button press, since the start screen must be // accessed prior to the leaderboard. leaderboardScreen.BackButton.Update(mState, prevMState); // Giving the back button its functionality if (leaderboardScreen.BackButton.IsClicked) { gameState = GameStates.StartScreen; } break; } case (GameStates.Pregame): { // Do not scroll. scrollingState = ScrollingStates.NotScrolling; // Pressing any key starts the game. // GetPressedKeys() returns an array of all keys that // are currently being pressed. By checking the // array's length, we can effectively see if any // key was pressed at all in the previous frame. if (kbState.GetPressedKeys().Length > 0) { gameState = GameStates.Playing; } break; } case (GameStates.Playing): { // "Activate" the player. playerState = PlayerStates.Active; // Scroll normally. scrollingState = ScrollingStates.Scrolling; break; } case (GameStates.HiScore): { // "Deactivate" the player. playerState = PlayerStates.Idle; // Do not scroll. scrollingState = ScrollingStates.NotScrolling; hiScoreNameEntryScreen.Submit.Update(mState, prevMState); hiScoreNameEntryScreen.Update(kbState, prevKBState); // Either clicking the Submit button or hitting enter saves scores // and changes the game's state. if (hiScoreNameEntryScreen.Submit.IsClicked || kbState.IsKeyDown(Keys.Enter)) { // Update and save leaderboard data. leaderboard.UpdateScores( leaderboardPosition, hiScoreNameEntryScreen.LiveString, scoreCounter.Value); leaderboard.SaveScores(); gameState = GameStates.GameOver; } break; } case (GameStates.GameOver): { // Explicitly deactivate the player and stop scrolling here too, in // case no new high score was set. playerState = PlayerStates.Idle; scrollingState = ScrollingStates.NotScrolling; gameOverScreen.ReturnToStartScreen.Update(mState, prevMState); // Pressing Enter restarts the game. // Check for single key press so Enter input from high score screen // does not interfere with current. if (kbState.IsKeyDown(Keys.Enter) && prevKBState.IsKeyUp(Keys.Enter)) { // Perform necessary data resets. if (ResetAction != null) { ResetAction(); } // This Reset() method's signature does not match that // of the event handler's. wallManager.ResetGame(wall); gameState = GameStates.Playing; } // Giving the button on the game over screen its // functionality if (gameOverScreen.ReturnToStartScreen.IsClicked) { // Reset and initialise the map to prepare for idle // scrolling on the start screen. wallManager.ResetMenu(wall); gameState = GameStates.StartScreen; } break; } } #endregion #region Player States FSM // This FSM tracks all player-related functions: movement and // collision detection. switch (playerState) { case (PlayerStates.Idle): { // Do nothing - player character is idle. break; } case (PlayerStates.Active): { // Player is now active and has access to movement. if (kbState.IsKeyDown(Keys.Left)) { player.Position -= player.StrafeVelocity; player.UpdateTracker(); } if (kbState.IsKeyDown(Keys.Right)) { player.Position += player.StrafeVelocity; player.UpdateTracker(); } // Touching a wall results in a game over. foreach (Wall wall in wallManager.LeftWalls) { if (player.Collided(wall)) { // Generate a leaderboard position, if applicable. leaderboardPosition = leaderboard.IsHighScore(scoreCounter.Value); // Check whether the score earned this run is a high score. // If it is not, proceed directly to the game over screen. if (leaderboardPosition == -1) { gameState = GameStates.GameOver; } // Display the high score screen if the player earns a // score that alters the leaderboard. else { gameState = GameStates.HiScore; } } } foreach (Wall wall in wallManager.RightWalls) { if (player.Collided(wall)) { // Generate a leaderboard position, if applicable. leaderboardPosition = leaderboard.IsHighScore(scoreCounter.Value); // Check whether the score earned this run is a high score. // If it is not, proceed directly to the game over screen. if (leaderboardPosition == -1) { gameState = GameStates.GameOver; } // Display the high score screen if the player earns a // score that alters the leaderboard. else { gameState = GameStates.HiScore; } } } break; } } #endregion #region Scrolling States FSM // This FSM tracks and controls all scrolling possibilities for // the map. switch (scrollingState) { // Idle scrolling is used on the start and leaderboard screens. case (ScrollingStates.IdleScrolling): { // Continuous scrolling wallManager.Scroll(); // Why is Update() called before SpawnWallPair() and // DespawnWallPair(), and not after? // HINT: Variables initialised to 0 before Update() wallManager.UpdateMenu(gameTime); // Manage memory by spawning and despawning walls as necessary. wallManager.SpawnWallPair(wall); wallManager.DespawnWallPair(); break; } // The game does not scroll during the pregame, high score entry, and game over screens. case (ScrollingStates.NotScrolling): { // Do nothing - no scrolling takes place. break; } // Regular scrolling is used during actual gameplay. case (ScrollingStates.Scrolling): { // Continuous scrolling wallManager.Scroll(); //// Controlled scrolling //if (kbState.IsKeyDown(Keys.Up)) //{ // wallManager.Scroll(); //} // Why is Update() called before SpawnWallPair() and // DespawnWallPair(), and not after? // HINT: Variables initialised to 0 before Update() wallManager.UpdateGame(gameTime); // Manage memory by spawning and despawning walls as necessary. wallManager.SpawnWallPair(wall); wallManager.DespawnWallPair(); // Update the player's score, level, and strafe speed. scoreCounter.Update(gameTime, levelCounter.Value); // This method invokes the event, calling all the other // level up methods along with it. if (levelCounter.LevelUp(gameTime)) { // When the above method is executed, alter the bool flag. levelUpAnimation.HasLeveledUp = true; currentColour = bgColourArray[generator.Next(bgColourArray.Length)]; } // The bool flag is used here to scroll the text. // levelUpAnimation.LevelUp() cannot be grouped together with // levelUpAction because the code only runs once per level // up. Text scrolling needs to be performed each frame // after a level up, as long as the text has not cleared // the screen. Hence, this if statement is necessary. if (levelUpAnimation.HasLeveledUp) { levelUpAnimation.LevelUp(); } break; } } #endregion #region Movement debugging code //if (kbState.IsKeyDown(Keys.Left)) //{ // player.Position -= player.StrafeVelocity; // player.UpdateTracker(); //} //if (kbState.IsKeyDown(Keys.Right)) //{ // player.Position += player.StrafeVelocity; // player.UpdateTracker(); //} //foreach (Wall wall in wallManager.LeftWalls) //{ // if (player.Collided(wall)) // { // System.Console.WriteLine("LEFT WALL COLLISION"); // } //} //foreach (Wall wall in wallManager.RightWalls) //{ // if (player.Collided(wall)) // { // System.Console.WriteLine("RIGHT WALL COLLISION"); // } //} //// Controlled scrolling //if (kbState.IsKeyDown(Keys.Up)) //{ // wallManager.Scroll(); //} ////wallManager.Scroll(); //// Manage memory by spawning and despawning walls as necessary. //wallManager.SpawnWallPair(wall); //wallManager.DespawnWallPair(); #endregion prevKBState = kbState; prevMState = mState; base.Update(gameTime); }