16 Commits

Author SHA1 Message Date
ef21cdf213 refactor: Actions to Delegates 2024-07-15 01:13:39 +03:00
2cf6135063 refactor: Renamed BehaviourCacher to BehaviourCollector 2024-02-09 09:43:15 +03:00
be06575f91 feat: BehaviourExtensions.FindBehaviour 2024-02-08 17:58:15 +03:00
ed6975bf24 fix: Null Reference Error on ITransform.SetParent 2024-02-07 14:24:19 +03:00
d9660c08b1 feat: Collider RigidBody Reference Update on Parent Change 2024-02-07 12:33:07 +03:00
3902f1caca feat: Parent Change Propagation to Children 2024-02-07 12:32:55 +03:00
14e3337daa feat: BehaviourControllerExtensions
- TryGetBehaviourInParent
- GetBehaviourInParent
- TryGetBehaviourInChildren
- GetBehaviourInChildren
2024-02-07 11:53:57 +03:00
f729cdc0a8 revert: refactor: ITransformWithGameObject
This reverts commit f96c58cbd4.
2024-02-07 11:45:14 +03:00
c767e1e856 docs(core): Parent & Child Methods 2024-02-06 17:42:24 +03:00
f96c58cbd4 refactor: ITransformWithGameObject 2024-02-06 17:38:11 +03:00
fed288859f feat: IAssignableGameObject to ITransform
I originally didn't want ITransform to have a reference to any IGameObject, since it didn't seem necessary to couple these two, and to make ITransform more flexible and reusable but without it we can't get a reference to the IGameObject(s) that's using that ITransform without doing some very stupid workarounds. I'll try to find a better way for this.
2024-02-06 17:32:39 +03:00
6e4c9b0ef8 feat: Transform Hierarchy System 2024-02-06 15:55:07 +03:00
b931abb735 feat: Shape to Vector2D Overlap 2024-02-06 10:56:54 +03:00
72492a9f5a docs: Basic README.md 2024-02-05 12:20:12 +03:00
2f043c19a6 feat: GameManagerExtensions.InstantiateGameObject 2024-02-05 11:20:15 +03:00
dbb263ebed feat: IEntity.Id & BaseEntity 2024-02-02 17:40:46 +03:00
37 changed files with 720 additions and 325 deletions

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
@@ -10,7 +8,7 @@ public interface IAssignable
/// <summary>
/// Event triggered when the <see cref="IAssignable"/>'s fields are unassigned and completely ready to recycle.
/// </summary>
Action<IAssignable>? OnUnassigned { get; set; }
event OnUnassignedDelegate? OnUnassigned;
/// <summary>
/// Unassign <see cref="IAssignable"/>'s all fields and make it ready to recycle.
@@ -19,4 +17,6 @@ public interface IAssignable
/// <see cref="true"/>, if the fields are unsigned successfully, <see cref="false"/> if not.
/// </returns>
bool Unassign();
delegate void OnUnassignedDelegate(IAssignable sender);
}

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
@@ -10,7 +8,7 @@ public interface IAssignableBehaviourController : IAssignable
/// <summary>
/// Event triggered when the <see cref="IBehaviourController"/> value has has been assigned a new value.
/// </summary>
Action<IAssignableBehaviourController>? OnBehaviourControllerAssigned { get; set; }
event OnBehaviourControllerAssignedDelegate? OnBehaviourControllerAssigned;
/// <inheritdoc cref="IBehaviourController" />
IBehaviourController BehaviourController { get; }
@@ -23,4 +21,6 @@ public interface IAssignableBehaviourController : IAssignable
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
/// </returns>
bool Assign(IBehaviourController behaviourController);
delegate void OnBehaviourControllerAssignedDelegate(IAssignableBehaviourController sender);
}

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
@@ -10,7 +8,7 @@ public interface IAssignableEntity : IAssignable
/// <summary>
/// Event triggered when the <see cref="IEntity"/> value has has been assigned a new value.
/// </summary>
Action<IAssignableEntity>? OnEntityAssigned { get; set; }
event OnEntityAssignedDelegate? OnEntityAssigned;
/// <inheritdoc cref="IEntity" />
IEntity Entity { get; }
@@ -23,4 +21,6 @@ public interface IAssignableEntity : IAssignable
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
/// </returns>
bool Assign(IEntity entity);
delegate void OnEntityAssignedDelegate(IAssignableEntity sender);
}

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
@@ -10,7 +8,7 @@ public interface IAssignableGameManager : IAssignable
/// <summary>
/// Event triggered when the <see cref="IGameManager"/> value has has been assigned a new value.
/// </summary>
Action<IAssignableGameManager>? OnGameManagerAssigned { get; set; }
event OnGameManagerAssignedDelegate? OnGameManagerAssigned;
/// <inheritdoc cref="IGameManager" />
IGameManager GameManager { get; }
@@ -23,4 +21,6 @@ public interface IAssignableGameManager : IAssignable
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
/// </returns>
bool Assign(IGameManager gameManager);
delegate void OnGameManagerAssignedDelegate(IAssignableGameManager sender);
}

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
@@ -10,7 +8,7 @@ public interface IAssignableGameObject : IAssignable
/// <summary>
/// Event triggered when the <see cref="IGameObject"/> value has has been assigned a new value.
/// </summary>
Action<IAssignableGameObject>? OnGameObjectAssigned { get; set; }
event OnGameObjectAssignedDelegate? OnGameObjectAssigned;
/// <inheritdoc cref="IGameObject" />
IGameObject GameObject { get; }
@@ -23,4 +21,6 @@ public interface IAssignableGameObject : IAssignable
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
/// </returns>
bool Assign(IGameObject gameObject);
delegate void OnGameObjectAssignedDelegate(IAssignableGameObject sender);
}

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
@@ -10,7 +8,7 @@ public interface IAssignableStateEnable : IAssignable
/// <summary>
/// Event triggered when the <see cref="IStateEnable"/> value has has been assigned a new value.
/// </summary>
Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; }
event OnStateEnableAssignedDelegate? OnStateEnableAssigned;
/// <inheritdoc cref="IStateEnable" />
IStateEnable StateEnable { get; }
@@ -23,4 +21,6 @@ public interface IAssignableStateEnable : IAssignable
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
/// </returns>
bool Assign(IStateEnable stateEnable);
delegate void OnStateEnableAssignedDelegate(IAssignableStateEnable sender);
}

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
@@ -10,7 +8,7 @@ public interface IAssignableTransform : IAssignable
/// <summary>
/// Event triggered when the <see cref="ITransform"/> value has has been assigned a new value.
/// </summary>
Action<IAssignableTransform>? OnTransformAssigned { get; set; }
event OnTransformAssignedDelegate? OnTransformAssigned;
/// <inheritdoc cref="ITransform" />
ITransform Transform { get; }
@@ -23,4 +21,6 @@ public interface IAssignableTransform : IAssignable
/// <see cref="true"/>, if the value given assigned successfully assigned, <see cref="false"/> if not.
/// </returns>
bool Assign(ITransform transform);
delegate void OnTransformAssignedDelegate(IAssignableTransform sender);
}

View File

