//****************************************************************// // This function updates the position of each enemy in the game // // and the player, it then checks if there are collisions between // // the enemy or the player and raises a PlayerCaught event. // // It also checks if the player has reached the end goal. // //****************************************************************// public void UpdatePositions(CGameState gameState, float time_delta) { /////////////////////////////////////////////////////////////////// // First we update the positions of the players and enemies. foreach (CSprite sprite in gameState.Enemies) { UpdatePosition(sprite, time_delta); } UpdatePosition(gameState.Player, time_delta); /////////////////////////////////////////////////////////////////// // Next we check if our player has reached the goal position. CPoint2f goalPositionPixels = CLevelUtils.GetPixelFromTileCoordinates(m_level.GoalPosition); if (IsInCollision(gameState.Player.Position, goalPositionPixels)) { OnGoalReached(this, "You Won!! Goal Reached!!"); return; } /////////////////////////////////////////////////////////////////// // Next we check each enemy in turn to see if the user has been // caught. foreach (CSprite sprite in gameState.Enemies) { if (IsInCollision(gameState.Player.Position, sprite.Position)) { OnPlayerCaught(this, "You Lose!! Player Caught!!"); break; } } }
//****************************************************************// // This function updates the velocity of each enemy in the game // // depending on whether it is in collision with a wall or an // // alternative move is available (e.g. turn left or right). It // // will only move backwards if no other move is available and a // // dead end has been encountered. // //****************************************************************// public void UpdateVelocities(CGameState gameState, float time_delta) { ///////////////////////////////////////////////// // We only update the velocities of the enemies, // the player is done through keyboard events. ///////////////////////////////////////////////////////// // Each enemy has a targetPosition aswell as a Position. // the target position is the centre of the square the enemy // is moving towards. this is done so that a monster always // walks along the centre of a corridor. // When we compute the velocity we are allowing the enemy to // change direction if he is close to a square centre. // If he is within 5 pixels of the target square we take the following steps: // 1. Update his position to be at the target position. // 2. Test if there are any available squares in any direction (forwards, left, right). // 3. If there are randomly select a direction and update velocity. // 4. Set the target as one complete tile in the new direction. // 5. If no squares are available set velocity to be backwards and target square the // next square backwards. foreach (CEnemy enemy in gameState.Enemies) { if (Math.Pow(enemy.TargetPosition.X - enemy.Position.X, 2.0) + Math.Pow(enemy.TargetPosition.Y - enemy.Position.Y, 2.0) <= AT_TARGET_TOLERANCE_SQ) { ///////////////////////////////////////////////////////// // Move the enemy to the target position. enemy.Position.X = enemy.TargetPosition.X; enemy.Position.Y = enemy.TargetPosition.Y; float xVel = 0.0f; float yVel = 0.0f; //////////////////////////////////////////////////////// // Set the velocity to be a complete tile in the // current direction. if (enemy.Velocity.X != 0.0f) { xVel = Math.Sign(enemy.Velocity.X) * CGameTextures.TILE_SIZE; } else { yVel = Math.Sign(enemy.Velocity.Y) * CGameTextures.TILE_SIZE; } bool[] ValidMoves = new bool[3] { false, false, false }; bool valid_move_found = false; CPoint2f testPos = new CPoint2f(); //////////////////////////////////////////////////////// // At each central tile position we test if the monster // can move in any direction other than backwards. testPos.X = enemy.Position.X + xVel; testPos.Y = enemy.Position.Y + yVel; if (!IsInCollisionWithWall(testPos) && !IsInCollisionWithFire(testPos)) { ValidMoves[0] = true; valid_move_found = true; } testPos.X = enemy.Position.X + yVel; testPos.Y = enemy.Position.Y + xVel; if (!IsInCollisionWithWall(testPos) && !IsInCollisionWithFire(testPos)) { ValidMoves[1] = true; valid_move_found = true; } testPos.X = enemy.Position.X - yVel; testPos.Y = enemy.Position.Y - xVel; if (!IsInCollisionWithWall(testPos) && !IsInCollisionWithFire(testPos)) { ValidMoves[2] = true; valid_move_found = true; } ///////////////////////////////////////////////////////// // If we've found a valid move we randomly select a new // direction. Otherwise we move backwards (since this must // be valid). if (valid_move_found) { ////////////////////////////////////////////////////// // Find a valid random move (this is a little inefficient). int i = m_randomGenerator.Next() % 3; while (!ValidMoves[i]) { i = m_randomGenerator.Next() % 3; } switch (i) { case 0: enemy.TargetPosition.X = enemy.Position.X + xVel; enemy.TargetPosition.Y = enemy.Position.Y + yVel; enemy.Velocity.X = enemy.Velocity.X; enemy.Velocity.Y = enemy.Velocity.Y; break; case 1: enemy.TargetPosition.X = enemy.Position.X + yVel; enemy.TargetPosition.Y = enemy.Position.Y + xVel; float temp = enemy.Velocity.X; enemy.Velocity.X = enemy.Velocity.Y; enemy.Velocity.Y = temp; break; default: case 2: enemy.TargetPosition.X = enemy.Position.X - yVel; enemy.TargetPosition.Y = enemy.Position.Y - xVel; temp = enemy.Velocity.X; enemy.Velocity.X = -enemy.Velocity.Y; enemy.Velocity.Y = -temp; break; } } else { //////////////////////////////////////////////////////////// // No move is available so we will move backwards. enemy.TargetPosition.X = enemy.Position.X - xVel; enemy.TargetPosition.Y = enemy.Position.Y - yVel; enemy.Velocity.X = -enemy.Velocity.X; enemy.Velocity.Y = -enemy.Velocity.Y; } } } }
//****************************************************************// // This function is used as an event handler for the load click *// // event. This is used to load a new level. In addition to *// // the data required for the level it also ensures the board is *// // displayed. *// //****************************************************************// private void btnLoad_Click(object sender, RoutedEventArgs e) { //////////////////////////////////////////////////////////// // clear any existing children from the canvas. cvsMainScreen.Children.Clear(); /////////////////////////////////////////////////////////// // Get the directory where the level data is stored and // load the data in. string fileDir = txtLevelDir.Text; currentLevel = CLevelParser.ImportLevel(fileDir); gameTextures = CLevelParser.ImportTextures(fileDir); /////////////////////////////////////////////////////////// // Draw the set of wall and floor tiles for the current // level and the goal icon. This is part of the game // we do not expect to change as it cannot move. DrawLevel(); ////////////////////////////////////////////////////////// // Add a game state, this represents the position and velocity // of all the enemies and the player. Basically, anything // that is dynamic that we expect to move around. gameState = new CGameState(currentLevel.EnemyPositions.Count()); /////////////////////////////////////////////////////////// // Set up the player to have the correct .bmp and set it to // its initial starting point. The player's position is stored // as a tile index on the Clevel class, this must be converted // to a pixel position on the game state. playerIcon = new Image(); playerIcon.Width = CGameTextures.TILE_SIZE; playerIcon.Height = CGameTextures.TILE_SIZE; playerIcon.Source = gameTextures.PlayerIcon; cvsMainScreen.Children.Add(playerIcon); ////////////////////////////////////////////////////////// // Create instances of the enemies and fires for display. We must do // this as each child on a canvas must be a distinct object, // we could not simply add the same image multiple times. enemyIcons = new Image[currentLevel.EnemyPositions.Count()]; for (int i = 0; i < currentLevel.EnemyPositions.Count(); i++) { enemyIcons[i] = new Image(); enemyIcons[i].Width = CGameTextures.TILE_SIZE; enemyIcons[i].Height = CGameTextures.TILE_SIZE; enemyIcons[i].Source = gameTextures.EnemyIcon; cvsMainScreen.Children.Add(enemyIcons[i]); } fireIcons = new Image[currentLevel.FirePositions.Count()]; for (int i = 0; i < currentLevel.FirePositions.Count(); i++) { fireIcons[i] = new Image(); fireIcons[i].Width = CGameTextures.TILE_SIZE; fireIcons[i].Height = CGameTextures.TILE_SIZE; fireIcons[i].Source = gameTextures.FireIcon; cvsMainScreen.Children.Add(fireIcons[i]); CPoint2i tilePosition = CLevelUtils.GetPixelFromTileCoordinates(new CPoint2i(currentLevel.FirePositions[i].X, currentLevel.FirePositions[i].Y)); Canvas.SetLeft(fireIcons[i], tilePosition.X); Canvas.SetTop(fireIcons[i], tilePosition.Y); } loadTextures(); //////////////////////////////////////////////////////////// // Set each instance of a dynamic object to its initial position // as defined by the current level object. InitialiseGameState(); //////////////////////////////////////////////////////////// // Render the current game state, this will render the player // and the enemies in their initial position. RenderGameState(); }