5 Commits

5 changed files with 315 additions and 77 deletions

View File

@@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Syntriax.Engine.Core.Exceptions; using Syntriax.Engine.Core.Exceptions;
@@ -38,6 +39,19 @@ public static class BehaviourControllerExtensions
public static T GetOrAddBehaviour<T>(this IBehaviourController behaviourController, params object?[]? args) where T : class, IBehaviour public static T GetOrAddBehaviour<T>(this IBehaviourController behaviourController, params object?[]? args) where T : class, IBehaviour
=> behaviourController.GetBehaviour<T>() ?? behaviourController.AddBehaviour<T>(args); => behaviourController.GetBehaviour<T>() ?? behaviourController.AddBehaviour<T>(args);
/// <summary>
/// Gets an existing <see cref="IBehaviour"/> of the specified type, or adds and returns the fallback type if it doesn't exist.
/// </summary>
/// <typeparam name="TOriginal">The type of <see cref="IBehaviour"/> to get.</typeparam>
/// <typeparam name="TFallback">The type of <see cref="IBehaviour"/> to add. It must be assignable from <typeparamref name="TOriginal"/></typeparam>
/// <param name="behaviourController">The <see cref="IBehaviourController"/> to search in.</param>
/// <param name="args">Optional arguments to pass to the constructor of the <see cref="IBehaviour"/> if a new one is added.</param>
/// <returns>The existing or newly added <see cref="IBehaviour"/> of the specified type.</returns>
public static TOriginal GetOrAddBehaviour<TOriginal, TFallback>(this IBehaviourController behaviourController, params object?[]? args)
where TOriginal : class
where TFallback : class, IBehaviour, TOriginal
=> behaviourController.GetBehaviour<TOriginal>() ?? behaviourController.AddBehaviour<TFallback>(args);
/// <summary> /// <summary>
/// Tries to get a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s parents recursively. /// Tries to get a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s parents recursively.
/// </summary> /// </summary>
@@ -81,6 +95,27 @@ public static class BehaviourControllerExtensions
public static T GetRequiredBehaviourInParent<T>(this IBehaviourController behaviourController) where T : class public static T GetRequiredBehaviourInParent<T>(this IBehaviourController behaviourController) where T : class
=> behaviourController.GetBehaviourInParent<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any parent"); => behaviourController.GetBehaviourInParent<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any parent");
/// <summary>
/// Gets all <see cref="IBehaviour"/>s of the specified type in it's <see cref="IUniverseObject"/>'s parents recursively and stores them in the provided list.
/// </summary>
/// <typeparam name="T">The type of <see cref="IBehaviour"/>s to get.</typeparam>
/// <param name="behavioursInParent">The list to store the <see cref="IBehaviour"/>s.</param>
public static void GetBehavioursInParent<T>(this IBehaviourController behaviourController, IList<T> behavioursInParent) where T : class
{
IBehaviourController? controller = behaviourController;
List<T> cache = [];
behavioursInParent.Clear();
while (controller is not null)
{
controller.GetBehaviours(cache);
foreach (T behaviour in cache)
behavioursInParent.Add(behaviour);
controller = controller.UniverseObject.Parent?.BehaviourController;
}
}
/// <summary> /// <summary>
/// Tries to get a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s children recursively. /// Tries to get a <see cref="IBehaviour"/> of the specified type in it's <see cref="IUniverseObject"/>'s children recursively.
/// </summary> /// </summary>
@@ -120,4 +155,28 @@ public static class BehaviourControllerExtensions
/// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns> /// <returns>The <see cref="IBehaviour"/> of the specified type if found; otherwise, throws <see cref="BehaviourNotFoundException"/>.</returns>
public static T GetRequiredBehaviourInChildren<T>(this IBehaviourController behaviourController) where T : class public static T GetRequiredBehaviourInChildren<T>(this IBehaviourController behaviourController) where T : class
=> behaviourController.GetBehaviourInChildren<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any children "); => behaviourController.GetBehaviourInChildren<T>() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any children ");
/// <summary>
/// Gets all <see cref="IBehaviour"/>s of the specified type in it's <see cref="IUniverseObject"/>'s children recursively and stores them in the provided list.
/// </summary>
/// <typeparam name="T">The type of <see cref="IBehaviour"/>s to get.</typeparam>
/// <param name="behavioursInChildren">The list to store the <see cref="IBehaviour"/>s.</param>
public static void GetBehavioursInChildren<T>(this IBehaviourController behaviourController, IList<T> behavioursInChildren) where T : class
{
List<T> cache = [];
behavioursInChildren.Clear();
TraverseChildrenForBehaviour(behaviourController.UniverseObject, behavioursInChildren, cache);
}
private static void TraverseChildrenForBehaviour<T>(IUniverseObject universeObject, IList<T> behaviours, IList<T> cache) where T : class
{
universeObject.BehaviourController.GetBehaviours(cache);
foreach (T behaviour in cache)
behaviours.Add(behaviour);
foreach (IUniverseObject child in universeObject)
TraverseChildrenForBehaviour(child, behaviours, cache);
}
} }

