using System; using Microsoft.Xna.Framework; namespace BouncyCatIsBouncy { struct OBB : ICollidable { public float Width { get { return m_width; } set { m_width = MathHelper.Max(value, 0.0f); m_halfWidth = m_width / 2.0f; } } public float Height { get { return m_height; } set { m_height = MathHelper.Max(value, 0.0f); m_halfHeight = m_height / 2.0f; } } public float Rotation { get { return m_rotation; } set { m_rotation = value; CalculateOrientations(); } } public Vector2 Center { get; set; } //Backing properties private float m_width, m_height; private float m_rotation; //Used internally for collision calculations private Vector2 m_unitX, m_unitY; private float m_halfWidth, m_halfHeight; public OBB(float width, float height, float rotation) : this() { Width = width; Height = height; Rotation = rotation; } public OBB(float width, float height, float rotation, Vector2 center) : this(width, height, rotation) { Center = center; } //Rotates a point by an angle static private Vector2 RotateVector(Vector2 position, float radians) { var sin = (float)Math.Sin(-radians); var cos = (float)Math.Cos(-radians); return new Vector2(position.X * cos + position.Y * -sin, position.X * sin + position.Y * cos); } //Recalculates the orientations of the axes after a rotation has been set private void CalculateOrientations() { m_unitX = RotateVector(Vector2.UnitX, Rotation); m_unitY = RotateVector(Vector2.UnitY, Rotation); } //Returns the distance of each axis from a point static private Vector2 GetDistance(Vector2 localSystem, Vector2 unit, float halfSize) { var distance = Vector2.Dot(localSystem, unit); return MathHelper.Clamp(distance, -halfSize, halfSize) * unit; } //Calculates the closest point on the OBB private Vector2 CalcClosestPointToOBB(Vector2 radialCenter) { var localSystem = radialCenter - Center; var closestPoint = Center; return closestPoint + GetDistance(localSystem, m_unitX, m_halfWidth) + GetDistance(localSystem, m_unitY, m_halfHeight); } //Returns information about a potential collision with a Radial Bounding Box public CollisionInfo Collides(RadialBB other) { var closestPoint = CalcClosestPointToOBB(other.Center); var difference = closestPoint - other.Center; var distSquared = difference.LengthSquared(); var radius = other.Radius; if (distSquared <= radius * radius) { var normal = Vector2.Normalize(difference); var penetration = radius - (float)Math.Sqrt(distSquared); return new CollisionInfo(true, normal, penetration); } return new CollisionInfo(); } } }