using System;
using System.Collections.Generic;
using System.Linq;
namespace Otter {
///
/// The base class used for collision detection. Entities contain Colliders that can check for collisions
/// which each other. Note that some functions for detecting collisions can take generic Enums as their tags.
/// Be aware that these functions are slightly slower than casting your Enums to int for the int tags functions.
///
public abstract class Collider {
#region Private Fields
#endregion
#region Public Fields
///
/// The X position of the Collider relative to the Entity.
///
public float X;
///
/// The Y position of the Collider relative to the Entity.
///
public float Y;
///
/// The OriginX of the Collider.
///
public float OriginX;
///
/// The OriginY of the Collider.
///
public float OriginY;
///
/// The entity that this collider is parented to.
///
public Entity Entity;
///
/// Determines if this collider is collidable. If false, it will not register collisions with
/// other colliders, but can still check for collisions with other colliders.
///
public bool Collidable = true;
#endregion
#region Public Properties
///
/// The tags that this Collider is registered with.
///
public List Tags { get; protected set; }
///
/// The width of the Collider.
///
public virtual float Width { get; set; }
///
/// The height of the Collider.
///
public virtual float Height { get; set; }
///
/// The X position of the center of the Collider.
///
public virtual float CenterX {
get { return X + Entity.X - OriginX + HalfWidth; }
}
///
/// The Y position of the center of the Collider.
///
public virtual float CenterY {
get { return Y + Entity.Y - OriginY + HalfHeight; }
}
///
/// The X position of the left side of the Collider.
///
public virtual float Left {
get { return X + Entity.X - OriginX; }
}
///
/// The X position of the right side of the Collider.
///
public virtual float Right {
get { return X + Entity.X + Width - OriginX; }
}
///
/// The Y position of the top of the Collider.
///
public virtual float Top {
get { return Y + Entity.Y - OriginY; }
}
///
/// The Y position of the bottom of the Collider.
///
public virtual float Bottom {
get { return Y + Entity.Y + Height - OriginY; }
}
///
/// Half of the Collider's height.
///
public float HalfHeight {
get { return Height / 2f; }
}
///
/// Half of the Collider's width.
///
public float HalfWidth {
get { return Width / 2f; }
}
#endregion
#region Constructors
internal Collider() {
Tags = new List();
Width = 0;
Height = 0;
}
#endregion
#region Public Methods
///
/// Checks for a collision with a specific point.
///
/// The x position to check.
/// The y position to check.
/// The x of the point to check.
/// The y of the point to check.
/// True if the Collider at position x, y overlaps px, py.
public bool Overlap(float x, float y, int px, int py) {
var point = new PointCollider(px, py);
var e = new Entity(0, 0, null, point);
return OverlapTest(this, point);
}
///
/// Checks for a collision against the specified tags and returns true or false.
///
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// True if there was a collision.
public bool Overlap(float x, float y, params int[] tags) {
return (Collide(x, y, tags) != null);
}
///
/// Checks for a collision against a specific Collider and returns true or false.
///
/// The x position to check.
/// The y position to check.
/// The Collider to check.
/// True if there was a collision.
public bool Overlap(float x, float y, Collider c) {
return (Collide(x, y, c) != null);
}
///
/// Checks for a collision against a specific Entity and returns true or false.
///
/// The x position to check.
/// The y position to check.
/// The Collider to check.
/// True of there was a collision.
public bool Overlap(float x, float y, Entity e) {
return (Collide(x, y, e) != null);
}
///
/// Checks for a collision against a list of Collliders and returns true or false.
///
/// The x position to check.
/// The y position to check.
/// The list of colliders to check.
/// True if there was a collision.
public bool Overlap(float x, float y, List tags) {
return (Collide(x, y, tags) != null);
}
///
/// Checks for a collision against the specified tags and returns true or false.
///
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// True if there was a collision.
public bool Overlap(float x, float y, params Enum[] tags) {
return (Collide(x, y, tags) != null);
}
///
/// Checks for a collision against the specified tags and returns true or false.
///
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// True if there was a collision.
public bool Overlap(float x, float y, List tags) {
return (Collide(x, y, tags) != null);
}
///
/// Checks for a collision against a list of Entities and all of their colliders.
///
/// The x position to check.
/// The y position to check.
/// The Entities to check.
/// True if there was a collision.
public bool Overlap(float x, float y, List entities) {
return (Collide(x, y, entities) != null);
}
///
/// Checks for a collision against a list of Entities and all of their colliders.
///
/// The type of entity to check.
/// The x position to check.
/// The y position to check.
/// The Entities to check.
/// True if there was a collision.
public bool Overlap(float x, float y, List entities) where T : Entity {
return (Collide(x, y, entities.ToList()) != null);
}
///
/// Checks for a collision against a list of Entities and all of their colliders.
///
/// The x position to check.
/// The y position to check.
/// The Entities to check.
/// True if there was a collision.
public Collider Collide(float x, float y, List entities) {
foreach (var e in entities) {
var c = Collide(x, y, e);
if (c != null) return c;
}
return null;
}
public Collider Collide(float x, float y, List entities) where T : Entity {
return Collide(x, y, entities.ToList());
}
///
/// Checks for a collision against specified tags.
///
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// The collider that was hit first.
public Collider Collide(float x, float y, params int[] tags) {
if (Entity == null) return null;
if (Entity.Scene == null) return null;
float tempX = Entity.X, tempY = Entity.Y;
Entity.X = x;
Entity.Y = y;
if (tags.Length == 0) {
tags = new int[Entity.Scene.Colliders.Count];
var id = 0;
foreach (int i in Entity.Scene.Colliders.Keys) {
tags[id] = i;
id++;
}
}
foreach (int t in tags) {
if (Entity.Scene.Colliders.ContainsKey(t)) {
foreach (Collider c in Entity.Scene.Colliders[t]) {
if (c.Entity != null) {
if (!c.Entity.Collidable) continue;
if (!c.Collidable) continue;
if (c.Entity == Entity) continue;
}
else { // Emergency back up fix. Colliders with no entity should NOT be checked, or in the list.
continue;
}
if (OverlapTest(this, c)) {
Entity.X = tempX;
Entity.Y = tempY;
return c;
}
}
}
}
Entity.X = tempX;
Entity.Y = tempY;
return null;
}
///
/// Checks for a collision against specified tags.
///
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// The collider that was hit first.
public Collider Collide(float x, float y, params Enum[] tags) {
return Collide(x, y, Util.EnumToIntArray(tags));
}
///
/// Checks for a collision with a specific collider.
///
/// The x position to check.
/// The y position to check.
/// The collider to check for.
/// The collider that was hit first.
public Collider Collide(float x, float y, Collider c) {
if (Entity == null) return null;
if (Entity.Scene == null) return null;
if (!c.Entity.Collidable) return null;
if (!c.Collidable) return null;
float tempX = Entity.X, tempY = Entity.Y;
Entity.X = x;
Entity.Y = y;
if (OverlapTest(this, c)) {
Entity.X = tempX;
Entity.Y = tempY;
return c;
}
Entity.X = tempX;
Entity.Y = tempY;
return null;
}
///
/// Checks for a collision with a specific entity.
///
/// The x position to check.
/// The y position to check.
/// The entity to check for.
/// The collider that was hit.
public Collider Collide(float x, float y, Entity e) {
if (Entity == null) return null;
if (Entity.Scene == null) return null;
if (Entity == e) return null; // Can't collide with self
if (!Entity.Collidable) return null;
float tempX = Entity.X, tempY = Entity.Y;
Entity.X = x;
Entity.Y = y;
foreach (Collider c in e.Colliders) {
if (!c.Collidable) continue;
if (OverlapTest(this, c)) {
Entity.X = tempX;
Entity.Y = tempY;
return c;
}
}
Entity.X = tempX;
Entity.Y = tempY;
return null;
}
///
/// Checks for a collision with a list of tags.
///
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// The Collider that was hit.
public Collider Collide(float x, float y, List tags) {
Collider result;
foreach (int c in tags) {
result = Collide(x, y, c);
if (result != null) {
return result;
}
}
return null;
}
///
/// Checks for a collision with a list of tags.
///
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// The Collider that was hit.
public Collider Collide(float x, float y, List tags) {
return Collide(x, y, Util.EnumToIntArray(tags));
}
///
/// Checks for a collision with a list of tags and returns an Entity.
///
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// The Entity that was hit.
public Entity CollideEntity(float x, float y, params int[] tags) {
var c = Collide(x, y, tags);
if (c == null) return null;
return c.Entity;
}
///
/// Checks for a collision with a list of tags and returns an Entity.
///
/// The type of Entity to return.
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// The Entity that was hit.
public T CollideEntity(float x, float y, params int[] tags) where T : Entity {
return (T)CollideEntity(x, y, tags);
}
///
/// Checks for a collision with a list of tags and returns an Entity.
///
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// The Entity that was hit.
public Entity CollideEntity(float x, float y, params Enum[] tags) {
return CollideEntity(x, y, Util.EnumToIntArray(tags));
}
///
/// Checks for a collision with a list of tags and returns an Entity.
///
/// The type of Entity to return.
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// The Entity that was hit.
public T CollideEntity(float x, float y, params Enum[] tags) where T : Entity {
return (T)CollideEntity(x, y, tags);
}
///
/// Checks for a collision with a specific Entity and returns an Entity.
///
/// The x position to check.
/// The y position to check.
/// The Entity to check for.
/// The Entity that was hit.
public Entity CollideEntity(float x, float y, Entity e) {
return Collide(x, y, e).Entity;
}
///
/// Checks for a collision with a specific Entity and returns an Entity.
///
/// The type of Entity to return.
/// The x position to check.
/// The y position to check.
/// The Entity to check for.
/// The Entity that was hit.
public T CollideEntity(float x, float y, Entity e) where T : Entity {
return (T)CollideEntity(x, y, e);
}
///
/// Checks for a collision with a specific Collider and returns an Entity.
///
/// The x position to check.
/// The y position to check.
/// The Collider to check for.
/// The Entity that was hit.
public Entity CollideEntity(float x, float y, Collider c) {
return Collide(x, y, c).Entity;
}
///
/// Checks for a collision with a specific Collider and returns an Entity.
///
/// The type of Entity to return.
/// The x position to check.
/// The y position to check.
/// The Collider to check for.
/// The Entity that was hit.
public T CollideEntity(float x, float y, Collider c) where T : Entity {
return (T)CollideEntity(x, y, c);
}
///
/// Checks for a collision with a list of tags and returns an Entity.
///
/// The x position to check.
/// The y position to check.
/// The list of tags to check.
/// The Entity that was hit.
public Entity CollideEntity(float x, float y, List tags) {
foreach (int t in tags) {
Collider c;
c = Collide(x, y, t);
if (c != null) {
return c.Entity;
}
}
return null;
}
///
/// Checks for a collision with a list of tags and returns an Entity.
///
/// The type of Entity to return.
/// The x position to check.
/// The y position to check.
/// The list of tags to check.
/// The Entity that was hit.
public T CollideEntity(float x, float y, List tags) where T : Entity {
return (T)CollideEntity(x, y, tags);
}
///
/// Checks for a collision with a list of Entities and returns an Entity.
///
/// The x position to check.
/// The y position to check.
/// The list of Entities to check.
/// The Entity that was hit.
public Entity CollideEntity(float x, float y, List entities) {
if (Collide(x, y, entities) == null) return null;
return Collide(x, y, entities).Entity;
}
///
/// Checks for a collision with a list of Entities and returns an Entity.
///
/// The type of Entity to return.
/// The x position to check.
/// The y position to check.
/// The list of Entities to check.
/// The Entity that was hit.
public T CollideEntity(float x, float y, List entities) where T : Entity {
return (T)CollideEntity(x, y, entities.ToList()); // This blows up for some reason sometimes?
}
///
/// Creates a list of Colliders that this Collider is colliding with.
///
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// A list of colliders.
public List CollideList(float x, float y, params int[] tags) {
List collided = new List();
if (Entity == null) return null;
if (Entity.Scene == null) return null;
float tempX = Entity.X, tempY = Entity.Y;
Entity.X = x;
Entity.Y = y;
if (tags.Length == 0) {
tags = new int[Entity.Scene.Colliders.Count];
var id = 0;
foreach (int i in Entity.Scene.Colliders.Keys) {
tags[id] = i;
id++;
}
}
foreach (int t in tags) {
if (Entity.Scene.Colliders.ContainsKey(t)) {
foreach (var c in Entity.Scene.Colliders[t]) {
if (c.Entity != null) {
if (!c.Entity.Collidable) continue;
if (!c.Collidable) continue;
}
if (OverlapTest(this, c)) {
collided.Add(c);
}
}
}
}
Entity.X = tempX;
Entity.Y = tempY;
return collided;
}
///
/// Creates a list of Colliders that this Collider is colliding with.
///
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// A list of colliders.
public List CollideList(float x, float y, params Enum[] tags) {
return CollideList(x, y, Util.EnumToIntArray(tags));
}
///
/// Creates a list of Colliders that this Collider is colliding with.
///
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// A list of colliders.
public List CollideList(float x, float y, List tags) {
return CollideList(x, y, Util.EnumToIntArray(tags));
}
///
/// Creates a list of Colliders that this Collider is colliding with.
///
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// A list of colliders.
public List CollideList(float x, float y, List tags) {
List collided = new List();
List c;
foreach (int tag in tags) {
c = CollideList(x, y, tag);
foreach (Collider col in c) {
collided.Add(col);
}
}
return collided;
}
///
/// Creates a list of Entities that the Collider has collided with.
///
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// A list of entities.
public List CollideEntities(float x, float y, params int[] tags) {
List collided = new List();
List clist = CollideList(x, y, tags);
foreach (Collider c in clist) {
if (!collided.Contains(c.Entity)) {
if (c.Entity != Entity) {
collided.Add(c.Entity);
}
}
}
return collided;
}
///
/// Creates a list of Entities that the Collider has collided with.
///
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// A list of entities.
public List CollideEntities(float x, float y, params Enum[] tags) {
return CollideEntities(x, y, Util.EnumToIntArray(tags));
}
///
/// Creates a list of Entities that the Collider has collided with.
///
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// A list of entities.
public List CollideEntities(float x, float y, List tags) {
return CollideEntities(x, y, Util.EnumToIntArray(tags));
}
///
/// Creates a list of Entities that the Collider has collided with.
///
/// The type of list to return.
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// A list of entities.
public List CollideEntities(float x, float y, params int[] tags) where T : Entity {
return CollideEntities(x, y, tags).Cast().ToList();
}
///
/// Creates a list of Entities that the Collider has collided with.
///
/// The type of list to return.
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// A list of entities.
public List CollideEntities(float x, float y, params Enum[] tags) where T : Entity {
return CollideEntities(x, y, tags).Cast().ToList();
}
///
/// Creates a list of Entities that the Collider has collided with.
///
/// The type of list to return.
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// A list of entities.
public List CollideEntities(float x, float y, List tags) where T : Entity {
return CollideEntities(x, y, tags).Cast().ToList();
}
///
/// Creates a list of Entities that the Collider has collided with.
///
/// The type of Entity.
/// The x position to check.
/// The y position to check.
/// The Entities to check.
/// A list of entities.
public List CollideEntities(float x, float y, List entities) where T : Entity {
var list = new List();
foreach (var e in entities) {
var c = Collide(x, y, e);
if (c != null) {
if (c.Entity != null) {
list.Add(e);
}
}
}
return list;
}
///
/// Creates a list of entities that the Collider has collided with.
///
/// The x position to check.
/// The y position to check.
/// The tags to check.
/// A list of entities.
public List CollideEntities(float x, float y, List tags) {
List collided = new List();
List c;
foreach (int tag in tags) {
c = CollideList(x, y, tag);
foreach (Collider col in c) {
if (collided.Contains(col.Entity)) continue;
collided.Add(col.Entity);
}
}
return collided;
}
///
/// Callback for when the Collider has been added to an Entity.
///
public virtual void Added() {
}
///
/// Callback for when the Collider has been removed from an Entity.
///
public virtual void Removed() {
}
///
/// Callback for when the Collider renders (usually for debugging purposes.)
///
public virtual void Render(Color color = null) {
}
///
/// Add a new tag to the Collider.
///
/// The tag to add.
/// The Collider.
public Collider AddTag(int tag) {
if (Tags.Contains(tag)) return this;
if (Entity != null) {
if (Entity.Scene != null) {
Entity.Scene.RemoveColliderInternal(this);
}
}
Tags.Add(tag);
if (Entity != null) {
if (Entity.Scene != null) {
Entity.Scene.AddColliderInternal(this);
}
}
return this;
}
///
/// Add new tags to the Collider.
///
/// The tags to add.
/// The Collider.
public Collider AddTag(params Enum[] tags) {
AddTag(Util.EnumToIntArray(tags));
return this;
}
public bool HasTag(params Enum[] tags) {
return HasTag(Util.EnumToIntArray(tags));
}
public bool HasTag(params int[] tags) {
foreach (var t in tags) {
if (Tags.Contains(t)) return true;
}
return false;
}
///
/// Add new tags to the Collider.
///
/// The tags to add.
/// The Collider.
public Collider AddTag(params int[] tags) {
foreach (int t in tags) {
AddTag(t);
}
return this;
}
///
/// Remove a tag from the Collider.
///
/// The tags to remove.
/// The Collider.
public Collider RemoveTag(int tag) {
if (Entity != null) {
if (Entity.Scene != null) {
Entity.Scene.RemoveColliderInternal(this);
}
}
Tags.Remove(tag);
if (Entity != null) {
if (Entity.Scene != null) {
Entity.Scene.AddColliderInternal(this);
}
}
return this;
}
///
/// Remove tags from the Collider.
///
/// The tags to remove.
/// The Collider.
public Collider RemoveTag(params int[] tags) {
foreach (int t in tags) {
RemoveTag(t);
}
return this;
}
///
/// Remove tags from the Collider.
///
/// The tags to remove.
/// The Collider.
public Collider RemoveTag(params Enum[] tags) {
RemoveTag(Util.EnumToIntArray(tags));
return this;
}
///
/// Center the origin of the Collider. Based off of Width and Height.
///
public virtual void CenterOrigin() {
OriginX = HalfWidth;
OriginY = HalfHeight;
}
///
/// Set the positon of the Collider.
///
/// The X position of the Collider.
/// The Y position of the Collider.
public void SetPosition(float x, float y) {
X = x;
Y = y;
}
///
/// Set the position of the Collider.
///
/// The Vector2 position of the Collider.
public void SetPosition(Vector2 xy) {
SetPosition(xy.X, xy.Y);
}
///
/// Set the origin of the Collider
///
/// The X origin of the Collider.
/// The Y origin of the Collider.
public void SetOrigin(float x, float y) {
OriginX = x;
OriginY = y;
}
///
/// Set the origin of the Collider.
///
/// The Vector2 origin of the Collider.
public void SetOrigin(Vector2 xy) {
SetOrigin(xy.X, xy.Y);
}
#endregion
#region Internal
///
/// Giant function that checks for overlaps between many different types
///
/// The first collider to test.
/// The second collider to test.
/// True if overlapping something
internal static bool OverlapTest(Collider first, Collider second) {
#region Box vs Box
if (first is BoxCollider && second is BoxCollider) {
if (first.Right <= second.Left) return false;
if (first.Bottom <= second.Top) return false;
if (first.Left >= second.Right) return false;
if (first.Top >= second.Bottom) return false;
return true;
}
#endregion
#region Box vs Circle
else if ((first is BoxCollider && second is CircleCollider) || (first is CircleCollider && second is BoxCollider)) {
CircleCollider circle;
BoxCollider box;
if (first is CircleCollider) {
circle = first as CircleCollider;
box = second as BoxCollider;
}
else {
circle = second as CircleCollider;
box = first as BoxCollider;
}
//check is c center point is in the rect
if (Util.InRect(circle.CenterX, circle.CenterY, box.Left, box.Top, box.Width, box.Height)) {
return true;
}
//check to see if any corners are in the circle
if (Util.DistanceRectPoint(circle.CenterX, circle.CenterY, box.Left, box.Top, box.Width, box.Height) < circle.Radius) {
return true;
}
//check to see if any lines on the box intersect the circle
Line2 boxLine;
boxLine = new Line2(box.Left, box.Top, box.Right, box.Top);
if (boxLine.IntersectCircle(new Vector2(circle.CenterX, circle.CenterY), circle.Radius)) return true;
boxLine = new Line2(box.Right, box.Top, box.Right, box.Bottom);
if (boxLine.IntersectCircle(new Vector2(circle.CenterX, circle.CenterY), circle.Radius)) return true;
boxLine = new Line2(box.Right, box.Bottom, box.Left, box.Bottom);
if (boxLine.IntersectCircle(new Vector2(circle.CenterX, circle.CenterY), circle.Radius)) return true;
boxLine = new Line2(box.Left, box.Bottom, box.Left, box.Top);
if (boxLine.IntersectCircle(new Vector2(circle.CenterX, circle.CenterY), circle.Radius)) return true;
return false;
}
#endregion
#region Box vs Grid
else if ((first is BoxCollider && second is GridCollider) || (first is GridCollider && second is BoxCollider)) {
BoxCollider box;
GridCollider grid;
if (first is BoxCollider) {
box = first as BoxCollider;
grid = second as GridCollider;
}
else {
box = second as BoxCollider;
grid = first as GridCollider;
}
if (box.Right <= grid.Left) return false;
if (box.Bottom <= grid.Top) return false;
if (box.Left >= grid.Right) return false;
if (box.Top >= grid.Bottom) return false;
//This is a quick fix and might have to be addressed later
if (grid.GetRect(box.Left, box.Top, box.Right - 1, box.Bottom - 1, false)) return true;
return false;
}
#endregion
#region Box vs Line
else if ((first is BoxCollider && second is LineCollider) || (first is LineCollider && second is BoxCollider)) {
BoxCollider box;
LineCollider line;
if (first is BoxCollider) {
box = first as BoxCollider;
line = second as LineCollider;
}
else {
box = second as BoxCollider;
line = first as LineCollider;
}
if (line.Line2.IntersectsRect(box.Left, box.Top, box.Width, box.Height)) return true;
return false;
}
#endregion
#region Circle vs Circle
else if (first is CircleCollider && second is CircleCollider) {
CircleCollider
circle1 = first as CircleCollider,
circle2 = second as CircleCollider;
if (Util.Distance(circle1.CenterX, circle1.CenterY, circle2.CenterX, circle2.CenterY) < circle1.Radius + circle2.Radius) {
return true;
}
return false;
}
#endregion
#region Circle vs Grid
else if ((first is CircleCollider && second is GridCollider) || (first is GridCollider && second is CircleCollider)) {
//make a rectangle out of the circle, check for any tiles in that rectangle
//if there are tiles, check each tile as a rectangle against the circle
CircleCollider circle;
GridCollider grid;
if (first is CircleCollider) {
circle = first as CircleCollider;
grid = second as GridCollider;
}
else {
circle = second as CircleCollider;
grid = first as GridCollider;
}
int gridx, gridy, gridx2, gridy2;
gridx = (int)(Util.SnapToGrid(circle.Left - grid.Left, grid.TileWidth) / grid.TileWidth);
gridy = (int)(Util.SnapToGrid(circle.Top - grid.Top, grid.TileHeight) / grid.TileHeight);
gridx2 = (int)(Util.SnapToGrid(circle.Right - grid.Left, grid.TileWidth) / grid.TileWidth);
gridy2 = (int)(Util.SnapToGrid(circle.Bottom - grid.Top, grid.TileHeight) / grid.TileHeight);
//if (grid.GetRect(gridx, gridy, gridx2, gridy2, false)) {
if (grid.GetRect(circle.Left, circle.Top, circle.Right, circle.Bottom, false)) {
float rectX, rectY;
for (int i = gridx; i <= gridx2; i++) {
for (int j = gridy; j <= gridy2; j++) {
if (grid.GetTile(i, j)) {
rectX = (i * grid.TileWidth) + grid.Left;
rectY = (j * grid.TileHeight) + grid.Top;
//check is c center point is in the rect
if (Util.InRect(circle.CenterX, circle.CenterY, rectX, rectY, grid.TileWidth, grid.TileHeight)) {
return true;
}
//check to see if any corners are in the circle
if (Util.DistanceRectPoint(circle.CenterX, circle.CenterY, rectX, rectY, grid.TileWidth, grid.TileHeight) < circle.Radius) {
//return true;
}
//check to see if any lines on the box intersect the circle
Line2 boxLine;
boxLine = new Line2(rectX, rectY, rectX + grid.TileWidth, rectY);
if (boxLine.IntersectCircle(new Vector2(circle.CenterX, circle.CenterY), circle.Radius)) return true;
boxLine = new Line2(rectX + grid.TileWidth, rectY, rectX + grid.TileWidth, rectY + grid.TileHeight);
if (boxLine.IntersectCircle(new Vector2(circle.CenterX, circle.CenterY), circle.Radius)) return true;
boxLine = new Line2(rectX + grid.TileWidth, rectY + grid.TileHeight, rectX, rectY + grid.TileHeight);
if (boxLine.IntersectCircle(new Vector2(circle.CenterX, circle.CenterY), circle.Radius)) return true;
boxLine = new Line2(rectX, rectY + grid.TileHeight, rectX, rectY);
if (boxLine.IntersectCircle(new Vector2(circle.CenterX, circle.CenterY), circle.Radius)) return true;
}
}
}
}
return false;
}
#endregion
#region Line vs Line
else if (first is LineCollider && second is LineCollider) {
return (first as LineCollider).Line2.Intersects((second as LineCollider).Line2);
}
#endregion
#region Line vs Grid
else if ((first is LineCollider && second is GridCollider) || (first is GridCollider && second is LineCollider)) {
//check any tiles along the line segment, somehow?
LineCollider line;
GridCollider grid;
if (first is LineCollider) {
line = first as LineCollider;
grid = second as GridCollider;
}
else {
line = second as LineCollider;
grid = first as GridCollider;
}
//make a rectangle out of the line segment, check for any tiles in that rectangle
//if there are tiles in there, loop through and check each one as a rectangle against the line
if (grid.GetRect(line.Left, line.Top, line.Right, line.Bottom, false)) {
float rectX, rectY;
int
gridx = grid.GridX(line.Left),
gridy = grid.GridY(line.Top),
gridx2 = grid.GridX(line.Right),
gridy2 = grid.GridY(line.Bottom);
for (int i = gridx; i <= gridx2; i++) {
for (int j = gridy; j <= gridy2; j++) {
if (grid.GetTile(i, j)) {
rectX = i * grid.TileWidth + grid.Left;
rectY = j * grid.TileHeight + grid.Top;
if (Util.InRect((float)line.Line2.PointA.X, (float)line.Line2.PointA.Y, rectX, rectY, grid.TileWidth, grid.TileHeight)) {
return true;
}
if (Util.InRect((float)line.Line2.PointB.X, (float)line.Line2.PointB.Y, rectX, rectY, grid.TileWidth, grid.TileHeight)) {
return true;
}
if (line.Line2.IntersectsRect(rectX, rectY, grid.TileWidth, grid.TileHeight)) {
return true;
}
}
}
}
}
return false;
}
#endregion
#region Line vs Circle
else if ((first is LineCollider && second is CircleCollider) || (first is CircleCollider && second is LineCollider)) {
CircleCollider circle;
LineCollider line;
if (first is LineCollider) {
line = first as LineCollider;
circle = second as CircleCollider;
}
else {
line = second as LineCollider;
circle = first as CircleCollider;
}
if (line.Line2.IntersectCircle(new Vector2(circle.CenterX, circle.CenterY), circle.Radius)) {
return true;
}
return false;
}
#endregion
#region Grid vs Grid
else if (first is GridCollider && second is GridCollider) {
//loop through one grid, check tile as rectangle in second grid?
//first check if grids even touch with basic box test
//then check each tile on first as a rect against second's tilemap
//maybe optimize by looping through the smaller (area wise) tilemap
if (!Util.IntersectRectangles(first.Left, first.Top, first.Width, first.Height, second.Left, second.Top, second.Width, second.Height)) {
return false;
}
GridCollider small, large;
if ((first as GridCollider).TileArea < (second as GridCollider).TileArea) {
small = first as GridCollider;
large = second as GridCollider;
}
else {
small = second as GridCollider;
large = first as GridCollider;
}
for (int i = 0; i < small.TileColumns; i++) {
for (int j = 0; j < small.TileRows; j++) {
if (small.GetTile(i, j)) {
//check rects
float rectx, recty;
rectx = i * small.TileWidth + small.Left;
recty = j * small.TileHeight + small.Top;
if (large.GetRect(rectx, recty, rectx + small.TileWidth, recty + small.TileHeight, false)) {
return true;
}
}
}
}
return false;
}
#endregion
#region Point vs Point
else if (first is PointCollider && second is PointCollider) {
if (first.Left != second.Left) return false;
if (first.Top != second.Top) return false;
return true;
}
#endregion
#region Point vs Circle
else if ((first is PointCollider && second is CircleCollider) || (first is CircleCollider && second is PointCollider)) {
PointCollider point;
CircleCollider circle;
if (first is PointCollider) {
point = first as PointCollider;
circle = second as CircleCollider;
}
else {
point = second as PointCollider;
circle = first as CircleCollider;
}
if (Util.Distance(point.Left, point.Top, circle.CenterX, circle.CenterY) < circle.Radius) return true;
return false;
}
#endregion
#region Point vs Box
else if ((first is PointCollider && second is BoxCollider) || (first is BoxCollider && second is PointCollider)) {
PointCollider point;
BoxCollider box;
if (first is PointCollider) {
point = first as PointCollider;
box = second as BoxCollider;
}
else {
point = second as PointCollider;
box = first as BoxCollider;
}
if (Util.InRect(point.Left, point.Top, box.Left, box.Top, box.Width, box.Height)) return true;
return false;
}
#endregion
#region Point vs Grid
else if ((first is PointCollider && second is GridCollider) || (first is GridCollider && second is PointCollider)) {
PointCollider point;
GridCollider grid;
if (first is PointCollider) {
point = first as PointCollider;
grid = second as GridCollider;
}
else {
point = second as PointCollider;
grid = first as GridCollider;
}
int gridx, gridy;
gridx = (int)Util.SnapToGrid(point.Left, grid.TileWidth) / grid.TileWidth;
gridy = (int)Util.SnapToGrid(point.Top, grid.TileHeight) / grid.TileHeight;
if (grid.GetTile(gridx, gridy)) return true;
return false;
}
#endregion
#region Point vs Line
else if ((first is PointCollider && second is LineCollider) || (first is LineCollider && second is PointCollider)) {
PointCollider point;
LineCollider line;
if (first is PointCollider) {
point = first as PointCollider;
line = second as LineCollider;
}
else {
point = second as PointCollider;
line = first as LineCollider;
}
//first take care of weird cases that might result in division by 0
Line2 line2 = line.Line2;
if (line2.X1 == line2.X2) {
if (line2.Y1 == line2.Y2) {
if (point.Left == line2.X1 && point.Top == line2.Y1) {
return true;
}
}
if (point.Left == line2.X1 && point.Top >= Math.Min(line2.Y1, line2.Y2) && point.Top <= Math.Max(line2.Y1, line2.Y2)) {
return true;
}
}
if (line2.Y1 == line2.Y2) {
if (point.Top == line2.Y1 && point.Left >= Math.Min(line2.X1, line2.X2) && point.Left <= Math.Max(line2.X1, line2.X2)) {
return true;
}
}
//if no special cases, this should work!
if ((point.Left - line2.X1) / (line2.X2 - line2.X1) == (point.Top - line2.Y1) / (line2.Y2 - line2.Y1)) return true;
return false;
}
#endregion
#region Pixel vs Pixel
else if ((first is PixelCollider && second is PixelCollider)) {
//AABB test first
var pixel1 = first as PixelCollider;
var pixel2 = second as PixelCollider;
if (first.Right <= second.Left) return false;
if (first.Left >= second.Right) return false;
if (first.Top >= second.Bottom) return false;
if (first.Bottom <= second.Top) return false;
//get intersecting area
float x1 = Math.Max(first.Left, second.Left);
float x2 = Math.Min(first.Right, second.Right);
float y1 = Math.Max(first.Top, second.Top);
float y2 = Math.Min(first.Bottom, second.Bottom);
//scan both pixels and see if they collide
var p1 = first as PixelCollider;
var p2 = second as PixelCollider;
for (var i = x1; i < x2; i++) {
for (var j = y1; j < y2; j++) {
var xx = (int)Math.Floor(i - first.Left);
var yy = (int)Math.Floor(j - first.Top);
if (p1.PixelAt(xx, yy)) {
xx = (int)Math.Floor(i - second.Left);
yy = (int)Math.Floor(j - second.Top);
if (p2.PixelAt(xx, yy)) {
return true;
}
}
}
}
return false;
}
#endregion
#region Pixel vs Box
else if ((first is PixelCollider && second is BoxCollider) || (first is BoxCollider && second is PixelCollider)) {
PixelCollider pixel;
BoxCollider box;
if (first is PixelCollider) {
pixel = first as PixelCollider;
box = second as BoxCollider;
}
else {
pixel = second as PixelCollider;
box = first as BoxCollider;
}
//AABB test
if (box.Right <= pixel.Left) return false;
if (box.Left >= pixel.Right) return false;
if (box.Top >= pixel.Bottom) return false;
if (box.Bottom <= pixel.Top) return false;
//get intersecting area
float x1 = Math.Max(box.Left, pixel.Left);
float x2 = Math.Min(box.Right, pixel.Right);
float y1 = Math.Max(box.Top, pixel.Top);
float y2 = Math.Min(box.Bottom, pixel.Bottom);
//scan for pixels in area
for (var i = x1; i < x2; i++) {
for (var j = y1; j < y2; j++) {
var xx = (int)Math.Floor(i - pixel.Left);
var yy = (int)Math.Floor(j - pixel.Top);
if (pixel.PixelAt(xx, yy)) {
return true;
}
}
}
return false;
}
#endregion
#region Pixel vs Circle
else if ((first is PixelCollider && second is CircleCollider) || (first is CircleCollider && second is PixelCollider)) {
PixelCollider pixel;
CircleCollider circle;
if (first is PixelCollider) {
pixel = first as PixelCollider;
circle = second as CircleCollider;
}
else {
pixel = second as PixelCollider;
circle = first as CircleCollider;
}
//circle to rectangle collision first
bool firstCollisionCheck = false;
//check is c center point is in the rect
if (Util.InRect(circle.CenterX, circle.CenterY, pixel.Left, pixel.Top, pixel.Width, pixel.Height)) {
firstCollisionCheck = true;
}
if (!firstCollisionCheck) {
//check to see if any corners are in the circle
if (Util.DistanceRectPoint(circle.CenterX, circle.CenterY, pixel.Left, pixel.Top, pixel.Width, pixel.Height) < circle.Radius) {
firstCollisionCheck = true;
}
}
if (!firstCollisionCheck) {
//check to see if any lines on the box intersect the circle
Line2 boxLine;
boxLine = new Line2(pixel.Left, pixel.Top, pixel.Right, pixel.Top);
if (boxLine.IntersectCircle(new Vector2(circle.CenterX, circle.CenterY), circle.Radius)) firstCollisionCheck = true;
if (!firstCollisionCheck) {
boxLine = new Line2(pixel.Right, pixel.Top, pixel.Right, pixel.Bottom);
if (boxLine.IntersectCircle(new Vector2(circle.CenterX, circle.CenterY), circle.Radius)) firstCollisionCheck = true;
}
if (!firstCollisionCheck) {
boxLine = new Line2(pixel.Right, pixel.Bottom, pixel.Left, pixel.Bottom);
if (boxLine.IntersectCircle(new Vector2(circle.CenterX, circle.CenterY), circle.Radius)) firstCollisionCheck = true;
}
if (!firstCollisionCheck) {
boxLine = new Line2(pixel.Left, pixel.Bottom, pixel.Left, pixel.Top);
if (boxLine.IntersectCircle(new Vector2(circle.CenterX, circle.CenterY), circle.Radius)) firstCollisionCheck = true;
}
}
if (!firstCollisionCheck) return false;
//get intersecting area as if circle was rect
//scan for pixels in area
//only check pixels inside circle radius
//okay the math for that is super hard so we're doing it the easy + dumb wy
//dumb way, scan through pixels to see if one of the pixels is in the circle?
for (var xx = 0; xx < pixel.Width; xx++) {
for (var yy = 0; yy < pixel.Height; yy++) {
var pixelx = xx + pixel.Left;
var pixely = yy + pixel.Top;
if (Util.Distance(pixelx, pixely, circle.CenterX, circle.CenterY) <= circle.Radius + 1) {
if (pixel.PixelAt(xx, yy)) {
return true;
}
}
}
}
}
#endregion
#region Pixel vs Point
else if ((first is PixelCollider && second is PointCollider) || (first is PointCollider && second is PixelCollider)) {
PixelCollider pixel;
PointCollider point;
if (first is PixelCollider) {
pixel = first as PixelCollider;
point = second as PointCollider;
}
else {
pixel = second as PixelCollider;
point = first as PointCollider;
}
//rectangle to point first
if (!Util.InRect(point.Left, point.Top, pixel.Left, pixel.Top, pixel.Width, pixel.Height)) return false;
//check for pixel at point
if (pixel.PixelAtRelative((int)point.Left, (int)point.Top)) {
return true;
}
return false;
}
#endregion
#region Pixel vs Line
else if ((first is PixelCollider && second is LineCollider) || (first is LineCollider && second is PixelCollider)) {
PixelCollider pixel;
LineCollider line;
if (first is PixelCollider) {
pixel = first as PixelCollider;
line = second as LineCollider;
}
else {
pixel = second as PixelCollider;
line = first as LineCollider;
}
//line to rectangle first
if (!line.Line2.IntersectsRect(pixel.Left, pixel.Top, pixel.Width, pixel.Height)) return false;
//dumb way, check all pixels for distance to line == 0
for (var xx = 0; xx < pixel.Width; xx++) {
for (var yy = 0; yy < pixel.Height; yy++) {
if (pixel.PixelAt(xx, yy)) {
if (Util.DistanceLinePoint(xx + pixel.Left, yy + pixel.Top, line.Line2) < 0.1f) {
return true;
}
}
}
}
}
#endregion
#region Pixel vs Grid
else if ((first is PixelCollider && second is GridCollider) || (first is GridCollider && second is PixelCollider)) {
PixelCollider pixel;
GridCollider grid;
if (first is PixelCollider) {
pixel = first as PixelCollider;
grid = second as GridCollider;
}
else {
pixel = second as PixelCollider;
grid = first as GridCollider;
}
//collide rectangle into tiles first
if (pixel.Right <= grid.Left) return false;
if (pixel.Bottom <= grid.Top) return false;
if (pixel.Left >= grid.Right) return false;
if (pixel.Top >= grid.Bottom) return false;
//This is a quick fix and might have to be addressed later
if (!grid.GetRect(pixel.Left, pixel.Top, pixel.Right - 1, pixel.Bottom - 1, false)) return false;
//go through tiles that pixel is overlapping
for (var xx = 0; xx < pixel.Width; xx++) {
for (var yy = 0; yy < pixel.Height; yy++) {
float checkx = xx + pixel.Left;
float checky = yy + pixel.Top;
if (grid.GetTileAtPosition(checkx, checky)) {
if (pixel.PixelAt(xx, yy)) {
return true;
}
}
}
}
}
#endregion
#region Polygon vs Polygon
else if (first is PolygonCollider && second is PolygonCollider) {
var p1 = first as PolygonCollider;
var p2 = second as PolygonCollider;
// make copies of the polygons with applied offsets for testing
var poly1 = new Polygon(p1.Polygon);
var poly2 = new Polygon(p2.Polygon);
poly1.OffsetPoints(p1.Left, p1.Top);
poly2.OffsetPoints(p2.Left, p2.Top);
return poly1.Overlap(poly2);
}
#endregion
#region Polygon vs Box
else if (first is PolygonCollider && second is BoxCollider || first is BoxCollider && second is PolygonCollider) {
PolygonCollider poly;
BoxCollider box;
if (first is PolygonCollider) {
poly = first as PolygonCollider;
box = second as BoxCollider;
}
else {
poly = second as PolygonCollider;
box = first as BoxCollider;
}
var poly1 = new Polygon(poly.Polygon);
poly1.OffsetPoints(poly.Left, poly.Top);
//create poly for box
var poly2 = new Polygon(box.Left, box.Top, box.Right, box.Top, box.Right, box.Bottom, box.Left, box.Bottom);
//test polys
return poly1.Overlap(poly2);
}
#endregion
#region Polygon vs Circle
else if (first is PolygonCollider && second is CircleCollider || first is CircleCollider && second is PolygonCollider) {
PolygonCollider poly;
CircleCollider circ;
if (first is PolygonCollider) {
poly = first as PolygonCollider;
circ = second as CircleCollider;
}
else {
poly = second as PolygonCollider;
circ = first as CircleCollider;
}
var poly1 = new Polygon(poly.Polygon);
poly1.OffsetPoints(poly.Left, poly.Top);
// check each point of poly for distance to circle
foreach (var p in poly1.Points) {
if (Util.Distance(p.X, p.Y, circ.CenterX, circ.CenterY) < circ.Radius) {
return true;
}
}
//check if center point is in poly
if (poly1.ContainsPoint(circ.CenterX, circ.CenterY)) {
return true;
}
//check each edge for intersection with the circle
var lines = poly1.GetEdgesAsLines();
foreach (var l in lines) {
if (l.IntersectCircle(new Vector2(circ.CenterX, circ.CenterY), circ.Radius)) {
return true;
}
}
return false;
}
#endregion
#region Polygon vs Grid
else if (first is PolygonCollider && second is GridCollider || first is GridCollider && second is PolygonCollider) {
PolygonCollider poly;
GridCollider grid;
if (first is PolygonCollider) {
poly = first as PolygonCollider;
grid = second as GridCollider;
}
else {
poly = second as PolygonCollider;
grid = first as GridCollider;
}
var poly1 = new Polygon(poly.Polygon);
poly1.OffsetPoints(poly.Left, poly.Top);
//test against grid bounding box first
var bbox = new Polygon(grid.Left, grid.Top, grid.Right, grid.Top, grid.Right, grid.Bottom, grid.Left, grid.Bottom);
if (!poly1.Overlap(bbox)) {
return false;
}
//check rect in bounding box where polygon is
if (!grid.GetRect(poly.Left, poly.Top, poly.Right, poly.Bottom, false)) {
return false;
}
//check each occupied cell as a box in the bounding box
int startX = grid.GridX(poly.Left - grid.Left);
int startY = grid.GridY(poly.Top - grid.Top);
int endX = grid.GridX(poly.Left - grid.Left + poly.Width) + 1;
int endY = grid.GridY(poly.Top - grid.Top + poly.Height) + 1;
//Console.WriteLine("StartX {0} StartY {1} EndX {2} EndY {3}", startX, startY, endX, endY);
for (var xx = startX; xx < endX; xx++) {
for (var yy = startY; yy < endY; yy++) {
if (grid.GetTile(xx, yy)) {
var realX = xx * grid.TileWidth + grid.Left;
var realY = yy * grid.TileHeight + grid.Top;
var gridPoly = new Polygon(
realX, realY,
realX + grid.TileWidth, realY,
realX + grid.TileWidth, realY + grid.TileHeight,
realX, realY + grid.TileHeight
);
if (gridPoly.Overlap(poly1)) {
//Console.WriteLine("Collision with tile X:{0} Y:{1}", xx, yy);
//Console.WriteLine("Poly overlapped with {0}", gridPoly);
return true;
}
}
}
}
return false;
}
#endregion
#region Polygon vs Line
else if (first is PolygonCollider && second is LineCollider || first is LineCollider && second is PolygonCollider) {
PolygonCollider poly;
LineCollider line;
if (first is PolygonCollider) {
poly = first as PolygonCollider;
line = second as LineCollider;
}
else {
poly = second as PolygonCollider;
line = first as LineCollider;
}
var poly1 = new Polygon(poly.Polygon);
poly1.OffsetPoints(poly.Left, poly.Top);
var poly2 = new Polygon(line.Left, line.Top, line.Right, line.Bottom);
return poly1.Overlap(poly2);
}
#endregion
#region Polygon vs Pixel
else if (first is PolygonCollider && second is PixelCollider || first is PixelCollider && second is PolygonCollider) {
PolygonCollider poly;
PixelCollider pixel;
if (first is PolygonCollider) {
poly = first as PolygonCollider;
pixel = second as PixelCollider;
}
else {
poly = second as PolygonCollider;
pixel = first as PixelCollider;
}
var poly1 = new Polygon(poly.Polygon);
poly1.OffsetPoints(poly.Left, poly.Top);
// check bounding box of pixel first
var bbox = new Polygon(pixel.Left, pixel.Top, pixel.Right, pixel.Top, pixel.Right, pixel.Bottom, pixel.Left, pixel.Bottom);
if (!poly1.Overlap(bbox)) {
return false;
}
// get intersecting area of the bounding boxes
float x1 = Math.Max(poly.Left, pixel.Left);
float x2 = Math.Min(poly.Right, pixel.Right);
float y1 = Math.Max(poly.Top, pixel.Top);
float y2 = Math.Min(poly.Bottom, pixel.Bottom);
// check each pixel in area as point in poly
for (var i = x1; i < x2; i++) {
for (var j = y1; j < y2; j++) {
var xx = (int)Math.Floor(i - pixel.Left);
var yy = (int)Math.Floor(j - pixel.Top);
if (pixel.PixelAt(xx, yy)) {
if (poly1.ContainsPoint(xx + pixel.Left, yy + pixel.Top)) {
return true;
}
}
}
}
return false;
}
#endregion
#region Polygon vs Point
else if (first is PolygonCollider && second is PointCollider || first is PointCollider && second is PolygonCollider) {
PolygonCollider poly;
PointCollider point;
if (first is PolygonCollider) {
poly = first as PolygonCollider;
point = second as PointCollider;
}
else {
poly = second as PolygonCollider;
point = first as PointCollider;
}
var poly1 = new Polygon(poly.Polygon);
poly1.OffsetPoints(poly.Left, poly.Top);
return poly1.ContainsPoint(point.Left, point.Top);
}
#endregion
#region Unknown
return false;
#endregion
}
#endregion
}
}