public override void Update() { // Return to normal if not in a hole. if (!monster.Physics.IsInHole) { monster.BeginNormalState(); return; } // Slip toward the hole's center. float slipSpeed = 0.2f; Point2I tileLocation = monster.RoomControl.GetTileLocation(monster.Position); Vector2F tilePosition = tileLocation * GameSettings.TILE_SIZE; Vector2F tileCenter = tilePosition + new Vector2F(8, 8); Rectangle2F holeRect = new Rectangle2F(tilePosition.X + 6, tilePosition.Y + 8, 4, 6); bool fallInHole = holeRect.Contains(monster.Position); Vector2F trajectory = tileCenter - monster.Center; if (trajectory.Length > slipSpeed) { monster.Physics.Velocity = (tileCenter - monster.Center).Normalized * slipSpeed; } else { fallInHole = true; monster.Physics.Velocity = Vector2F.Zero; monster.SetPositionByCenter(tileCenter); } if (fallInHole) { monster.RoomControl.SpawnEntity(new EffectFallingObject(), monster.Center); AudioSystem.PlaySound(GameData.SOUND_OBJECT_FALL); monster.Destroy(); } }
// Returns true if the specified rectangle is colliding with this rectangle. public bool Colliding(Rectangle2F rect) { if (IsEmpty || rect.IsEmpty) { return(false); } return((rect.Min < Max) && (rect.Max > Min)); }
// Returns true if the specified rectangle is inside this rectangle. public bool Contains(Rectangle2F rect) { if (IsEmpty || rect.IsEmpty) { return(false); } return((rect.Min >= Min) && (rect.Max <= Max)); }
// Return whether this and another rectangle are intersecting. public bool Intersects(Rectangle2F other) { if (IsEmpty && other.IsEmpty) { return(false); } return(!(other.Left - Right >= 0 || other.Top - Bottom >= 0 || Left - other.Right >= 0 || Top - other.Bottom >= 0)); }
// Creates a new rectangle that exactly contains two other rectangles. public static Rectangle2F Union(Rectangle2F r1, Rectangle2F r2) { float x1 = Math.Min(r1.Left, r2.Left); float y1 = Math.Min(r1.Top, r2.Top); float x2 = Math.Max(r1.Right, r2.Right); float y2 = Math.Max(r1.Bottom, r2.Bottom); return(new Rectangle2F(x1, y1, x2 - x1, y2 - y1)); }
//----------------------------------------------------------------------------- // Static methods //----------------------------------------------------------------------------- public static bool Intersecting(CollisionModel model, Vector2F modelPosition, Rectangle2F box, Vector2F boxPosition) { Rectangle2F boxTranslated = Rectangle2F.Translate(box, boxPosition - modelPosition); for (int i = 0; i < model.boxes.Count; i++) { Rectangle2F modelBox = model.boxes[i]; if (boxTranslated.Intersects(modelBox)) return true; } return false; }
//----------------------------------------------------------------------------- // Static methods //----------------------------------------------------------------------------- // Returns the intersection between two rectangles. // Returns the Empty rect if there is no intersection. public static Rectangle2F Intersect(Rectangle2F r1, Rectangle2F r2) { float x1 = Math.Max(r1.Left, r2.Left); float y1 = Math.Max(r1.Top, r2.Top); float x2 = Math.Min(r1.Right, r2.Right); float y2 = Math.Min(r1.Bottom, r2.Bottom); if (x2 > x1 && y2 > y1) { return(new Rectangle2F(x1, y1, x2 - x1, y2 - y1)); } return(Rectangle2F.Zero); }
public static bool Intersecting(CollisionModel model, Vector2F modelPosition, Rectangle2F box, Vector2F boxPosition) { Rectangle2F boxTranslated = Rectangle2F.Translate(box, boxPosition - modelPosition); for (int i = 0; i < model.boxes.Count; i++) { Rectangle2F modelBox = model.boxes[i]; if (boxTranslated.Intersects(modelBox)) { return(true); } } return(false); }
private float zVelocity; // Z-Velocity in pixels per frame. #endregion Fields #region Constructors //----------------------------------------------------------------------------- // Constructors //----------------------------------------------------------------------------- // By default, physics are disabled. public PhysicsComponent(Entity entity) { this.isEnabled = false; this.flags = PhysicsFlags.None; this.entity = entity; this.velocity = Vector2F.Zero; this.zVelocity = 0.0f; this.gravity = GameSettings.DEFAULT_GRAVITY; this.maxFallSpeed = GameSettings.DEFAULT_MAX_FALL_SPEED; this.collisionBox = new Rectangle2F(-4, -10, 8, 9); // TEMPORARY: this is the player collision box. this.softCollisionBox = new Rectangle2F(-6, -14, 12, 13); // TEMPORARY: this is the player collision box. this.topTileFlags = TileFlags.None; this.allTileFlags = TileFlags.None; this.isColliding = false; this.autoDodgeDistance = 6; this.hasLanded = false; this.collisionInfo = new CollisionInfo[Directions.Count]; for (int i = 0; i < Directions.Count; i++) collisionInfo[i].Clear(); }
/** <summary> Returns true if the specified rectangle is colliding with this line. </summary> */ public bool Colliding(Rectangle2F rect) { if (IsEmpty || rect.IsEmpty) { return(false); } if (!Bounds.Colliding(rect) && !IsStraight) { return(false); } if (rect.Contains(End1)) { return(true); } foreach (Line2F l in rect.Lines) { if (Colliding(l)) { return(true); } } return(false); }
// Creates a copy of a rectangle translated by an amount. public static Rectangle2F Translate(Rectangle2F r, float x, float y) { r.Point.X += x; r.Point.Y += y; return(r); }
// Determine the clipping direction for a collision. private int GetCollisionClipDirection(Entity entity, object other, Rectangle2F solidBox) { Rectangle2F entityBox = entity.Physics.PositionedCollisionBox; // For moving tiles, use the move direction to determine clip direction. Tile tile = other as Tile; if (tile != null && tile.IsMoving) { if ((solidBox.Center - entityBox.Center).Dot(Directions.ToVector(tile.MoveDirection)) < 0.0f) return Directions.Reverse(tile.MoveDirection); } // Get the nearest direction from the collision intersection to the center of the solid box. Vector2F intersectionCenter = Rectangle2F.Intersect(entityBox, solidBox).Center; int clipDirection = Directions.NearestFromVector(solidBox.Center - intersectionCenter); // If the collision can't be resolved, then try to use a direction on the opposite axis. CollisionInfoNew testCollision = CreateCollisionInfo(entity, other, solidBox, clipDirection); if (!testCollision.IsAllowedClipping && !CanResolveCollision(entity, testCollision)) { int newClipDirection; int a = 1 - Directions.ToAxis(clipDirection); if (entityBox.Center[a] < solidBox.Center[a]) newClipDirection = (a == Axes.X ? Directions.Right : Directions.Down); else newClipDirection = (a == Axes.X ? Directions.Left : Directions.Up); testCollision = CreateCollisionInfo(entity, other, solidBox, newClipDirection); if (testCollision.IsAllowedClipping || CanResolveCollision(entity, testCollision)) clipDirection = newClipDirection; } return clipDirection; }
// Calculate a collision's penetration distance in the given direction. public float GetClipPenetration(Rectangle2F entityBox, Rectangle2F solidBox, int clipDirection) { if (clipDirection == Directions.Right) return entityBox.Right - solidBox.Left; else if (clipDirection == Directions.Up) return solidBox.Bottom - entityBox.Top; else if (clipDirection == Directions.Left) return solidBox.Right - entityBox.Left; else if (clipDirection == Directions.Down) return entityBox.Bottom - solidBox.Top; return 0.0f; }
// Resolve any movement collision between the entity and a solid object private void ResolveMovementCollision(Entity entity, int axis, object other, Rectangle2F solidBox) { // Setup the entity's translated collision box based on which axis we're checking. Rectangle2F entityBox; if (axis == Axes.X) { entityBox = Rectangle2F.Translate( entity.Physics.CollisionBox, entity.X + entity.Physics.Velocity.X, entity.Y); } else { entityBox = Rectangle2F.Translate( entity.Physics.CollisionBox, entity.Position + entity.Physics.Velocity); } // Check if there actually is a collision. if (!entityBox.Intersects(solidBox)) return; // Determine clipping direction. int clipDirection = -1; if (axis == Axes.X) { if (entityBox.Center.X < solidBox.Center.X) clipDirection = Directions.Right; else clipDirection = Directions.Left; } else { if (entityBox.Center.Y < solidBox.Center.Y) clipDirection = Directions.Down; else clipDirection = Directions.Up; } // Ignore collision if the entity is already colliding in the opposite // direction or if the entity is not moving in the clip direction. if (entity.Physics.ClipCollisionInfo[Directions.Reverse(clipDirection)].IsCollidingAndNotAllowedClipping || entity.Physics.Velocity.Dot(Directions.ToVector(clipDirection)) <= 0.0f) return; // Ignore collisions that are within the allowed clipping range. if (IsSafeClipping(entity, 1 - axis, entityBox, other, solidBox)) return; // Ignore the collision if the entity is clipped into a solid object that shares // a clip edge with this object and the entity is also moving parallel with that edge. for (int i = 0; i < 2; i++) { int dir = Axes.GetOpposite(axis) + (i * 2); CollisionInfoNew checkCollision = entity.Physics.ClipCollisionInfo[dir]; if (checkCollision.IsColliding && AreEdgesAligned( checkCollision.CollisionBox, solidBox, Directions.Reverse(dir))) { return; } } // Resolve the collision. if (!entity.Physics.IsColliding && !entity.Physics.ClipCollisionInfo[clipDirection].IsColliding) { // Determine the penetration. float penetrationDistance = GetClipPenetration(entityBox, solidBox, clipDirection); float maxAllowedPenetration = 0.0f; if (entity.Physics.Velocity[axis] == 0.0f) maxAllowedPenetration = GetAllowedEdgeClipAmount(entity, other); // Snap the entity's position to the edge of the solid object. Vector2F newPos = entity.Position; newPos[axis] += entity.Physics.Velocity[axis]; newPos -= Directions.ToVector(clipDirection) * penetrationDistance; if (penetrationDistance <= maxAllowedPenetration) newPos += Directions.ToVector(clipDirection) * penetrationDistance; entity.Position = newPos; entity.Physics.CollisionInfo[clipDirection].SetCollision(other, clipDirection); } // Zero the entity's velocity for this axis. Vector2F velocity = entity.Physics.Velocity; velocity[axis] = 0.0f; entity.Physics.Velocity = velocity; entity.Physics.MovementCollisions[clipDirection] = true; if (!entity.Physics.CollisionInfo[clipDirection].IsColliding) entity.Physics.CollisionInfo[clipDirection].SetCollision(other, clipDirection); // Perform collision dodging. bool canDodge = true; if (entity is Player) // TODO: this won't work for when player is knocked back canDodge = ((Player) entity).Movement.AllowMovementControl && Controls.Arrows[clipDirection].IsDown(); if (entity.Physics.AutoDodges && canDodge) { if (roomControl.IsSideScrolling && axis == Axes.X && (!(entity is Player) || !((Player) entity).Movement.IsOnSideScrollLadder || ((other is Tile) && ((Tile) other).IsInMotion)) && (entity.IsOnGround || entity.Physics.PreviousCollisionInfo[Directions.Down].IsColliding)) { // Step up onto blocks if it is a small enough distance. PerformSideScrollCollisionSnap(entity, clipDirection, other, solidBox); } else { // Auto dodging. PerformCollisionDodge(entity, clipDirection, other, solidBox); } } }
// Create a clip collision info between an entity and solid object with a specified clip direction. private CollisionInfoNew CreateCollisionInfo(Entity entity, object other, Rectangle2F solidBox, int clipDirection) { return new CollisionInfoNew() { IsColliding = true, Entity = entity, CollidedObject = other, CollisionBox = solidBox, PenetrationDirection = clipDirection, PenetrationDistance = GetClipPenetration(entity.Physics.PositionedCollisionBox, solidBox, clipDirection), MaxAllowedPenetrationDistance = GetAllowedEdgeClipAmount(entity, other) }; }
public CollisionTestSettings(Type entityType, Rectangle2F myBox, Rectangle2F otherBox, float maxZDistance = 10) { this.requiredType = entityType; this.collisionBox1 = myBox; this.collisionBox2 = otherBox; this.collisionBoxType1 = CollisionBoxType.Custom; this.collisionBoxType2 = CollisionBoxType.Custom; this.requiredFlags = PhysicsFlags.None; this.maxZDistance = maxZDistance; }
// Attempt to snap a collision. private bool PerformSideScrollCollisionSnap(Entity entity, int direction, object solidObject, Rectangle2F solidBox) { Rectangle2F entityBox = entity.Physics.PositionedCollisionBox; float penetrationDistance = GetClipPenetration( entity.Physics.PositionedCollisionBox, solidBox, direction); float distanceToEdge = entityBox.Bottom - solidBox.Top; //Vector2F goalPosition = entity.Position + Directions.ToVector(direction) + //(Directions.ToVector(Directions.Up) * distanceToEdge); Vector2F goalPosition = entity.Position + Directions.ToVector(direction) - new Vector2F(0.0f, distanceToEdge); // Check if the distance to the edge is within dodge range. // Make sure the entity is not colliding when placed at the solid object's edge. if (distanceToEdge <= entity.Physics.AutoDodgeDistance && !IsCollidingAt(entity, goalPosition, false, direction, penetrationDistance)) { entity.Position = goalPosition; entity.Physics.MovementCollisions[Directions.Down] = true; entity.Physics.CollisionInfo[Directions.Down].SetCollision(solidObject, Directions.Down); return true; } return false; }
// Translates the rectangle based on the translation and precision settings. private Rectangle NewRect(Rectangle2F destinationRect) { if (useTranslation) return (UseIntegerPrecision ? (Rectangle)new Rectangle2F(GMath.Round(destinationRect.Point + translation), GMath.Round(destinationRect.Size)) : (Rectangle)(destinationRect + translation)); else return (Rectangle)destinationRect; }
public void DrawSprite(Sprite sprite, int variantID, Rectangle2F destination, Color color, float depth = 0.0f) { if (sprite.Image == null) return; for (Sprite part = sprite; part != null; part = part.NextPart) { Image image = sprite.Image.GetVariant(variantID); destination.Point = NewPos(destination.Point) + (Vector2F)part.DrawOffset; spriteBatch.Draw(image, (Rectangle)destination, (Rectangle)part.SourceRect, (XnaColor)color, 0.0f, Vector2.Zero, SpriteEffects.None, depth); } }
// Draws the specified rectangle. public void DrawRectangle(Rectangle2F rect, float thickness, Color color, float depth = 0.0f) { DrawLine(new Line2F(rect.Point, rect.Point + new Vector2F(rect.Width - 1, 0.0f)), thickness, color, depth); DrawLine(new Line2F(rect.Point, rect.Point + new Vector2F(0.0f, rect.Height - 1)), thickness, color, depth); DrawLine(new Line2F(rect.Point + rect.Size - Vector2F.One, rect.Point + new Vector2F(rect.Width - 1, 0.0f)), thickness, color, depth); DrawLine(new Line2F(rect.Point + rect.Size - Vector2F.One, rect.Point + new Vector2F(0.0f, rect.Height - 1)), thickness, color, depth); }
// Draws part of the image at the specified position. public void DrawImage(Texture2D texture, Vector2F position, Rectangle2F sourceRect, Color color, double depth = 0.0) { spriteBatch.Draw(texture, NewPos(position), (Rectangle)sourceRect, color, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, (float)depth); }
//----------------------------------------------------------------------------- // Static methods //----------------------------------------------------------------------------- public static bool Intersecting(CollisionModel model, Vector2F modelPosition, Rectangle2F box) { return(Intersecting(model, modelPosition, box, Vector2F.Zero)); }
// Draws part of the image at the specified region. public void DrawImage(Texture2D texture, Rectangle2F destinationRect, Rectangle2F sourceRect, Vector2F origin, double rotation, Color? color = null, SpriteEffects flipEffect = SpriteEffects.None, double depth = 0.0) { spriteBatch.Draw(texture, NewRect(destinationRect), (Rectangle)sourceRect, color ?? XnaColor.White, (float)GMath.ConvertToRadians(rotation), (Vector2)origin, flipEffect, (float)depth); }
// Creates a copy of a rectangle translated by an amount. public static Rectangle2F Translate(Rectangle2F r, Vector2F amount) { r.Point += amount; return(r); }
public void DrawSprite(Sprite sprite, Rectangle2F destination, Color color, float depth = 0.0f) { DrawSprite(sprite, 0, destination, color, depth); }
// Constructs a copy of the specified rectangle. public Rectangle2F(Rectangle2F r) { this.Point = r.Point; this.Size = r.Size; }
// Draws the specified filled rectangle. public void FillRectangle(Rectangle2F rect, Color color, double depth = 0.0) { rect.X = GMath.Round(rect.X); rect.Y = GMath.Round(rect.Y); DrawImage(white1x1, rect, Vector2F.Zero, 0.0, color, SpriteEffects.None, depth); //DrawImage(white1x1, rect.Center + 0.5, new Vector2D(0.5, 0.5), rect.Size, 0, color, SpriteEffects.None, depth); }
/** <summary> Returns true if the specified rectangle is inside this line. </summary> */ public bool Contains(Rectangle2F rect) { return false; }
// Translates the rectangle based on the translation and precision settings. private Rectangle NewRect(Vector2F position, Vector2F size) { Rectangle2F destinationRect = new Rectangle2F(position, size); if (useTranslation) return (UseIntegerPrecision ? (Rectangle)new Rectangle2F(GMath.Round(destinationRect.Point + translation), GMath.Round(destinationRect.Size + translation)) : (Rectangle)(destinationRect + translation)); else return (Rectangle)destinationRect; }
// Return an enumerable list of solid tiles colliding with the given collision box. public IEnumerable<Tile> GetSolidTilesColliding(Rectangle2F collisionBox, TileLayerOrder layerOrder = TileLayerOrder.LowestToHighest) { Rectangle2I area = GetTileAreaFromRect(collisionBox); foreach (Tile tile in GetTilesInArea(area, layerOrder)) { if (tile.IsSolid && tile.CollisionModel != null && CollisionModel.Intersecting(tile.CollisionModel, tile.Position, collisionBox)) yield return tile; } }
// Attempt to dodge a collision. private bool PerformCollisionDodge(Entity entity, int direction, object solidObject, Rectangle2F solidBox) { // Only dodge if moving perpendicular to the edge. int axis = Directions.ToAxis(direction); if (Math.Abs(entity.Physics.Velocity[1 - axis]) > 0.001f) return false; // Can't dodge moving tiles. if ((solidObject is Tile) && ((Tile) solidObject).IsMoving) return false; Rectangle2F entityBox = entity.Physics.PositionedCollisionBox; float penetrationDistance = GetClipPenetration( entity.Physics.PositionedCollisionBox, solidBox, direction); // Check dodging for both edges of the solid object. for (int side = 0; side < 2; side++) { int moveDirection = (direction + (side == 0 ? 1 : 3)) % 4; float distanceToEdge = Math.Abs(entityBox.GetEdge( Directions.Reverse(moveDirection)) - solidBox.GetEdge(moveDirection)); // Check if the distance to the edge is within dodge range. if (distanceToEdge <= entity.Physics.AutoDodgeDistance) { float moveAmount = Math.Min(entity.Physics.AutoDodgeSpeed, distanceToEdge); Vector2F nextPosition = GMath.Round(entity.Position) + (Directions.ToVector(moveDirection) * moveAmount); Vector2F goalPosition = entity.Position + Directions.ToVector(direction) + (Directions.ToVector(moveDirection) * distanceToEdge); // Make sure the entity is not colliding when placed at the solid object's edge. if (!IsCollidingAt(entity, nextPosition, false, direction, penetrationDistance) && !IsCollidingAt(entity, goalPosition, false, direction, penetrationDistance)) { entity.Position += Directions.ToVector(moveDirection) * moveAmount; entity.Physics.CollisionInfo[direction].IsAutoDodged = true; //entity.Physics.MovementCollisions[direction] = false; // TODO: Figure out complications for removing this. return true; } } } return false; }
// inflateAmount inflates the output rectangle. public Rectangle2I GetTileAreaFromRect(Rectangle2F rect, int inflateAmount = 0) { Rectangle2I area; area.Point = (Point2I) GMath.Floor(rect.TopLeft / tileGridCellSize); area.Size = (Point2I) GMath.Ceiling(rect.BottomRight / tileGridCellSize) - area.Point; if (inflateAmount != 0) area.Inflate(inflateAmount, inflateAmount); return Rectangle2I.Intersect(area, new Rectangle2I(Point2I.Zero, gridDimensions)); }
private void ResolveCircularCollision(Entity entity, object solidObject, Rectangle2F solidBox) { Rectangle2F collisionBox = entity.Physics.CollisionBox; Rectangle2F entityBoxPrev = entity.Physics.CollisionBox; entityBoxPrev.Point += entity.Position; // If already colliding with the object, then push away from its center. if (entityBoxPrev.Intersects(solidBox)) { Vector2F correction = (entity.Position - solidBox.Center).Normalized; correction = Vector2F.SnapDirectionByCount(correction, 16); entity.Position += 1.0f * correction; return; } // Predict collisions for each axis. for (int axis = 0; axis < 2; axis++) { Rectangle2F entityBox = entity.Physics.CollisionBox; entityBox.Point += entity.Position; if (axis == 0) entityBox.X += entity.Physics.VelocityX; else entityBox.Point += entity.Physics.Velocity; if (entityBox.Intersects(solidBox)) { // Snap the position. Vector2F newPos = entity.Position; if (entityBox.Center[axis] < solidBox.Center[axis]) newPos[axis] = solidBox.TopLeft[axis] - collisionBox.BottomRight[axis]; else newPos[axis] = solidBox.BottomRight[axis] - collisionBox.TopLeft[axis]; entity.Position = newPos; Vector2F newVelocity = entity.Physics.Velocity; if (Math.Abs(entity.Physics.Velocity[1 - axis]) < 0.1f) { // Slightly push away from the center of the solid object. Vector2F distance = entity.Physics.PositionedCollisionBox.Center - solidBox.Center; //Vector2F correction = (entity.Position - solidBox.Center).Normalized; //correction = Vector2F.SnapDirectionByCount(correction, 16); if (GMath.Abs(distance[1 - axis]) > 1.0f) { //distance = GMath.Sqr(GMath.Abs(distance)) * GMath.Sign(distance); distance = (distance * distance) * GMath.Sign(distance); //newVelocity[1 - axis] += distance[1 - axis] * 0.015f; newPos = entity.Position; newPos[1 - axis] += distance[1 - axis] * 0.015f; if (!IsCollidingAt(entity, newPos, false)) entity.Position = newPos; } } newVelocity[axis] = 0.0f; entity.Physics.Velocity = newVelocity; } } }
// Return an enumerable list of tiles touching the given rectangular bounds. public IEnumerable<Tile> GetTilesTouching(Rectangle2F bounds, TileLayerOrder layerOrder = TileLayerOrder.LowestToHighest) { Rectangle2I area = GetTileAreaFromRect(bounds); foreach (Tile tile in GetTilesInArea(area, layerOrder)) { if (tile.Bounds.Intersects(bounds)) yield return tile; } }
// Returns true if two rectangles share an edge in the given direction. public static bool AreEdgesAligned(Rectangle2F box1, Rectangle2F box2, int edgeDirection) { return Math.Abs(box1.GetEdge(edgeDirection) - box2.GetEdge(edgeDirection)) < 0.1f; }
//----------------------------------------------------------------------------- // Tile Mutators //----------------------------------------------------------------------------- // Place a tile in highest empty layer at the given location. // Returns true if there was an empty space to place the tile. public bool PlaceTileOnHighestLayer(Tile tile, Point2I location) { Rectangle2F tileBounds = new Rectangle2F( location * GameSettings.TILE_SIZE, tile.Size * GameSettings.TILE_SIZE); Rectangle2I area = GetTileAreaFromRect(tileBounds); // Determine which layers are free. bool[] freeLayers = new bool[layerCount]; for (int i = 0; i < layerCount; i++) { freeLayers[i] = true; for (int x = area.Left; x < area.Right && freeLayers[i]; x++) { for (int y = area.Top; y < area.Bottom && freeLayers[i]; y++) { Tile t = tiles[x, y, i]; if (t != null) freeLayers[i] = false; } } } // Choose the highest free layer. int layer = -1; for (int i = layerCount - 1; i >= 0; i--) { if (freeLayers[i]) { layer = i; break; } } if (layer < 0) return false; // Place the tile in that layer. PlaceTile(tile, location, layer); return true; }
// Detect a clip collision between the entity and a solid object. private void DetectClipCollision(Entity entity, object other, Rectangle2F solidBox) { // Check if there actually is a collision. Rectangle2F entityBox = entity.Physics.PositionedCollisionBox; if (entityBox.Intersects(solidBox)) { // Determine clip direction. int clipDirection = GetCollisionClipDirection(entity, other, solidBox); // Set or replace the collision info for this clip direction. if (clipDirection >= 0) { CollisionInfoNew oldCollisionInfo = entity.Physics.ClipCollisionInfo[clipDirection]; CollisionInfoNew newCollisionInfo = CreateCollisionInfo(entity, other, solidBox, clipDirection); if (!oldCollisionInfo.IsColliding || (newCollisionInfo.PenetrationDistance > oldCollisionInfo.PenetrationDistance)) entity.Physics.ClipCollisionInfo[clipDirection] = newCollisionInfo; } } }
private void UpdateTileGridArea(Tile tile) { Rectangle2F tileBounds = new Rectangle2F(tile.Position, tile.Size * GameSettings.TILE_SIZE); Rectangle2I nextArea = GetTileAreaFromRect(tileBounds); Rectangle2I prevArea = tile.TileGridArea; if (nextArea != prevArea) { // Determine the highest free layer for the tile to move to. int newLayer = -1; for (int i = tile.Layer; i < layerCount; i++) { bool isLayerFree = true; for (int x = nextArea.Left; x < nextArea.Right && isLayerFree; x++) { for (int y = nextArea.Top; y < nextArea.Bottom && isLayerFree; y++) { Tile t = tiles[x, y, i]; if (t != null && t != tile) isLayerFree = false; } } if (isLayerFree) { newLayer = i; break; } } if (newLayer < 0) { RemoveTile(tile); return; } // Remove the tile from its old area. for (int x = prevArea.Left; x < prevArea.Right; x++) { for (int y = prevArea.Top; y < prevArea.Bottom; y++) { tiles[x, y, tile.Layer] = null; } } // Place the tile into its new area. for (int x = nextArea.Left; x < nextArea.Right; x++) { for (int y = nextArea.Top; y < nextArea.Bottom; y++) { tiles[x, y, tile.Layer] = tile; } } tile.TileGridArea = nextArea; } // Check for covered/uncovered tiles. Rectangle2F tileBoundsOld = tile.PreviousBounds; Rectangle2F tileBoundsNew = tile.Bounds; if (tileBoundsOld != tileBoundsNew) { foreach (Tile t in GetTilesInArea(nextArea).Union(GetTilesInArea(prevArea))) { Rectangle2F tBounds = t.Bounds; if (t != tile) { if (tileBoundsNew.Contains(tBounds) && !tileBoundsOld.Contains(tBounds)) t.OnCoverComplete(tile); else if (tileBoundsNew.Intersects(tBounds) && !tileBoundsOld.Intersects(tBounds)) t.OnCoverBegin(tile); else if (tileBoundsOld.Contains(tBounds) && !tileBoundsNew.Contains(tBounds)) t.OnUncoverBegin(tile); else if (tileBoundsOld.Intersects(tBounds) && !tileBoundsNew.Intersects(tBounds)) t.OnUncoverComplete(tile); } } } }
// Returns an enumerable list of all possible collisions. private IEnumerable<CollisionCheck> GetCollisions(Entity entity, Rectangle2F area) { // Find nearby solid entities. if (entity.Physics.CollideWithEntities) { foreach (Entity other in RoomControl.Entities) { if (other != entity && CanCollideWithEntity(entity, other)) { yield return new CollisionCheck() { SolidBox = other.Physics.PositionedCollisionBox, SolidObject = other }; } } } // Find nearby solid tiles tiles. if (entity.Physics.CollideWithWorld) { foreach (Tile tile in RoomControl.TileManager.GetTilesTouching(area)) { if (CanCollideWithTile(entity, tile) && tile.CollisionStyle == CollisionStyle.Rectangular) { foreach (Rectangle2I box in tile.CollisionModel.Boxes) { Rectangle2F tileBox = box; tileBox.Point += tile.Position; yield return new CollisionCheck() { SolidBox = tileBox, SolidObject = tile }; } } } } }
// Draws part of the image at the specified position. public void DrawImage(Texture2D texture, Vector2F position, Rectangle2F sourceRect) { spriteBatch.Draw(texture, NewPos(position), (Rectangle)sourceRect, XnaColor.White); }
// Returns true if the entity allowably clipping the given collision. private bool IsSafeClipping(Entity entity, int axis, Rectangle2F entityBox, object other, Rectangle2F solidBox) { if (entity.Physics.AllowEdgeClipping) { float allowedEdgeClipAmount = GetAllowedEdgeClipAmount(entity, other); float penetration = Math.Min( solidBox.BottomRight[axis] - entityBox.TopLeft[axis], entityBox.BottomRight[axis] - solidBox.TopLeft[axis]); return (penetration <= allowedEdgeClipAmount); } return false; }
/** <summary> Returns true if the specified rectangle is inside this line. </summary> */ public bool Contains(Rectangle2F rect) { return(false); }