From 3f7a646bf0de759fab5e9c7dc7fa9f3d86773161 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Tue, 8 Apr 2025 20:30:32 +0300 Subject: [PATCH] refactor: updated PhysicsEngine2D to be plug & play --- Engine.Physics2D/PhysicsEngine2D.cs | 88 +++++----- Engine.Physics2D/PhysicsEngine2DStandalone.cs | 150 ++++++++++++++++++ 2 files changed, 195 insertions(+), 43 deletions(-) create mode 100644 Engine.Physics2D/PhysicsEngine2DStandalone.cs diff --git a/Engine.Physics2D/PhysicsEngine2D.cs b/Engine.Physics2D/PhysicsEngine2D.cs index 6b579f1..ad8b149 100644 --- a/Engine.Physics2D/PhysicsEngine2D.cs +++ b/Engine.Physics2D/PhysicsEngine2D.cs @@ -1,43 +1,27 @@ -using System.Collections.Generic; - +using Syntriax.Engine.Core; using Syntriax.Engine.Core.Abstract; using Syntriax.Engine.Physics2D.Abstract; namespace Syntriax.Engine.Physics2D; -public class PhysicsEngine2D : IPhysicsEngine2D +public class PhysicsEngine2D : HierarchyObject, IPhysicsEngine2D { public event IPhysicsEngine2D.PhysicsIterationEventHandler? OnPhysicsIteration = null; public event IPhysicsEngine2D.PhysicsStepEventHandler? OnPhysicsStep = null; - private readonly List rigidBodies = new(32); - private readonly List colliders = new(64); + private float physicsTicker = 0f; + private int _iterationPerStep = 1; + private float _iterationPeriod = 1f / 60f; - private int _iterationCount = 1; + protected readonly ICollisionDetector2D collisionDetector = null!; + protected readonly ICollisionResolver2D collisionResolver = null!; - private readonly ICollisionDetector2D collisionDetector = null!; - private readonly ICollisionResolver2D collisionResolver = null!; + protected BehaviourCollector rigidBodyCollector = new(); + protected BehaviourCollector colliderCollector = new(); + protected BehaviourCollector physicsUpdateCollector = new(); - public int IterationPerStep { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; } - - public void AddRigidBody(IRigidBody2D rigidBody) - { - if (rigidBodies.Contains(rigidBody)) - return; - - rigidBodies.Add(rigidBody); - - foreach (ICollider2D collider2D in rigidBody.BehaviourController.GetBehaviours()) - colliders.Add(collider2D); - - rigidBody.BehaviourController.OnBehaviourAdded += OnBehaviourAdded; - rigidBody.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved; - } - - public void RemoveRigidBody(IRigidBody2D rigidBody) - { - rigidBodies.Remove(rigidBody); - } + public int IterationPerStep { get => _iterationPerStep; set => _iterationPerStep = value < 1 ? 1 : value; } + public float IterationPeriod { get => _iterationPeriod; set => _iterationPeriod = value.Max(0.0001f); } public void Step(float deltaTime) { @@ -46,23 +30,23 @@ public class PhysicsEngine2D : IPhysicsEngine2D for (int iterationIndex = 0; iterationIndex < IterationPerStep; iterationIndex++) { // Can Parallel - for (int i = 0; i < rigidBodies.Count; i++) - StepRigidBody(rigidBodies[i], intervalDeltaTime); + foreach (IRigidBody2D rigidBody in rigidBodyCollector) + StepRigidBody(rigidBody, intervalDeltaTime); // Can Parallel - foreach (ICollider2D collider in colliders) + foreach (ICollider2D collider in colliderCollector) collider.Recalculate(); // Can Parallel - for (int x = 0; x < colliders.Count; x++) + for (int x = 0; x < colliderCollector.Behaviours.Count; x++) { - ICollider2D? colliderX = colliders[x]; + ICollider2D? colliderX = colliderCollector.Behaviours[x]; if (!colliderX.IsActive) return; - for (int y = x + 1; y < colliders.Count; y++) + for (int y = x + 1; y < colliderCollector.Behaviours.Count; y++) { - ICollider2D? colliderY = colliders[y]; + ICollider2D? colliderY = colliderCollector.Behaviours[y]; if (!colliderY.IsActive) return; @@ -106,8 +90,13 @@ public class PhysicsEngine2D : IPhysicsEngine2D } } } + OnPhysicsIteration?.Invoke(this, intervalDeltaTime); } + + foreach (IPhysicsUpdate physicsUpdate in physicsUpdateCollector) + physicsUpdate.PhysicsUpdate(deltaTime); + OnPhysicsStep?.Invoke(this, deltaTime); } @@ -120,20 +109,33 @@ public class PhysicsEngine2D : IPhysicsEngine2D rigidBody.Transform.Rotation += rigidBody.AngularVelocity * intervalDeltaTime; } - private void OnBehaviourAdded(IBehaviourController controller, IBehaviour behaviour) + protected override void OnEnteringHierarchy(IGameManager gameManager) { - if (behaviour is not ICollider2D collider2D) - return; + physicsUpdateCollector.Assign(gameManager); + colliderCollector.Assign(gameManager); + rigidBodyCollector.Assign(gameManager); - colliders.Add(collider2D); + gameManager.OnPreUpdate += OnEnginePreUpdate; } - private void OnBehaviourRemoved(IBehaviourController controller, IBehaviour behaviour) + protected override void OnExitingHierarchy(IGameManager gameManager) { - if (behaviour is not ICollider2D collider2D) - return; + physicsUpdateCollector.Unassign(); + colliderCollector.Unassign(); + rigidBodyCollector.Unassign(); - colliders.Remove(collider2D); + gameManager.OnPreUpdate -= OnEnginePreUpdate; + } + + private void OnEnginePreUpdate(IGameManager sender, EngineTime engineTime) + { + physicsTicker += engineTime.DeltaTime; + + while (physicsTicker >= IterationPeriod) + { + physicsTicker -= IterationPeriod; + Step(physicsTicker); + } } public PhysicsEngine2D() diff --git a/Engine.Physics2D/PhysicsEngine2DStandalone.cs b/Engine.Physics2D/PhysicsEngine2DStandalone.cs new file mode 100644 index 0000000..c4d1a69 --- /dev/null +++ b/Engine.Physics2D/PhysicsEngine2DStandalone.cs @@ -0,0 +1,150 @@ +using System.Collections.Generic; + +using Syntriax.Engine.Core.Abstract; +using Syntriax.Engine.Physics2D.Abstract; + +namespace Syntriax.Engine.Physics2D; + +public class PhysicsEngine2DStandalone : IPhysicsEngine2D +{ + public event IPhysicsEngine2D.PhysicsIterationEventHandler? OnPhysicsIteration = null; + public event IPhysicsEngine2D.PhysicsStepEventHandler? OnPhysicsStep = null; + + private readonly List rigidBodies = new(32); + private readonly List colliders = new(64); + + private int _iterationCount = 1; + + private readonly ICollisionDetector2D collisionDetector = null!; + private readonly ICollisionResolver2D collisionResolver = null!; + + public int IterationPerStep { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; } + + public void AddRigidBody(IRigidBody2D rigidBody) + { + if (rigidBodies.Contains(rigidBody)) + return; + + rigidBodies.Add(rigidBody); + + foreach (ICollider2D collider2D in rigidBody.BehaviourController.GetBehaviours()) + colliders.Add(collider2D); + + rigidBody.BehaviourController.OnBehaviourAdded += OnBehaviourAdded; + rigidBody.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved; + } + + public void RemoveRigidBody(IRigidBody2D rigidBody) + { + rigidBodies.Remove(rigidBody); + } + + public void Step(float deltaTime) + { + float intervalDeltaTime = deltaTime / IterationPerStep; + + for (int iterationIndex = 0; iterationIndex < IterationPerStep; iterationIndex++) + { + // Can Parallel + for (int i = 0; i < rigidBodies.Count; i++) + StepRigidBody(rigidBodies[i], intervalDeltaTime); + + // Can Parallel + foreach (ICollider2D collider in colliders) + collider.Recalculate(); + + // Can Parallel + for (int x = 0; x < colliders.Count; x++) + { + ICollider2D? colliderX = colliders[x]; + if (!colliderX.IsActive) + return; + + for (int y = x + 1; y < colliders.Count; y++) + { + ICollider2D? colliderY = colliders[y]; + + if (!colliderY.IsActive) + return; + + if (colliderX.RigidBody2D == colliderY.RigidBody2D) + continue; + + bool bothCollidersAreTriggers = colliderX.IsTrigger && colliderX.IsTrigger == colliderY.IsTrigger; + if (bothCollidersAreTriggers) + continue; + + bool bothCollidersAreStatic = (colliderX.RigidBody2D?.IsStatic ?? true) && (colliderY.RigidBody2D?.IsStatic ?? true); + if (bothCollidersAreStatic) + continue; + + if (collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation information)) + { + if (colliderX.IsTrigger) + { + colliderX.Trigger(colliderY); + continue; + } + else if (colliderY.IsTrigger) + { + colliderY.Trigger(colliderX); + continue; + } + + if (information.Detector == colliderX) + { + colliderX.Detect(information); + colliderY.Detect(information.Reverse()); + } + else + { + colliderX.Detect(information.Reverse()); + colliderY.Detect(information); + } + + collisionResolver?.Resolve(information); + } + } + } + OnPhysicsIteration?.Invoke(this, intervalDeltaTime); + } + OnPhysicsStep?.Invoke(this, deltaTime); + } + + private static void StepRigidBody(IRigidBody2D rigidBody, float intervalDeltaTime) + { + if (rigidBody.IsStatic || !rigidBody.IsActive) + return; + + rigidBody.Transform.Position += rigidBody.Velocity * intervalDeltaTime; + rigidBody.Transform.Rotation += rigidBody.AngularVelocity * intervalDeltaTime; + } + + private void OnBehaviourAdded(IBehaviourController controller, IBehaviour behaviour) + { + if (behaviour is not ICollider2D collider2D) + return; + + colliders.Add(collider2D); + } + + private void OnBehaviourRemoved(IBehaviourController controller, IBehaviour behaviour) + { + if (behaviour is not ICollider2D collider2D) + return; + + colliders.Remove(collider2D); + } + + public PhysicsEngine2DStandalone() + { + collisionDetector = new CollisionDetector2D(); + collisionResolver = new CollisionResolver2D(); + } + + public PhysicsEngine2DStandalone(ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver) + { + this.collisionDetector = collisionDetector; + this.collisionResolver = collisionResolver; + } +}