Compare commits
	
		
			17 Commits
		
	
	
		
			c32add40ff
			...
			6a104d8abd
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 6a104d8abd | |||
| affd2bb8c4 | |||
| 15788f2eca | |||
| ab2489f6cf | |||
| 574104c224 | |||
| 0d4c96a2fc | |||
| 5620f2f1eb | |||
| a3c4afb223 | |||
| 4d9121118d | |||
| 05d88f7ca2 | |||
| 309c8db6e1 | |||
| 0ba6913a61 | |||
| 9556be6f17 | |||
| bd43d39367 | |||
| 32e2a6e7d3 | |||
| 7b47703ba0 | |||
| b14d10db0c | 
@@ -5,6 +5,7 @@ using Syntriax.Engine.Core.Exceptions;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Core;
 | 
			
		||||
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("Priority: {Priority}, Initialized: {Initialized}")]
 | 
			
		||||
public abstract class Behaviour : IBehaviour
 | 
			
		||||
{
 | 
			
		||||
    public Action<IAssignable>? OnUnassigned { get; set; } = null;
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ using Syntriax.Engine.Core.Abstract;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Core;
 | 
			
		||||
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count()}")]
 | 
			
		||||
public class BehaviourController : IBehaviourController
 | 
			
		||||
{
 | 
			
		||||
    public Action<IBehaviourController>? OnPreUpdate { get; set; }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,8 +2,8 @@ using System;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Core;
 | 
			
		||||
 | 
			
		||||
public record EngineTime
 | 
			
		||||
(
 | 
			
		||||
    TimeSpan Total,
 | 
			
		||||
    TimeSpan Elapsed
 | 
			
		||||
);
 | 
			
		||||
public readonly struct EngineTime(TimeSpan Total, TimeSpan Elapsed)
 | 
			
		||||
{
 | 
			
		||||
    public readonly TimeSpan Total { get; init; } = Total;
 | 
			
		||||
    public readonly TimeSpan Elapsed { get; init; } = Elapsed;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ using Syntriax.Engine.Core.Factory;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Core;
 | 
			
		||||
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("GameObject Count: {_gameObjects.Count()}")]
 | 
			
		||||
public class GameManager : IEntity, IEnumerable<IGameObject>
 | 
			
		||||
{
 | 
			
		||||
    public Action<GameManager>? OnCameraChanged { get; set; } = null;
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ using Syntriax.Engine.Core.Exceptions;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Core;
 | 
			
		||||
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("Name: {Name}, Initialized: {Initialized}")]
 | 
			
		||||
public class GameObject : IGameObject
 | 
			
		||||
{
 | 
			
		||||
    public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ using Syntriax.Engine.Core.Abstract;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Core;
 | 
			
		||||
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("Position: {Position.ToString(), nq}, Scale: {Scale.ToString(), nq}, Rotation: {Rotation}")]
 | 
			
		||||
public class Transform : ITransform
 | 
			
		||||
{
 | 
			
		||||
    public Action<ITransform>? OnPositionChanged { get; set; } = null;
 | 
			
		||||
 
 | 
			
		||||
@@ -3,11 +3,14 @@ using System;
 | 
			
		||||
namespace Syntriax.Engine.Core;
 | 
			
		||||
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized}")]
 | 
			
		||||
public record Vector2D(float X, float Y)
 | 
			
		||||
public readonly struct Vector2D(float X, float Y)
 | 
			
		||||
{
 | 
			
		||||
    public float Magnitude => Length(this);
 | 
			
		||||
    public float MagnitudeSquared => LengthSquared(this);
 | 
			
		||||
    public Vector2D Normalized => Normalize(this);
 | 
			
		||||
    public readonly float X { get; init; } = X;
 | 
			
		||||
    public readonly float Y { get; init; } = Y;
 | 
			
		||||
 | 
			
		||||
    public readonly float Magnitude => Length(this);
 | 
			
		||||
    public readonly float MagnitudeSquared => LengthSquared(this);
 | 
			
		||||
    public readonly Vector2D Normalized => Normalize(this);
 | 
			
		||||
 | 
			
		||||
    public readonly static Vector2D Up = new(0f, 1f);
 | 
			
		||||
    public readonly static Vector2D Down = new(0f, -1f);
 | 
			
		||||
@@ -22,6 +25,8 @@ public record Vector2D(float X, float Y)
 | 
			
		||||
    public static Vector2D operator *(Vector2D vector, float value) => new(vector.X * value, vector.Y * value);
 | 
			
		||||
    public static Vector2D operator *(float value, Vector2D vector) => new(vector.X * value, vector.Y * value);
 | 
			
		||||
    public static Vector2D operator /(Vector2D vector, float value) => new(vector.X / value, vector.Y / value);
 | 
			
		||||
    public static bool operator ==(Vector2D left, Vector2D right) => left.X == right.X && left.Y == right.Y;
 | 
			
		||||
    public static bool operator !=(Vector2D left, Vector2D right) => left.X != right.X || left.Y != right.Y;
 | 
			
		||||
 | 
			
		||||
    public static float Length(Vector2D vector) => MathF.Sqrt(LengthSquared(vector));
 | 
			
		||||
    public static float LengthSquared(Vector2D vector) => vector.X * vector.X + vector.Y * vector.Y;
 | 
			
		||||
@@ -72,4 +77,7 @@ public record Vector2D(float X, float Y)
 | 
			
		||||
        => left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon);
 | 
			
		||||
 | 
			
		||||
    public override string ToString() => $"{nameof(Vector2D)}({X}, {Y})";
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object? obj) => obj is Vector2D objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y);
 | 
			
		||||
    public override int GetHashCode() => HashCode.Combine(X, Y);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,10 +6,13 @@ namespace Syntriax.Engine.Physics2D.Abstract;
 | 
			
		||||
 | 
			
		||||
