Compare commits
17 Commits
c7f63dc638
...
main
Author | SHA1 | Date | |
---|---|---|---|
29a7f5880f | |||
eee3056614 | |||
152b0e93db | |||
3f914fe46f | |||
62b54ee836 | |||
6a41407005 | |||
adfa6c6ba0 | |||
a53766f472 | |||
40735c713a | |||
2054ae3a35 | |||
9066e11c12 | |||
f16a7e55c9 | |||
e3b32b3c4a | |||
a02584f3b6 | |||
45524e474e | |||
fbdea47dc7 | |||
f5fbd4e5ef |
@@ -20,6 +20,26 @@ public interface ITransform2D : IBehaviour
|
||||
/// </summary>
|
||||
Event<ITransform2D, RotationChangedArguments> OnRotationChanged { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Vector2D"/> pointing upwards in world space.
|
||||
/// </summary>
|
||||
Vector2D Up { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Vector2D"/> pointing upwards in world space.
|
||||
/// </summary>
|
||||
Vector2D Down { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Vector2D"/> pointing upwards in world space.
|
||||
/// </summary>
|
||||
Vector2D Left { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Vector2D"/> pointing upwards in world space.
|
||||
/// </summary>
|
||||
Vector2D Right { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The world position of the <see cref="ITransform2D"/> in 2D space.
|
||||
/// </summary>
|
||||
|
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public class CoroutineManager : Behaviour
|
||||
public class CoroutineManager : Behaviour, IUpdate
|
||||
{
|
||||
private readonly List<IEnumerator> enumerators = [];
|
||||
|
||||
@@ -18,17 +18,7 @@ public class CoroutineManager : Behaviour
|
||||
enumerators.Remove(enumerator);
|
||||
}
|
||||
|
||||
protected override void OnEnteredUniverse(IUniverse universe)
|
||||
{
|
||||
universe.OnUpdate.AddListener(OnUpdate);
|
||||
}
|
||||
|
||||
protected override void OnExitedUniverse(IUniverse universe)
|
||||
{
|
||||
universe.OnUpdate.RemoveListener(OnUpdate);
|
||||
}
|
||||
|
||||
private void OnUpdate(IUniverse sender, IUniverse.UpdateArguments args)
|
||||
void IUpdate.Update()
|
||||
{
|
||||
for (int i = enumerators.Count - 1; i >= 0; i--)
|
||||
{
|
||||
@@ -39,4 +29,6 @@ public class CoroutineManager : Behaviour
|
||||
enumerators.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
public CoroutineManager() => Priority = int.MinValue;
|
||||
}
|
||||
|
@@ -1,18 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public static class DelegateExtensions
|
||||
{
|
||||
[Obsolete($"{nameof(InvokeSafe)} causes memory allocation, please use Invoke() instead.")]
|
||||
public static void InvokeSafe(this Delegate @delegate, params object?[] args)
|
||||
{
|
||||
foreach (Delegate invocation in Delegate.EnumerateInvocationList(@delegate))
|
||||
try { invocation.DynamicInvoke(args); }
|
||||
catch (Exception exception)
|
||||
{
|
||||
string methodCallRepresentation = $"{invocation.Method.DeclaringType?.FullName}.{invocation.Method.Name}({string.Join(", ", args)})";
|
||||
Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}");
|
||||
}
|
||||
}
|
||||
}
|
@@ -5,8 +5,8 @@ namespace Syntriax.Engine.Core;
|
||||
|
||||
public class Event
|
||||
{
|
||||
private readonly List<EventHandler> listeners = new(4);
|
||||
private readonly List<EventHandler> onceListeners = new(2);
|
||||
private readonly List<EventHandler> listeners = null!;
|
||||
private readonly List<EventHandler> onceListeners = null!;
|
||||
|
||||
public void AddListener(EventHandler listener) => listeners.Add(listener);
|
||||
public void AddOnceListener(EventHandler listener) => onceListeners.Add(listener);
|
||||
@@ -35,13 +35,25 @@ public class Event
|
||||
}
|
||||
}
|
||||
|
||||
public Event(int initialListenerCount = 4, int initialOnceListenerCount = 2)
|
||||
{
|
||||
listeners = new(initialListenerCount);
|
||||
onceListeners = new(initialOnceListenerCount);
|
||||
}
|
||||
|
||||
public Event()
|
||||
{
|
||||
listeners = new(4);
|
||||
onceListeners = new(2);
|
||||
}
|
||||
|
||||
public delegate void EventHandler();
|
||||
}
|
||||
|
||||
public class Event<TSender>
|
||||
{
|
||||
private readonly List<EventHandler> listeners = new(4);
|
||||
private readonly List<EventHandler> onceListeners = new(2);
|
||||
private readonly List<EventHandler> listeners = null!;
|
||||
private readonly List<EventHandler> onceListeners = null!;
|
||||
|
||||
public void AddListener(EventHandler listener) => listeners.Add(listener);
|
||||
public void AddOnceListener(EventHandler listener) => onceListeners.Add(listener);
|
||||
@@ -70,13 +82,25 @@ public class Event<TSender>
|
||||
}
|
||||
}
|
||||
|
||||
public Event(int initialListenerCount = 4, int initialOnceListenerCount = 2)
|
||||
{
|
||||
listeners = new(initialListenerCount);
|
||||
onceListeners = new(initialOnceListenerCount);
|
||||
}
|
||||
|
||||
public Event()
|
||||
{
|
||||
listeners = new(4);
|
||||
onceListeners = new(2);
|
||||
}
|
||||
|
||||
public delegate void EventHandler(TSender sender);
|
||||
}
|
||||
|
||||
public class Event<TSender, TArguments>
|
||||
{
|
||||
private readonly List<EventHandler> listeners = new(4);
|
||||
private readonly List<EventHandler> onceListeners = new(2);
|
||||
private readonly List<EventHandler> listeners = null!;
|
||||
private readonly List<EventHandler> onceListeners = null!;
|
||||
|
||||
public void AddListener(EventHandler listener) => listeners.Add(listener);
|
||||
public void AddOnceListener(EventHandler listener) => onceListeners.Add(listener);
|
||||
@@ -105,5 +129,17 @@ public class Event<TSender, TArguments>
|
||||
}
|
||||
}
|
||||
|
||||
public Event(int initialListenerCount = 4, int initialOnceListenerCount = 2)
|
||||
{
|
||||
listeners = new(initialListenerCount);
|
||||
onceListeners = new(initialOnceListenerCount);
|
||||
}
|
||||
|
||||
public Event()
|
||||
{
|
||||
listeners = new(4);
|
||||
onceListeners = new(2);
|
||||
}
|
||||
|
||||
public delegate void EventHandler(TSender sender, TArguments args);
|
||||
}
|
||||
|
10
Engine.Core/Helpers/IPool.cs
Normal file
10
Engine.Core/Helpers/IPool.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public interface IPool<T>
|
||||
{
|
||||
Event<IPool<T>, T> OnRemoved { get; }
|
||||
Event<IPool<T>, T> OnReturned { get; }
|
||||
|
||||
T Get();
|
||||
void Return(T item);
|
||||
}
|
40
Engine.Core/Helpers/ListPool.cs
Normal file
40
Engine.Core/Helpers/ListPool.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public class ListPool<T> : IPool<List<T>>
|
||||
{
|
||||
public Event<IPool<List<T>>, List<T>> OnReturned { get; } = new();
|
||||
public Event<IPool<List<T>>, List<T>> OnRemoved { get; } = new();
|
||||
|
||||
private readonly Func<List<T>> generator = null!;
|
||||
private readonly Queue<List<T>> queue = new();
|
||||
|
||||
public List<T> Get()
|
||||
{
|
||||
if (!queue.TryDequeue(out List<T>? result))
|
||||
result = generator();
|
||||
|
||||
result.Clear();
|
||||
OnRemoved?.Invoke(this, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Return(List<T> list)
|
||||
{
|
||||
if (queue.Contains(list))
|
||||
return;
|
||||
|
||||
list.Clear();
|
||||
queue.Enqueue(list);
|
||||
OnReturned?.Invoke(this, list);
|
||||
}
|
||||
|
||||
public ListPool(Func<List<T>> generator, int initialCapacity = 1)
|
||||
{
|
||||
this.generator = generator;
|
||||
for (int i = 0; i < initialCapacity; i++)
|
||||
queue.Enqueue(generator());
|
||||
}
|
||||
}
|
38
Engine.Core/Helpers/Pool.cs
Normal file
38
Engine.Core/Helpers/Pool.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public class Pool<T> : IPool<T>
|
||||
{
|
||||
public Event<IPool<T>, T> OnRemoved { get; } = new();
|
||||
public Event<IPool<T>, T> OnReturned { get; } = new();
|
||||
|
||||
private readonly Func<T> generator = null!;
|
||||
private readonly Queue<T> queue = new();
|
||||
|
||||
public T Get()
|
||||
{
|
||||
if (!queue.TryDequeue(out T? result))
|
||||
result = generator();
|
||||
|
||||
OnRemoved?.Invoke(this, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Return(T item)
|
||||
{
|
||||
if (queue.Contains(item))
|
||||
return;
|
||||
|
||||
queue.Enqueue(item);
|
||||
OnReturned?.Invoke(this, item);
|
||||
}
|
||||
|
||||
public Pool(Func<T> generator, int initialCapacity = 1)
|
||||
{
|
||||
this.generator = generator;
|
||||
for (int i = 0; i < initialCapacity; i++)
|
||||
queue.Enqueue(generator());
|
||||
}
|
||||
}
|
@@ -167,17 +167,14 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
|
||||
/// </summary>
|
||||
public static Vector2D ClosestPointTo(Line2D line, Vector2D point)
|
||||
{
|
||||
Vector2D edgeVector = line.From.FromTo(line.To);
|
||||
Vector2D pointVector = point - line.From;
|
||||
Vector2D lineRelativeVector = line.From.FromTo(line.To);
|
||||
|
||||
float t = (pointVector.X * edgeVector.X + pointVector.Y * edgeVector.Y) / (edgeVector.X * edgeVector.X + edgeVector.Y * edgeVector.Y);
|
||||
Vector2D lineDirection = lineRelativeVector.Normalized;
|
||||
Vector2D pointVector = line.From.FromTo(point);
|
||||
|
||||
t = Math.Max(0, Math.Min(1, t));
|
||||
float dot = lineDirection.Dot(pointVector).Clamp(0f, lineRelativeVector.Magnitude);
|
||||
|
||||
float closestX = line.From.X + t * edgeVector.X;
|
||||
float closestY = line.From.Y + t * edgeVector.Y;
|
||||
|
||||
return new Vector2D((float)closestX, (float)closestY);
|
||||
return lineDirection * dot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -205,6 +202,9 @@ public static class Line2DExtensions
|
||||
/// <inheritdoc cref="Line2D.Intersects(Line2D, Vector2D)" />
|
||||
public static bool Intersects(this Line2D line, Vector2D point) => Line2D.Intersects(line, point);
|
||||
|
||||
/// <inheritdoc cref="Line2D.IntersectionPoint(Line2D, Line2D)" />
|
||||
public static Vector2D IntersectionPoint(this Line2D left, Line2D right) => Line2D.IntersectionPoint(left, right);
|
||||
|
||||
/// <inheritdoc cref="Line2D.GetT(Line2D, Vector2D)" />
|
||||
public static float GetT(this Line2D line, Vector2D point) => Line2D.GetT(line, point);
|
||||
|
||||
@@ -214,6 +214,9 @@ public static class Line2DExtensions
|
||||
/// <inheritdoc cref="Line2D.Intersects(Line2D, Line2D, out Vector2D?)" />
|
||||
public static bool Intersects(this Line2D left, Line2D right, [NotNullWhen(returnValue: true)] out Vector2D? point) => Line2D.Intersects(left, right, out point);
|
||||
|
||||
/// <inheritdoc cref="Line2D.ClosestPointTo(Line2D, Vector2D)" />
|
||||
public static Vector2D ClosestPointTo(this Line2D line, Vector2D point) => Line2D.ClosestPointTo(line, point);
|
||||
|
||||
/// <inheritdoc cref="Line2D.ApproximatelyEquals(Line2D, Line2D, float)" />
|
||||
public static bool ApproximatelyEquals(this Line2D left, Line2D right, float epsilon = float.Epsilon) => Line2D.ApproximatelyEquals(left, right, epsilon);
|
||||
}
|
||||
|
66
Engine.Core/Primitives/Ray2D.cs
Normal file
66
Engine.Core/Primitives/Ray2D.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
namespace Syntriax.Engine.Core;
|
||||
|
||||
public readonly struct Ray2D(Vector2D Origin, Vector2D Direction)
|
||||
{
|
||||
/// <summary>
|
||||
/// The starting point of the <see cref="Ray2D"/>.
|
||||
/// </summary>
|
||||
public readonly Vector2D Origin = Origin;
|
||||
|
||||
/// <summary>
|
||||
/// The direction in which the <see cref="Ray2D"/> points. Should be a normalized vector.
|
||||
/// </summary>
|
||||
public readonly Vector2D Direction = Direction;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="Ray2D"/> with the same origin but with the direction reversed.
|
||||
/// </summary>
|
||||
public readonly Ray2D Reversed => new(Origin, -Direction);
|
||||
|
||||
public static implicit operator Ray2D(Line2D line) => new(line.From, line.From.FromTo(line.To).Normalized);
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="Line2D"/> from a <see cref="Ray2D"/>, extending from its origin in the <see cref="Ray2D"/>'s direction for a given distance.
|
||||
/// </summary>
|
||||
/// <param name="ray">The source <see cref="Ray2D"/>.</param>
|
||||
/// <param name="distance">The length of the line segment to create from the <see cref="Ray2D"/>.</param>
|
||||
/// <returns>A <see cref="Line2D"/> representing the segment of the <see cref="Ray2D"/>.</returns>
|
||||
public static Line2D GetLine(Ray2D ray, float distance)
|
||||
=> new(ray.Origin, ray.Origin + ray.Direction * distance);
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the point on the <see cref="Ray2D"/> at a specified distance from its origin.
|
||||
/// </summary>
|
||||
/// <param name="ray">The <see cref="Ray2D"/> to evaluate.</param>
|
||||
/// <param name="distanceFromOrigin">The distance from the origin along the <see cref="Ray2D"/>'s direction.</param>
|
||||
/// <returns>A <see cref="Vector2D"/> representing the point at the given distance on the <see cref="Ray2D"/>.</returns>
|
||||
public static Vector2D Evaluate(Ray2D ray, float distanceFromOrigin)
|
||||
=> ray.Origin + ray.Direction * distanceFromOrigin;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the closest point on the <see cref="Ray2D"/> to the specified point.
|
||||
/// </summary>
|
||||
public static Vector2D ClosestPointTo(Ray2D ray, Vector2D point)
|
||||
{
|
||||
Vector2D originToPoint = ray.Origin.FromTo(point);
|
||||
|
||||
float dot = ray.Direction.Dot(originToPoint);
|
||||
|
||||
return ray.Origin + ray.Direction * dot;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for the <see cref="Ray2D"/> struct.
|
||||
/// </summary>
|
||||
public static class Ray2DExtensions
|
||||
{
|
||||
/// <inheritdoc cref="Ray2D.GetLine(Ray2D, float) />
|
||||
public static Line2D ToLine(this Ray2D ray, float distance) => Ray2D.GetLine(ray, distance);
|
||||
|
||||
/// <inheritdoc cref="Ray2D.Evaluate(Ray2D, float) />
|
||||
public static Vector2D Evaluate(this Ray2D ray, float distanceFromOrigin) => Ray2D.Evaluate(ray, distanceFromOrigin);
|
||||
|
||||
/// <inheritdoc cref="Ray2D.ClosestPointTo(Ray2D, Vector2D) />
|
||||
public static Vector2D ClosestPointTo(this Ray2D ray, Vector2D point) => Ray2D.ClosestPointTo(ray, point);
|
||||
}
|
@@ -31,6 +31,11 @@ public readonly struct Vector2D(float x, float y)
|
||||
/// </summary>
|
||||
public float MagnitudeSquared => LengthSquared(this);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="Vector2D"/> with the direction reversed.
|
||||
/// </summary>
|
||||
public readonly Vector2D Reversed => -this;
|
||||
|
||||
/// <summary>
|
||||
/// The normalized form of the <see cref="Vector2D"/> (a <see cref="Vector2D"/> with the same direction and a magnitude of 1).
|
||||
/// </summary>
|
||||
|
@@ -19,6 +19,11 @@ public class Transform2D : Behaviour, ITransform2D
|
||||
|
||||
private ITransform2D? parentTransform = null;
|
||||
|
||||
public Vector2D Up => Vector2D.Up.Rotate(Rotation * Math.DegreeToRadian);
|
||||
public Vector2D Down => Vector2D.Down.Rotate(Rotation * Math.DegreeToRadian);
|
||||
public Vector2D Left => Vector2D.Left.Rotate(Rotation * Math.DegreeToRadian);
|
||||
public Vector2D Right => Vector2D.Right.Rotate(Rotation * Math.DegreeToRadian);
|
||||
|
||||
public Vector2D Position
|
||||
{
|
||||
get => _position;
|
||||
|
@@ -44,7 +44,7 @@ public interface ICollider2D : IBehaviour
|
||||
void Resolve(CollisionDetectionInformation collisionDetectionInformation);
|
||||
void Trigger(ICollider2D initiator);
|
||||
|
||||
readonly record struct CollisionDetectedArguments(ICollider2D sender, CollisionDetectionInformation collisionDetectionInformation);
|
||||
readonly record struct CollisionResolvedArguments(ICollider2D sender, CollisionDetectionInformation collisionDetectionInformation);
|
||||
readonly record struct TriggeredArguments(ICollider2D sender, ICollider2D initiatorCollider);
|
||||
readonly record struct CollisionDetectedArguments(ICollider2D Sender, CollisionDetectionInformation CollisionDetectionInformation);
|
||||
readonly record struct CollisionResolvedArguments(ICollider2D Sender, CollisionDetectionInformation CollisionDetectionInformation);
|
||||
readonly record struct TriggeredArguments(ICollider2D Sender, ICollider2D InitiatorCollider);
|
||||
}
|
||||
|
@@ -1,3 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
@@ -35,6 +37,26 @@ public interface IPhysicsEngine2D
|
||||
/// <param name="deltaTime">The time step.</param>
|
||||
void StepIndividual(IRigidBody2D rigidBody, float deltaTime);
|
||||
|
||||
readonly record struct PhysicsIterationArguments(IPhysicsEngine2D sender, float iterationDeltaTime);
|
||||
readonly record struct PhysicsStepArguments(IPhysicsEngine2D sender, float stepDeltaTime);
|
||||
/// <summary>
|
||||
/// Casts a <see cref="Ray2D"/> into the scene and returns the closest <see cref="RaycastResult"/> it hits, if any.
|
||||
/// </summary>
|
||||
/// <param name="ray">The <see cref="Ray2D"/> to cast.</param>
|
||||
/// <param name="length">The maximum distance to check for intersections. Defaults to <see cref="float.MaxValue"/>.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="RaycastResult"/> containing information about the hit, or <see cref="null"/> if no hit was detected.
|
||||
/// </returns>
|
||||
RaycastResult? Raycast(Ray2D ray, float length = float.MaxValue);
|
||||
|
||||
/// <summary>
|
||||
/// Casts a <see cref="Ray2D"/> into the scene and stores all hit results in the provided list.
|
||||
/// </summary>
|
||||
/// <param name="ray">The <see cref="Ray2D"/> to cast.</param>
|
||||
/// <param name="results">
|
||||
/// A list to which all <see cref="RaycastResult"/>s will be added. The list will be populated with zero or more <see cref="RaycastResult"/> objects.
|
||||
/// </param>
|
||||
/// <param name="length">The maximum distance to check for intersections. Defaults to <see cref="float.MaxValue"/>.</param>
|
||||
void Raycast(Ray2D ray, IList<RaycastResult> results, float length = float.MaxValue);
|
||||
|
||||
readonly record struct PhysicsIterationArguments(IPhysicsEngine2D Sender, float IterationDeltaTime);
|
||||
readonly record struct PhysicsStepArguments(IPhysicsEngine2D Sender, float StepDeltaTime);
|
||||
}
|
||||
|
23
Engine.Physics2D/Abstract/IRaycastResolver2D.cs
Normal file
23
Engine.Physics2D/Abstract/IRaycastResolver2D.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 2D raycast resolver.
|
||||
/// </summary>
|
||||
public interface IRaycastResolver2D
|
||||
{
|
||||
/// <summary>
|
||||
/// Casts a <see cref="Ray2D"/> against a specific <see cref="ICollider2D"/> shape and returns the first hit, if any.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the <see cref="ICollider2D"/>, which must implement <see cref="ICollider2D"/>.</typeparam>
|
||||
/// <param name="shape">The <see cref="ICollider2D"/> shape to test against.</param>
|
||||
/// <param name="ray">The <see cref="Ray2D"/> to cast.</param>
|
||||
/// <param name="length">
|
||||
/// The maximum distance to check along the <see cref="Ray2D"/>. Defaults to <see cref="float.MaxValue"/>.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A <see cref="RaycastResult"/> containing information about the intersection, or <see cref="null"/> if there was no hit.
|
||||
/// </returns>
|
||||
RaycastResult? RaycastAgainst<T>(T shape, Ray2D ray, float length = float.MaxValue) where T : ICollider2D;
|
||||
}
|
14
Engine.Physics2D/Abstract/RaycastResult.cs
Normal file
14
Engine.Physics2D/Abstract/RaycastResult.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public readonly struct RaycastResult(Ray2D ray, ICollider2D collider2D, Vector2D position, Vector2D normal)
|
||||
{
|
||||
public readonly Ray2D Ray = ray;
|
||||
|
||||
public readonly Vector2D Position = position;
|
||||
public readonly Vector2D Normal = normal;
|
||||
|
||||
public readonly ICollider2D Collider2D = collider2D;
|
||||
public readonly RigidBody2D? RigidBody2D = collider2D.BehaviourController.GetBehaviourInParent<RigidBody2D>();
|
||||
}
|
15
Engine.Physics2D/Abstract/Updates/IPhysicsIteration.cs
Normal file
15
Engine.Physics2D/Abstract/Updates/IPhysicsIteration.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a <see cref="IBehaviour"/> that listens to physics simulation update phase.
|
||||
/// </summary>
|
||||
public interface IPhysicsIteration : IBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Iterates the physics state of the object based on the elapsed time since the last iteration.
|
||||
/// </summary>
|
||||
/// <param name="delta">The time elapsed since the last physics iteration.</param>
|
||||
void PhysicsIterate(float delta);
|
||||
}
|
@@ -10,6 +10,6 @@ public interface IPhysicsUpdate : IBehaviour
|
||||
/// <summary>
|
||||
/// Updates the physics state of the object based on the elapsed time since the last update.
|
||||
/// </summary>
|
||||
/// <param name="delta">The time elapsed since the last physics update, typically in seconds.</param>
|
||||
/// <param name="delta">The time elapsed since the last physics update.</param>
|
||||
void PhysicsUpdate(float delta);
|
||||
}
|
||||
|
@@ -10,6 +10,6 @@ public interface IPostPhysicsUpdate : IBehaviour
|
||||
/// <summary>
|
||||
/// Execute logic that should occur after the physics simulation has been updated.
|
||||
/// </summary>
|
||||
/// <param name="delta">The time elapsed since the last physics update, typically in seconds.</param>
|
||||
/// <param name="delta">The time elapsed since the last physics update.</param>
|
||||
void PostPhysicsUpdate(float delta);
|
||||
}
|
||||
|
@@ -10,6 +10,6 @@ public interface IPrePhysicsUpdate : IBehaviour
|
||||
/// <summary>
|
||||
/// Execute logic that should occur before the physics simulation is updated.
|
||||
/// </summary>
|
||||
/// <param name="delta">The time elapsed since the last physics update, typically in seconds.</param>
|
||||
/// <param name="delta">The time elapsed since the last physics update.</param>
|
||||
void PrePhysicsUpdate(float delta);
|
||||
}
|
||||
|
@@ -5,12 +5,9 @@ using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public class PhysicsCoroutineManager : Behaviour
|
||||
public class PhysicsCoroutineManager : Behaviour, IPhysicsUpdate
|
||||
{
|
||||
private readonly Event<IUniverse, IUniverse.UpdateArguments>.EventHandler delegateOnUpdate = null!;
|
||||
|
||||
private readonly List<IEnumerator> enumerators = [];
|
||||
private IPhysicsEngine2D? physicsEngine = null;
|
||||
|
||||
public IEnumerator CreateCoroutine(IEnumerator enumerator)
|
||||
{
|
||||
@@ -23,16 +20,7 @@ public class PhysicsCoroutineManager : Behaviour
|
||||
enumerators.Remove(enumerator);
|
||||
}
|
||||
|
||||
protected override void OnEnteredUniverse(IUniverse universe)
|
||||
{
|
||||
physicsEngine = universe.FindRequiredBehaviour<IPhysicsEngine2D>();
|
||||
if (physicsEngine is IPhysicsEngine2D foundPhysicsEngine)
|
||||
foundPhysicsEngine.OnPhysicsStep.RemoveListener(OnPhysicsStep);
|
||||
else
|
||||
universe.OnUpdate.AddListener(OnUpdate);
|
||||
}
|
||||
|
||||
private void OnPhysicsStep(IPhysicsEngine2D sender, float stepDeltaTime)
|
||||
public void PhysicsUpdate(float delta)
|
||||
{
|
||||
for (int i = enumerators.Count - 1; i >= 0; i--)
|
||||
{
|
||||
@@ -44,28 +32,5 @@ public class PhysicsCoroutineManager : Behaviour
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnExitedUniverse(IUniverse universe)
|
||||
{
|
||||
if (physicsEngine is IPhysicsEngine2D existingPhysicsEngine)
|
||||
existingPhysicsEngine.OnPhysicsStep.RemoveListener(OnPhysicsStep);
|
||||
universe.OnUpdate.RemoveListener(delegateOnUpdate);
|
||||
}
|
||||
|
||||
private void OnUpdate(IUniverse sender, IUniverse.UpdateArguments args)
|
||||
{
|
||||
if (Universe is not IUniverse universe)
|
||||
return;
|
||||
|
||||
physicsEngine = universe.FindRequiredBehaviour<IPhysicsEngine2D>();
|
||||
if (physicsEngine is IPhysicsEngine2D foundPhysicsEngine)
|
||||
{
|
||||
foundPhysicsEngine.OnPhysicsStep.AddListener(OnPhysicsStep);
|
||||
universe.OnUpdate.RemoveListener(delegateOnUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
public PhysicsCoroutineManager()
|
||||
{
|
||||
delegateOnUpdate = OnUpdate;
|
||||
}
|
||||
public PhysicsCoroutineManager() => Priority = int.MinValue;
|
||||
}
|
||||
|
@@ -4,30 +4,65 @@ using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public class PhysicsEngine2D : Behaviour, IPhysicsEngine2D
|
||||
public class PhysicsEngine2D : Behaviour, IPreUpdate, IPhysicsEngine2D
|
||||
{
|
||||
public Event<IPhysicsEngine2D, float> OnPhysicsIteration { get; } = new();
|
||||
public Event<IPhysicsEngine2D, float> OnPhysicsStep { get; } = new();
|
||||
|
||||
private readonly Event<IUniverse, IUniverse.UpdateArguments>.EventHandler delegateOnPreUpdate = null!;
|
||||
|
||||
private float physicsTicker = 0f;
|
||||
private int _iterationPerStep = 1;
|
||||
private float _iterationPeriod = 1f / 60f;
|
||||
|
||||
protected readonly ICollisionDetector2D collisionDetector = null!;
|
||||
protected readonly ICollisionResolver2D collisionResolver = null!;
|
||||
protected readonly IRaycastResolver2D raycastResolver = null!;
|
||||
|
||||
private static Comparer<IBehaviour> SortByPriority() => Comparer<IBehaviour>.Create((x, y) => y.Priority.CompareTo(x.Priority));
|
||||
protected ActiveBehaviourCollectorSorted<IPrePhysicsUpdate> physicsPreUpdateCollector = new() { SortBy = SortByPriority() };
|
||||
protected ActiveBehaviourCollectorSorted<IPhysicsUpdate> physicsUpdateCollector = new() { SortBy = SortByPriority() };
|
||||
protected ActiveBehaviourCollectorSorted<IPhysicsIteration> physicsIterationCollector = new() { SortBy = SortByPriority() };
|
||||
protected ActiveBehaviourCollectorSorted<IPostPhysicsUpdate> physicsPostUpdateCollector = new() { SortBy = SortByPriority() };
|
||||
protected BehaviourCollector<IRigidBody2D> rigidBodyCollector = new();
|
||||
protected BehaviourCollector<ICollider2D> colliderCollector = new();
|
||||
|
||||
private readonly ListPool<ICollider2D> colliderPool = new(() => new(32));
|
||||
private readonly ListPool<IPrePhysicsUpdate> prePhysicsUpdatePool = new(() => new(32));
|
||||
private readonly ListPool<IPhysicsUpdate> physicsUpdatePool = new(() => new(32));
|
||||
private readonly ListPool<IPhysicsIteration> physicsIterationPool = new(() => new(32));
|
||||
private readonly ListPool<IPostPhysicsUpdate> postPhysicsUpdatePool = new(() => new(32));
|
||||
|
||||
public int IterationPerStep { get => _iterationPerStep; set => _iterationPerStep = value < 1 ? 1 : value; }
|
||||
public float IterationPeriod { get => _iterationPeriod; set => _iterationPeriod = value.Max(0.0001f); }
|
||||
|
||||
public RaycastResult? Raycast(Ray2D ray, float length = float.MaxValue)
|
||||
{
|
||||
RaycastResult? closestResult = null;
|
||||
float closestDistance = float.MaxValue;
|
||||
|
||||
for (int i = 0; i < colliderCollector.Count; i++)
|
||||
if (raycastResolver.RaycastAgainst(colliderCollector[i], ray, length) is RaycastResult raycastResult)
|
||||
{
|
||||
float magnitudeSquared = raycastResult.Position.FromTo(ray.Origin).MagnitudeSquared;
|
||||
|
||||
if (magnitudeSquared > closestDistance)
|
||||
continue;
|
||||
|
||||
closestDistance = magnitudeSquared;
|
||||
closestResult = raycastResult;
|
||||
}
|
||||
|
||||
return closestResult;
|
||||
}
|
||||
|
||||
public void Raycast(Ray2D ray, IList<RaycastResult> results, float length = float.MaxValue)
|
||||
{
|
||||
results.Clear();
|
||||
|
||||
for (int i = 0; i < colliderCollector.Count; i++)
|
||||
if (raycastResolver.RaycastAgainst(colliderCollector[i], ray, length) is RaycastResult raycastResult)
|
||||
results.Add(raycastResult);
|
||||
}
|
||||
|
||||
public void Step(float deltaTime)
|
||||
{
|
||||
float intervalDeltaTime = deltaTime / IterationPerStep;
|
||||
@@ -40,6 +75,9 @@ public class PhysicsEngine2D : Behaviour, IPhysicsEngine2D
|
||||
|
||||
for (int iterationIndex = 0; iterationIndex < IterationPerStep; iterationIndex++)
|
||||
{
|
||||
for (int i = physicsIterationCollector.Count - 1; i >= 0; i--)
|
||||
physicsIterationCollector[i].PhysicsIterate(intervalDeltaTime);
|
||||
|
||||
// Can Parallel
|
||||
for (int i = rigidBodyCollector.Count - 1; i >= 0; i--)
|
||||
StepRigidBody(rigidBodyCollector[i], intervalDeltaTime);
|
||||
@@ -79,24 +117,29 @@ public class PhysicsEngine2D : Behaviour, IPhysicsEngine2D
|
||||
{
|
||||
float intervalDeltaTime = deltaTime / IterationPerStep;
|
||||
|
||||
List<ICollider2D> childColliders = [];
|
||||
List<IPrePhysicsUpdate> physicsPreUpdates = [];
|
||||
List<IPhysicsUpdate> physicsUpdates = [];
|
||||
List<IPostPhysicsUpdate> physicsPostUpdates = [];
|
||||
List<ICollider2D> childColliders = colliderPool.Get();
|
||||
List<IPrePhysicsUpdate> physicsPreUpdates = prePhysicsUpdatePool.Get();
|
||||
List<IPhysicsUpdate> physicsUpdates = physicsUpdatePool.Get();
|
||||
List<IPhysicsIteration> physicsIterations = physicsIterationPool.Get();
|
||||
List<IPostPhysicsUpdate> physicsPostUpdates = postPhysicsUpdatePool.Get();
|
||||
|
||||
rigidBody.BehaviourController.GetBehavioursInChildren(childColliders);
|
||||
rigidBody.BehaviourController.GetBehavioursInChildren(physicsPreUpdates);
|
||||
rigidBody.BehaviourController.GetBehavioursInChildren(physicsUpdates);
|
||||
rigidBody.BehaviourController.GetBehavioursInChildren(physicsIterations);
|
||||
rigidBody.BehaviourController.GetBehavioursInChildren(physicsPostUpdates);
|
||||
|
||||
foreach (IPrePhysicsUpdate physicsPreUpdate in physicsPreUpdates)
|
||||
physicsPreUpdate.PrePhysicsUpdate(deltaTime);
|
||||
for (int i = physicsPreUpdates.Count - 1; i >= 0; i--)
|
||||
physicsPreUpdates[i].PrePhysicsUpdate(deltaTime);
|
||||
|
||||
foreach (IPhysicsUpdate physicsUpdate in physicsUpdates)
|
||||
physicsUpdate.PhysicsUpdate(deltaTime);
|
||||
for (int i = physicsUpdates.Count - 1; i >= 0; i--)
|
||||
physicsUpdates[i].PhysicsUpdate(deltaTime);
|
||||
|
||||
for (int iterationIndex = 0; iterationIndex < IterationPerStep; iterationIndex++)
|
||||
{
|
||||
for (int i = physicsIterations.Count - 1; i >= 0; i--)
|
||||
physicsIterations[i].PhysicsIterate(intervalDeltaTime);
|
||||
|
||||
StepRigidBody(rigidBody, intervalDeltaTime);
|
||||
|
||||
for (int i = childColliders.Count - 1; i >= 0; i--)
|
||||
@@ -120,8 +163,14 @@ public class PhysicsEngine2D : Behaviour, IPhysicsEngine2D
|
||||
}
|
||||
}
|
||||
|
||||
foreach (IPostPhysicsUpdate physicsPostUpdate in physicsPostUpdates)
|
||||
physicsPostUpdate.PostPhysicsUpdate(deltaTime);
|
||||
for (int i = physicsPostUpdates.Count - 1; i >= 0; i--)
|
||||
physicsPostUpdates[i].PostPhysicsUpdate(deltaTime);
|
||||
|
||||
colliderPool.Return(childColliders);
|
||||
prePhysicsUpdatePool.Return(physicsPreUpdates);
|
||||
physicsUpdatePool.Return(physicsUpdates);
|
||||
physicsIterationPool.Return(physicsIterations);
|
||||
postPhysicsUpdatePool.Return(physicsPostUpdates);
|
||||
}
|
||||
|
||||
private void ResolveColliders(ICollider2D colliderX, ICollider2D colliderY)
|
||||
@@ -178,27 +227,25 @@ public class PhysicsEngine2D : Behaviour, IPhysicsEngine2D
|
||||
{
|
||||
physicsPreUpdateCollector.Assign(universe);
|
||||
physicsUpdateCollector.Assign(universe);
|
||||
physicsIterationCollector.Assign(universe);
|
||||
physicsPostUpdateCollector.Assign(universe);
|
||||
colliderCollector.Assign(universe);
|
||||
rigidBodyCollector.Assign(universe);
|
||||
|
||||
universe.OnPreUpdate.AddListener(OnEnginePreUpdate);
|
||||
}
|
||||
|
||||
protected override void OnExitedUniverse(IUniverse universe)
|
||||
{
|
||||
physicsPreUpdateCollector.Unassign();
|
||||
physicsUpdateCollector.Unassign();
|
||||
physicsIterationCollector.Unassign();
|
||||
physicsPostUpdateCollector.Unassign();
|
||||
colliderCollector.Unassign();
|
||||
rigidBodyCollector.Unassign();
|
||||
|
||||
universe.OnPreUpdate.RemoveListener(OnEnginePreUpdate);
|
||||
}
|
||||
|
||||
private void OnEnginePreUpdate(IUniverse sender, IUniverse.UpdateArguments args)
|
||||
void IPreUpdate.PreUpdate()
|
||||
{
|
||||
physicsTicker += args.EngineTime.DeltaTime;
|
||||
physicsTicker += Universe.Time.DeltaTime;
|
||||
|
||||
while (physicsTicker >= IterationPeriod)
|
||||
{
|
||||
@@ -211,15 +258,15 @@ public class PhysicsEngine2D : Behaviour, IPhysicsEngine2D
|
||||
{
|
||||
collisionDetector = new CollisionDetector2D();
|
||||
collisionResolver = new CollisionResolver2D();
|
||||
|
||||
delegateOnPreUpdate = OnEnginePreUpdate;
|
||||
raycastResolver = new RaycastResolver2D();
|
||||
Priority = int.MaxValue;
|
||||
}
|
||||
|
||||
public PhysicsEngine2D(ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver)
|
||||
public PhysicsEngine2D(ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver, IRaycastResolver2D raycastResolver)
|
||||
{
|
||||
this.collisionDetector = collisionDetector;
|
||||
this.collisionResolver = collisionResolver;
|
||||
|
||||
delegateOnPreUpdate = OnEnginePreUpdate;
|
||||
this.raycastResolver = raycastResolver;
|
||||
Priority = int.MaxValue;
|
||||
}
|
||||
}
|
||||
|
@@ -19,6 +19,7 @@ public class PhysicsEngine2DStandalone : IPhysicsEngine2D
|
||||
|
||||
private readonly ICollisionDetector2D collisionDetector = null!;
|
||||
private readonly ICollisionResolver2D collisionResolver = null!;
|
||||
private readonly IRaycastResolver2D raycastResolver = null!;
|
||||
|
||||
public int IterationPerStep { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; }
|
||||
|
||||
@@ -43,6 +44,34 @@ public class PhysicsEngine2DStandalone : IPhysicsEngine2D
|
||||
rigidBody.BehaviourController.OnBehaviourAdded.RemoveListener(delegateOnBehaviourAdded);
|
||||
rigidBody.BehaviourController.OnBehaviourRemoved.RemoveListener(delegateOnBehaviourRemoved);
|
||||
}
|
||||
public RaycastResult? Raycast(Ray2D ray, float length = float.MaxValue)
|
||||
{
|
||||
RaycastResult? closestResult = null;
|
||||
float closestDistance = float.MaxValue;
|
||||
|
||||
for (int i = 0; i < colliders.Count; i++)
|
||||
if (raycastResolver.RaycastAgainst(colliders[i], ray, length) is RaycastResult raycastResult)
|
||||
{
|
||||
float magnitudeSquared = raycastResult.Position.FromTo(ray.Origin).MagnitudeSquared;
|
||||
|
||||
if (magnitudeSquared > closestDistance)
|
||||
continue;
|
||||
|
||||
closestDistance = magnitudeSquared;
|
||||
closestResult = raycastResult;
|
||||
}
|
||||
|
||||
return closestResult;
|
||||
}
|
||||
|
||||
public void Raycast(Ray2D ray, IList<RaycastResult> results, float length = float.MaxValue)
|
||||
{
|
||||
results.Clear();
|
||||
|
||||
for (int i = 0; i < colliders.Count; i++)
|
||||
if (raycastResolver.RaycastAgainst(colliders[i], ray, length) is RaycastResult raycastResult)
|
||||
results.Add(raycastResult);
|
||||
}
|
||||
|
||||
public void Step(float deltaTime)
|
||||
{
|
||||
@@ -86,25 +115,30 @@ public class PhysicsEngine2DStandalone : IPhysicsEngine2D
|
||||
List<ICollider2D> childColliders = [];
|
||||
List<IPrePhysicsUpdate> physicsPreUpdates = [];
|
||||
List<IPhysicsUpdate> physicsUpdates = [];
|
||||
List<IPhysicsIteration> physicsIterations = [];
|
||||
List<IPostPhysicsUpdate> physicsPostUpdates = [];
|
||||
|
||||
rigidBody.BehaviourController.GetBehavioursInChildren(childColliders);
|
||||
rigidBody.BehaviourController.GetBehavioursInChildren(physicsPreUpdates);
|
||||
rigidBody.BehaviourController.GetBehavioursInChildren(physicsUpdates);
|
||||
rigidBody.BehaviourController.GetBehavioursInChildren(physicsIterations);
|
||||
rigidBody.BehaviourController.GetBehavioursInChildren(physicsPostUpdates);
|
||||
|
||||
foreach (IPrePhysicsUpdate physicsPreUpdate in physicsPreUpdates)
|
||||
physicsPreUpdate.PrePhysicsUpdate(deltaTime);
|
||||
for (int i = physicsPreUpdates.Count - 1; i >= 0; i--)
|
||||
physicsPreUpdates[i].PrePhysicsUpdate(deltaTime);
|
||||
|
||||
foreach (IPhysicsUpdate physicsUpdate in physicsUpdates)
|
||||
physicsUpdate.PhysicsUpdate(deltaTime);
|
||||
for (int i = physicsUpdates.Count - 1; i >= 0; i--)
|
||||
physicsUpdates[i].PhysicsUpdate(deltaTime);
|
||||
|
||||
for (int iterationIndex = 0; iterationIndex < IterationPerStep; iterationIndex++)
|
||||
{
|
||||
for (int i = physicsIterations.Count - 1; i >= 0; i--)
|
||||
physicsIterations[i].PhysicsIterate(intervalDeltaTime);
|
||||
|
||||
StepRigidBody(rigidBody, intervalDeltaTime);
|
||||
|
||||
foreach (ICollider2D collider in childColliders)
|
||||
collider.Recalculate();
|
||||
for (int i = childColliders.Count - 1; i >= 0; i--)
|
||||
childColliders[i].Recalculate();
|
||||
|
||||
for (int x = 0; x < childColliders.Count; x++)
|
||||
{
|
||||
@@ -124,8 +158,8 @@ public class PhysicsEngine2DStandalone : IPhysicsEngine2D
|
||||
}
|
||||
}
|
||||
|
||||
foreach (IPostPhysicsUpdate physicsPostUpdate in physicsPostUpdates)
|
||||
physicsPostUpdate.PostPhysicsUpdate(deltaTime);
|
||||
for (int i = physicsPostUpdates.Count - 1; i >= 0; i--)
|
||||
physicsPostUpdates[i].PostPhysicsUpdate(deltaTime);
|
||||
}
|
||||
|
||||
private void ResolveColliders(ICollider2D colliderX, ICollider2D colliderY)
|
||||
@@ -198,15 +232,17 @@ public class PhysicsEngine2DStandalone : IPhysicsEngine2D
|
||||
{
|
||||
collisionDetector = new CollisionDetector2D();
|
||||
collisionResolver = new CollisionResolver2D();
|
||||
raycastResolver = new RaycastResolver2D();
|
||||
|
||||
delegateOnBehaviourAdded = OnBehaviourAdded;
|
||||
delegateOnBehaviourRemoved = OnBehaviourRemoved;
|
||||
}
|
||||
|
||||
public PhysicsEngine2DStandalone(ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver)
|
||||
public PhysicsEngine2DStandalone(ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver, IRaycastResolver2D raycastResolver)
|
||||
{
|
||||
this.collisionDetector = collisionDetector;
|
||||
this.collisionResolver = collisionResolver;
|
||||
this.raycastResolver = raycastResolver;
|
||||
|
||||
delegateOnBehaviourAdded = OnBehaviourAdded;
|
||||
delegateOnBehaviourRemoved = OnBehaviourRemoved;
|
||||
|
100
Engine.Physics2D/RaycastResolver2D.cs
Normal file
100
Engine.Physics2D/RaycastResolver2D.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Syntriax.Engine.Core;
|
||||
|
||||
namespace Syntriax.Engine.Physics2D;
|
||||
|
||||
public class RaycastResolver2D : IRaycastResolver2D
|
||||
{
|
||||
private readonly ListPool<Line2D> lineCacheQueue = new(() => new(4));
|
||||
|
||||
RaycastResult? IRaycastResolver2D.RaycastAgainst<T>(T shape, Ray2D ray, float length)
|
||||
{
|
||||
if (shape is IShapeCollider2D shapeCollider)
|
||||
return RaycastAgainstShape(shapeCollider, ray, length);
|
||||
else if (shape is ICircleCollider2D circleCollider)
|
||||
return RaycastAgainstCircle(circleCollider, ray, length);
|
||||
|
||||
throw new System.NotSupportedException($"{shape.GetType().FullName} is not supported by {GetType().FullName}. Please implement a {nameof(IRaycastResolver2D)} and use it as the raycast resolver.");
|
||||
}
|
||||
|
||||
public RaycastResult? RaycastAgainstShape(IShapeCollider2D shapeCollider, Ray2D ray, float length)
|
||||
{
|
||||
List<Line2D> line2Ds = lineCacheQueue.Get();
|
||||
|
||||
RaycastResult? raycastResult = null;
|
||||
float closestRaycastResultSquared = float.MaxValue;
|
||||
|
||||
shapeCollider.ShapeWorld.ToLines(line2Ds);
|
||||
|
||||
Line2D rayLine = ray.ToLine(length);
|
||||
|
||||
foreach (Line2D line in line2Ds)
|
||||
{
|
||||
if (line.Intersects(rayLine))
|
||||
{
|
||||
Vector2D hitPosition = line.IntersectionPoint(rayLine);
|
||||
|
||||
float hitDistanceSquared = ray.Origin.FromTo(hitPosition).MagnitudeSquared;
|
||||
|
||||
if (closestRaycastResultSquared < hitDistanceSquared)
|
||||
continue;
|
||||
|
||||
closestRaycastResultSquared = hitDistanceSquared;
|
||||
|
||||
Vector2D lineDirection = line.Direction;
|
||||
|
||||
Vector2D normal1 = lineDirection.Perpendicular();
|
||||
Vector2D normal2 = -lineDirection.Perpendicular();
|
||||
|
||||
float normalDot1 = rayLine.Direction.Dot(normal1);
|
||||
float normalDot2 = rayLine.Direction.Dot(normal2);
|
||||
|
||||
Vector2D hitNormal = normalDot1 < normalDot2 ? normal1 : normal2;
|
||||
|
||||
if (shapeCollider.ShapeWorld.Overlaps(ray.Origin))
|
||||
hitNormal = hitNormal.Reversed;
|
||||
|
||||
raycastResult = new(ray, shapeCollider, hitPosition, hitNormal);
|
||||
}
|
||||
}
|
||||
|
||||
lineCacheQueue.Return(line2Ds);
|
||||
|
||||
return raycastResult;
|
||||
}
|
||||
|
||||
public RaycastResult? RaycastAgainstCircle(ICircleCollider2D circleCollider, Ray2D ray, float length)
|
||||
{
|
||||
Circle circle = circleCollider.CircleWorld;
|
||||
|
||||
if (circle.Overlaps(ray.Origin, out _, out float depth))
|
||||
{
|
||||
Vector2D insideNormal = circle.Center.FromTo(ray.Origin).Normalized;
|
||||
Vector2D insidePosition = circle.Center + insideNormal * circle.Radius;
|
||||
|
||||
return new(ray, circleCollider, insidePosition, insideNormal);
|
||||
}
|
||||
|
||||
Vector2D originToCircle = ray.Origin.FromTo(circle.Center);
|
||||
float distanceToClosest = ray.Direction.Dot(originToCircle);
|
||||
|
||||
Vector2D closestPoint = ray.Origin + ray.Direction * distanceToClosest;
|
||||
Vector2D closestPointToCircle = closestPoint.FromTo(circle.Center);
|
||||
|
||||
float closestToCircleDistanceSquared = closestPointToCircle.MagnitudeSquared;
|
||||
|
||||
if (closestToCircleDistanceSquared > circle.RadiusSquared)
|
||||
return null;
|
||||
|
||||
float distanceToHit = distanceToClosest - (circle.Radius - closestToCircleDistanceSquared.Sqrt());
|
||||
|
||||
if (distanceToHit > length || distanceToHit < 0f)
|
||||
return null;
|
||||
|
||||
Vector2D hitPosition = ray.Evaluate(distanceToHit);
|
||||
Vector2D hitNormal = circle.Center.FromTo(hitPosition).Normalized;
|
||||
|
||||
return new(ray, circleCollider, hitPosition, hitNormal);
|
||||
}
|
||||
}
|
@@ -18,6 +18,6 @@ public interface IReadOnlyTimer
|
||||
|
||||
TimerState State { get; }
|
||||
|
||||
readonly record struct TimerDeltaArguments(double delta);
|
||||
readonly record struct TimerDeltaArguments(double Delta);
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user