@@ -0,0 +1,105 @@
using System;
namespace Syntriax.Engine.Core.Abstract;
public abstract class BaseEntity : IEntity
{
public event IEntity.OnIdChangedDelegate? OnIdChanged = null;
public event IInitialize.OnInitializedDelegate? OnInitialized = null;
public event IInitialize.OnFinalizedDelegate? OnFinalized = null;
public event IAssignableStateEnable.OnStateEnableAssignedDelegate? OnStateEnableAssigned = null;
public event IAssignable.OnUnassignedDelegate? OnUnassigned = null;
private IStateEnable _stateEnable = null!;
private bool _initialized = false;
private string _id = string.Empty;
public virtual IStateEnable StateEnable => _stateEnable;
public virtual bool IsActive => StateEnable.Enabled;
public string Id
{
get => _id;
set
{
if (value == _id)
return;
string previousId = _id;
_id = value;
OnIdChanged?.Invoke(this, previousId);
}
}
public bool Initialized
{
get => _initialized;
private set
{
if (value == _initialized)
return;
_initialized = value;
if (value)
OnInitialized?.Invoke(this);
else
OnFinalized?.Invoke(this);
}
}
public bool Assign(IStateEnable stateEnable)
{
if (Initialized)
return false;
_stateEnable = stateEnable;
_stateEnable.Assign(this);
OnStateEnableAssigned?.Invoke(this);
return true;
}
protected virtual void UnassignInternal() { }
public bool Unassign()
{
if (Initialized)
return false;
UnassignInternal();
OnUnassigned?.Invoke(this);
return true;
}
protected virtual void InitializeInternal() { }
public bool Initialize()
{
if (Initialized)
return false;
InitializeInternal();
Initialized = true;
return true;
}
protected virtual void FinalizeInternal() { }
public bool Finalize()
{
if (!Initialized)
return false;
FinalizeInternal();
Initialized = false;
return true;
}
protected BaseEntity() => _id = Guid.NewGuid().ToString("D");
protected BaseEntity(string id) => _id = id;
}

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
@@ -10,7 +8,7 @@ public interface IBehaviour : IEntity, IAssignableBehaviourController, IAssignab
/// <summary>
/// Event triggered when the priority of the <see cref="IBehaviour"/> changes.
/// </summary>
Action<IBehaviour>? OnPriorityChanged { get; set; }
event OnPriorityChangedDelegate? OnPriorityChanged;
/// <summary>
/// The priority of the <see cref="IBehaviour"/>.
@@ -21,4 +19,6 @@ public interface IBehaviour : IEntity, IAssignableBehaviourController, IAssignab
/// The value indicating whether the <see cref="IBehaviour"/> is active.
/// </summary>
bool IsActive { get; }
delegate void OnPriorityChangedDelegate(IBehaviour sender, int previousPriority);
}

View File

@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@@ -12,27 +11,27 @@ public interface IBehaviourController : IAssignableGameObject, IEnumerable<IBeha
/// <summary>
/// Event triggered before the update of <see cref="IBehaviour"/>s.
/// </summary>
Action<IBehaviourController>? OnPreUpdate { get; set; }
event OnPreUpdateDelegate? OnPreUpdate;
/// <summary>
/// Event triggered during the update of <see cref="IBehaviour"/>s.
/// </summary>
Action<IBehaviourController>? OnUpdate { get; set; }
event OnUpdateDelegate? OnUpdate;
/// <summary>
/// Event triggered before the drawing phase.
/// </summary>
Action<IBehaviourController>? OnPreDraw { get; set; }
event OnPreDrawDelegate? OnPreDraw;
/// <summary>
/// Event triggered when a <see cref="IBehaviour"/> is added to the <see cref="IBehaviourController"/>.
/// </summary>
Action<IBehaviourController, IBehaviour>? OnBehaviourAdded { get; set; }
event OnBehaviourAddedDelegate? OnBehaviourAdded;
/// <summary>
/// Event triggered when a <see cref="IBehaviour"/> is removed from the <see cref="IBehaviourController"/>.
/// </summary>
Action<IBehaviourController, IBehaviour>? OnBehaviourRemoved { get; set; }
event OnBehaviourRemovedDelegate? OnBehaviourRemoved;
/// <summary>
/// Adds a <see cref="IBehaviour"/> to the <see cref="IBehaviourController"/>.
@@ -102,4 +101,11 @@ public interface IBehaviourController : IAssignableGameObject, IEnumerable<IBeha
/// Performs pre-draw operations.
/// </summary>
void UpdatePreDraw();
delegate void OnPreUpdateDelegate(IBehaviourController sender);
delegate void OnUpdateDelegate(IBehaviourController sender);
delegate void OnPreDrawDelegate(IBehaviourController sender);
delegate void OnBehaviourAddedDelegate(IBehaviourController sender, IBehaviour behaviourAdded);
delegate void OnBehaviourRemovedDelegate(IBehaviourController sender, IBehaviour behaviourRemoved);
}

View File

@@ -5,4 +5,16 @@ namespace Syntriax.Engine.Core.Abstract;
/// </summary>
public interface IEntity : IInitialize, IAssignableStateEnable
{
/// <summary>
/// Event triggered when the <see cref="Id"/> of the <see cref="IEntity"/> changes.
/// The string action parameter is the previous <see cref="Id"/> of the <see cref="IEntity"/>.
/// </summary>
event OnIdChangedDelegate? OnIdChanged;
/// <summary>
/// The ID of the <see cref="IEntity"/>.
/// </summary>
string Id { get; set; }
delegate void OnIdChangedDelegate(IEntity sender, string previousId);
}

View File

@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
namespace Syntriax.Engine.Core.Abstract;
@@ -11,12 +10,12 @@ public interface IGameManager : IEntity, IEnumerable<IGameObject>
/// <summary>
/// Event triggered when a <see cref="IGameObject"/> is registered to the <see cref="IGameManager"/>.
/// </summary>
Action<IGameManager, IGameObject>? OnGameObjectRegistered { get; set; }
event OnGameObjectRegisteredDelegate? OnGameObjectRegistered;
/// <summary>
/// Event triggered when a <see cref="IGameObject"/> is unregistered from the <see cref="IGameManager"/>.
/// </summary>
Action<IGameManager, IGameObject>? OnGameObjectUnRegistered { get; set; }
event OnGameObjectUnRegisteredDelegate? OnGameObjectUnRegistered;
/// <summary>
/// Gets a read-only list of <see cref="IGameObject"/>s managed by the <see cref="IGameManager"/>.
@@ -54,4 +53,7 @@ public interface IGameManager : IEntity, IEnumerable<IGameObject>
/// Performs operations that should be done before the draw calls.
/// </summary>
void PreDraw();
delegate void OnGameObjectRegisteredDelegate(IGameManager sender, IGameObject gameObjectRegistered);
delegate void OnGameObjectUnRegisteredDelegate(IGameManager sender, IGameObject gameObjectUnregistered);
}

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
@@ -10,10 +8,12 @@ public interface IGameObject : IEntity, IAssignableGameManager, IAssignableTrans
/// <summary>
/// Event triggered when the <see cref="Update"/> method is called.
/// </summary>
Action<IGameObject>? OnUpdated { get; set; }
event OnUpdatedDelegate? OnUpdated;
/// <summary>
/// Updates the game object.
/// </summary>
void Update();
delegate void OnUpdatedDelegate(IGameObject sender);
}

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
@@ -10,12 +8,12 @@ public interface IInitialize
/// <summary>
/// Event triggered when the <see cref="Initialize"/> method is called successfully.
/// </summary>
Action<IInitialize>? OnInitialized { get; set; }
event OnInitializedDelegate? OnInitialized;
/// <summary>
/// Event triggered when the <see cref="Finalize"/> method is called successfully.
/// </summary>
Action<IInitialize>? OnFinalized { get; set; }
event OnFinalizedDelegate? OnFinalized;
/// <summary>
/// The value indicating whether the entity has been initialized.
@@ -33,4 +31,7 @@ public interface IInitialize
/// </summary>
/// <returns><see cref="true"/> if finalization is successful, otherwise <see cref="false"/>.</returns>
bool Finalize();
delegate void OnInitializedDelegate(IInitialize sender);
delegate void OnFinalizedDelegate(IInitialize sender);
}

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
@@ -10,10 +8,12 @@ public interface INameable
/// <summary>
/// Event triggered when the name of the entity changes.
/// </summary>
Action<IEntity>? OnNameChanged { get; set; }
event OnNameChangedDelegate? OnNameChanged;
/// <summary>
/// The name of the entity.
/// </summary>
string Name { get; set; }
delegate void OnNameChangedDelegate(INameable sender, string previousName);
}

