// Returns the clip collision that has resolution priority (or null if neither are colliding). private CollisionInfoNew GetPriorityCollision(Entity entity, CollisionInfoNew collision1, CollisionInfoNew collision2) { if (!collision1.IsCollidingAndNotAllowedClipping && !collision2.IsCollidingAndNotAllowedClipping) return null; else if (collision1.IsCollidingAndNotAllowedClipping && !collision2.IsCollidingAndNotAllowedClipping) return collision1; else if (collision2.IsCollidingAndNotAllowedClipping && !collision1.IsCollidingAndNotAllowedClipping) return collision2; // Resolvable collisions over non resolvable collisions. if (collision1.IsResolvable && !collision2.IsResolvable) return collision1; else if (collision2.IsResolvable && !collision1.IsResolvable) return collision2; // Dynamic collisions over static collisions. if (IsSolidObjectDynamic(collision1.CollidedObject) && !IsSolidObjectDynamic(collision2.CollidedObject)) return collision1; if (IsSolidObjectDynamic(collision2.CollidedObject) && !IsSolidObjectDynamic(collision1.CollidedObject)) return collision2; Tile tile1 = collision1.CollidedObject as Tile; Tile tile2 = collision2.CollidedObject as Tile; Entity entity1 = collision1.CollidedObject as Entity; Entity entity2 = collision2.CollidedObject as Entity; // Entities over tiles. if (entity1 != null && tile2 != null) return collision1; if (entity2 != null && tile1 != null) return collision2; // Compare two entities. if (entity1 != null && entity2 != null) { if (entity1.EntityIndex < entity2.EntityIndex) return collision1; if (entity2.EntityIndex < entity1.EntityIndex) return collision2; } // Compare two tiles. if (tile1 != null && tile2 != null) { if (tile1.Position.Y > tile2.Position.Y) return collision1; else if (tile2.Position.Y > tile1.Position.Y) return collision2; else if (tile1.Position.X > tile2.Position.X) return collision1; else if (tile2.Position.X > tile1.Position.X) return collision2; else if (tile1.Layer > tile2.Layer) return collision1; else if (tile2.Layer > tile1.Layer) return collision2; } return collision1; }
// Get the positional correction needed to resolve a collision. private Vector2F GetPositionalCorrection(CollisionInfoNew collision) { float resolveDistance = Math.Max(0.0f, collision.PenetrationDistance - collision.MaxAllowedPenetrationDistance); return -Directions.ToVector(collision.PenetrationDirection) * resolveDistance; }
// Returns true if the given collision needs resolution. private bool CanResolveCollision(Entity entity, CollisionInfoNew collision) { if (!collision.IsColliding || collision.IsAllowedClipping) return false; // Don't resolve collisions if they are within the allowable edge clip amount. if (IsSafeClipping(entity, 1 - Directions.ToAxis(collision.PenetrationDirection), entity.Physics.PositionedCollisionBox, collision.CollidedObject, collision.CollisionBox)) return false; // Static collisions cannot resolve into colliding with other static objects. Tile tile = collision.CollidedObject as Tile; if (tile != null && !tile.IsInMotion) { Vector2F newPosition = entity.Position + GetPositionalCorrection(collision); if (IsCollidingAt(entity, newPosition, true)) return false; } if ((collision.CollidedObject is Tile) && !entity.Physics.CollideWithWorld) return false; if ((collision.CollidedObject is Entity) && !entity.Physics.CollideWithEntities) return false; return true; }
// Returns true if the given clip collision needs resolution. private bool CanResolveCollisionPair(Entity entity, CollisionInfoNew collision1, CollisionInfoNew collision2) { // Static collisions cannot resolve into colliding with other static objects. Tile tile1 = collision1.CollidedObject as Tile; Tile tile2 = collision2.CollidedObject as Tile; if ((tile1 != null && !tile1.IsInMotion) || (tile2 != null && !tile2.IsInMotion)) { Vector2F newPosition = entity.Position; newPosition += GetPositionalCorrection(collision1); newPosition += GetPositionalCorrection(collision2); if (IsCollidingAt(entity, newPosition, true)) return false; } return true; }
// Resolve clip collisions. private void ResolveClipCollisions(Entity entity) { // Determine which collisions have priority. CollisionInfoNew[] collisions = new CollisionInfoNew[2]; collisions[Axes.X] = GetPriorityCollision(entity, entity.Physics.ClipCollisionInfo[Directions.Right], entity.Physics.ClipCollisionInfo[Directions.Left]); collisions[Axes.Y] = GetPriorityCollision(entity, entity.Physics.ClipCollisionInfo[Directions.Up], entity.Physics.ClipCollisionInfo[Directions.Down]); for (int i = 0; i < 2; i++) { CollisionInfoNew collision = collisions[i]; if (collision != null && collision.IsResolvable) { // Resolve the collision. entity.Position += GetPositionalCorrection(collision); collision.IsResolved = true; // Add to the penetration distance of the opposite collision. CollisionInfoNew otherCollision = entity.Physics.ClipCollisionInfo[Directions.Reverse(collision.PenetrationDirection)]; float resolveDistance = Math.Max(0.0f, collision.PenetrationDistance - collision.MaxAllowedPenetrationDistance); otherCollision.PenetrationDistance += resolveDistance; } } // Check if the collisions can be only resolved together and NOT separately. if (collisions[0] != null && collisions[1] != null && !collisions[0].IsResolvable && !collisions[1].IsResolvable && CanResolveCollisionPair(entity, collisions[0], collisions[1])) { for (int i = 0; i < 2; i++) { CollisionInfoNew collision = collisions[i]; // Resolve the collision. entity.Position += GetPositionalCorrection(collision); collision.IsResolved = true; // Add to the penetration distance of the opposite collision. CollisionInfoNew otherCollision = entity.Physics.ClipCollisionInfo[Directions.Reverse(collision.PenetrationDirection)]; float resolveDistance = Math.Max(0.0f, collision.PenetrationDistance - collision.MaxAllowedPenetrationDistance); otherCollision.PenetrationDistance += resolveDistance; } } }