7 Commits

Author SHA1 Message Date
de336d0ee5 refactor: Dependency Injection to PhysicsEngine2Ds 2024-01-31 10:08:13 +03:00
8619778d52 feat: PhysicsEngine2DCacher
This class uses BehaviourCacher to track IGameManager's Rigidbody2D & Collider2Ds
2024-01-31 10:01:50 +03:00
4facfdb6cf fix: Physics Engine Stepping Inactive Rigid Bodies Fixed 2024-01-31 09:59:42 +03:00
f61f71dfe0 BREAKING CHANGE: Removed Add & Remove Rigidbody Methods from IPhysicsEngine2D 2024-01-31 09:59:11 +03:00
005c78a26e feat: BehaviourCacher<T> 2024-01-31 09:57:54 +03:00
01a99daf8a feat: IBehaviourController Enumarable<IGameObject> 2024-01-31 09:29:39 +03:00
8269c789a6 fix: IGameManager Action Types 2024-01-31 09:27:24 +03:00
8 changed files with 277 additions and 11 deletions

View File

@@ -7,7 +7,7 @@ namespace Syntriax.Engine.Core.Abstract;
/// <summary> /// <summary>
/// Responsible for controlling <see cref="IBehaviour"/>s and notify them accordingly about the engine's updates. Connected to an <see cref="IGameObject"/>. /// Responsible for controlling <see cref="IBehaviour"/>s and notify them accordingly about the engine's updates. Connected to an <see cref="IGameObject"/>.
/// </summary> /// </summary>
public interface IBehaviourController : IAssignableGameObject public interface IBehaviourController : IAssignableGameObject, IEnumerable<IBehaviour>
{ {
/// <summary> /// <summary>
/// Callback triggered when the <see cref="Update()"/> is called but right before the <see cref="OnUpdate"/> action is triggered. /// Callback triggered when the <see cref="Update()"/> is called but right before the <see cref="OnUpdate"/> action is triggered.

View File

@@ -5,8 +5,8 @@ namespace Syntriax.Engine.Core.Abstract;
public interface IGameManager : IEntity, IEnumerable<IGameObject> public interface IGameManager : IEntity, IEnumerable<IGameObject>
{ {
Action<GameManager, IGameObject>? OnGameObjectRegistered { get; set; } Action<IGameManager, IGameObject>? OnGameObjectRegistered { get; set; }
Action<GameManager, IGameObject>? OnGameObjectUnRegistered { get; set; } Action<IGameManager, IGameObject>? OnGameObjectUnRegistered { get; set; }
IReadOnlyList<IGameObject> GameObjects { get; } IReadOnlyList<IGameObject> GameObjects { get; }

View File

@@ -0,0 +1,102 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public class BehaviourCacher<T> : IAssignableGameManager, IEnumerable<T>
{
public Action<IAssignable>? OnUnassigned { get; set; } = null;
public Action<IAssignableGameManager>? OnGameManagerAssigned { get; set; } = null;
public Action<BehaviourCacher<T>, T>? OnCached { get; set; } = null;
public Action<BehaviourCacher<T>, T>? OnUncached { get; set; } = null;
private readonly List<T> _behaviours = new(32);
public IReadOnlyList<T> Behaviours => _behaviours;
public IGameManager GameManager { get; private set; } = null!;
public T this[Index index] => _behaviours[index];
public BehaviourCacher() { }
public BehaviourCacher(IGameManager gameManager) => Assign(gameManager);
private void OnGameObjectRegistered(IGameManager manager, IGameObject gameObject)
{
gameObject.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
gameObject.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
}
private void OnGameObjectUnregistered(IGameManager manager, IGameObject gameObject)
{
gameObject.BehaviourController.OnBehaviourAdded -= OnBehaviourAdded;
gameObject.BehaviourController.OnBehaviourRemoved -= OnBehaviourRemoved;
}
private void OnBehaviourAdded(IBehaviourController controller, IBehaviour behaviour)
{
if (behaviour is not T tBehaviour)
return;
_behaviours.Add(tBehaviour);
OnCached?.Invoke(this, tBehaviour);
}
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviour behaviour)
{
if (behaviour is not T tBehaviour)
return;
if (!_behaviours.Remove(tBehaviour))
return;
OnUncached?.Invoke(this, tBehaviour);
}
public bool Assign(IGameManager gameManager)
{
if (GameManager is not null)
return false;
foreach (IGameObject gameObject in gameManager)
{
OnGameObjectRegistered(gameManager, gameObject);
foreach (IBehaviour behaviour in gameObject.BehaviourController)
OnBehaviourAdded(gameObject.BehaviourController, behaviour);
}
gameManager.OnGameObjectRegistered += OnGameObjectRegistered;
gameManager.OnGameObjectUnRegistered += OnGameObjectUnregistered;
GameManager = gameManager;
OnGameManagerAssigned?.Invoke(this);
return true;
}
public bool Unassign()
{
if (GameManager is null)
return false;
foreach (IGameObject gameObject in GameManager)
{
OnGameObjectUnregistered(GameManager, gameObject);
foreach (IBehaviour behaviour in gameObject.BehaviourController)
OnBehaviourRemoved(gameObject.BehaviourController, behaviour);
}
GameManager.OnGameObjectRegistered -= OnGameObjectRegistered;
GameManager.OnGameObjectUnRegistered -= OnGameObjectUnregistered;
GameManager = null!;
OnUnassigned?.Invoke(this);
return true;
}
public IEnumerator<T> GetEnumerator() => _behaviours.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _behaviours.GetEnumerator();
}

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
@@ -169,4 +170,7 @@ public class BehaviourController : IBehaviourController
behaviours.Remove(behaviour); behaviours.Remove(behaviour);
InsertBehaviourByPriority(behaviour); InsertBehaviourByPriority(behaviour);
} }
public IEnumerator<IBehaviour> GetEnumerator() => behaviours.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => behaviours.GetEnumerator();
} }