View File

@@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
@@ -10,10 +8,12 @@ public interface IStateEnable : IAssignableEntity
/// <summary>
/// Event triggered when the <see cref="Enabled"/> state of the <see cref="IStateEnable"/> changes.
/// </summary>
Action<IStateEnable>? OnEnabledChanged { get; set; }
event OnNameChangedDelegate? OnEnabledChanged;
/// <summary>
/// The value indicating whether the <see cref="IStateEnable"/> is enabled.
/// </summary>
bool Enabled { get; set; }
delegate void OnNameChangedDelegate(IStateEnable sender, bool previousState);
}

View File

@@ -1,39 +1,105 @@
using System;
using System.Collections.Generic;
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Represents the transformation properties of an object such as position, scale, and rotation.
/// </summary>
public interface ITransform
public interface ITransform : IAssignableGameObject, IEnumerable<ITransform>
{
/// <summary>
/// Event triggered when the <see cref="Position"/> of the <see cref="ITransform"/> changes.
/// </summary>
Action<ITransform>? OnPositionChanged { get; set; }
event OnPositionChangedDelegate? OnPositionChanged;
/// <summary>
/// Event triggered when the <see cref="Scale"/> of the <see cref="ITransform"/> changes.
/// </summary>
Action<ITransform>? OnScaleChanged { get; set; }
event OnScaleChangedDelegate? OnScaleChanged;
/// <summary>
/// Event triggered when the <see cref="Rotation"/> of the <see cref="ITransform"/> changes.
/// </summary>
Action<ITransform>? OnRotationChanged { get; set; }
event OnRotationChangedDelegate? OnRotationChanged;
/// <summary>
/// The position of the <see cref="ITransform"/> in 2D space.
/// Event triggered when the <see cref="Parent"/> of the <see cref="ITransform"/> changes. The second parameter is the old <see cref="ITransform"/>.
/// </summary>
event OnParentChangedDelegate? OnParentChanged;
/// <summary>
/// Event triggered when a new <see cref="ITransform"/> is added to the <see cref="Children"/>.
/// </summary>
event OnChildrenAddedDelegate? OnChildrenAdded;
/// <summary>
/// Event triggered when an <see cref="ITransform"/> is removed from the <see cref="Children"/>.
/// </summary>
event OnChildrenRemovedDelegate? OnChildrenRemoved;
/// <summary>
/// The world position of the <see cref="ITransform"/> in 2D space.
/// </summary>
Vector2D Position { get; set; }
/// <summary>
/// The scale of the <see cref="ITransform"/>.
/// The world scale of the <see cref="ITransform"/>.
/// </summary>
Vector2D Scale { get; set; }
/// <summary>
/// The rotation of the <see cref="ITransform"/> in degrees.
/// The world rotation of the <see cref="ITransform"/> in degrees.
/// </summary>
float Rotation { get; set; }
/// <summary>
/// The local position of the <see cref="ITransform"/> in 2D space.
/// </summary>
Vector2D LocalPosition { get; set; }
/// <summary>
/// The local scale of the <see cref="ITransform"/>.
/// </summary>
Vector2D LocalScale { get; set; }
/// <summary>
/// The local rotation of the <see cref="ITransform"/> in degrees.
/// </summary>
float LocalRotation { get; set; }
/// <summary>
/// The parent <see cref="ITransform"/> of the <see cref="ITransform"/>.
/// </summary>
ITransform? Parent { get; }
/// <summary>
/// The <see cref="ITransform"/>s that have this <see cref="ITransform"/> as their <see cref="Parent"/>.
/// </summary>
IReadOnlyList<ITransform> Children { get; }
/// <summary>
/// Sets the parent <see cref="ITransform"/> of this <see cref="ITransform"/>.
/// </summary>
/// <param name="transform">The parent <see cref="ITransform"/> to set.</param>
void SetParent(ITransform? transform);
/// <summary>
/// Adds a child <see cref="ITransform"/> to this <see cref="ITransform"/>.
/// </summary>
/// <param name="transform">The child <see cref="ITransform"/> to add.</param>
void AddChild(ITransform transform);
/// <summary>
/// Removes a child <see cref="ITransform"/> from this <see cref="ITransform"/>.
/// </summary>
/// <param name="transform">The child <see cref="ITransform"/> to remove.</param>
void RemoveChild(ITransform transform);
delegate void OnPositionChangedDelegate(ITransform sender);
delegate void OnScaleChangedDelegate(ITransform sender);
delegate void OnRotationChangedDelegate(ITransform sender);
delegate void OnParentChangedDelegate(ITransform sender, ITransform? newParent);
delegate void OnChildrenAddedDelegate(ITransform sender, ITransform childrenAdded);
delegate void OnChildrenRemovedDelegate(ITransform sender, ITransform childrenRemoved);
}

View File

@@ -1,48 +1,24 @@
using System;
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Core.Exceptions;
namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("{GetType().Name, nq}, Priority: {Priority}, Initialized: {Initialized}")]
public abstract class Behaviour : IBehaviour
public abstract class Behaviour : BaseEntity, IBehaviour
{
public Action<IAssignable>? OnUnassigned { get; set; } = null;
public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
public Action<IAssignableBehaviourController>? OnBehaviourControllerAssigned { get; set; } = null;
public event IAssignableBehaviourController.OnBehaviourControllerAssignedDelegate? OnBehaviourControllerAssigned = null;
public Action<IInitialize>? OnInitialized { get; set; } = null;
public Action<IInitialize>? OnFinalized { get; set; } = null;
public Action<IBehaviour>? OnPriorityChanged { get; set; } = null;
public event IBehaviour.OnPriorityChangedDelegate? OnPriorityChanged = null;
private IBehaviourController _behaviourController = null!;
private IStateEnable _stateEnable = null!;
private bool _initialized = false;
private int _priority = 0;
public IStateEnable StateEnable => _stateEnable;
public IBehaviourController BehaviourController => _behaviourController;
public bool IsActive => StateEnable.Enabled && BehaviourController.GameObject.StateEnable.Enabled;
public bool Initialized
{
get => _initialized;
private set
{
if (value == _initialized)
return;
_initialized = value;
if (value)
OnInitialized?.Invoke(this);
else
OnFinalized?.Invoke(this);
}
}
public override bool IsActive => base.IsActive && BehaviourController.GameObject.StateEnable.Enabled;
public int Priority
{
@@ -52,22 +28,12 @@ public abstract class Behaviour : IBehaviour
if (value == _priority)
return;
int previousPriority = _priority;
_priority = value;
OnPriorityChanged?.Invoke(this);
OnPriorityChanged?.Invoke(this, previousPriority);
}
}
public bool Assign(IStateEnable stateEnable)
{
if (Initialized)
return false;
_stateEnable = stateEnable;
_stateEnable.Assign(this);
OnStateEnableAssigned?.Invoke(this);
return true;
}
public bool Assign(IBehaviourController behaviourController)
{
if (Initialized)
@@ -78,36 +44,16 @@ public abstract class Behaviour : IBehaviour
return true;
}
public bool Unassign()
protected override void UnassignInternal()
{
if (Initialized)
return false;
_stateEnable = null!;
base.UnassignInternal();
_behaviourController = null!;
OnUnassigned?.Invoke(this);
return true;
}
public bool Initialize()
protected override void InitializeInternal()
{
if (Initialized)
return false;
base.InitializeInternal();
NotAssignedException.Check(this, _behaviourController);
NotAssignedException.Check(this, _stateEnable);
Initialized = true;
return true;
}
public bool Finalize()
{
if (!Initialized)
return false;
Initialized = false;
return true;
NotAssignedException.Check(this, StateEnable);
}
}

