Texture2D[] skyboxTextures; // textures to display around the skybox #endregion Fields #region Methods /// <summary> /// Draw the skybox to the screen. /// </summary> /// <param name="device"></param> /// <param name="gameCamera">Used to get the view and projection matrices</param> /// <param name="player">The player's position governs where the skybox is drawn</param> public void Draw(ref GraphicsDevice device, Camera gameCamera, Player player) { device.SamplerStates[0] = clampTextureAddressMode; DepthStencilState dss = new DepthStencilState(); dss.DepthBufferEnable = false; device.DepthStencilState = dss; Matrix[] skyboxTransforms = new Matrix[skyboxModel.Bones.Count]; skyboxModel.CopyAbsoluteBoneTransformsTo(skyboxTransforms); int i = 0; foreach (ModelMesh mesh in skyboxModel.Meshes) { foreach (Effect currentEffect in mesh.Effects) { Matrix worldMatrix = skyboxTransforms[mesh.ParentBone.Index] * Matrix.CreateTranslation(player.Position); currentEffect.CurrentTechnique = currentEffect.Techniques["Textured"]; currentEffect.Parameters["xWorld"].SetValue(worldMatrix); currentEffect.Parameters["xView"].SetValue(gameCamera.ViewMatrix); currentEffect.Parameters["xProjection"].SetValue(gameCamera.ProjectionMatrix); currentEffect.Parameters["xTexture"].SetValue(skyboxTextures[i++]); } mesh.Draw(); } dss = new DepthStencilState(); dss.DepthBufferEnable = true; device.DepthStencilState = dss; }
/// <summary> /// Update the positions of the missile and, if applicable, updates the player's /// health if the player has been hit by a missile /// </summary> /// <param name="player">To update the player's health, if applicable</param> /// <param name="floorPlan">To determine if a missile is no longer in the map</param> /// <param name="gameState">The current state of the game</param> /// <remarks> /// If the player has lost all health, the game will transition to the end state /// </remarks> public void Update(Player player, int[,] floorPlan, ref GameConstants.GameState gameState) { // update each missile for (int i = 0; i < missileList.Count; i++) { missileList[i].Update(); if (missileList[i].Position.Y < 0.0f || floorPlan[(int)missileList[i].Position.X, (int)-missileList[i].Position.Z] != 0) { // this missile is off of the map (either ran into a building or under the map) missileList.Remove(missileList[i]); continue; } if (player.BoundingSphere.Contains(missileList[i].Position) != ContainmentType.Disjoint) { // the player was hit missileList.Remove(missileList[i]); player.HitByMissile(ref gameState); explosionEffect.Play(); } } }
private Texture2D whiteRect; // holds a white rectangle texture #endregion Fields #region Methods /// <summary> /// Draw the minimap to the screen /// </summary> /// <param name="gameTime">Gives information on elapsed time for flashing enemies</param> /// <param name="player">The position of player is needed for minimap</param> /// <param name="enemies">The position of enemies is needed for minimap</param> /// <param name="map">The position of the fuel barrels and building is needed for minimap</param> public void Draw(GameTime gameTime, Player player, Enemies enemies, Map map) { // this rectangle describes the position on screen where an object will be drawn Rectangle rect = new Rectangle(); rect.Width = (int) Math.Round((rectWidth * GameConstants.ViewportWidth)); rect.Height = (int) Math.Round((rectHeight * GameConstants.ViewportHeight)); // get items to display int[,] floorPlan = map.FloorPlan; Fuel[] fuelBarrels = map.FuelBarrels; Bonuses bonuses = map.Bonuses; spriteBatch.Begin(); // draw floor plan for (int x = 0; x < floorPlan.GetLength(0); x++) { for (int z = 0; z < floorPlan.GetLength(1); z++) { if (floorPlan[x, z] != 0) { // move the rectangle into the correct position of the screen rect.X = (int)(xOffset * GameConstants.ViewportWidth) + rect.Width * z; rect.Y = (int)(yOffset * GameConstants.ViewportHeight) + rect.Height * x; spriteBatch.Draw(whiteRect, rect, Color.Orange); } } } // draw fuel barrels for (int i = 0; i < fuelBarrels.Length; i++) { rect.X = (int) (xOffset * GameConstants.ViewportWidth) - rect.Width * (int)fuelBarrels[i].Position.Z; rect.Y = (int)(yOffset * GameConstants.ViewportHeight) + rect.Height * (int)fuelBarrels[i].Position.X; spriteBatch.Draw(whiteCircle, rect, Color.White); } // draw bonuses foreach (Bonus b in bonuses) { rect.X = (int)(xOffset * GameConstants.ViewportWidth) - rect.Width * (int)b.Position.Z; rect.Y = (int)(yOffset * GameConstants.ViewportHeight) + rect.Height * (int)b.Position.X; spriteBatch.Draw(whiteCircle, rect, Color.Purple); } // draw player rect.X = (int)(xOffset * GameConstants.ViewportWidth) - rect.Width * (int)player.Position.Z; rect.Y = (int)(yOffset * GameConstants.ViewportHeight) + rect.Height * (int)player.Position.X; spriteBatch.Draw(whiteRect, rect, Color.Green); // draw enemies foreach (Enemy e in enemies) { if (e.Chasing == true) { // flash twice per second if chasing if (gameTime.TotalGameTime.TotalMilliseconds % 500 < 250) { rect.X = (int)(xOffset * GameConstants.ViewportWidth) - rect.Width * (int)e.Position.Z; rect.Y = (int)(yOffset * GameConstants.ViewportHeight) + rect.Height * (int)e.Position.X; spriteBatch.Draw(whiteRect, rect, Color.Red); } } else { rect.X = (int)(xOffset * GameConstants.ViewportWidth) - rect.Width * (int)e.Position.Z; rect.Y = (int)(yOffset * GameConstants.ViewportHeight) + rect.Height * (int)e.Position.X; spriteBatch.Draw(whiteRect, rect, Color.Red); } } //draw enemies next positions (for debugging) //foreach (Enemy e in enemies) //{ // rect.X = (int)(xOffset * GameConstants.ViewportWidth) - rect.Width * (int)e.nextPosition.Z; // rect.Y = (int)(yOffset * GameConstants.ViewportHeight) + rect.Height * (int)e.nextPosition.X; // spriteBatch.Draw(whiteRect, rect, Color.Purple); //} //draw the enemies A* path (for debugging) //if (Helpers.AStarDebugList != null) //{ // for (int i = 1; i < Helpers.AStarDebugList.Count - 1; i++) // { // rect.X = (int)(xOffset * GameConstants.ViewportWidth) + rect.Width * (int)Helpers.AStarDebugList[i].Y; // rect.Y = (int)(yOffset * GameConstants.ViewportHeight) + rect.Height * (int)Helpers.AStarDebugList[i].X; // spriteBatch.Draw(whiteRect, rect, Color.Pink); // } //} spriteBatch.End(); }
/// <summary> /// Allows the game to perform any initialization it needs to before starting to run. /// This is where it can query for any required services and load any non-graphic /// related content. Calling base.Initialize will enumerate through any components /// and initialize them as well. /// </summary> protected override void Initialize() { // set up the display window graphics.PreferredBackBufferWidth = 500; graphics.PreferredBackBufferHeight = 500; graphics.ApplyChanges(); // update the ViewportHeight and ViewportWidth constants so the screens know // how to draw themselves GameConstants.ViewportHeight = graphics.GraphicsDevice.Viewport.Height; GameConstants.ViewportWidth = graphics.GraphicsDevice.Viewport.Width; // display title in the window Window.Title = "Alien Attack"; // setup the state of the game currentGameState = GameConstants.GameState.Title; currentKeyboardState = Keyboard.GetState(); prevKeyBoardState = currentKeyboardState; // set up the screens titleScreen = new TitleScreen(); instructionScreen = new InstructionScreen(); highScoreScreen = new HighScoreScreen(); creditsScreen = new CreditsScreen(); introScreen = new IntroScreen(); readyScreen = new GetReadyScreen(); gameOverScreen = new GameOverScreen(); // set up the game objects gameCamera = new Camera(); player = new Player(); enemies = new Enemies(); map = new Map(); miniMap = new MiniMap(); skybox = new Skybox(); // set up the ingame displays score = new Score(); highScore = new HighScore(); // set up the soundtrack gameSongs = new GameSongs(); base.Initialize(); }
/// <summary> /// Returns true if this enemy is the closest enemy to the player in terms /// of linear distance and no other enemy is chasing, false otherwise /// </summary> /// <param name="player">For the player's position</param> /// <param name="enemies">For the position of the enemies</param> private bool ShouldMoveTowardPlayer(Player player, Enemies enemies) { float distanceToPlayer = Helpers.LinearDistance2D(Position, player.Position); foreach (Enemy e in enemies) { if (e != this) { if (distanceToPlayer > Helpers.LinearDistance2D(e.Position, player.Position) || e.chasing == true) return false; } } return true; }
/// <summary> /// Sets the foward direction of the enemy to point toward the player /// </summary> /// <param name="player">For the player's position</param> private void SetDirectionTowardPlayer(Player player) { int xOffsetPToE = (int)player.Position.X - (int)Position.X; // x displacement from player to enemy int zOffsetPToE = (int)-player.Position.Z + (int)Position.Z;// z displacement from player to enemy if (zOffsetPToE != 0) { // Must move left or right if (zOffsetPToE > 0) { // Move right ForwardDirection = 0; } else { // Move left ForwardDirection = MathHelper.Pi; } } else { // Must move up or down if (xOffsetPToE > 0) { // Move down, player below ForwardDirection = 3 * MathHelper.PiOver2; } else { // Move up, player above ForwardDirection = MathHelper.PiOver2; } } }
/// <summary> /// This method implements dead reckoning. Based on the position of the player, the velocity /// of the player, and the velocity of the missile, the target position /// where the missile should be fired to strike the enemy is calculated. /// </summary> /// <param name="player">For the player position</param> /// <returns> /// A vector representing the target position the enemy should fire /// a missile. /// </returns> private Vector3 FindTargetPosition(Player player) { Vector3 target = new Vector3(0.0f, player.Position.Y, 0.0f); // Uses an approximation to determine where to fire the misile // 1) Calculate the distance between user and the missile // 2) Calculate the time (t) for the missile to travel to the user given the speed of the missile // 3) Calculate the position of the player at time (t) float d_enemyToPlayer = Helpers.LinearDistance2D(player.Position, this.Position); // 1 float t_missiletoplayer = d_enemyToPlayer / Missile.MissileSpeed; // 2 target.X = player.Position.X - player.Velocity * (float)Math.Sin(player.ForwardDirection) * t_missiletoplayer; // 3 target.Z = player.Position.Z - player.Velocity * (float)Math.Cos(player.ForwardDirection) * t_missiletoplayer; // 3 return target; }
/// <summary> /// Checks if the player is in the line of sight of the enemy, that is, there are no building /// or other enemies between the player and the enemy craft /// </summary> /// <param name="player">For the player's position</param> /// <param name="floorPlan">For the layout of the building</param> /// <returns>True if the enemy can see the player, false otherwise</returns> private bool CheckIfInLineOfSight(Player player, int[,] floorPlan) { int xOffsetToPlayer = (int)player.Position.X - (int)Position.X; // x displacement from player to enemy int zOffsetToPlayer = (int)-player.Position.Z + (int)Position.Z;// z displacement from player to enemy bool inLineOfSight = false; // can we see the player? if (zOffsetToPlayer == 0) { inLineOfSight = true; // assume that we can see the player // move up the map to the player position and check for obstacles in the way // if there are obstacles, mark inLineOfSight false and break for (int i = (int)Position.X; i < (int)player.Position.X; i++) { if (floorPlan[i, (int)-Position.Z] != 0) { inLineOfSight = false; break; } } // move down the map to the player position and check for obstacles in the way // if there are obstacles, mark inLineOfSight false and break for (int i = (int)Position.X; i > (int)player.Position.X; i--) { if (floorPlan[i, (int)-Position.Z] != 0) { inLineOfSight = false; break; } } } else if (xOffsetToPlayer == 0) { inLineOfSight = true; // move right on the map to the player position and check for obstacles in the way // if there are obstacles, mark inLineOfSight false and break for (int i = (int)-Position.Z; i < (int)-player.Position.Z; i++) { if (floorPlan[(int)Position.X, i] != 0) { inLineOfSight = false; break; } } // move left on the map to the player position and check for obstacles in the way // if there are obstacles, mark inLineOfSight false and break for (int i = (int)-Position.Z; i > (int)-player.Position.Z; i--) { if (floorPlan[(int)Position.X, i] != 0) { inLineOfSight = false; break; } } } return inLineOfSight; }
/// <summary> /// Allows the client to move the enemy craft /// </summary> /// <param name="enemies">For positions of all of the other enemies in the game</param> /// <param name="player">Allows queries for attributes of the player</param> /// <param name="floorPlan">Gives the arrangement of the building in the city</param> ///<param name="gameTime">Information on the time since last update for missiles</param> /// <param name="gameState">Gives the current state of the game</param> /// <remarks> /// If the player is caught or the player is hit by a missile and runs /// out of health, the gamestate will transition to end /// </remarks> public void Update(Enemies enemies, Player player, int[,] floorPlan, GameTime gameTime, ref GameConstants.GameState gameState) { bool canMakeMovement = false; // boolean for status if the player was able to make a move on this update Vector3 movement; // the movement to be applied to the enemy // check if the player has been caught, that is the enemy craft and player are in the same // grid unit, and if so transition to game ending state if (((int)Position.X - (int)player.Position.X) == 0 && ((int)Position.Z - (int)player.Position.Z) == 0) { gameState = GameConstants.GameState.End; return; } // Update the missiles and time during chase missiles.Update(player, floorPlan, ref gameState); if (chasing) { // Update the timeOfChase with the elapsed time since the last update timeOfChase += gameTime.ElapsedGameTime.TotalSeconds; } if (ReadyForNextMove()) { // mark the spots on the grid where the enemies are and where they are headed in the next move MarkEnemySquares(enemies, floorPlan); // the player is in the center of a grid square, and can decide on the next direction to face // now, check if the player is in the enemy's line of sight (no buildings or enemies obstructing view // from enemy to player), and, if so, set the chasing property // to true chasing = CheckIfInLineOfSight(player, floorPlan); if (chasing) { // we are chasing the player, set direction toward the player SetDirectionTowardPlayer(player); // we can make a movement since nothing is obstructing between player and this enemy canMakeMovement = true; // Check if we are locked on to the player, and if so, attempt to fire if (this.LockedOn) { missiles.FireAt(this.Position, FindTargetPosition(player), gameTime); } } else { // reset the time of chase to zero since we are not chasing timeOfChase = 0.0; // check to see if no other enemyies are chasing and this is the closest enemy // in terms of linear distance // If so, find an A* path to the player if possible. // This makes the game more challenging since the player will always be under pressure // from some enemy if (ShouldMoveTowardPlayer(player, enemies)) { List<Vector2> aStarPath = AStar.FindPath(floorPlan, Position, player.Position); //Helpers.AStarDebugList = aStarPath; if (aStarPath == null) { // We could not find an A* path to the player, move randomly canMakeMovement = MoveRandomly(floorPlan); // returns true if a move was able to be made } else { // set the enemy direction to follow the A* path UpdateDirectionFromPath(aStarPath); canMakeMovement = true; } } else { // we are not chasing or the closest enemy // move randomly around the grid until we spot the player next canMakeMovement = MoveRandomly(floorPlan); // returns true if a move was able to be made } } // unmark the spots on the grid where the enemies are and where they are headed in the next move UnmarkEnemySquares(enemies, floorPlan); if (!canMakeMovement) { // the enemy is surrounded and cannot make a move // set velocity to zero and stay put for this move speed = 0.0f; } else { // update positionOfLastMove with grid square of current position positionOfLastMove = new Rectangle((int)Position.X, (int)-Position.Z, 1, 1); // update nextPosition with next grid space in the direction // we are headed nextPosition = new Rectangle( (int)(Position.X - Math.Sin(ForwardDirection)), (int)(Position.Z - Math.Cos(ForwardDirection)), 1, 1); speed = EnemySpeed; } } // Make the movement movement = Vector3.Transform(new Vector3(0.0f, 0.0f, -1.0f), Matrix.CreateRotationY(ForwardDirection)); movement *= speed; UpdatePositionAndBoundingSphere(Position + movement); }
/// <summary> /// Moves the enemy towards the player when the game is over or during the introduction /// </summary> /// <param name="player">For the position of the player</param> /// <remarks> /// This method is called when the game is over or during the intrdocution. /// It is known that this enemy and the player are in valid positions. Thus, no checks are /// made to ensure the location is valid. /// </remarks> public void MoveTowardPlayer(Player player) { Vector3 movement; float distancePlayerToEnemy = Helpers.LinearDistance2D(this.Position, player.Position); if (distancePlayerToEnemy > speed) { // we are atleast one timestep away from the player, move toward the player // find direction from this enemy's position to the player ForwardDirection = (float) Math.Atan2(Position.X - player.Position.X, -player.Position.Z + Position.Z); // Make the movement movement = Vector3.Transform(new Vector3(0.0f, 0.0f, -1.0f), Matrix.CreateRotationY(ForwardDirection)); movement *= speed; UpdatePositionAndBoundingSphere(Position + movement); } }