View File

@@ -11,8 +11,8 @@ namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("GameObject Count: {_gameObjects.Count}")] [System.Diagnostics.DebuggerDisplay("GameObject Count: {_gameObjects.Count}")]
public class GameManager : IGameManager public class GameManager : IGameManager
{ {
public Action<GameManager, IGameObject>? OnGameObjectRegistered { get; set; } = null; public Action<IGameManager, IGameObject>? OnGameObjectRegistered { get; set; } = null;
public Action<GameManager, IGameObject>? OnGameObjectUnRegistered { get; set; } = null; public Action<IGameManager, IGameObject>? OnGameObjectUnRegistered { get; set; } = null;
public Action<IInitialize>? OnInitialized { get; set; } = null; public Action<IInitialize>? OnInitialized { get; set; } = null;
public Action<IInitialize>? OnFinalized { get; set; } = null; public Action<IInitialize>? OnFinalized { get; set; } = null;

View File

@@ -4,8 +4,5 @@ public interface IPhysicsEngine2D
{ {
int IterationCount { get; set; } int IterationCount { get; set; }
void AddRigidBody(IRigidBody2D rigidBody);
void RemoveRigidBody(IRigidBody2D rigidBody);
void Step(float deltaTime); void Step(float deltaTime);
} }

View File

@@ -12,8 +12,9 @@ public class PhysicsEngine2D : IPhysicsEngine2D
private readonly List<ICollider2D> colliders = new(64); private readonly List<ICollider2D> colliders = new(64);
private int _iterationCount = 1; private int _iterationCount = 1;
private ICollisionDetector2D collisionDetector = new CollisionDetector2D();
private ICollisionResolver2D collisionResolver = new CollisionResolver2D(); private readonly ICollisionDetector2D collisionDetector = null!;
private readonly ICollisionResolver2D collisionResolver = null!;
public int IterationCount { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; } public int IterationCount { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; }
@@ -100,7 +101,7 @@ public class PhysicsEngine2D : IPhysicsEngine2D
private static void StepRigidBody(IRigidBody2D rigidBody, float intervalDeltaTime) private static void StepRigidBody(IRigidBody2D rigidBody, float intervalDeltaTime)
{ {
if (rigidBody.IsStatic) if (rigidBody.IsStatic || !rigidBody.IsActive)
return; return;
rigidBody.Transform.Position += rigidBody.Velocity * intervalDeltaTime; rigidBody.Transform.Position += rigidBody.Velocity * intervalDeltaTime;
@@ -122,4 +123,16 @@ public class PhysicsEngine2D : IPhysicsEngine2D
colliders.Remove(collider2D); colliders.Remove(collider2D);
} }
public PhysicsEngine2D()
{
collisionDetector = new CollisionDetector2D();
collisionResolver = new CollisionResolver2D();
}
public PhysicsEngine2D(ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver)
{
this.collisionDetector = collisionDetector;
this.collisionResolver = collisionResolver;
}
} }