View File

@@ -6,13 +6,13 @@ using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public class BehaviourCacher<T> : IAssignableGameManager, IEnumerable<T>
public class BehaviourCollector<T> : IAssignableGameManager, IEnumerable<T>
{
public Action<IAssignable>? OnUnassigned { get; set; } = null;
public Action<IAssignableGameManager>? OnGameManagerAssigned { get; set; } = null;
public event IAssignable.OnUnassignedDelegate? OnUnassigned = null;
public event IAssignableGameManager.OnGameManagerAssignedDelegate? OnGameManagerAssigned = null;
public Action<BehaviourCacher<T>, T>? OnCached { get; set; } = null;
public Action<BehaviourCacher<T>, T>? OnUncached { get; set; } = null;
public event OnCollectedDelegate? OnCollected = null;
public event OnRemovedDelegate? OnRemoved = null;
private readonly List<T> _behaviours = new(32);
@@ -21,8 +21,8 @@ public class BehaviourCacher<T> : IAssignableGameManager, IEnumerable<T>
public T this[Index index] => _behaviours[index];
public BehaviourCacher() { }
public BehaviourCacher(IGameManager gameManager) => Assign(gameManager);
public BehaviourCollector() { }
public BehaviourCollector(IGameManager gameManager) => Assign(gameManager);
private void OnGameObjectRegistered(IGameManager manager, IGameObject gameObject)
{
@@ -42,7 +42,7 @@ public class BehaviourCacher<T> : IAssignableGameManager, IEnumerable<T>
return;
_behaviours.Add(tBehaviour);
OnCached?.Invoke(this, tBehaviour);
OnCollected?.Invoke(this, tBehaviour);
}
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviour behaviour)
@@ -53,7 +53,7 @@ public class BehaviourCacher<T> : IAssignableGameManager, IEnumerable<T>
if (!_behaviours.Remove(tBehaviour))
return;
OnUncached?.Invoke(this, tBehaviour);
OnRemoved?.Invoke(this, tBehaviour);
}
public bool Assign(IGameManager gameManager)
@@ -99,4 +99,8 @@ public class BehaviourCacher<T> : IAssignableGameManager, IEnumerable<T>
public IEnumerator<T> GetEnumerator() => _behaviours.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _behaviours.GetEnumerator();
public delegate void OnCollectedDelegate(BehaviourCollector<T> sender, T behaviourCollected);
public delegate void OnRemovedDelegate(BehaviourCollector<T> sender, T behaviourRemoved);
}

View File

@@ -11,14 +11,14 @@ namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count}")]
public class BehaviourController : IBehaviourController
{
public Action<IBehaviourController>? OnPreUpdate { get; set; }
public Action<IBehaviourController>? OnUpdate { get; set; } = null;
public Action<IBehaviourController>? OnPreDraw { get; set; } = null;
public event IBehaviourController.OnPreUpdateDelegate? OnPreUpdate = null;
public event IBehaviourController.OnUpdateDelegate? OnUpdate = null;
public event IBehaviourController.OnPreDrawDelegate? OnPreDraw = null;
public Action<IBehaviourController, IBehaviour>? OnBehaviourAdded { get; set; } = null;
public Action<IBehaviourController, IBehaviour>? OnBehaviourRemoved { get; set; } = null;
public Action<IAssignable>? OnUnassigned { get; set; } = null;
public Action<IAssignableGameObject>? OnGameObjectAssigned { get; set; } = null;
public event IBehaviourController.OnBehaviourAddedDelegate? OnBehaviourAdded = null;
public event IBehaviourController.OnBehaviourRemovedDelegate? OnBehaviourRemoved = null;
public event IAssignable.OnUnassignedDelegate? OnUnassigned;
public event IAssignableGameObject.OnGameObjectAssignedDelegate? OnGameObjectAssigned = null;
private readonly IList<IBehaviour> behaviours = new List<IBehaviour>(Constants.BEHAVIOURS_SIZE_INITIAL);
@@ -165,10 +165,11 @@ public class BehaviourController : IBehaviourController
behaviours.Add(behaviour);
}
private void OnPriorityChange(IBehaviour behaviour)
private void OnPriorityChange(IBehaviour sender, int previousPriority)
{
behaviours.Remove(behaviour);
InsertBehaviourByPriority(behaviour);
behaviours.Remove(sender);
InsertBehaviourByPriority(sender);
}
public IEnumerator<IBehaviour> GetEnumerator() => behaviours.GetEnumerator();

View File