View File

@@ -26,6 +26,13 @@ public interface IPhysicsEngine2D
/// <param name="deltaTime">The time step.</param> /// <param name="deltaTime">The time step.</param>
void Step(float deltaTime); void Step(float deltaTime);
/// <summary>
/// Advances the physics simulation by the specified time on a single <see cref="IRigidBody2D"/>.
/// </summary>
/// <param name="rigidBody">The <see cref="IRigidBody2D"/> to be progressed individually.</param>
/// <param name="deltaTime">The time step.</param>
void StepIndividual(IRigidBody2D rigidBody, float deltaTime);
delegate void PhysicsIterationEventHandler(IPhysicsEngine2D sender, float iterationDeltaTime); delegate void PhysicsIterationEventHandler(IPhysicsEngine2D sender, float iterationDeltaTime);
delegate void PhysicsStepEventHandler(IPhysicsEngine2D sender, float stepDeltaTime); delegate void PhysicsStepEventHandler(IPhysicsEngine2D sender, float stepDeltaTime);
} }

View File

@@ -49,37 +49,102 @@ public class PhysicsEngine2D : UniverseObject, IPhysicsEngine2D
{ {
ICollider2D? colliderX = colliderCollector.Behaviours[x]; ICollider2D? colliderX = colliderCollector.Behaviours[x];
if (!colliderX.IsActive) if (!colliderX.IsActive)
return; continue;
for (int y = x + 1; y < colliderCollector.Behaviours.Count; y++) for (int y = x + 1; y < colliderCollector.Behaviours.Count; y++)
{ {
ICollider2D? colliderY = colliderCollector.Behaviours[y]; ICollider2D? colliderY = colliderCollector.Behaviours[y];
if (!colliderY.IsActive) if (!colliderY.IsActive)
return;
if (colliderX.RigidBody2D == colliderY.RigidBody2D)
continue; continue;
ResolveColliders(colliderX, colliderY);
}
}
OnPhysicsIteration?.InvokeSafe(this, intervalDeltaTime);
}
foreach (IPostPhysicsUpdate physicsPostUpdate in physicsPostUpdateCollector)
physicsPostUpdate.PostPhysicsUpdate(deltaTime);
OnPhysicsStep?.InvokeSafe(this, deltaTime);
}
public void StepIndividual(IRigidBody2D rigidBody, float deltaTime)
{
float intervalDeltaTime = deltaTime / IterationPerStep;
System.Collections.Generic.List<ICollider2D> childColliders = [];
System.Collections.Generic.List<IPrePhysicsUpdate> physicsPreUpdates = [];
System.Collections.Generic.List<IPhysicsUpdate> physicsUpdates = [];
System.Collections.Generic.List<IPostPhysicsUpdate> physicsPostUpdates = [];
rigidBody.BehaviourController.GetBehavioursInChildren(childColliders);
rigidBody.BehaviourController.GetBehavioursInChildren(physicsPreUpdates);
rigidBody.BehaviourController.GetBehavioursInChildren(physicsUpdates);
rigidBody.BehaviourController.GetBehavioursInChildren(physicsPostUpdates);
foreach (IPrePhysicsUpdate physicsPreUpdate in physicsPreUpdates)
physicsPreUpdate.PrePhysicsUpdate(deltaTime);
foreach (IPhysicsUpdate physicsUpdate in physicsUpdates)
physicsUpdate.PhysicsUpdate(deltaTime);
for (int iterationIndex = 0; iterationIndex < IterationPerStep; iterationIndex++)
{
StepRigidBody(rigidBody, intervalDeltaTime);
foreach (ICollider2D collider in childColliders)
collider.Recalculate();
for (int x = 0; x < childColliders.Count; x++)
{
ICollider2D? colliderX = childColliders[x];
if (!colliderX.IsActive)
continue;
for (int y = 0; y < colliderCollector.Behaviours.Count; y++)
{
ICollider2D? colliderY = colliderCollector.Behaviours[y];
if (!colliderY.IsActive)
continue;
ResolveColliders(colliderX, colliderY);
}
}
}
foreach (IPostPhysicsUpdate physicsPostUpdate in physicsPostUpdates)
physicsPostUpdate.PostPhysicsUpdate(deltaTime);
}
private void ResolveColliders(ICollider2D colliderX, ICollider2D colliderY)
{
if (colliderX.RigidBody2D == colliderY.RigidBody2D)
return;
bool bothCollidersAreTriggers = colliderX.IsTrigger && colliderX.IsTrigger == colliderY.IsTrigger; bool bothCollidersAreTriggers = colliderX.IsTrigger && colliderX.IsTrigger == colliderY.IsTrigger;
if (bothCollidersAreTriggers) if (bothCollidersAreTriggers)
continue; return;
bool bothCollidersAreStatic = (colliderX.RigidBody2D?.IsStatic ?? true) && (colliderY.RigidBody2D?.IsStatic ?? true); bool bothCollidersAreStatic = (colliderX.RigidBody2D?.IsStatic ?? true) && (colliderY.RigidBody2D?.IsStatic ?? true);
if (bothCollidersAreStatic) if (bothCollidersAreStatic)
continue; return;
if (!collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation information))
return;
if (collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation information))
{
if (colliderX.IsTrigger) if (colliderX.IsTrigger)
{ {
colliderX.Trigger(colliderY); colliderX.Trigger(colliderY);
continue; return;
} }
else if (colliderY.IsTrigger) else if (colliderY.IsTrigger)
{ {
colliderY.Trigger(colliderX); colliderY.Trigger(colliderX);
continue; return;
} }
if (information.Detector == colliderX) if (information.Detector == colliderX)
@@ -95,17 +160,6 @@ public class PhysicsEngine2D : UniverseObject, IPhysicsEngine2D
collisionResolver?.Resolve(information); collisionResolver?.Resolve(information);
} }
}
}
OnPhysicsIteration?.InvokeSafe(this, intervalDeltaTime);
}
foreach (IPostPhysicsUpdate physicsPostUpdate in physicsPostUpdateCollector)
physicsPostUpdate.PostPhysicsUpdate(deltaTime);
OnPhysicsStep?.InvokeSafe(this, deltaTime);
}
private static void StepRigidBody(IRigidBody2D rigidBody, float intervalDeltaTime) private static void StepRigidBody(IRigidBody2D rigidBody, float intervalDeltaTime)
{ {

View File

@@ -57,37 +57,96 @@ public class PhysicsEngine2DStandalone : IPhysicsEngine2D
{ {
ICollider2D? colliderX = colliders[x]; ICollider2D? colliderX = colliders[x];
if (!colliderX.IsActive) if (!colliderX.IsActive)
return; continue;
for (int y = x + 1; y < colliders.Count; y++) for (int y = x + 1; y < colliders.Count; y++)
{ {
ICollider2D? colliderY = colliders[y]; ICollider2D? colliderY = colliders[y];
if (!colliderY.IsActive) if (!colliderY.IsActive)
return; return;
if (colliderX.RigidBody2D == colliderY.RigidBody2D) ResolveColliders(colliderX, colliderY);
}
}
OnPhysicsIteration?.InvokeSafe(this, intervalDeltaTime);
}
OnPhysicsStep?.InvokeSafe(this, deltaTime);
}
public void StepIndividual(IRigidBody2D rigidBody, float deltaTime)
{
float intervalDeltaTime = deltaTime / IterationPerStep;
List<ICollider2D> childColliders = [];
List<IPrePhysicsUpdate> physicsPreUpdates = [];
List<IPhysicsUpdate> physicsUpdates = [];
List<IPostPhysicsUpdate> physicsPostUpdates = [];
rigidBody.BehaviourController.GetBehavioursInChildren(childColliders);
rigidBody.BehaviourController.GetBehavioursInChildren(physicsPreUpdates);
rigidBody.BehaviourController.GetBehavioursInChildren(physicsUpdates);
rigidBody.BehaviourController.GetBehavioursInChildren(physicsPostUpdates);
foreach (IPrePhysicsUpdate physicsPreUpdate in physicsPreUpdates)
physicsPreUpdate.PrePhysicsUpdate(deltaTime);
foreach (IPhysicsUpdate physicsUpdate in physicsUpdates)
physicsUpdate.PhysicsUpdate(deltaTime);
for (int iterationIndex = 0; iterationIndex < IterationPerStep; iterationIndex++)
{
StepRigidBody(rigidBody, intervalDeltaTime);
foreach (ICollider2D collider in childColliders)
collider.Recalculate();
for (int x = 0; x < childColliders.Count; x++)
{
ICollider2D? colliderX = childColliders[x];
if (!colliderX.IsActive)
continue; continue;
for (int y = 0; y < colliders.Count; y++)
{
ICollider2D? colliderY = colliders[y];
if (!colliderY.IsActive)
continue;
ResolveColliders(colliderX, colliderY);
}
}
}
foreach (IPostPhysicsUpdate physicsPostUpdate in physicsPostUpdates)
physicsPostUpdate.PostPhysicsUpdate(deltaTime);
}
private void ResolveColliders(ICollider2D colliderX, ICollider2D colliderY)
{
if (colliderX.RigidBody2D == colliderY.RigidBody2D)
return;
bool bothCollidersAreTriggers = colliderX.IsTrigger && colliderX.IsTrigger == colliderY.IsTrigger; bool bothCollidersAreTriggers = colliderX.IsTrigger && colliderX.IsTrigger == colliderY.IsTrigger;
if (bothCollidersAreTriggers) if (bothCollidersAreTriggers)
continue; return;
bool bothCollidersAreStatic = (colliderX.RigidBody2D?.IsStatic ?? true) && (colliderY.RigidBody2D?.IsStatic ?? true); bool bothCollidersAreStatic = (colliderX.RigidBody2D?.IsStatic ?? true) && (colliderY.RigidBody2D?.IsStatic ?? true);
if (bothCollidersAreStatic) if (bothCollidersAreStatic)
continue; return;
if (!collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation information))
return;
if (collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation information))
{
if (colliderX.IsTrigger) if (colliderX.IsTrigger)
{ {
colliderX.Trigger(colliderY); colliderX.Trigger(colliderY);
continue; return;
} }
else if (colliderY.IsTrigger) else if (colliderY.IsTrigger)
{ {
colliderY.Trigger(colliderX); colliderY.Trigger(colliderX);
continue; return;
} }
if (information.Detector == colliderX) if (information.Detector == colliderX)
@@ -103,12 +162,6 @@ public class PhysicsEngine2DStandalone : IPhysicsEngine2D
collisionResolver?.Resolve(information); collisionResolver?.Resolve(information);
} }
}
}
OnPhysicsIteration?.InvokeSafe(this, intervalDeltaTime);
}
OnPhysicsStep?.InvokeSafe(this, deltaTime);
}
private static void StepRigidBody(IRigidBody2D rigidBody, float intervalDeltaTime) private static void StepRigidBody(IRigidBody2D rigidBody, float intervalDeltaTime)
{ {

View File

@@ -39,4 +39,69 @@ public static class TweenTransform2DExtensions
float initialLocalRotation = transform2D.LocalRotation; float initialLocalRotation = transform2D.LocalRotation;
return tweenManager.StartTween(duration, t => transform2D.LocalRotation = initialLocalRotation.Lerp(targetLocalRotation, t)); return tweenManager.StartTween(duration, t => transform2D.LocalRotation = initialLocalRotation.Lerp(targetLocalRotation, t));
} }
public static ITween TweenPositionAdditive(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D additivePosition)
{
Vector2D progressedPosition = Vector2D.Zero;
return tweenManager.StartTween(duration, t =>
{
Vector2D displacement = Vector2D.Zero.Lerp(additivePosition, t) - progressedPosition;
transform2D.Position += displacement;
progressedPosition += displacement;
});
}
public static ITween TweenScaleAdditive(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D additiveScale)
{
Vector2D progressedScale = Vector2D.Zero;
return tweenManager.StartTween(duration, t =>
{
Vector2D displacement = Vector2D.Zero.Lerp(additiveScale, t) - progressedScale;
transform2D.Scale += displacement;
progressedScale += displacement;
});
}
public static ITween TweenRotationAdditive(this ITransform2D transform2D, ITweenManager tweenManager, float duration, float additiveRotation)
{
float progressedRotation = 0f;
return tweenManager.StartTween(duration, t =>
{
float displacement = 0f.Lerp(additiveRotation, t) - progressedRotation;
transform2D.Rotation += displacement;
progressedRotation += displacement;
});
}
public static ITween TweenLocalPositionAdditive(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D additiveLocalPosition)
{
Vector2D progressedLocalPosition = Vector2D.Zero;
return tweenManager.StartTween(duration, t =>
{
Vector2D displacement = Vector2D.Zero.Lerp(additiveLocalPosition, t) - progressedLocalPosition;
transform2D.LocalPosition += displacement;
progressedLocalPosition += displacement;
});
}
public static ITween TweenLocalScaleAdditive(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D additiveLocalScale)
{
Vector2D progressedLocalScale = Vector2D.Zero;
return tweenManager.StartTween(duration, t =>
{
Vector2D displacement = Vector2D.Zero.Lerp(additiveLocalScale, t) - progressedLocalScale;
transform2D.LocalScale += displacement;
progressedLocalScale += displacement;
});
}
public static ITween TweenLocalRotationAdditive(this ITransform2D transform2D, ITweenManager tweenManager, float duration, float additiveLocalRotation)
{
float progressedLocalRotation = 0f;
return tweenManager.StartTween(duration, t =>
{
float displacement = 0f.Lerp(additiveLocalRotation, t) - progressedLocalRotation;
transform2D.LocalRotation += displacement;
progressedLocalRotation += displacement;
});
}
} }