2024-01-25 18:32:13 +03:00
|
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
|
2024-01-25 18:21:52 +03:00
|
|
|
using Syntriax.Engine.Core;
|
|
|
|
using Syntriax.Engine.Physics2D.Abstract;
|
2024-01-26 18:54:53 +03:00
|
|
|
using Syntriax.Engine.Physics2D.Primitives;
|
2024-01-25 18:21:52 +03:00
|
|
|
|
|
|
|
namespace Syntriax.Engine.Physics2D;
|
|
|
|
|
|
|
|
public class CollisionDetector : ICollisionDetector
|
|
|
|
{
|
2024-01-25 18:32:13 +03:00
|
|
|
public bool TryDetect<T1, T2>(T1 left, T2 right, [NotNullWhen(true)] out CollisionDetectionInformation? collisionInformation)
|
2024-01-25 18:21:52 +03:00
|
|
|
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)
|
2024-01-25 18:32:13 +03:00
|
|
|
return DetectCircleShape(circleColliderLeft, shapeColliderRight, out collisionInformation);
|
2024-01-25 18:21:52 +03:00
|
|
|
else if (right is ICircleCollider2D circleColliderRight)
|
|
|
|
return DetectCircleCircle(circleColliderLeft, circleColliderRight, out collisionInformation);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-01-26 18:54:53 +03:00
|
|
|
private static bool DetectCircleShape(ICircleCollider2D circleCollider, IShapeCollider2D shapeCollider, out CollisionDetectionInformation? collisionInformation)
|
2024-01-25 18:32:13 +03:00
|
|
|
{
|
2024-01-26 18:54:53 +03:00
|
|
|
return DetectShapeCircle(shapeCollider, circleCollider, out collisionInformation);
|
2024-01-25 18:32:13 +03:00
|
|
|
}
|
|
|
|
|
2024-01-25 18:21:52 +03:00
|
|
|
private static bool DetectShapeShape(IShapeCollider2D left, IShapeCollider2D right, out CollisionDetectionInformation? collisionInformation)
|
|
|
|
{
|
2024-01-26 18:56:31 +03:00
|
|
|
collisionInformation = default;
|
2024-01-26 20:35:05 +03:00
|
|
|
return DetectShapeShapeOneWay(left, right, ref collisionInformation) && DetectShapeShapeOneWay(right, left, ref collisionInformation);
|
|
|
|
}
|
2024-01-26 18:56:31 +03:00
|
|
|
|
2024-01-26 20:35:05 +03:00
|
|
|
private static bool DetectShapeShapeOneWay(IShapeCollider2D left, IShapeCollider2D right, ref CollisionDetectionInformation? collisionInformation)
|
|
|
|
{
|
2024-01-26 18:56:31 +03:00
|
|
|
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;
|
2024-01-25 18:21:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
private static bool DetectShapeCircle(IShapeCollider2D shapeCollider, ICircleCollider2D circleCollider, out CollisionDetectionInformation? collisionInformation)
|
|
|
|
{
|
2024-01-26 18:54:53 +03:00
|
|
|
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;
|
2024-01-25 18:21:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
private static bool DetectCircleCircle(ICircleCollider2D left, ICircleCollider2D right, out CollisionDetectionInformation? collisionInformation)
|
|
|
|
{
|
|
|
|
collisionInformation = default;
|
|
|
|
|
2024-01-26 12:45:26 +03:00
|
|
|
Vector2D leftToRightCenterProjectionVector = left.CircleWorld.Center.FromTo(right.CircleWorld.Center).Normalized;
|
2024-01-25 18:21:52 +03:00
|
|
|
|
2024-01-26 12:45:26 +03:00
|
|
|
Projection leftProjection = left.CircleWorld.ToProjection(leftToRightCenterProjectionVector);
|
|
|
|
Projection rightProjection = right.CircleWorld.ToProjection(leftToRightCenterProjectionVector);
|
2024-01-25 18:21:52 +03:00
|
|
|
|
2024-01-26 12:45:26 +03:00
|
|
|
bool collision = leftProjection.Overlaps(rightProjection, out float depth);
|
2024-01-25 18:21:52 +03:00
|
|
|
|
|
|
|
if (collision)
|
2024-01-26 12:45:26 +03:00
|
|
|
collisionInformation = new(left, right, leftToRightCenterProjectionVector, depth);
|
2024-01-25 18:21:52 +03:00
|
|
|
|
|
|
|
return collision;
|
|
|
|
}
|
2024-01-26 12:45:26 +03:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
// }
|
2024-01-25 18:21:52 +03:00
|
|
|
}
|