@@ -0,0 +1,47 @@
using System.Diagnostics.CodeAnalysis;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public static class BehaviourControllerExtensions
{
public static bool TryGetBehaviourInParent<T>(this IBehaviourController behaviourController, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
{
behaviour = GetBehaviourInParent<T>(behaviourController);
return behaviour is not null;
}
public static T? GetBehaviourInParent<T>(this IBehaviourController behaviourController) where T : class
{
IBehaviourController? controller = behaviourController;
while (controller is not null)
{
if (behaviourController.GetBehaviour<T>() is T behaviour)
return behaviour;
controller = controller.GameObject.Transform.Parent?.GameObject.BehaviourController;
}
return default;
}
public static bool TryGetBehaviourInChildren<T>(this IBehaviourController behaviourController, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
{
behaviour = GetBehaviourInChildren<T>(behaviourController);
return behaviour is not null;
}
public static T? GetBehaviourInChildren<T>(this IBehaviourController behaviourController) where T : class
{
if (behaviourController.GetBehaviour<T>() is T localBehaviour)
return localBehaviour;
foreach (ITransform transform in behaviourController.GameObject.Transform)
if (GetBehaviourInChildren<T>(transform.GameObject.BehaviourController) is T behaviour)
return behaviour;
return default;
}
}

View File

@@ -7,18 +7,22 @@ namespace Syntriax.Engine.Core;
public static class BehaviourExtensions
{
public static bool TryFindBehaviour<T>(this IEnumerable<IGameObject> gameObjects, [NotNullWhen(returnValue: true)] out T? behaviour)
public static T? FindBehaviour<T>(this IEnumerable<IGameObject> gameObjects) where T : class
{
behaviour = default;
foreach (IGameObject gameObject in gameObjects)
if (gameObject.BehaviourController.TryGetBehaviour(out behaviour))
return true;
if (gameObject.BehaviourController.TryGetBehaviour(out T? behaviour))
return behaviour;
return false;
return default;
}
public static void FindBehaviours<T>(this IEnumerable<IGameObject> gameObjects, List<T> behaviours)
public static bool TryFindBehaviour<T>(this IEnumerable<IGameObject> gameObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
{
behaviour = FindBehaviour<T>(gameObjects);
return behaviour is not null;
}
public static void FindBehaviours<T>(this IEnumerable<IGameObject> gameObjects, List<T> behaviours) where T : class
{
behaviours.Clear();
List<T> cache = [];

View File

@@ -0,0 +1,9 @@
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public static class GameManagerExtensions
{
public static IGameObject InstantiateGameObject(this IGameManager gameManager, params object?[]? args)
=> gameManager.InstantiateGameObject<GameObject>(args);
}

View File

@@ -4,11 +4,16 @@ namespace Syntriax.Engine.Core;
public static class TransformExtensions
{
public static ITransform SetTransform(this ITransform transform, Vector2D? position = null, float? rotation = null, Vector2D? scale = null)
public static ITransform SetTransform(this ITransform transform,
Vector2D? position = null, float? rotation = null, Vector2D? scale = null,
Vector2D? localPosition = null, float? localRotation = null, Vector2D? localScale = null)
{
if (position.HasValue) transform.Position = position.Value;
if (rotation.HasValue) transform.Rotation = rotation.Value;
if (scale.HasValue) transform.Scale = scale.Value;
if (localPosition.HasValue) transform.LocalPosition = localPosition.Value;
if (localRotation.HasValue) transform.LocalRotation = localRotation.Value;
if (localScale.HasValue) transform.LocalScale = localScale.Value;
return transform;
}
}

View File

@@ -22,6 +22,9 @@ public class GameObjectFactory
behaviourController ??= TypeFactory.Get<BehaviourController>();
stateEnable ??= TypeFactory.Get<StateEnable>();
if (!transform.Assign(gameObject))
throw AssignException.From(transform, gameObject);
if (!behaviourController.Assign(gameObject))
throw AssignException.From(behaviourController, gameObject);
if (!stateEnable.Assign(gameObject))

View File

@@ -9,22 +9,15 @@ using Syntriax.Engine.Core.Factory;
namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("GameObject Count: {_gameObjects.Count}")]
public class GameManager : IGameManager
public class GameManager : BaseEntity, IGameManager
{
public Action<IGameManager, IGameObject>? OnGameObjectRegistered { get; set; } = null;
public Action<IGameManager, IGameObject>? OnGameObjectUnRegistered { get; set; } = null;
public Action<IInitialize>? OnInitialized { get; set; } = null;
public Action<IInitialize>? OnFinalized { get; set; } = null;
public Action<IAssignable>? OnUnassigned { get; set; } = null;
public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
public event IGameManager.OnGameObjectRegisteredDelegate? OnGameObjectRegistered = null;
public event IGameManager.OnGameObjectUnRegisteredDelegate? OnGameObjectUnRegistered = null;
private readonly List<IGameObject> _gameObjects = new(Constants.GAME_OBJECTS_SIZE_INITIAL);
private IStateEnable _stateEnable = null!;
private GameObjectFactory _gameObjectFactory = null!;
private bool _initialized = false;
private GameObjectFactory GameObjectFactory
{
@@ -36,21 +29,20 @@ public class GameManager : IGameManager
}
}
public bool Initialized => _initialized;
public IReadOnlyList<IGameObject> GameObjects => _gameObjects;
public IStateEnable StateEnable
public override IStateEnable StateEnable
{
get
{
if (_stateEnable is null)
if (base.StateEnable is null)
{
Assign(new StateEnableFactory().Instantiate(this));
if (_stateEnable is null)
throw NotAssignedException.From(this, _stateEnable);
if (base.StateEnable is null)
throw NotAssignedException.From(this, base.StateEnable);
}
return _stateEnable;
return base.StateEnable;
}
}
@@ -78,52 +70,20 @@ public class GameManager : IGameManager
return gameObject;
}
public bool Initialize()
protected override void InitializeInternal()
{
if (Initialized)
return false;
base.InitializeInternal();
NotAssignedException.Check(this, StateEnable);
foreach (var gameObject in GameObjects)
gameObject.Initialize();
_initialized = true;
OnInitialized?.Invoke(this);
return true;
}
public bool Finalize()
protected override void FinalizeInternal()
{
if (!Initialized)
return false;
base.FinalizeInternal();
for (int i = GameObjects.Count; i >= 0; i--)
GameObjects[i].Finalize();
OnFinalized?.Invoke(this);
_initialized = false;
return true;
}
public bool Assign(IStateEnable stateEnable)
{
if (Initialized)
return false;
_stateEnable = stateEnable;
OnStateEnableAssigned?.Invoke(this);
return true;
}
public bool Unassign()
{
if (Initialized)
return false;
_stateEnable = null!;
OnUnassigned?.Invoke(this);
return true;
}
public void Update(EngineTime time)

View File

@@ -1,25 +1,18 @@
using System;
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Core.Exceptions;
namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("Name: {Name}, Initialized: {Initialized}")]
public class GameObject : IGameObject
public class GameObject : BaseEntity, IGameObject
{
public Action<IAssignableStateEnable>? OnStateEnableAssigned { get; set; } = null;
public Action<IAssignableTransform>? OnTransformAssigned { get; set; } = null;
public Action<IAssignable>? OnUnassigned { get; set; } = null;
public Action<IAssignableBehaviourController>? OnBehaviourControllerAssigned { get; set; } = null;
public Action<IAssignableGameManager>? OnGameManagerAssigned { get; set; } = null;
public event IAssignableTransform.OnTransformAssignedDelegate? OnTransformAssigned = null;
public event IAssignableGameManager.OnGameManagerAssignedDelegate? OnGameManagerAssigned = null;
public event IAssignableBehaviourController.OnBehaviourControllerAssignedDelegate? OnBehaviourControllerAssigned = null;
public Action<IEntity>? OnNameChanged { get; set; } = null;
public event INameable.OnNameChangedDelegate? OnNameChanged = null;
public Action<IInitialize>? OnInitialized { get; set; } = null;
public Action<IInitialize>? OnFinalized { get; set; } = null;
public Action<IGameObject>? OnUpdated { get; set; } = null;
public event IGameObject.OnUpdatedDelegate? OnUpdated = null;
private ITransform _transform = null!;
@@ -28,29 +21,11 @@ public class GameObject : IGameObject
private IGameManager _gameManager = null!;
private string _name = nameof(GameObject);
private bool _initialized = false;
public ITransform Transform => _transform;
public IBehaviourController BehaviourController => _behaviourController;
public IStateEnable StateEnable => _stateEnable;
public IGameManager GameManager => _gameManager;
public bool Initialized
{
get => _initialized;
private set
{
if (value == _initialized)
return;
_initialized = value;
if (value)
OnInitialized?.Invoke(this);
else
OnFinalized?.Invoke(this);
}
}
public string Name
{
get => _name;
@@ -58,23 +33,20 @@ public class GameObject : IGameObject
{
if (value == _name) return;
string previousName = _name;
_name = value;
OnNameChanged?.Invoke(this);
OnNameChanged?.Invoke(this, previousName);
}
}
public bool Initialize()
protected override void InitializeInternal()
{
if (Initialized)
return false;
base.InitializeInternal();
NotAssignedException.Check(this, _transform);
NotAssignedException.Check(this, _behaviourController);
NotAssignedException.Check(this, _stateEnable);
NotAssignedException.Check(this, _gameManager);
Initialized = true;
return true;
}
public void Update()
@@ -85,29 +57,12 @@ public class GameObject : IGameObject
OnUpdated?.Invoke(this);
}
public bool Finalize()
protected override void FinalizeInternal()
{
if (!Initialized)
return false;
base.FinalizeInternal();
System.Threading.Tasks.Parallel.ForEach(
_behaviourController.GetBehaviours<IBehaviour>(),
behaviour => behaviour.Finalize()
);
Initialized = false;
return true;
}
public bool Assign(IStateEnable stateEnable)
{
if (Initialized)
return false;
_stateEnable = stateEnable;
OnStateEnableAssigned?.Invoke(this);
return true;
foreach (IBehaviour behaviour in _behaviourController.GetBehaviours<IBehaviour>())
behaviour.Finalize();
}
public bool Assign(ITransform transform)
@@ -140,21 +95,18 @@ public class GameObject : IGameObject
return true;
}
public bool Unassign()
protected override void UnassignInternal()
{
if (Initialized)
return false;
base.UnassignInternal();
_stateEnable = null!;
_transform = null!;
_behaviourController = null!;
_gameManager = null!;
OnUnassigned?.Invoke(this);
return true;
}
public GameObject() { OnBehaviourControllerAssigned += ConnectBehaviourController; }
private void ConnectBehaviourController(IAssignableBehaviourController controller)
{
controller.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;

View File

@@ -1,14 +1,12 @@
using System;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public class StateEnable : IStateEnable
{
public Action<IAssignable>? OnUnassigned { get; set; } = null;
public Action<IAssignableEntity>? OnEntityAssigned { get; set; } = null;
public Action<IStateEnable>? OnEnabledChanged { get; set; } = null;
public event IAssignable.OnUnassignedDelegate? OnUnassigned = null;
public event IAssignableEntity.OnEntityAssignedDelegate? OnEntityAssigned = null;
public event IStateEnable.OnNameChangedDelegate? OnEnabledChanged = null;
private bool _enabled = true;
private IEntity _entity = null!;
@@ -23,8 +21,9 @@ public class StateEnable : IStateEnable
if (value == _enabled)
return;
bool previousState = _enabled;
_enabled = value;
OnEnabledChanged?.Invoke(this);
OnEnabledChanged?.Invoke(this, previousState);
}
}

View File

@@ -1,20 +1,41 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("Position: {Position.ToString(), nq}, Scale: {Scale.ToString(), nq}, Rotation: {Rotation}")]
[System.Diagnostics.DebuggerDisplay("Name: {GameObject.Name, nq} Position: {Position.ToString(), nq}, Scale: {Scale.ToString(), nq}, Rotation: {Rotation}")]
public class Transform : ITransform
{
public Action<ITransform>? OnPositionChanged { get; set; } = null;
public Action<ITransform>? OnScaleChanged { get; set; } = null;
public Action<ITransform>? OnRotationChanged { get; set; } = null;
public event IAssignableGameObject.OnGameObjectAssignedDelegate? OnGameObjectAssigned = null;
public event IAssignable.OnUnassignedDelegate? OnUnassigned = null;
public event ITransform.OnPositionChangedDelegate? OnPositionChanged = null;
public event ITransform.OnScaleChangedDelegate? OnScaleChanged = null;
public event ITransform.OnRotationChangedDelegate? OnRotationChanged = null;
public event ITransform.OnParentChangedDelegate? OnParentChanged = null;
public event ITransform.OnChildrenAddedDelegate? OnChildrenAdded = null;
public event ITransform.OnChildrenRemovedDelegate? OnChildrenRemoved = null;
private Vector2D _position = Vector2D.Zero;
private Vector2D _scale = Vector2D.One;
private float _rotation = 0f;
private Vector2D _localPosition = Vector2D.Zero;
private Vector2D _localScale = Vector2D.One;
private float _localRotation = 0f;
private readonly List<ITransform> _children = [];
public IGameObject GameObject { get; private set; } = null!;
public ITransform? Parent { get; private set; } = null;
public IReadOnlyList<ITransform> Children => _children;
public Vector2D Position
{
get => _position;
@@ -24,6 +45,7 @@ public class Transform : ITransform
return;
_position = value;
UpdateLocalPosition();
OnPositionChanged?.Invoke(this);
}
}
@@ -37,6 +59,7 @@ public class Transform : ITransform
return;
_scale = value;
UpdateLocalScale();
OnScaleChanged?.Invoke(this);
}
}
@@ -50,7 +73,210 @@ public class Transform : ITransform
return;
_rotation = value;
UpdateLocalPosition();
OnRotationChanged?.Invoke(this);
}
}
public Vector2D LocalPosition
{
get => _localPosition;
set
{
if (value == _localPosition)
return;
_localPosition = value;
UpdatePosition();
OnPositionChanged?.Invoke(this);
}
}
public Vector2D LocalScale
{
get => _localScale;
set
{
if (value == _localScale)
return;
_localScale = value;
UpdateScale();
OnScaleChanged?.Invoke(this);
}
}
public float LocalRotation
{
get => _localRotation;
set
{
if (value == _localRotation)
return;
_localRotation = value;
UpdateRotation();
OnRotationChanged?.Invoke(this);
}
}
public void SetParent(ITransform? transform)
{
if (transform == this || Parent == transform)
return;
ITransform? previousParent = Parent;
if (previousParent is not null)
{
previousParent.RemoveChild(this);
previousParent.OnPositionChanged -= RecalculatePosition;
previousParent.OnScaleChanged -= RecalculateScale;
previousParent.OnRotationChanged -= RecalculateRotation;
previousParent.OnParentChanged -= NotifyChildrenOnParentChange;
}
Parent = transform;
if (transform is not null)
{
transform.AddChild(this);
transform.OnPositionChanged += RecalculatePosition;
transform.OnScaleChanged += RecalculateScale;
transform.OnRotationChanged += RecalculateRotation;
transform.OnParentChanged += NotifyChildrenOnParentChange;
}
UpdateLocalPosition();
UpdateLocalScale();
UpdateLocalRotation();
OnParentChanged?.Invoke(this, previousParent);
}
public void AddChild(ITransform transform)
{
if (_children.Contains(transform))
return;
_children.Add(transform);
transform.SetParent(this);
OnChildrenAdded?.Invoke(this, transform);
}
public void RemoveChild(ITransform transform)
{
if (!_children.Remove(transform))
return;
transform.SetParent(null);
OnChildrenRemoved?.Invoke(this, transform);
}
public IEnumerator<ITransform> GetEnumerator() => _children.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _children.GetEnumerator();
private void NotifyChildrenOnParentChange(ITransform transform, ITransform? previousParent)
{
// TODO No idea how logical this is to propagate this to the children the way I'm doing right now.
// I was originally gonna just call `child.OnParentChanged?.Invoke(child, child.Parent);` but seems an unnecessary call too?
foreach (var child in Children) // TODO CHECK ERRORS
child.SetParent(this);
}
private void RecalculatePosition(ITransform _)
{
if (Parent is null)
return;
UpdatePosition();
OnPositionChanged?.Invoke(this);
}
private void RecalculateScale(ITransform _)
{
if (Parent is null)
return;
UpdateScale();
OnScaleChanged?.Invoke(this);
}
private void RecalculateRotation(ITransform _)
{
if (Parent is null)
return;
UpdatePosition();
UpdateRotation();
OnPositionChanged?.Invoke(this);
OnRotationChanged?.Invoke(this);
}
private void UpdateLocalPosition()
{
if (Parent is null)
_localPosition = Position;
else
_localPosition = Parent.Position.FromTo(Position).Scale(Parent.Scale);
}
private void UpdateLocalScale()
{
if (Parent is null)
_localScale = Scale;
else
_localScale = Scale.Scale(new(1f / Parent.Scale.X, 1f / Parent.Scale.Y));
}
private void UpdateLocalRotation()
{
if (Parent is null)
_localRotation = Rotation;
else
_localRotation = Rotation - Parent.Rotation;
}
private void UpdatePosition()
{
if (Parent is null)
_position = LocalPosition.Scale(Vector2D.One).Rotate(0f * Math.DegreeToRadian);
else
_position = Parent.Position + LocalPosition.Scale(new(1f / Parent.Scale.X, 1f / Parent.Scale.Y)).Rotate(Parent.Rotation * Math.DegreeToRadian);
}
private void UpdateScale()
{
if (Parent is null)
_scale = LocalScale;
else
_scale = Vector2D.Scale(Parent.Scale, LocalScale);
}
private void UpdateRotation()
{
if (Parent is null)
_rotation = LocalRotation;
else
_rotation = Parent.Rotation + LocalRotation;
}
public bool Assign(IGameObject gameObject)
{
if (GameObject is not null && GameObject.Initialized)
return false;
GameObject = gameObject;
OnGameObjectAssigned?.Invoke(this);
return true;
}
public bool Unassign()
{
if (GameObject is not null && GameObject.Initialized)
return false;
GameObject = null!;
OnUnassigned?.Invoke(this);
return true;
}
}

