/// <summary> /// Instantiates an activeHero, puts him in the level, and remembers where to put him when he is resurrected. /// </summary> private void LoadHero() { if (ActiveHero != null) { throw new NotSupportedException("A level may only have one starting point."); } //where the player starts in the map start = RectangleExtensions.GetBottomCenter(GetBounds((int)map.StartTile.X, (int)map.StartTile.Y)); Heroes[0] = new HeroStrength(this, new Vector2(-1, -1)); Heroes[1] = new HeroSpeed(this, new Vector2(-1, -1)); Heroes[2] = new HeroFlight(this, new Vector2(-1, -1)); activeHero = (Hero)Heroes[1]; activeHero.Position = start; }
/// <summary> /// Detects and resolves all collisions between the player and his neighboring /// tiles. When a collision is detected, the player is pushed away along one /// axis to prevent overlapping. There is some special logic for the Y axis to /// handle platforms which behave differently depending on direction of movement. /// </summary> private void HandleCollisions() { // Get the player's bounding rectangle and find neighboring tiles. Rectangle bounds = character.BoundingRectangle; int leftTile = (int)Math.Floor((float)bounds.Left / character.Level.TileWidth); int rightTile = (int)Math.Ceiling(((float)bounds.Right / character.Level.TileWidth)) - 1; int topTile = (int)Math.Floor((float)bounds.Top / character.Level.TileHeight); int bottomTile = (int)Math.Ceiling(((float)bounds.Bottom / character.Level.TileHeight)) - 1; // Reset flag to search for ground collision. character.IsOnGround = false; // TODO This shit is overly complex. We should clean it up if we get time. // For each potentially colliding tile, for (int y = topTile; y <= bottomTile; ++y) { for (int x = leftTile; x <= rightTile; ++x) { // If this tile is collidable, TileCollision collision = character.Level.GetCollision(x, y); if (collision != TileCollision.Passable) { // Determine collision depth (with direction) and magnitude. Rectangle tileBounds = character.Level.GetBounds(x, y); Vector2 depth = RectangleExtensions.GetIntersectionDepth(bounds, tileBounds); if (depth != Vector2.Zero) { float absDepthX = Math.Abs(depth.X); float absDepthY = Math.Abs(depth.Y); // Resolve the collision along the shallow axis. if (absDepthY < absDepthX || collision == TileCollision.Platform) { // If we crossed the top of a tile, we are on the ground. if (previousBottom <= tileBounds.Top) { character.IsOnGround = true; } // Ignore platforms, unless we are on the ground. if (collision == TileCollision.Impassable || character.IsOnGround) { // Resolve the collision along the Y axis. character.Position = new Vector2(character.Position.X, character.Position.Y + depth.Y); // Perform further collisions with the new bounds. bounds = character.BoundingRectangle; } } else if (collision == TileCollision.Impassable) // Ignore platforms. { // Resolve the collision along the X axis. character.Position = new Vector2(character.Position.X + depth.X, character.Position.Y); // Perform further collisions with the new bounds. bounds = character.BoundingRectangle; } } } } } // Save the new bounds bottom. previousBottom = bounds.Bottom; }
/// <summary> /// Instantiates an enemy and puts him in the level. /// </summary> public void SpawnEnemy(int x, int y, string enemyType) { Vector2 position = RectangleExtensions.GetBottomCenter(GetTileAtPoint(x, y)); enemies.Add(EnemyFactory.NewEnemy(this, position, enemyType)); }