public interface ICollider2D : IBehaviour, IAssignableTransform
 | 
			
		||||
{
 | 
			
		||||
    Action<ICollider2D, ICollider2D>? OnCollisionPreResolve { get; set; }
 | 
			
		||||
    Action<ICollider2D, ICollider2D>? OnCollisionResolved { get; set; }
 | 
			
		||||
    Action<ICollider2D, CollisionDetectionInformation>? OnCollisionDetected { get; set; }
 | 
			
		||||
    Action<ICollider2D, CollisionDetectionInformation>? OnCollisionResolved { get; set; }
 | 
			
		||||
 | 
			
		||||
    Action<ICollider2D, ICollider2D>? OnTriggered { get; set; }
 | 
			
		||||
 | 
			
		||||
    IRigidBody2D? RigidBody2D { get; }
 | 
			
		||||
    bool IsTrigger { get; set; }
 | 
			
		||||
 | 
			
		||||
    void Recalculate();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								Engine.Physics2D/Abstract/ICollisionDetector2D.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Engine.Physics2D/Abstract/ICollisionDetector2D.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
using Syntriax.Engine.Physics2D.Abstract;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Physics2D;
 | 
			
		||||
 | 
			
		||||
public interface ICollisionDetector2D
 | 
			
		||||
{
 | 
			
		||||
    bool TryDetect<T1, T2>(T1 left, T2 right, out CollisionDetectionInformation collisionInformation) where T1 : ICollider2D where T2 : ICollider2D;
 | 
			
		||||
}
 | 
			
		||||
@@ -2,5 +2,5 @@ namespace Syntriax.Engine.Physics2D.Abstract;
 | 
			
		||||
 | 
			
		||||
public interface ICollisionResolver2D
 | 
			
		||||
{
 | 
			
		||||
    void ResolveCollision(ICollider2D colliderA, ICollider2D colliderB);
 | 
			
		||||
    void Resolve(CollisionDetectionInformation collisionInformation);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,17 +8,20 @@ namespace Syntriax.Engine.Physics2D;
 | 
			
		||||
 | 
			
		||||
public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D
 | 
			
		||||
{
 | 
			
		||||
    public Action<ICollider2D, ICollider2D>? OnCollisionPreResolve { get; set; } = null;
 | 
			
		||||
    public Action<ICollider2D, ICollider2D>? OnCollisionResolved { get; set; } = null;
 | 
			
		||||
    public Action<ICollider2D, CollisionDetectionInformation>? OnCollisionDetected { get; set; } = null;
 | 
			
		||||
    public Action<ICollider2D, CollisionDetectionInformation>? OnCollisionResolved { get; set; } = null;
 | 
			
		||||
    public Action<ICollider2D, ICollider2D>? OnTriggered { get; set; } = null;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    protected bool NeedsRecalculation { get; private set; } = true;
 | 
			
		||||
    protected IRigidBody2D? _rigidBody2D = null;
 | 
			
		||||
 | 
			
		||||
    public IRigidBody2D? RigidBody2D => _rigidBody2D;
 | 
			
		||||
    public bool IsTrigger { get; set; } = false;
 | 
			
		||||
 | 
			
		||||
    ITransform IAssignableTransform.Transform => Transform;
 | 
			
		||||
    Action<IAssignableTransform>? IAssignableTransform.OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; }
 | 
			
		||||
 | 
			
		||||
    bool IAssignableTransform.Assign(ITransform transform) => GameObject.Assign(transform);
 | 
			
		||||
 | 
			
		||||
    public void Recalculate()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
using Syntriax.Engine.Core;
 | 
			
		||||
using Syntriax.Engine.Physics2D.Abstract;
 | 
			
		||||
using Syntriax.Engine.Physics2D.Primitives;
 | 
			
		||||
 | 
			
		||||
@@ -6,9 +5,17 @@ namespace Syntriax.Engine.Physics2D;
 | 
			
		||||
 | 
			
		||||
public class Collider2DCircleBehaviour : Collider2DBehaviourBase, ICircleCollider2D
 | 
			
		||||
{
 | 
			
		||||
    public Circle CircleWorld { get; protected set; } = new(Vector2D.Zero, 1f);
 | 
			
		||||
    public Circle CircleLocal { get; set; } = new(Vector2D.Zero, 1f);
 | 
			
		||||
    public Circle CircleWorld { get; protected set; } = Circle.UnitCircle;
 | 
			
		||||
    public Circle CircleLocal { get; set; } = Circle.UnitCircle;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public override void CalculateCollider() => CircleWorld = Transform.TransformCircle(CircleLocal);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public Collider2DCircleBehaviour() { }
 | 
			
		||||
    public Collider2DCircleBehaviour(Circle circle)
 | 
			
		||||
    {
 | 
			
		||||
        CircleLocal = circle;
 | 
			
		||||
        Recalculate();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,10 +5,18 @@ namespace Syntriax.Engine.Physics2D;
 | 
			
		||||
 | 
			
		||||
public class Collider2DShapeBehaviour : Collider2DBehaviourBase, IShapeCollider2D
 | 
			
		||||
{
 | 
			
		||||
    public Shape ShapeWorld => _shapeWorld;
 | 
			
		||||
    public Shape ShapeLocal { get; set; } = new([new(1f, 1f), new(-1f, 1f), new(-1f, -1f), new(1f, -1f)]);
 | 
			
		||||
    public Shape ShapeWorld { get => _shapeWorld; protected set => _shapeWorld = value; }
 | 
			
		||||
    public Shape ShapeLocal { get; set; } = Shape.Box;
 | 
			
		||||
 | 
			
		||||
    protected Shape _shapeWorld = new([new(1f, 1f), new(-1f, 1f), new(-1f, -1f), new(1f, -1f)]);
 | 
			
		||||
    private Shape _shapeWorld = Shape.Box;
 | 
			
		||||
 | 
			
		||||
    public override void CalculateCollider() => Transform.TransformShape(ShapeLocal, ref _shapeWorld);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public Collider2DShapeBehaviour() { }
 | 
			
		||||
    public Collider2DShapeBehaviour(Shape shape)
 | 
			
		||||
    {
 | 
			
		||||
        ShapeLocal = shape;
 | 
			
		||||
        Recalculate();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,10 +3,17 @@ using Syntriax.Engine.Physics2D.Abstract;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Physics2D;
 | 
			
		||||
 | 
			
		||||
public record CollisionDetectionInformation
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("Normal: {Normal.ToString(), nq}, Penetration: {Penetration}")]
 | 
			
		||||
public readonly struct CollisionDetectionInformation
 | 
			
		||||
(
 | 
			
		||||
    ICollider2D Left,
 | 
			
		||||
    ICollider2D Right,
 | 
			
		||||
    Vector2D Normal,
 | 
			
		||||
    float Penetration
 | 
			
		||||
);
 | 
			
		||||
)
 | 
			
		||||
{
 | 
			
		||||
    public ICollider2D Left { get; init; } = Left;
 | 
			
		||||
    public ICollider2D Right { get; init; } = Right;
 | 
			
		||||
    public Vector2D Normal { get; init; } = Normal;
 | 
			
		||||
    public float Penetration { get; init; } = Penetration;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,12 @@
 | 
			
		||||
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 class CollisionDetector2D : ICollisionDetector2D
 | 
			
		||||
{
 | 
			
		||||
    public bool TryDetect<T1, T2>(T1 left, T2 right, [NotNullWhen(true)] out CollisionDetectionInformation? collisionInformation)
 | 
			
		||||
    public bool TryDetect<T1, T2>(T1 left, T2 right, out CollisionDetectionInformation collisionInformation)
 | 
			
		||||
        where T1 : ICollider2D
 | 
			
		||||
        where T2 : ICollider2D
 | 
			
		||||
    {
 | 
			
		||||
@@ -31,18 +29,18 @@ public class CollisionDetector : ICollisionDetector
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static bool DetectCircleShape(ICircleCollider2D circleCollider, IShapeCollider2D shapeCollider, out CollisionDetectionInformation? collisionInformation)
 | 
			
		||||
    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)
 | 
			
		||||
    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)
 | 
			
		||||
    private static bool DetectShapeShapeOneWay(IShapeCollider2D left, IShapeCollider2D right, ref CollisionDetectionInformation collisionInformation)
 | 
			
		||||
    {
 | 
			
		||||
        var vertices = left.ShapeWorld.Vertices;
 | 
			
		||||
        int count = vertices.Count;
 | 
			
		||||
@@ -57,30 +55,17 @@ public class CollisionDetector : ICollisionDetector
 | 
			
		||||
            if (!leftProjection.Overlaps(rightProjection, out float depth))
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            if (collisionInformation == default || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth))
 | 
			
		||||
            if (collisionInformation.Left is null || 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)
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
@@ -94,14 +79,27 @@ public class CollisionDetector : ICollisionDetector
 | 
			
		||||
            if (!shapeProjection.Overlaps(circleProjection, out float depth))
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            if (collisionInformation == default || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth))
 | 
			
		||||
            if (collisionInformation.Left is null || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth))
 | 
			
		||||
                collisionInformation = new(shapeCollider, circleCollider, projectionVector, depth);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            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.Left is null || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth))
 | 
			
		||||
                collisionInformation = new(shapeCollider, circleCollider, shapeToCircleProjectionVector, depth);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static bool DetectCircleCircle(ICircleCollider2D left, ICircleCollider2D right, out CollisionDetectionInformation? collisionInformation)
 | 
			
		||||
    private static bool DetectCircleCircle(ICircleCollider2D left, ICircleCollider2D right, out CollisionDetectionInformation collisionInformation)
 | 
			
		||||
    {
 | 
			
		||||
        collisionInformation = default;
 | 
			
		||||
 | 
			
		||||
@@ -118,7 +116,7 @@ public class CollisionDetector : ICollisionDetector
 | 
			
		||||
        return collision;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // private static bool DetectCircleCircle(ICircleCollider2D left, ICircleCollider2D right, out CollisionDetectionInformation? collisionInformation)
 | 
			
		||||
    // private static bool DetectCircleCircle(ICircleCollider2D left, ICircleCollider2D right, out CollisionDetectionInformation collisionInformation)
 | 
			
		||||
    // {
 | 
			
		||||
    //     collisionInformation = default;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										44
									
								
								Engine.Physics2D/CollisionResolver2D.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								Engine.Physics2D/CollisionResolver2D.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
using Syntriax.Engine.Core;
 | 
			
		||||
using Syntriax.Engine.Physics2D.Abstract;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Physics2D;
 | 
			
		||||
 | 
			
		||||
public class CollisionResolver2D : ICollisionResolver2D
 | 
			
		||||
{
 | 
			
		||||
    public void Resolve(CollisionDetectionInformation collisionInformation)
 | 
			
		||||
    {
 | 
			
		||||
        Vector2D displacementVector = collisionInformation.Normal * collisionInformation.Penetration;
 | 
			
		||||
 | 
			
		||||
        ICollider2D left = collisionInformation.Left;
 | 
			
		||||
        ICollider2D right = collisionInformation.Right;
 | 
			
		||||
 | 
			
		||||
        bool isLeftStatic = left.RigidBody2D?.IsStatic ?? true;
 | 
			
		||||
        bool isRightStatic = right.RigidBody2D?.IsStatic ?? true;
 | 
			
		||||
 | 
			
		||||
        if (isLeftStatic && isRightStatic)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (isLeftStatic)
 | 
			
		||||
            right.Transform.Position += displacementVector;
 | 
			
		||||
        else if (isRightStatic)
 | 
			
		||||
            left.Transform.Position -= displacementVector;
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            float leftMass = left.RigidBody2D?.Mass ?? float.Epsilon;
 | 
			
		||||
            float rightMass = right.RigidBody2D?.Mass ?? float.Epsilon;
 | 
			
		||||
            float sumMass = leftMass + rightMass;
 | 
			
		||||
 | 
			
		||||
            float leftMomentumPercentage = leftMass / sumMass;
 | 
			
		||||
            float rightMomentumPercentage = rightMass / sumMass;
 | 
			
		||||
 | 
			
		||||
            right.Transform.Position += leftMomentumPercentage * displacementVector;
 | 
			
		||||
            left.Transform.Position -= rightMomentumPercentage * displacementVector;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        left.Recalculate();
 | 
			
		||||
        right.Recalculate();
 | 
			
		||||
 | 
			
		||||
        left.OnCollisionResolved?.Invoke(collisionInformation.Left, collisionInformation);
 | 
			
		||||
        right.OnCollisionResolved?.Invoke(right, collisionInformation);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
using System.Diagnostics.CodeAnalysis;
 | 
			
		||||
 | 
			
		||||
using Syntriax.Engine.Physics2D.Abstract;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Physics2D;
 | 
			
		||||
 | 
			
		||||
public interface ICollisionDetector
 | 
			
		||||
{
 | 
			
		||||
    bool TryDetect<T1, T2>(T1 left, T2 right, [NotNullWhen(returnValue: true)] out CollisionDetectionInformation? collisionInformation) where T1 : ICollider2D where T2 : ICollider2D;
 | 
			
		||||
}
 | 
			
		||||
@@ -12,7 +12,8 @@ public class PhysicsEngine2D : IPhysicsEngine2D
 | 
			
		||||
    private readonly List<ICollider2D> colliders = new(64);
 | 
			
		||||
 | 
			
		||||
    private int _iterationCount = 1;
 | 
			
		||||
    private ICollisionDetector collisionDetector = new CollisionDetector();
 | 
			
		||||
    private ICollisionDetector2D collisionDetector = new CollisionDetector2D();
 | 
			
		||||
    private ICollisionResolver2D collisionResolver = new CollisionResolver2D();
 | 
			
		||||
 | 
			
		||||
    public int IterationCount { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; }
 | 
			
		||||
 | 
			
		||||
@@ -60,13 +61,27 @@ public class PhysicsEngine2D : IPhysicsEngine2D
 | 
			
		||||
                    if (colliderX.RigidBody2D == colliderY.RigidBody2D)
 | 
			
		||||
                        continue;
 | 
			
		||||
 | 
			
		||||
                    if (collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation? information))
 | 
			
		||||
                    bool bothCollidersAreTriggers = colliderX.IsTrigger && colliderX.IsTrigger == colliderY.IsTrigger;
 | 
			
		||||
                    if (bothCollidersAreTriggers)
 | 
			
		||||
                        continue;
 | 
			
		||||
 | 
			
		||||
                    if (collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation information))
 | 
			
		||||
                    {
 | 
			
		||||
                        Vector2D displacementVector = .5f * information.Normal * information.Penetration;
 | 
			
		||||
                        information.Left.Transform.Position -= displacementVector;
 | 
			
		||||
                        information.Right.Transform.Position += displacementVector;
 | 
			
		||||
                        information.Left.Recalculate();
 | 
			
		||||
                        information.Right.Recalculate();
 | 
			
		||||
                        if (colliderX.IsTrigger)
 | 
			
		||||
                        {
 | 
			
		||||
                            colliderX.OnTriggered?.Invoke(colliderX, colliderY);
 | 
			
		||||
                            continue;
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (colliderY.IsTrigger)
 | 
			
		||||
                        {
 | 
			
		||||
                            colliderY.OnTriggered?.Invoke(colliderY, colliderY);
 | 
			
		||||
                            continue;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        colliderX.OnCollisionDetected?.Invoke(colliderX, information);
 | 
			
		||||
                        colliderY.OnCollisionDetected?.Invoke(colliderY, information);
 | 
			
		||||
 | 
			
		||||
                        collisionResolver?.Resolve(information);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -75,6 +90,9 @@ public class PhysicsEngine2D : IPhysicsEngine2D
 | 
			
		||||
 | 
			
		||||
    private static void StepRigidBody(IRigidBody2D rigidBody, float intervalDeltaTime)
 | 
			
		||||
    {
 | 
			
		||||
        if (rigidBody.IsStatic)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        rigidBody.Transform.Position += rigidBody.Velocity * intervalDeltaTime;
 | 
			
		||||
        rigidBody.Transform.Rotation += rigidBody.AngularVelocity * intervalDeltaTime;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,4 +2,8 @@ using Syntriax.Engine.Physics2D.Abstract;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Physics2D;
 | 
			
		||||
 | 
			
		||||
public record PhysicsMaterial2D(float Friction, float Restitution) : IPhysicsMaterial2D { }
 | 
			
		||||
public readonly struct PhysicsMaterial2D(float Friction, float Restitution) : IPhysicsMaterial2D
 | 
			
		||||
{
 | 
			
		||||
    public readonly float Friction { get; init; } = Friction;
 | 
			
		||||
    public readonly float Restitution { get; init; } = Restitution;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,10 @@
 | 
			
		||||
using Syntriax.Engine.Physics2D.Abstract;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Physics2D;
 | 
			
		||||
 | 
			
		||||
public record PhysicsMaterial2DDefault : PhysicsMaterial2D
 | 
			
		||||
public readonly struct PhysicsMaterial2DDefault : IPhysicsMaterial2D
 | 
			
		||||
{
 | 
			
		||||
    public PhysicsMaterial2DDefault() : base(.1f, .1f) { }
 | 
			
		||||
    public readonly float Friction => .1f;
 | 
			
		||||
 | 
			
		||||
    public readonly float Restitution => .1f;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,11 +4,15 @@ using Syntriax.Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Physics2D.Primitives;
 | 
			
		||||
 | 
			
		||||
public record AABB(Vector2D LowerBoundary, Vector2D UpperBoundary)
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("LowerBoundary: {LowerBoundary.ToString(), nq}, UpperBoundary: {UpperBoundary.ToString(), nq}")]
 | 
			
		||||
public readonly struct AABB(Vector2D LowerBoundary, Vector2D UpperBoundary)
 | 
			
		||||
{
 | 
			
		||||
    public Vector2D Center => (LowerBoundary + UpperBoundary) * .5f;
 | 
			
		||||
    public Vector2D Size => LowerBoundary.FromTo(UpperBoundary).Abs();
 | 
			
		||||
    public Vector2D SizeHalf => Size * .5f;
 | 
			
		||||
    public readonly Vector2D LowerBoundary { get; init; } = LowerBoundary;
 | 
			
		||||
    public readonly Vector2D UpperBoundary { get; init; } = UpperBoundary;
 | 
			
		||||
 | 
			
		||||
    public readonly Vector2D Center => (LowerBoundary + UpperBoundary) * .5f;
 | 
			
		||||
    public readonly Vector2D Size => LowerBoundary.FromTo(UpperBoundary).Abs();
 | 
			
		||||
    public readonly Vector2D SizeHalf => Size * .5f;
 | 
			
		||||
 | 
			
		||||
    public static AABB FromVectors(IEnumerable<Vector2D> vectors)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,10 +3,16 @@ using Syntriax.Engine.Core.Abstract;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Physics2D.Primitives;
 | 
			
		||||
 | 
			
		||||
public record Circle(Vector2D Center, float Radius)
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("Center: {Center.ToString(), nq}, Radius: {Radius}")]
 | 
			
		||||
public readonly struct Circle(Vector2D Center, float Radius)
 | 
			
		||||
{
 | 
			
		||||
    public float RadiusSquared => Radius * Radius;
 | 
			
		||||
    public float Diameter => 2f * Radius;
 | 
			
		||||
    public static readonly Circle UnitCircle = new(Vector2D.Zero, 1f);
 | 
			
		||||
 | 
			
		||||
    public readonly Vector2D Center { get; init; } = Center;
 | 
			
		||||
    public readonly float Radius { get; init; } = Radius;
 | 
			
		||||
 | 
			
		||||
    public readonly float RadiusSquared => Radius * Radius;
 | 
			
		||||
    public readonly float Diameter => 2f * Radius;
 | 
			
		||||
 | 
			
		||||
    public static Circle SetCenter(Circle circle, Vector2D center) => new(center, circle.Radius);
 | 
			
		||||
    public static Circle SetRadius(Circle circle, float radius) => new(circle.Center, radius);
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,16 @@ using Syntriax.Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Physics2D.Primitives;
 | 
			
		||||
 | 
			
		||||
public record Line(Vector2D From, Vector2D To)
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("From: {From.ToString(), nq}, To: {To.ToString(), nq}, Direction: {Direction.ToString(), nq}, Length: {Length}")]
 | 
			
		||||
public readonly struct Line(Vector2D From, Vector2D To)
 | 
			
		||||
{
 | 
			
		||||
    public Line Reversed => new(To, From);
 | 
			
		||||
    public Vector2D Direction => From.FromTo(To).Normalize();
 | 
			
		||||
    public float Length => From.FromTo(To).Length();
 | 
			
		||||
    public float LengthSquared => From.FromTo(To).LengthSquared();
 | 
			
		||||
    public readonly Vector2D From { get; init; } = From;
 | 
			
		||||
    public readonly Vector2D To { get; init; } = To;
 | 
			
		||||
 | 
			
		||||
    public readonly Line Reversed => new(To, From);
 | 
			
		||||
    public readonly Vector2D Direction => From.FromTo(To).Normalize();
 | 
			
		||||
    public readonly float Length => From.FromTo(To).Length();
 | 
			
		||||
    public readonly float LengthSquared => From.FromTo(To).LengthSquared();
 | 
			
		||||
 | 
			
		||||
    public static LineEquation GetLineEquation(Line line)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,8 +2,12 @@ using Syntriax.Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Physics2D.Primitives;
 | 
			
		||||
 | 
			
		||||
public record LineEquation(float Slope, float OffsetY)
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("y = {Slope}x + {OffsetY}")]
 | 
			
		||||
public readonly struct LineEquation(float Slope, float OffsetY)
 | 
			
		||||
{
 | 
			
		||||
    public readonly float Slope { get; init; } = Slope;
 | 
			
		||||
    public readonly float OffsetY { get; init; } = OffsetY;
 | 
			
		||||
 | 
			
		||||
    public static float Resolve(LineEquation lineEquation, float x) => lineEquation.Slope * x + lineEquation.OffsetY; // y = mx + b
 | 
			
		||||
 | 
			
		||||
    public static bool ApproximatelyEquals(LineEquation left, LineEquation right)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,11 @@
 | 
			
		||||
namespace Syntriax.Engine.Physics2D.Primitives;
 | 
			
		||||
 | 
			
		||||
public record Projection(float Min, float Max)
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("Min: {Min}, Max: {Max}")]
 | 
			
		||||
public readonly struct Projection(float Min, float Max)
 | 
			
		||||
{
 | 
			
		||||
    public readonly float Min { get; init; } = Min;
 | 
			
		||||
    public readonly float Max { get; init; } = 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)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,13 +6,16 @@ using Syntriax.Engine.Core.Abstract;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Physics2D.Primitives;
 | 
			
		||||
 | 
			
		||||
public record Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("Vertices Count: {Vertices.Count()}")]
 | 
			
		||||
public readonly struct Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
 | 
			
		||||
{
 | 
			
		||||
    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 readonly IList<Vector2D> Vertices { get; init; } = Vertices;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public Vector2D this[System.Index index] => Vertices[index];
 | 
			
		||||
 | 
			
		||||
@@ -89,9 +92,9 @@ public record Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
 | 
			
		||||
        float min = float.MaxValue;
 | 
			
		||||
        float max = float.MinValue;
 | 
			
		||||
 | 
			
		||||
        foreach (var vertex in shape)
 | 
			
		||||
        for (int i = 0; i < shape.Vertices.Count; i++)
 | 
			
		||||
        {
 | 
			
		||||
            float projectedLength = projectionVector.Dot(vertex);
 | 
			
		||||
            float projectedLength = projectionVector.Dot(shape.Vertices[i]);
 | 
			
		||||
            min = Math.Min(projectedLength, min);
 | 
			
		||||
            max = Math.Max(projectedLength, max);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -4,9 +4,14 @@ using Syntriax.Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Physics2D.Primitives;
 | 
			
		||||
 | 
			
		||||
public record Triangle(Vector2D A, Vector2D B, Vector2D C)
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("A: {A.ToString(), nq}, B: {B.ToString(), nq}, B: {C.ToString(), nq}")]
 | 
			
		||||
public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
 | 
			
		||||
{
 | 
			
		||||
    public float Area
 | 
			
		||||
    public readonly Vector2D A { get; init; } = A;
 | 
			
		||||
    public readonly Vector2D B { get; init; } = B;
 | 
			
		||||
    public readonly Vector2D C { get; init; } = C;
 | 
			
		||||
 | 
			
		||||
    public readonly float Area
 | 
			
		||||
        => .5f * MathF.Abs(
 | 
			
		||||
                A.X * (B.Y - C.Y) +
 | 
			
		||||
                B.X * (C.Y - A.Y) +
 | 
			
		||||
 
 | 
			
		||||
@@ -10,14 +10,18 @@ public class RigidBody2D : BehaviourOverride, IRigidBody2D
 | 
			
		||||
{
 | 
			
		||||
    public Action<IAssignableTransform>? OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; }
 | 
			
		||||
 | 
			
		||||
    private const float LOWEST_ALLOWED_MASS = 0.00001f;
 | 
			
		||||
    private float _mass = 1f;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public IPhysicsMaterial2D Material { get; set; } = new PhysicsMaterial2DDefault();
 | 
			
		||||
 | 
			
		||||
    public Vector2D Velocity { get; set; } = Vector2D.Zero;
 | 
			
		||||
    public float AngularVelocity { get; set; } = 0f;
 | 
			
		||||
    public float Mass { get; set; } = 1f;
 | 
			
		||||
    public bool IsStatic { get; set; } = false;
 | 
			
		||||
 | 
			
		||||
    public float Mass { get => _mass; set => _mass = Core.Math.Max(value, LOWEST_ALLOWED_MASS); }
 | 
			
		||||
 | 
			
		||||
    ITransform IAssignableTransform.Transform => Transform;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user