View File

@@ -1,5 +1,3 @@
using System;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Physics2D.Abstract;
@@ -12,17 +10,17 @@ public interface ICollider2D : IBehaviour, IAssignableTransform
/// <summary>
/// Event triggered when a collision is detected.
/// </summary>
Action<ICollider2D, CollisionDetectionInformation>? OnCollisionDetected { get; set; }
event OnCollisionDetectedDelegate? OnCollisionDetected;
/// <summary>
/// Event triggered when a collision is resolved.
/// </summary>
Action<ICollider2D, CollisionDetectionInformation>? OnCollisionResolved { get; set; }
event OnCollisionResolvedDelegate? OnCollisionResolved;
/// <summary>
/// Event triggered when another <see cref="ICollider2D"/> triggers this <see cref="ICollider2D"/>.
/// </summary>
Action<ICollider2D, ICollider2D>? OnTriggered { get; set; }
event OnTriggeredDelegate? OnTriggered;
/// <summary>
/// The <see cref="IRigidBody2D"/> associated with the <see cref="ICollider2D"/>.
@@ -38,4 +36,12 @@ public interface ICollider2D : IBehaviour, IAssignableTransform
/// Recalculates <see cref="ICollider2D"/> properties.
/// </summary>
void Recalculate();
void Detect(CollisionDetectionInformation collisionDetectionInformation);
void Resolve(CollisionDetectionInformation collisionDetectionInformation);
void Trigger(ICollider2D initiator);
delegate void OnCollisionDetectedDelegate(ICollider2D sender, CollisionDetectionInformation collisionDetectionInformation);
delegate void OnCollisionResolvedDelegate(ICollider2D sender, CollisionDetectionInformation collisionDetectionInformation);
delegate void OnTriggeredDelegate(ICollider2D sender, ICollider2D initiatorCollider);
}