View File

@@ -0,0 +1,150 @@
using System;
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Physics2D.Abstract;
namespace Syntriax.Engine.Physics2D;
public class PhysicsEngine2DCacher : IPhysicsEngine2D, IAssignableGameManager
{
public Action<IAssignable>? OnUnassigned { get; set; } = null;
public Action<IAssignableGameManager>? OnGameManagerAssigned { get; set; } = null;
private int _iterationCount = 1;
protected readonly ICollisionDetector2D collisionDetector = null!;
protected readonly ICollisionResolver2D collisionResolver = null!;
protected BehaviourCacher<IRigidBody2D> rigidBodyCacher = new();
protected BehaviourCacher<ICollider2D> colliderCacher = new();
public int IterationCount { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; }
public IGameManager GameManager { get; private set; } = null!;
public void Step(float deltaTime)
{
float intervalDeltaTime = deltaTime / IterationCount;
for (int iterationIndex = 0; iterationIndex < IterationCount; iterationIndex++)
{
// Can Parallel
foreach (var rigidBody in rigidBodyCacher)
StepRigidBody(rigidBody, intervalDeltaTime);
// Can Parallel
foreach (var collider in colliderCacher)
collider.Recalculate();
// Can Parallel
for (int x = 0; x < colliderCacher.Behaviours.Count; x++)
{
ICollider2D? colliderX = colliderCacher.Behaviours[x];
if (!colliderX.IsActive)
return;
for (int y = x + 1; y < colliderCacher.Behaviours.Count; y++)
{
ICollider2D? colliderY = colliderCacher.Behaviours[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 && colliderX.RigidBody2D?.IsStatic == colliderY.RigidBody2D?.IsStatic;
if (bothCollidersAreStatic)
continue;
if (collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation information))
{
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);
}
}
}
}
}
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;
}
public bool Assign(IGameManager gameManager)
{
if (GameManager is not null)
return false;
colliderCacher.Assign(gameManager);
rigidBodyCacher.Assign(gameManager);
GameManager = gameManager;
OnGameManagerAssigned?.Invoke(this);
return true;
}
public bool Unassign()
{
if (GameManager is null)
return false;
colliderCacher.Unassign();
rigidBodyCacher.Unassign();
GameManager = null!;
OnUnassigned?.Invoke(this);
return true;
}
public PhysicsEngine2DCacher()
{
collisionDetector = new CollisionDetector2D();
collisionResolver = new CollisionResolver2D();
}
public PhysicsEngine2DCacher(IGameManager gameManager)
{
Assign(gameManager);
collisionDetector = new CollisionDetector2D();
collisionResolver = new CollisionResolver2D();
}
public PhysicsEngine2DCacher(IGameManager gameManager, ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver)
{
Assign(gameManager);
this.collisionDetector = collisionDetector;
this.collisionResolver = collisionResolver;
}
public PhysicsEngine2DCacher(ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver)
{
this.collisionDetector = collisionDetector;
this.collisionResolver = collisionResolver;
}
}