using System.Diagnostics.CodeAnalysis; using Syntriax.Engine.Core; using Syntriax.Engine.Physics2D.Abstract; using Syntriax.Engine.Physics2D.Primitives; namespace Syntriax.Engine.Physics2D; public class CollisionDetector : ICollisionDetector { public bool TryDetect(T1 left, T2 right, [NotNullWhen(true)] out CollisionDetectionInformation? collisionInformation) where T1 : ICollider2D where T2 : ICollider2D { collisionInformation = default; if (left is IShapeCollider2D shapeColliderLeft) { if (right is IShapeCollider2D shapeColliderRight) return DetectShapeShape(shapeColliderLeft, shapeColliderRight, out collisionInformation); else if (right is ICircleCollider2D circleColliderRight) return DetectShapeCircle(shapeColliderLeft, circleColliderRight, out collisionInformation); } else if (left is ICircleCollider2D circleColliderLeft) { if (right is IShapeCollider2D shapeColliderRight) return DetectCircleShape(circleColliderLeft, shapeColliderRight, out collisionInformation); else if (right is ICircleCollider2D circleColliderRight) return DetectCircleCircle(circleColliderLeft, circleColliderRight, out collisionInformation); } return false; } private static bool DetectCircleShape(ICircleCollider2D circleCollider, IShapeCollider2D shapeCollider, out CollisionDetectionInformation? collisionInformation) { return DetectShapeCircle(shapeCollider, circleCollider, out collisionInformation); } private static bool DetectShapeShape(IShapeCollider2D left, IShapeCollider2D right, out CollisionDetectionInformation? collisionInformation) { collisionInformation = default; return DetectShapeShapeOneWay(left, right, ref collisionInformation) && DetectShapeShapeOneWay(right, left, ref collisionInformation); } private static bool DetectShapeShapeOneWay(IShapeCollider2D left, IShapeCollider2D right, ref CollisionDetectionInformation? collisionInformation) { var vertices = left.ShapeWorld.Vertices; int count = vertices.Count; for (int indexProjection = 0; indexProjection < count; indexProjection++) { Vector2D projectionVector = vertices[indexProjection].FromTo(vertices[(indexProjection + 1) % count]).Perpendicular().Normalized; Projection leftProjection = left.ShapeWorld.ToProjection(projectionVector); Projection rightProjection = right.ShapeWorld.ToProjection(projectionVector); if (!leftProjection.Overlaps(rightProjection, out float depth)) return false; if (collisionInformation == default || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth)) collisionInformation = new(left, right, projectionVector, depth); } return true; } private static bool DetectShapeCircle(IShapeCollider2D shapeCollider, ICircleCollider2D circleCollider, out CollisionDetectionInformation? collisionInformation) { collisionInformation = default; { Vector2D shapeToCircleProjectionVector = shapeCollider.Transform.Position.FromTo(circleCollider.CircleWorld.Center).Normalized; Projection shapeProjection = shapeCollider.ShapeWorld.ToProjection(shapeToCircleProjectionVector); Projection circleProjection = circleCollider.CircleWorld.ToProjection(shapeToCircleProjectionVector); if (!shapeProjection.Overlaps(circleProjection, out float depth)) return false; if (collisionInformation == default || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth)) collisionInformation = new(shapeCollider, circleCollider, shapeToCircleProjectionVector, depth); } var vertices = shapeCollider.ShapeWorld.Vertices; int count = vertices.Count; for (int indexProjection = 0; indexProjection < count; indexProjection++) { Vector2D projectionVector = vertices[indexProjection].FromTo(vertices[(indexProjection + 1) % count]).Perpendicular().Normalized; Projection shapeProjection = shapeCollider.ShapeWorld.ToProjection(projectionVector); Projection circleProjection = circleCollider.CircleWorld.ToProjection(projectionVector); if (!shapeProjection.Overlaps(circleProjection, out float depth)) return false; if (collisionInformation == default || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth)) collisionInformation = new(shapeCollider, circleCollider, projectionVector, depth); } return true; } private static bool DetectCircleCircle(ICircleCollider2D left, ICircleCollider2D right, out CollisionDetectionInformation? collisionInformation) { collisionInformation = default; Vector2D leftToRightCenterProjectionVector = left.CircleWorld.Center.FromTo(right.CircleWorld.Center).Normalized; Projection leftProjection = left.CircleWorld.ToProjection(leftToRightCenterProjectionVector); Projection rightProjection = right.CircleWorld.ToProjection(leftToRightCenterProjectionVector); bool collision = leftProjection.Overlaps(rightProjection, out float depth); if (collision) collisionInformation = new(left, right, leftToRightCenterProjectionVector, depth); return collision; } // private static bool DetectCircleCircle(ICircleCollider2D left, ICircleCollider2D right, out CollisionDetectionInformation? collisionInformation) // { // collisionInformation = default; // Vector2D leftToRightCenter = left.CircleWorld.Center.FromTo(right.CircleWorld.Center); // float distanceCircleCenter = leftToRightCenter.Magnitude; // float radiusSum = left.CircleWorld.Radius + right.CircleWorld.Radius; // float circleSurfaceDistance = distanceCircleCenter - radiusSum; // bool collision = circleSurfaceDistance <= 0f; // if (collision) // collisionInformation = new(left, right, leftToRightCenter.Normalized, -circleSurfaceDistance); // return collision; // } }