View File

@@ -1,5 +1,3 @@
using System;
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Physics2D.Abstract;
@@ -8,9 +6,9 @@ namespace Syntriax.Engine.Physics2D;
public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D
{
public Action<ICollider2D, CollisionDetectionInformation>? OnCollisionDetected { get; set; } = null;
public Action<ICollider2D, CollisionDetectionInformation>? OnCollisionResolved { get; set; } = null;
public Action<ICollider2D, ICollider2D>? OnTriggered { get; set; } = null;
public event ICollider2D.OnCollisionDetectedDelegate? OnCollisionDetected = null;
public event ICollider2D.OnCollisionResolvedDelegate? OnCollisionResolved = null;
public event ICollider2D.OnTriggeredDelegate? OnTriggered = null;
protected bool NeedsRecalculation { get; private set; } = true;
@@ -20,7 +18,7 @@ public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D
public bool IsTrigger { get; set; } = false;
ITransform IAssignableTransform.Transform => Transform;
Action<IAssignableTransform>? IAssignableTransform.OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; }
public event IAssignableTransform.OnTransformAssignedDelegate? OnTransformAssigned { add => GameObject.OnTransformAssigned += value; remove => GameObject.OnTransformAssigned -= value; }
bool IAssignableTransform.Assign(ITransform transform) => GameObject.Assign(transform);
@@ -37,7 +35,7 @@ public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D
protected override void OnInitialize()
{
BehaviourController.TryGetBehaviour(out _rigidBody2D);
BehaviourController.TryGetBehaviourInParent(out _rigidBody2D);
BehaviourController.OnBehaviourAdded += OnBehaviourAddedToController;
BehaviourController.OnBehaviourRemoved += OnBehaviourRemovedFromController;
@@ -45,6 +43,12 @@ public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D
Transform.OnPositionChanged += SetNeedsRecalculation;
Transform.OnRotationChanged += SetNeedsRecalculation;
Transform.OnScaleChanged += SetNeedsRecalculation;
Transform.OnParentChanged += UpdateRigidBody2D;
}
private void UpdateRigidBody2D(ITransform _1, ITransform? _2)
{
BehaviourController.TryGetBehaviourInParent(out _rigidBody2D);
}
private void OnBehaviourAddedToController(IBehaviourController _, IBehaviour behaviour)
@@ -70,4 +74,8 @@ public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D
Transform.OnPositionChanged -= SetNeedsRecalculation;
Transform.OnRotationChanged -= SetNeedsRecalculation;
}
public void Detect(CollisionDetectionInformation collisionDetectionInformation) => OnCollisionDetected?.Invoke(this, collisionDetectionInformation);
public void Resolve(CollisionDetectionInformation collisionDetectionInformation) => OnCollisionResolved?.Invoke(this, collisionDetectionInformation);
public void Trigger(ICollider2D initiator) => OnTriggered?.Invoke(this, initiator);
}

View File

@@ -38,7 +38,7 @@ public class CollisionResolver2D : ICollisionResolver2D
left.Recalculate();
right.Recalculate();
left.OnCollisionResolved?.Invoke(collisionInformation.Left, collisionInformation);
right.OnCollisionResolved?.Invoke(right, collisionInformation);
left.Resolve(collisionInformation);
right.Resolve(collisionInformation);
}
}

View File

@@ -1,13 +1,33 @@
using System;
using Syntriax.Engine.Core;
using Syntriax.Engine.Physics2D;
using Syntriax.Engine.Physics2D.Primitives;
namespace Engine.Physics2D;
public static partial class Physics2D
{
public static bool Overlaps(this Shape shape, Vector2D point) => Overlaps(shape, point, out var _);
public static bool Overlaps(this Shape shape, Vector2D point, out float depth)
{
depth = float.MaxValue;
var vertices = shape.Vertices;
int count = vertices.Count;
for (int indexProjection = 0; indexProjection < count; indexProjection++)
{
Vector2D projectionVector = vertices[indexProjection].FromTo(vertices[(indexProjection + 1) % count]).Perpendicular().Normalized;
Projection shapeProjection = shape.ToProjection(projectionVector);
float projectedPoint = point.Dot(projectionVector);
if (shapeProjection.Max < projectedPoint || shapeProjection.Min > projectedPoint)
return false;
depth = Math.Min(depth, Math.Abs(Math.AbsMin(shapeProjection.Max - projectedPoint, shapeProjection.Min - projectedPoint)));
}
return true;
}
public static bool Overlaps(this Circle left, Circle right)
{
float distanceSquared = left.Center.FromTo(right.Center).LengthSquared();
@@ -27,7 +47,7 @@ public static partial class Physics2D
normal = distanceVector.Normalized;
if (isOverlapping)
depth = MathF.Sqrt(radiusSumSquared - distanceSquared);
depth = Math.Sqrt(radiusSumSquared - distanceSquared);
return isOverlapping;
}
@@ -44,7 +64,7 @@ public static partial class Physics2D
normal = distanceVector.Normalized;
if (isOverlapping)
depth = MathF.Sqrt(radiusSquared - distanceSquared);
depth = Math.Sqrt(radiusSquared - distanceSquared);
return isOverlapping;
}

