Compare commits
12 Commits
ceebe21041
...
85bad951ff
Author | SHA1 | Date | |
---|---|---|---|
85bad951ff | |||
6a84c3ec1a | |||
ceb29cc42f | |||
c6d2bad23e | |||
2bfd391286 | |||
ac09b78edd | |||
4607955d55 | |||
271a9a244b | |||
f980546d37 | |||
dfcc877e58 | |||
8ebde9dedf | |||
238bf2d574 |
@@ -19,6 +19,7 @@ public static class Vector2DExtensions
|
||||
public static Vector2D FromTo(this Vector2D from, Vector2D to) => Vector2D.FromTo(from, to);
|
||||
public static Vector2D Scale(this Vector2D vector, Vector2D scale) => Vector2D.Scale(vector, scale);
|
||||
|
||||
public static Vector2D Perpendicular(this Vector2D vector) => Vector2D.Perpendicular(vector);
|
||||
public static Vector2D Rotate(this Vector2D vector, float angleInRadian) => Vector2D.Rotate(vector, angleInRadian);
|
||||
public static Vector2D Min(this Vector2D left, Vector2D right) => Vector2D.Min(left, right);
|
||||
public static Vector2D Max(this Vector2D left, Vector2D right) => Vector2D.Max(left, right);
|
||||
|
@@ -40,6 +40,7 @@ public record Vector2D(float X, float Y)
|
||||
public static Vector2D FromTo(Vector2D from, Vector2D to) => to - from;
|
||||
public static Vector2D Scale(Vector2D vector, Vector2D scale) => new(vector.X * scale.X, vector.Y * scale.Y);
|
||||
|
||||
public static Vector2D Perpendicular(Vector2D vector) => new(-vector.Y, vector.X);
|
||||
public static Vector2D Rotate(Vector2D vector, float angleInRadian) => new(MathF.Cos(angleInRadian) * vector.X - MathF.Sin(angleInRadian) * vector.Y, MathF.Sin(angleInRadian) * vector.X + MathF.Cos(angleInRadian) * vector.Y);
|
||||
public static Vector2D Min(Vector2D left, Vector2D right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y);
|
||||
public static Vector2D Max(Vector2D left, Vector2D right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y);
|
||||
|
@@ -39,13 +39,15 @@ public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D
|
||||
BehaviourController.OnBehaviourAdded += OnBehaviourAddedToController;
|
||||
BehaviourController.OnBehaviourRemoved += OnBehaviourRemovedFromController;
|
||||
|
||||
Transform.OnPositionChanged += OnPositionChanged;
|
||||
Transform.OnPositionChanged += SetNeedsRecalculation;
|
||||
Transform.OnRotationChanged += SetNeedsRecalculation;
|
||||
Transform.OnScaleChanged += SetNeedsRecalculation;
|
||||
}
|
||||
|
||||
private void OnBehaviourAddedToController(IBehaviourController _, IBehaviour behaviour)
|
||||
{
|
||||
if (behaviour is IRigidBody2D rigidbody)
|
||||
_rigidBody2D = rigidbody;
|
||||
if (behaviour is IRigidBody2D rigidBody)
|
||||
_rigidBody2D = rigidBody;
|
||||
}
|
||||
|
||||
private void OnBehaviourRemovedFromController(IBehaviourController _, IBehaviour behaviour)
|
||||
@@ -54,13 +56,15 @@ public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D
|
||||
_rigidBody2D = null;
|
||||
}
|
||||
|
||||
private void OnPositionChanged(ITransform transform) => NeedsRecalculation = true;
|
||||
private void SetNeedsRecalculation(ITransform transform) => NeedsRecalculation = true;
|
||||
|
||||
protected override void OnFinalize()
|
||||
{
|
||||
BehaviourController.OnBehaviourAdded -= OnBehaviourAddedToController;
|
||||
BehaviourController.OnBehaviourRemoved -= OnBehaviourRemovedFromController;
|
||||
Transform.OnScaleChanged -= SetNeedsRecalculation;
|
||||
|
||||
Transform.OnPositionChanged -= OnPositionChanged;
|
||||
Transform.OnPositionChanged -= SetNeedsRecalculation;
|
||||
Transform.OnRotationChanged -= SetNeedsRecalculation;
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
using Syntriax.Engine.Physics2D.Abstract;
|
||||
using Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
@@ -30,36 +31,104 @@ public class CollisionDetector : ICollisionDetector
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool DetectCircleShape(ICircleCollider2D circleCollider, IShapeCollider2D shapeCollider, out CollisionDetectionInformation? collisionInformation)
|
||||
private static bool DetectCircleShape(ICircleCollider2D circleCollider, IShapeCollider2D shapeCollider, out CollisionDetectionInformation? collisionInformation)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
return DetectShapeCircle(shapeCollider, circleCollider, out collisionInformation);
|
||||
}
|
||||
|
||||
private static bool DetectShapeShape(IShapeCollider2D left, IShapeCollider2D right, out CollisionDetectionInformation? collisionInformation)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
collisionInformation = default;
|
||||
|
||||
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)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
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 leftToRightCenter = left.CircleWorld.Center.FromTo(right.CircleWorld.Center);
|
||||
float distanceCircleCenter = leftToRightCenter.Magnitude;
|
||||
float radiusSum = left.CircleWorld.Radius + right.CircleWorld.Radius;
|
||||
Vector2D leftToRightCenterProjectionVector = left.CircleWorld.Center.FromTo(right.CircleWorld.Center).Normalized;
|
||||
|
||||
float circleSurfaceDistance = distanceCircleCenter - radiusSum;
|
||||
Projection leftProjection = left.CircleWorld.ToProjection(leftToRightCenterProjectionVector);
|
||||
Projection rightProjection = right.CircleWorld.ToProjection(leftToRightCenterProjectionVector);
|
||||
|
||||
bool collision = circleSurfaceDistance <= 0f;
|
||||
bool collision = leftProjection.Overlaps(rightProjection, out float depth);
|
||||
|
||||
if (collision)
|
||||
collisionInformation = new(left, right, leftToRightCenter.Normalized, -circleSurfaceDistance);
|
||||
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;
|
||||
// }
|
||||
}
|
||||
|
@@ -13,6 +13,12 @@ public record Circle(Vector2D Center, float Radius)
|
||||
|
||||
public static Circle Displace(Circle circle, Vector2D displaceVector) => new(circle.Center + displaceVector, circle.Radius);
|
||||
|
||||
public static Projection Project(Circle circle, Vector2D projectionVector)
|
||||
{
|
||||
float projectedCenter = circle.Center.Dot(projectionVector);
|
||||
return new(projectedCenter - circle.Radius, projectedCenter + circle.Radius);
|
||||
}
|
||||
|
||||
public static Circle TransformCircle(ITransform transform, Circle circle)
|
||||
=> new(transform.TransformVector2D(circle.Center), circle.Radius * transform.Scale.Magnitude);
|
||||
|
||||
@@ -27,6 +33,8 @@ public static class CircleExtensions
|
||||
|
||||
public static Circle Displace(this Circle circle, Vector2D displaceVector) => Circle.Displace(circle, displaceVector);
|
||||
|
||||
public static Projection ToProjection(this Circle circle, Vector2D projectionVector) => Circle.Project(circle, projectionVector);
|
||||
|
||||
public static Circle TransformCircle(this ITransform transform, Circle circle) => Circle.TransformCircle(transform, circle);
|
||||
|
||||
public static bool ApproximatelyEquals(this Circle left, Circle right) => Circle.ApproximatelyEquals(left, right);
|
||||
|
45
Engine.Physics2D/Primitives/Projection.cs
Normal file
45
Engine.Physics2D/Primitives/Projection.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
public record Projection(float Min, float Max)
|
||||
{
|
||||
public static bool Overlaps(Projection left, Projection right) => Overlaps(left, right, out var _);
|
||||
public static bool Overlaps(Projection left, Projection right, out float depth)
|
||||
{
|
||||
// TODO Try to improve this
|
||||
bool rightMinInLeft = right.Min > left.Min && right.Min < left.Max;
|
||||
if (rightMinInLeft)
|
||||
{
|
||||
depth = left.Max - right.Min;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rightMaxInLeft = right.Max < left.Max && right.Max > left.Min;
|
||||
if (rightMaxInLeft)
|
||||
{
|
||||
depth = left.Min - right.Max;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool leftMinInRight = left.Min > right.Min && left.Min < right.Max;
|
||||
if (leftMinInRight)
|
||||
{
|
||||
depth = right.Max - left.Min;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool leftMaxInRight = left.Max < right.Max && left.Max > right.Min;
|
||||
if (leftMaxInRight)
|
||||
{
|
||||
depth = right.Min - left.Max;
|
||||
return true;
|
||||
}
|
||||
|
||||
depth = 0f;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static class ProjectionExtensions
|
||||
{
|
||||
public static bool Overlaps(this Projection left, Projection right) => Projection.Overlaps(left, right);
|
||||
public static bool Overlaps(this Projection left, Projection right, out float depth) => Projection.Overlaps(left, right, out depth);
|
||||
}
|
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -9,7 +8,31 @@ namespace Syntriax.Engine.Physics2D.Primitives;
|
||||
|
||||
public record Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
|
||||
{
|
||||
public Vector2D this[Index index] => Vertices[index];
|
||||
public static readonly Shape Triangle = CreateNgon(3, Vector2D.Up);
|
||||
public static readonly Shape Box = CreateNgon(4, Vector2D.One);
|
||||
public static readonly Shape Pentagon = CreateNgon(5, Vector2D.Up);
|
||||
public static readonly Shape Hexagon = CreateNgon(6, Vector2D.Right);
|
||||
|
||||
|
||||
public Vector2D this[System.Index index] => Vertices[index];
|
||||
|
||||
public static Shape CreateCopy(Shape shape) => new(new List<Vector2D>(shape.Vertices));
|
||||
|
||||
public static Shape CreateNgon(int vertexCount) => CreateNgon(vertexCount, Vector2D.Up);
|
||||
public static Shape CreateNgon(int vertexCount, Vector2D positionToRotate)
|
||||
{
|
||||
if (vertexCount < 3)
|
||||
throw new System.ArgumentException($"{nameof(vertexCount)} must have a value of more than 2.");
|
||||
|
||||
List<Vector2D> vertices = new(vertexCount);
|
||||
|
||||
float radiansPerVertex = 360f / vertexCount * Math.DegreeToRadian;
|
||||
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
vertices.Add(positionToRotate.Rotate(i * radiansPerVertex));
|
||||
|
||||
return new(vertices);
|
||||
}
|
||||
|
||||
public static Triangle GetSuperTriangle(Shape shape)
|
||||
{
|
||||
@@ -18,15 +41,15 @@ public record Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
|
||||
|
||||
foreach (Vector2D point in shape.Vertices)
|
||||
{
|
||||
minX = MathF.Min(minX, point.X);
|
||||
minY = MathF.Min(minY, point.Y);
|
||||
maxX = MathF.Max(maxX, point.X);
|
||||
maxY = MathF.Max(maxY, point.Y);
|
||||
minX = Math.Min(minX, point.X);
|
||||
minY = Math.Min(minY, point.Y);
|
||||
maxX = Math.Max(maxX, point.X);
|
||||
maxY = Math.Max(maxY, point.Y);
|
||||
}
|
||||
|
||||
float dx = maxX - minX;
|
||||
float dy = maxY - minY;
|
||||
float deltaMax = MathF.Max(dx, dy);
|
||||
float deltaMax = Math.Max(dx, dy);
|
||||
float midX = (minX + maxX) / 2;
|
||||
float midY = (minY + maxY) / 2;
|
||||
|
||||
@@ -52,6 +75,30 @@ public record Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
|
||||
return lines;
|
||||
}
|
||||
|
||||
public static void Project(Shape shape, Vector2D projectionVector, IList<float> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
int count = shape.Vertices.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
list.Add(projectionVector.Dot(shape[i]));
|
||||
}
|
||||
|
||||
public static Projection Project(Shape shape, Vector2D projectionVector)
|
||||
{
|
||||
float min = float.MaxValue;
|
||||
float max = float.MinValue;
|
||||
|
||||
foreach (var vertex in shape)
|
||||
{
|
||||
float projectedLength = projectionVector.Dot(vertex);
|
||||
min = Math.Min(projectedLength, min);
|
||||
max = Math.Max(projectedLength, max);
|
||||
}
|
||||
|
||||
return new(min, max);
|
||||
}
|
||||
|
||||
public static Shape TransformShape(Shape shape, ITransform transform)
|
||||
{
|
||||
List<Vector2D> vertices = new(shape.Vertices.Count);
|
||||
@@ -90,10 +137,14 @@ public record Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
|
||||
|
||||
public static class ShapeExtensions
|
||||
{
|
||||
public static Shape CreateCopy(Shape shape) => Shape.CreateCopy(shape);
|
||||
public static Triangle ToSuperTriangle(this Shape shape) => Shape.GetSuperTriangle(shape);
|
||||
public static void ToLines(this Shape shape, IList<Line> lines) => Shape.GetLines(shape, lines);
|
||||
public static List<Line> ToLines(this Shape shape) => Shape.GetLines(shape);
|
||||
|
||||
public static void ToProjection(this Shape shape, Vector2D projectionVector, IList<float> list) => Shape.Project(shape, projectionVector, list);
|
||||
public static Projection ToProjection(this Shape shape, Vector2D projectionVector) => Shape.Project(shape, projectionVector);
|
||||
|
||||
public static Shape TransformShape(this ITransform transform, Shape shape) => Shape.TransformShape(shape, transform);
|
||||
public static void TransformShape(this ITransform transform, Shape from, ref Shape to) => Shape.TransformShape(from, transform, ref to);
|
||||
|
||||
|
Reference in New Issue
Block a user