public void ForEachCollidingTile(Movable source, Action <TileType, Vector2> action) { var sourceBB = source.GetBoundingBox(); int minX = (int)Math.Max(Math.Floor((sourceBB.Left - BoundingBox.X) / TileSize), 0); int minY = (int)Math.Max(Math.Floor((sourceBB.Bottom - BoundingBox.Y) / TileSize), 0); int maxX = (int)Math.Min(Math.Floor((sourceBB.Right - BoundingBox.X) / TileSize) + 1, Width); int maxY = (int)Math.Min(Math.Floor((sourceBB.Top - BoundingBox.Y) / TileSize) + 1, Height); for (int x = minX; x < maxX; ++x) { for (int y = minY; y < maxY; ++y) { if (TileObjects.ContainsKey(GetTile(x, y))) { action(TileObjects[GetTile(x, y)], new Vector2(BoundingBox.X + (x + 0.5F) * TileSize, BoundingBox.Y + (y + 0.5F) * TileSize)); } } } }
/// <summary> /// Tests collision from a given entity to an arbitrary Box. Swept AABB will be performed with a moving source onto the target. /// </summary> /// <param name="source">The entity with which to test collsion</param> /// <param name="target">The box with which to test collision.</param> /// <param name="timestep">A copy of the timestep size</param> /// <param name="direction">Returns the direction of collisions. Mostly used by Box Entities, will typically be zero otherwise.</param> /// <param name="time">For moving Entities, returns at which point in time the collision occured. Specified in multiples of the timestep (will always be in [0,1])</param> /// <param name="corner">In the case of Box entities, returns true iff the collision was exactly on a corner. Used to allow smooth movement across multiple boxes.</param> public static bool CollideMovable(Movable source, RectangleF target, float timestep, out int direction, out float time, out bool corner) { corner = false; RectangleF motionBB; RectangleF sourceBB = source.GetBoundingBox(); Vector2 sourceMotion = source.Velocity * timestep; motionBB.X = sourceBB.X + (float)Math.Min(0.0, sourceMotion.X); motionBB.Y = sourceBB.Y + (float)Math.Min(0.0, sourceMotion.Y); motionBB.Width = sourceBB.Width + (float)Math.Abs(sourceMotion.X); motionBB.Height = sourceBB.Height + (float)Math.Abs(sourceMotion.Y); if (!motionBB.Intersects(target)) { direction = 0; time = -1; return(false); } Vector2 InvEntry; Vector2 InvExit; if (sourceMotion.X > 0.0f) { InvEntry.X = target.Left - sourceBB.Right; InvExit.X = target.Right - sourceBB.Left; } else { InvEntry.X = target.Right - sourceBB.Left; InvExit.X = target.Left - sourceBB.Right; } if (sourceMotion.Y > 0.0f) { InvEntry.Y = target.Bottom - sourceBB.Top; InvExit.Y = target.Top - sourceBB.Bottom; } else { InvEntry.Y = target.Top - sourceBB.Bottom; InvExit.Y = target.Bottom - sourceBB.Top; } Vector2 Entry; Vector2 Exit; if (sourceMotion.X == 0.0f) { Entry.X = float.NegativeInfinity; Exit.X = float.PositiveInfinity; } else { Entry.X = InvEntry.X / sourceMotion.X; Exit.X = InvExit.X / sourceMotion.X; } if (sourceMotion.Y == 0.0f) { Entry.Y = float.NegativeInfinity; Exit.Y = float.PositiveInfinity; } else { Entry.Y = InvEntry.Y / sourceMotion.Y; Exit.Y = InvExit.Y / sourceMotion.Y; } float entryTime = Math.Max(Entry.X, Entry.Y); float exitTime = Math.Min(Exit.X, Exit.Y); if (entryTime > exitTime || Entry.X < 0.0f && Entry.Y < 0.0f || Entry.X > 1 || Entry.Y > 1) { direction = 0; time = -1; return(false); } else { // calculate normal of collided surface if (Entry.X > Entry.Y) { if (sourceMotion.X < 0.0f) { direction = Chunk.Left; } else { direction = Chunk.Right; } } else if (Entry.X < Entry.Y) { if (sourceMotion.Y < 0.0f) { direction = Chunk.Down; } else { direction = Chunk.Up; } } else { corner = true; if (Math.Abs(sourceMotion.X) <= Math.Abs(sourceMotion.Y)) { if (sourceMotion.X < 0.0f) { direction = Chunk.Left; } else { direction = Chunk.Right; } } else { if (sourceMotion.Y < 0.0f) { direction = Chunk.Down; } else { direction = Chunk.Up; } } } // return the time of collision time = entryTime; return(true); } }