View File

@@ -80,17 +80,17 @@ public class PhysicsEngine2D : IPhysicsEngine2D
{
if (colliderX.IsTrigger)
{
colliderX.OnTriggered?.Invoke(colliderX, colliderY);
colliderX.Trigger(colliderY);
continue;
}
else if (colliderY.IsTrigger)
{
colliderY.OnTriggered?.Invoke(colliderY, colliderY);
colliderY.Trigger(colliderY);
continue;
}
colliderX.OnCollisionDetected?.Invoke(colliderX, information);
colliderY.OnCollisionDetected?.Invoke(colliderY, information);
colliderX.Detect(information);
colliderY.Detect(information);
collisionResolver?.Resolve(information);
}

View File

@@ -1,15 +1,13 @@
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 class PhysicsEngine2DCollector : IPhysicsEngine2D, IAssignableGameManager
{
public Action<IAssignable>? OnUnassigned { get; set; } = null;
public Action<IAssignableGameManager>? OnGameManagerAssigned { get; set; } = null;
public event IAssignable.OnUnassignedDelegate? OnUnassigned = null;
public event IAssignableGameManager.OnGameManagerAssignedDelegate? OnGameManagerAssigned = null;
private int _iterationPerStep = 1;
@@ -17,8 +15,8 @@ public class PhysicsEngine2DCacher : IPhysicsEngine2D, IAssignableGameManager
protected readonly ICollisionDetector2D collisionDetector = null!;
protected readonly ICollisionResolver2D collisionResolver = null!;
protected BehaviourCacher<IRigidBody2D> rigidBodyCacher = new();
protected BehaviourCacher<ICollider2D> colliderCacher = new();
protected BehaviourCollector<IRigidBody2D> rigidBodyCollector = new();
protected BehaviourCollector<ICollider2D> colliderCollector = new();
public int IterationPerStep { get => _iterationPerStep; set => _iterationPerStep = value < 1 ? 1 : value; }
@@ -31,23 +29,23 @@ public class PhysicsEngine2DCacher : IPhysicsEngine2D, IAssignableGameManager
for (int iterationIndex = 0; iterationIndex < IterationPerStep; iterationIndex++)
{
// Can Parallel
foreach (var rigidBody in rigidBodyCacher)
foreach (var rigidBody in rigidBodyCollector)
StepRigidBody(rigidBody, intervalDeltaTime);
// Can Parallel
foreach (var collider in colliderCacher)
foreach (var collider in colliderCollector)
collider.Recalculate();
// Can Parallel
for (int x = 0; x < colliderCacher.Behaviours.Count; x++)
for (int x = 0; x < colliderCollector.Behaviours.Count; x++)
{
ICollider2D? colliderX = colliderCacher.Behaviours[x];
ICollider2D? colliderX = colliderCollector.Behaviours[x];
if (!colliderX.IsActive)
return;
for (int y = x + 1; y < colliderCacher.Behaviours.Count; y++)
for (int y = x + 1; y < colliderCollector.Behaviours.Count; y++)
{
ICollider2D? colliderY = colliderCacher.Behaviours[y];
ICollider2D? colliderY = colliderCollector.Behaviours[y];
if (!colliderY.IsActive)
return;
@@ -67,17 +65,17 @@ public class PhysicsEngine2DCacher : IPhysicsEngine2D, IAssignableGameManager
{
if (colliderX.IsTrigger)
{
colliderX.OnTriggered?.Invoke(colliderX, colliderY);
colliderX.Trigger(colliderY);
continue;
}
else if (colliderY.IsTrigger)
{
colliderY.OnTriggered?.Invoke(colliderY, colliderY);
colliderY.Trigger(colliderY);
continue;
}
colliderX.OnCollisionDetected?.Invoke(colliderX, information);
colliderY.OnCollisionDetected?.Invoke(colliderY, information);
colliderX.Detect(information);
colliderY.Detect(information);
collisionResolver?.Resolve(information);
}
@@ -100,8 +98,8 @@ public class PhysicsEngine2DCacher : IPhysicsEngine2D, IAssignableGameManager
if (GameManager is not null)
return false;
colliderCacher.Assign(gameManager);
rigidBodyCacher.Assign(gameManager);
colliderCollector.Assign(gameManager);
rigidBodyCollector.Assign(gameManager);
GameManager = gameManager;
OnGameManagerAssigned?.Invoke(this);
@@ -114,35 +112,35 @@ public class PhysicsEngine2DCacher : IPhysicsEngine2D, IAssignableGameManager
if (GameManager is null)
return false;
colliderCacher.Unassign();
rigidBodyCacher.Unassign();
colliderCollector.Unassign();
rigidBodyCollector.Unassign();
GameManager = null!;
OnUnassigned?.Invoke(this);
return true;
}
public PhysicsEngine2DCacher()
public PhysicsEngine2DCollector()
{
collisionDetector = new CollisionDetector2D();
collisionResolver = new CollisionResolver2D();
}
public PhysicsEngine2DCacher(IGameManager gameManager)
public PhysicsEngine2DCollector(IGameManager gameManager)
{
Assign(gameManager);
collisionDetector = new CollisionDetector2D();
collisionResolver = new CollisionResolver2D();
}
public PhysicsEngine2DCacher(IGameManager gameManager, ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver)
public PhysicsEngine2DCollector(IGameManager gameManager, ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver)
{
Assign(gameManager);
this.collisionDetector = collisionDetector;
this.collisionResolver = collisionResolver;
}
public PhysicsEngine2DCacher(ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver)
public PhysicsEngine2DCollector(ICollisionDetector2D collisionDetector, ICollisionResolver2D collisionResolver)
{
this.collisionDetector = collisionDetector;
this.collisionResolver = collisionResolver;

View File

@@ -1,5 +1,3 @@
using System;
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Physics2D.Abstract;
@@ -8,7 +6,7 @@ namespace Syntriax.Engine.Physics2D;
public class RigidBody2D : BehaviourOverride, IRigidBody2D
{
public Action<IAssignableTransform>? OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; }
event IAssignableTransform.OnTransformAssignedDelegate? IAssignableTransform.OnTransformAssigned { add => GameObject.OnTransformAssigned += value; remove => GameObject.OnTransformAssigned -= value; }
private const float LOWEST_ALLOWED_MASS = 0.00001f;
private float _mass = 1f;

17
README.md Normal file
View File

@@ -0,0 +1,17 @@
# Work In Progress
This engine is still in development but the implemented features include:
- Modular Systems
- Behaviour System
- 2D Physics Engine(**Not Fully Completed, but usable**)
- Rigid Body Simulations
- Collision Detection (Convex Shape & Circle)
- Collision Resolution (**Not Fully Completed**)
- Vector2D, AABB, Circle, Line, LineEquation, Projection & Shape Data Types
- General Math
---
**A detailed README file will be written in the future. If you want to check out how to use this, please checkout this example Pong game made using this engine on top of [MonoGame](https://monogame.net/) from the link bellow.**
[Pong Source Code](https://git.syntriax.com/Syntriax/Engine-Pong)