Compare commits

...

20 Commits

Author SHA1 Message Date
5756b96002 chore: removed unused code piece from HierarchyObject 2025-04-01 19:19:06 +03:00
c71bf71fb3 chore: cleanup 2025-04-01 13:29:56 +03:00
417ddca972 feat: IActive interface added for hierarchy active state 2025-04-01 13:22:14 +03:00
d4c6288b38 feat: added behaviour collector for active behaviours only 2025-04-01 12:19:58 +03:00
21600b856f refactor: removed unnecessary underscore on behaviour collector 2025-04-01 12:19:34 +03:00
803b670433 fix: IBehaviourCollector delegate parameter forgot on the concrete implementation 2025-04-01 12:19:12 +03:00
067bc51487 refactor: made factories static 2025-04-01 12:18:33 +03:00
9f3e39e337 feat: basic tween system added 2025-03-31 19:38:01 +03:00
cd2cd89eae fix: Engine referencing removed class library 2025-03-31 19:37:09 +03:00
90c1dd9348 chore: EnumExtensions added 2025-03-31 16:46:19 +03:00
e8ef41af41 chore: forgotten HierarchyObject extension method name 2025-03-31 16:45:58 +03:00
d1a289885b refactor: IGameManager.Enumerable<IHierarchyObject> 2025-03-31 14:25:00 +03:00
6170de4a73 refactor: moved Engine.Input into Engine.Systems 2025-03-31 13:00:30 +03:00
e2fdf1f538 chore: added root namespace definitions for projects 2025-03-31 12:48:05 +03:00
7a1dd7eb1a feat: time systems added 2025-03-30 20:34:42 +03:00
b73c9ed0ae refactor: got rid of the static Time class and implemented EngineTime on IGameManager 2025-03-30 20:27:22 +03:00
82705ba378 chore: added Engine class library to include all projects 2025-03-29 21:59:36 +03:00
f9785462b0 refactor: delegate names updated to have no "On" prefix 2025-03-29 21:51:51 +03:00
5c3e0f6581 feat: added basic state machine system & Engine.Systems class library 2025-03-29 21:40:30 +03:00
b9ee1ec232 fix: unassigned fields on factories 2025-03-29 10:30:31 +03:00
74 changed files with 1365 additions and 726 deletions

View File

@ -8,7 +8,7 @@ public interface IAssignable
/// <summary>
/// Event triggered when the <see cref="IAssignable"/>'s fields are unassigned and completely ready to recycle.
/// </summary>
event OnUnassignedEventHandler? OnUnassigned;
event UnassignEventHandler? OnUnassigned;
/// <summary>
/// Unassign <see cref="IAssignable"/>'s all fields and make it ready to recycle.
@ -18,5 +18,5 @@ public interface IAssignable
/// </returns>
bool Unassign();
delegate void OnUnassignedEventHandler(IAssignable sender);
delegate void UnassignEventHandler(IAssignable sender);
}

View File

@ -8,7 +8,7 @@ public interface IHasBehaviourController : IAssignable
/// <summary>
/// Event triggered when the <see cref="IBehaviourController"/> value has has been assigned a new value.
/// </summary>
event OnBehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned;
event BehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned;
/// <inheritdoc cref="IBehaviourController" />
IBehaviourController BehaviourController { get; }
@ -22,5 +22,5 @@ public interface IHasBehaviourController : IAssignable
/// </returns>
bool Assign(IBehaviourController behaviourController);
delegate void OnBehaviourControllerAssignedEventHandler(IHasBehaviourController sender);
delegate void BehaviourControllerAssignedEventHandler(IHasBehaviourController sender);
}

View File

@ -8,7 +8,7 @@ public interface IHasEntity : IAssignable
/// <summary>
/// Event triggered when the <see cref="IEntity"/> value has has been assigned a new value.
/// </summary>
event OnEntityAssignedEventHandler? OnEntityAssigned;
event EntityAssignedEventHandler? OnEntityAssigned;
/// <inheritdoc cref="IEntity" />
IEntity Entity { get; }
@ -22,5 +22,5 @@ public interface IHasEntity : IAssignable
/// </returns>
bool Assign(IEntity entity);
delegate void OnEntityAssignedEventHandler(IHasEntity sender);
delegate void EntityAssignedEventHandler(IHasEntity sender);
}

View File

@ -8,7 +8,7 @@ public interface IHasGameManager : IAssignable
/// <summary>
/// Event triggered when the <see cref="IGameManager"/> value has has been assigned a new value.
/// </summary>
event OnGameManagerAssignedEventHandler? OnGameManagerAssigned;
event GameManagerAssignedEventHandler? OnGameManagerAssigned;
/// <inheritdoc cref="IGameManager" />
IGameManager GameManager { get; }
@ -22,5 +22,5 @@ public interface IHasGameManager : IAssignable
/// </returns>
bool Assign(IGameManager gameManager);
delegate void OnGameManagerAssignedEventHandler(IHasGameManager sender);
delegate void GameManagerAssignedEventHandler(IHasGameManager sender);
}

View File

@ -8,7 +8,7 @@ public interface IHasHierarchyObject : IAssignable
/// <summary>
/// Event triggered when the <see cref="IHierarchyObject"/> value has has been assigned a new value.
/// </summary>
event OnHierarchyObjectAssignedEventHandler? OnHierarchyObjectAssigned;
event HierarchyObjectAssignedEventHandler? OnHierarchyObjectAssigned;
/// <inheritdoc cref="IHierarchyObject" />
IHierarchyObject HierarchyObject { get; }
@ -22,5 +22,5 @@ public interface IHasHierarchyObject : IAssignable
/// </returns>
bool Assign(IHierarchyObject hierarchyObject);
delegate void OnHierarchyObjectAssignedEventHandler(IHasHierarchyObject sender);
delegate void HierarchyObjectAssignedEventHandler(IHasHierarchyObject sender);
}

View File

@ -8,7 +8,7 @@ public interface IHasStateEnable : IAssignable
/// <summary>
/// Event triggered when the <see cref="IStateEnable"/> value has has been assigned a new value.
/// </summary>
event OnStateEnableAssignedEventHandler? OnStateEnableAssigned;
event StateEnableAssignedEventHandler? OnStateEnableAssigned;
/// <inheritdoc cref="IStateEnable" />
IStateEnable StateEnable { get; }
@ -22,5 +22,5 @@ public interface IHasStateEnable : IAssignable
/// </returns>
bool Assign(IStateEnable stateEnable);
delegate void OnStateEnableAssignedEventHandler(IHasStateEnable sender);
delegate void StateEnableAssignedEventHandler(IHasStateEnable sender);
}

View File

@ -0,0 +1,19 @@
namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Represents an entity which can be active or not.
/// </summary>
public interface IActive
{
/// <summary>
/// Event triggered when the <see cref="IsActive"/> state of the <see cref="IActive"/> changes.
/// </summary>
event ActiveChangedEventHandler? OnActiveChanged;
/// <summary>
/// The value indicating whether the <see cref="IActive"/> is enabled.
/// </summary>
bool IsActive { get; }
delegate void ActiveChangedEventHandler(IActive sender, bool previousState);
}

View File

@ -3,22 +3,17 @@ namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Represents a behaviour that any object in the game might use to interact with itself or other objects.
/// </summary>
public interface IBehaviour : IEntity, IHasBehaviourController, IHasStateEnable
public interface IBehaviour : IEntity, IActive, IHasBehaviourController, IHasStateEnable
{
/// <summary>
/// Event triggered when the priority of the <see cref="IBehaviour"/> changes.
/// </summary>
event OnPriorityChangedEventHandler? OnPriorityChanged;
event PriorityChangedEventHandler? OnPriorityChanged;
/// <summary>
/// The priority of the <see cref="IBehaviour"/>.
/// </summary>
int Priority { get; set; }
/// <summary>
/// The value indicating whether the <see cref="IBehaviour"/> is active.
/// </summary>
bool IsActive { get; }
delegate void OnPriorityChangedEventHandler(IBehaviour sender, int previousPriority);
delegate void PriorityChangedEventHandler(IBehaviour sender, int previousPriority);
}

View File

@ -12,24 +12,24 @@ public interface IBehaviourCollector<T> : IHasGameManager, IEnumerable<T> where
/// <summary>
/// Event triggered when an object of type <typeparamref name="T"/> is added to the collector.
/// </summary>
event OnCollectedEventHandler? OnCollected;
event CollectedEventHandler? OnCollected;
/// <summary>
/// Event triggered when an object of type <typeparamref name="T"/> is removed from the collector.
/// </summary>
event OnRemovedEventHandler? OnRemoved;
event RemovedEventHandler? OnRemoved;
/// <summary>
/// Delegate for handling the <see cref="OnCollected"/> event.
/// </summary>
/// <param name="sender">The instance of the <see cref="BehaviourCollector{T}"/> that triggered the event.</param>
/// <param name="sender">The instance of the <see cref="IBehaviourCollector{T}"/> that triggered the event.</param>
/// <param name="behaviourCollected">The object of type <typeparamref name="T"/> that was added to the collector.</param>
public delegate void OnCollectedEventHandler(BehaviourCollector<T> sender, T behaviourCollected);
delegate void CollectedEventHandler(IBehaviourCollector<T> sender, T behaviourCollected);
/// <summary>
/// Delegate for handling the <see cref="OnRemoved"/> event.
/// </summary>
/// <param name="sender">The instance of the <see cref="BehaviourCollector{T}"/> that triggered the event.</param>
/// <param name="sender">The instance of the <see cref="IBehaviourCollector{T}"/> that triggered the event.</param>
/// <param name="behaviourRemoved">The object of type <typeparamref name="T"/> that was removed from the collector.</param>
public delegate void OnRemovedEventHandler(BehaviourCollector<T> sender, T behaviourRemoved);
delegate void RemovedEventHandler(IBehaviourCollector<T> sender, T behaviourRemoved);
}

View File

@ -10,27 +10,27 @@ public interface IBehaviourController : IInitializable, IHasHierarchyObject, IEn
/// <summary>
/// Event triggered before the update of <see cref="IBehaviour"/>s.
/// </summary>
event OnPreUpdateEventHandler? OnPreUpdate;
event PreUpdateEventHandler? OnPreUpdate;
/// <summary>
/// Event triggered during the update of <see cref="IBehaviour"/>s.
/// </summary>
event OnUpdateEventHandler? OnUpdate;
event UpdateEventHandler? OnUpdate;
/// <summary>
/// Event triggered before the drawing phase.
/// </summary>
event OnPreDrawEventHandler? OnPreDraw;
event PreDrawEventHandler? OnPreDraw;
/// <summary>
/// Event triggered when a <see cref="IBehaviour"/> is added to the <see cref="IBehaviourController"/>.
/// </summary>
event OnBehaviourAddedEventHandler? OnBehaviourAdded;
event BehaviourAddedEventHandler? OnBehaviourAdded;
/// <summary>
/// Event triggered when a <see cref="IBehaviour"/> is removed from the <see cref="IBehaviourController"/>.
/// </summary>
event OnBehaviourRemovedEventHandler? OnBehaviourRemoved;
event BehaviourRemovedEventHandler? OnBehaviourRemoved;
/// <summary>
/// Adds a <see cref="IBehaviour"/> to the <see cref="IBehaviourController"/>.
@ -93,10 +93,10 @@ public interface IBehaviourController : IInitializable, IHasHierarchyObject, IEn
/// </summary>
void UpdatePreDraw();
delegate void OnPreUpdateEventHandler(IBehaviourController sender);
delegate void OnUpdateEventHandler(IBehaviourController sender);
delegate void OnPreDrawEventHandler(IBehaviourController sender);
delegate void OnBehaviourAddedEventHandler(IBehaviourController sender, IBehaviour behaviourAdded);
delegate void OnBehaviourRemovedEventHandler(IBehaviourController sender, IBehaviour behaviourRemoved);
delegate void PreUpdateEventHandler(IBehaviourController sender);
delegate void UpdateEventHandler(IBehaviourController sender);
delegate void PreDrawEventHandler(IBehaviourController sender);
delegate void BehaviourAddedEventHandler(IBehaviourController sender, IBehaviour behaviourAdded);
delegate void BehaviourRemovedEventHandler(IBehaviourController sender, IBehaviour behaviourRemoved);
}

View File

@ -9,12 +9,12 @@ public interface IEntity : IInitializable, IHasStateEnable
/// 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 OnIdChangedEventHandler? OnIdChanged;
event IdChangedEventHandler? OnIdChanged;
/// <summary>
/// The ID of the <see cref="IEntity"/>.
/// </summary>
string Id { get; set; }
delegate void OnIdChangedEventHandler(IEntity sender, string previousId);
delegate void IdChangedEventHandler(IEntity sender, string previousId);
}

View File

@ -5,27 +5,32 @@ namespace Syntriax.Engine.Core.Abstract;
/// <summary>
/// Represents a game world responsible for managing <see cref="IHierarchyObject"/>s.
/// </summary>
public interface IGameManager : IEntity
public interface IGameManager : IEntity, IEnumerable<IHierarchyObject>
{
/// <summary>
/// Event triggered when <see cref="Update(EngineTime)"/> is called on the <see cref="IGameManager"/>.
/// Event triggered when <see cref="Update(double)"/> is called on the <see cref="IGameManager"/>.
/// </summary>
event OnUpdateEventHandler? OnUpdate;
event UpdateEventHandler? OnUpdate;
/// <summary>
/// Event triggered when <see cref="PreDraw"/> is called on the <see cref="IGameManager"/>.
/// </summary>
event OnPreDawEventHandler? OnPreDraw;
event PreDawEventHandler? OnPreDraw;
/// <summary>
/// Event triggered when a <see cref="IHierarchyObject"/> is registered to the <see cref="IGameManager"/>.
/// </summary>
event OnHierarchyObjectRegisteredEventHandler? OnHierarchyObjectRegistered;
event HierarchyObjectRegisteredEventHandler? OnHierarchyObjectRegistered;
/// <summary>
/// Event triggered when a <see cref="IHierarchyObject"/> is unregistered from the <see cref="IGameManager"/>.
/// </summary>
event OnHierarchyObjectUnRegisteredEventHandler? OnHierarchyObjectUnRegistered;
event HierarchyObjectUnRegisteredEventHandler? OnHierarchyObjectUnRegistered;
/// <summary>
/// Contains time data related to this <see cref="IGameManager"/>.
/// </summary>
EngineTime Time { get; }
/// <summary>
/// Gets a read-only list of <see cref="IHierarchyObject"/>s managed by the <see cref="IGameManager"/>.
@ -53,19 +58,19 @@ public interface IGameManager : IEntity
void Remove(IHierarchyObject hierarchyObject);
/// <summary>
/// Updates the <see cref="IGameManager"/> with the given engine time data.
/// Updates the <see cref="IGameManager"/> with the given delta time.
/// </summary>
/// <param name="time">The engine time.</param>
void Update(EngineTime time);
/// <param name="engineTime">Delta time.</param>
void Update(EngineTime engineTime);
/// <summary>
/// Performs operations that should be done before the draw calls.
/// </summary>
void PreDraw();
delegate void OnUpdateEventHandler(IGameManager sender, EngineTime time);
delegate void OnPreDawEventHandler(IGameManager sender);
delegate void UpdateEventHandler(IGameManager sender, EngineTime engineTime);
delegate void PreDawEventHandler(IGameManager sender);
delegate void OnHierarchyObjectRegisteredEventHandler(IGameManager sender, IHierarchyObject hierarchyObjectRegistered);
delegate void OnHierarchyObjectUnRegisteredEventHandler(IGameManager sender, IHierarchyObject hierarchyObjectUnregistered);
delegate void HierarchyObjectRegisteredEventHandler(IGameManager sender, IHierarchyObject hierarchyObjectRegistered);
delegate void HierarchyObjectUnRegisteredEventHandler(IGameManager sender, IHierarchyObject hierarchyObjectUnregistered);
}

View File

@ -7,32 +7,32 @@ namespace Syntriax.Engine.Core.Abstract;
/// This interface allows for tracking the object's presence in the hierarchy and provides events
/// for notifying when the see enters or exits the hierarchy.
/// </summary>
public interface IHierarchyObject : IEntity, INameable, IHasBehaviourController, IEnumerable<IHierarchyObject>
public interface IHierarchyObject : IEntity, IActive, INameable, IHasBehaviourController, IEnumerable<IHierarchyObject>
{
/// <summary>
/// Event triggered when the <see cref="IHierarchyObject"/> enters the hierarchy.
/// </summary>
event OnEnteredHierarchyEventHandler? OnEnteredHierarchy;
event EnteredHierarchyEventHandler? OnEnteredHierarchy;
/// <summary>
/// Event triggered when the <see cref="IHierarchyObject"/> exits the hierarchy.
/// </summary>
event OnExitedHierarchyEventHandler? OnExitedHierarchy;
event ExitedHierarchyEventHandler? OnExitedHierarchy;
/// <summary>
/// Event triggered when the <see cref="Parent"/> of the <see cref="IHierarchyObject"/> changes. The second parameter is the old <see cref="IHierarchyObject"/>.
/// </summary>
event OnParentChangedEventHandler? OnParentChanged;
event ParentChangedEventHandler? OnParentChanged;
/// <summary>
/// Event triggered when a new <see cref="IHierarchyObject"/> is added to the <see cref="Children"/>.
/// </summary>
event OnChildrenAddedEventHandler? OnChildrenAdded;
event ChildrenAddedEventHandler? OnChildrenAdded;
/// <summary>
/// Event triggered when an <see cref="IHierarchyObject"/> is removed from the <see cref="Children"/>.
/// </summary>
event OnChildrenRemovedEventHandler? OnChildrenRemoved;
event ChildrenRemovedEventHandler? OnChildrenRemoved;
/// <summary>
/// Gets the <see cref="IGameManager"/> this <see cref="IHierarchyObject"/> is connected to, if any.
@ -98,14 +98,14 @@ public interface IHierarchyObject : IEntity, INameable, IHasBehaviourController,
/// </summary>
/// <param name="sender">The <see cref="IHierarchyObject"/> that entered the hierarchy.</param>
/// <param name="gameManager">The <see cref="IGameManager"/> that the <see cref="IHierarchyObject"/> has entered it's hierarchy.</param>
delegate void OnEnteredHierarchyEventHandler(IHierarchyObject sender, IGameManager gameManager);
delegate void EnteredHierarchyEventHandler(IHierarchyObject sender, IGameManager gameManager);
/// <summary>
/// EventHandler delegate for the event triggered when the <see cref="IHierarchyObject"/> exits the hierarchy of a <see cref="IGameManager">.
/// </summary>
/// <param name="sender">The <see cref="IHierarchyObject"/> that exited the hierarchy.</param>
/// <param name="gameManager">The <see cref="IGameManager"/> that the <see cref="IHierarchyObject"/> has exited it's hierarchy.</param>
delegate void OnExitedHierarchyEventHandler(IHierarchyObject sender, IGameManager gameManager);
delegate void ExitedHierarchyEventHandler(IHierarchyObject sender, IGameManager gameManager);
/// <summary>
/// Delegate for the event triggered when the <see cref="IHierarchyObject"/>'s parent changes.
@ -113,19 +113,19 @@ public interface IHierarchyObject : IEntity, INameable, IHasBehaviourController,
/// <param name="sender">The <see cref="IHierarchyObject"/> that the parent has changed.</param>
/// <param name="previousParent">The previous <see cref="IHierarchyObject"/> the sender was a child of.</param>
/// <param name="newParent">The new and current <see cref="IHierarchyObject"/> the sender is a child of.</param>
delegate void OnParentChangedEventHandler(IHierarchyObject sender, IHierarchyObject? previousParent, IHierarchyObject? newParent);
delegate void ParentChangedEventHandler(IHierarchyObject sender, IHierarchyObject? previousParent, IHierarchyObject? newParent);
/// <summary>
/// Delegate for the event triggered when a new <see cref="IHierarchyObject"/> added as a child.
/// </summary>
/// <param name="sender">The parent <see cref="IHierarchyObject"/> this event is being called from.</param>
/// <param name="childrenAdded">The <see cref="IHierarchyObject"/> that got removed as a children of the sender <see cref="IHierarchyObject"/>.</param>
delegate void OnChildrenAddedEventHandler(IHierarchyObject sender, IHierarchyObject childrenAdded);
delegate void ChildrenAddedEventHandler(IHierarchyObject sender, IHierarchyObject childrenAdded);
/// <summary>
/// Delegate for the event triggered when a new <see cref="IHierarchyObject"/> removed from being a child.
/// </summary>
/// <param name="sender">The parent <see cref="IHierarchyObject"/> this event is being called from.</param>
/// <param name="childrenAdded">The <see cref="IHierarchyObject"/> that got removed as a children of the sender <see cref="IHierarchyObject"/>.</param>
delegate void OnChildrenRemovedEventHandler(IHierarchyObject sender, IHierarchyObject childrenRemoved);
delegate void ChildrenRemovedEventHandler(IHierarchyObject sender, IHierarchyObject childrenRemoved);
}

View File

@ -8,12 +8,12 @@ public interface IInitializable
/// <summary>
/// Event triggered when the <see cref="Initialize"/> method is called successfully.
/// </summary>
event OnInitializedEventHandler? OnInitialized;
event InitializedEventHandler? OnInitialized;
/// <summary>
/// Event triggered when the <see cref="IInitializable"/> method is called successfully.
/// </summary>
event OnFinalizedEventHandler? OnFinalized;
event FinalizedEventHandler? OnFinalized;
/// <summary>
/// The value indicating whether the entity has been initialized.
@ -32,6 +32,6 @@ public interface IInitializable
/// <returns><see cref="true"/> if finalization is successful, otherwise <see cref="false"/>.</returns>
bool Finalize();
delegate void OnInitializedEventHandler(IInitializable sender);
delegate void OnFinalizedEventHandler(IInitializable sender);
delegate void InitializedEventHandler(IInitializable sender);
delegate void FinalizedEventHandler(IInitializable sender);
}

View File

@ -8,12 +8,12 @@ public interface INameable
/// <summary>
/// Event triggered when the name of the entity changes.
/// </summary>
event OnNameChangedEventHandler? OnNameChanged;
event NameChangedEventHandler? OnNameChanged;
/// <summary>
/// The name of the entity.
/// </summary>
string Name { get; set; }
delegate void OnNameChangedEventHandler(INameable sender, string previousName);
delegate void NameChangedEventHandler(INameable sender, string previousName);
}

View File

@ -8,12 +8,12 @@ public interface IStateEnable : IHasEntity
/// <summary>
/// Event triggered when the <see cref="Enabled"/> state of the <see cref="IStateEnable"/> changes.
/// </summary>
event OnNameChangedEventHandler? OnEnabledChanged;
event EnabledChangedEventHandler? OnEnabledChanged;
/// <summary>
/// The value indicating whether the <see cref="IStateEnable"/> is enabled.
/// </summary>
bool Enabled { get; set; }
delegate void OnNameChangedEventHandler(IStateEnable sender, bool previousState);
delegate void EnabledChangedEventHandler(IStateEnable sender, bool previousState);
}

View File

@ -8,17 +8,17 @@ public interface ITransform2D : IBehaviour
/// <summary>
/// Event triggered when the <see cref="Position"/> of the <see cref="ITransform2D"/> changes.
/// </summary>
event OnPositionChangedEventHandler? OnPositionChanged;
event PositionChangedEventHandler? OnPositionChanged;
/// <summary>
/// Event triggered when the <see cref="Scale"/> of the <see cref="ITransform2D"/> changes.
/// </summary>
event OnScaleChangedEventHandler? OnScaleChanged;
event ScaleChangedEventHandler? OnScaleChanged;
/// <summary>
/// Event triggered when the <see cref="Rotation"/> of the <see cref="ITransform"/> changes.
/// </summary>
event OnRotationChangedEventHandler? OnRotationChanged;
event RotationChangedEventHandler? OnRotationChanged;
/// <summary>
/// The world position of the <see cref="ITransform2D"/> in 2D space.
@ -55,19 +55,19 @@ public interface ITransform2D : IBehaviour
/// </summary>
/// <param name="sender">The <see cref="ITransform2D"/> that the parent has changed.</param>
/// <param name="previousPosition">The previous <see cref="Position"/> of the <see cref="ITransform2D"/>.</param>
delegate void OnPositionChangedEventHandler(ITransform2D sender, Vector2D previousPosition);
delegate void PositionChangedEventHandler(ITransform2D sender, Vector2D previousPosition);
/// <summary>
/// Delegate for the event triggered when the <see cref="ITransform2D"/>'s rotation changes.
/// </summary>
/// <param name="sender">The <see cref="ITransform2D"/> that the parent has changed.</param>
/// <param name="previousScale">The previous <see cref="Scale"/> of the <see cref="ITransform2D"/>.</param>
delegate void OnScaleChangedEventHandler(ITransform2D sender, Vector2D previousScale);
delegate void ScaleChangedEventHandler(ITransform2D sender, Vector2D previousScale);
/// <summary>
/// Delegate for the event triggered when the <see cref="ITransform2D"/>'s rotation changes.
/// </summary>
/// <param name="sender">The <see cref="ITransform2D"/> that the parent has changed.</param>
/// <param name="previousRotation">The previous <see cref="Rotation"/> of the <see cref="ITransform2D"/>.</param>
delegate void OnRotationChangedEventHandler(ITransform2D sender, float previousRotation);
delegate void RotationChangedEventHandler(ITransform2D sender, float previousRotation);
}

View File

@ -0,0 +1,127 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : class, IBehaviour
{
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
public event IHasGameManager.GameManagerAssignedEventHandler? OnGameManagerAssigned = null;
public event IBehaviourCollector<T>.CollectedEventHandler? OnCollected = null;
public event IBehaviourCollector<T>.RemovedEventHandler? OnRemoved = null;
private readonly List<T> monitoringBehaviours = new(32);
protected readonly List<T> activeBehaviours = new(32);
protected readonly Dictionary<IActive, T> monitoringActiveToBehaviour = new(32);
public IReadOnlyList<T> Behaviours => activeBehaviours;
public IGameManager GameManager { get; private set; } = null!;
public T this[Index index] => activeBehaviours[index];
public ActiveBehaviourCollector() { }
public ActiveBehaviourCollector(IGameManager gameManager) => Assign(gameManager);
private void OnHierarchyObjectRegistered(IGameManager manager, IHierarchyObject hierarchyObject)
{
hierarchyObject.BehaviourController.OnBehaviourAdded += OnBehaviourAdded;
hierarchyObject.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved;
foreach (IBehaviour item in hierarchyObject.BehaviourController)
OnBehaviourAdded(hierarchyObject.BehaviourController, item);
}
private void OnHierarchyObjectUnregistered(IGameManager manager, IHierarchyObject hierarchyObject)
{
hierarchyObject.BehaviourController.OnBehaviourAdded -= OnBehaviourAdded;
hierarchyObject.BehaviourController.OnBehaviourRemoved -= OnBehaviourRemoved;
foreach (IBehaviour item in hierarchyObject.BehaviourController)
OnBehaviourRemoved(hierarchyObject.BehaviourController, item);
}
protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
private void OnBehaviourAdded(IBehaviourController controller, IBehaviour behaviour)
{
if (behaviour is not T tBehaviour)
return;
monitoringBehaviours.Add(tBehaviour);
monitoringActiveToBehaviour.Add(tBehaviour, tBehaviour);
tBehaviour.OnActiveChanged += OnBehaviourStateChanged;
OnBehaviourStateChanged(tBehaviour, !tBehaviour.IsActive);
}
private void OnBehaviourStateChanged(IActive sender, bool previousState)
{
T behaviour = monitoringActiveToBehaviour[sender];
if (sender.IsActive)
{
activeBehaviours.Add(behaviour);
OnBehaviourAdd(behaviour);
OnCollected?.Invoke(this, behaviour);
}
else if (activeBehaviours.Remove(behaviour))
{
OnBehaviourRemove(behaviour);
OnRemoved?.Invoke(this, behaviour);
}
}
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviour behaviour)
{
if (behaviour is not T tBehaviour)
return;
if (!monitoringBehaviours.Remove(tBehaviour) || !monitoringActiveToBehaviour.Remove(tBehaviour))
return;
tBehaviour.OnActiveChanged -= OnBehaviourStateChanged;
if (activeBehaviours.Remove(tBehaviour))
{
OnBehaviourRemove(tBehaviour);
OnRemoved?.Invoke(this, tBehaviour);
}
}
public bool Assign(IGameManager gameManager)
{
if (GameManager is not null)
return false;
foreach (IHierarchyObject hierarchyObject in gameManager.HierarchyObjects)
OnHierarchyObjectRegistered(gameManager, hierarchyObject);
gameManager.OnHierarchyObjectRegistered += OnHierarchyObjectRegistered;
gameManager.OnHierarchyObjectUnRegistered += OnHierarchyObjectUnregistered;
GameManager = gameManager;
OnGameManagerAssigned?.Invoke(this);
return true;
}
public bool Unassign()
{
if (GameManager is null)
return false;
foreach (IHierarchyObject hierarchyObject in GameManager.HierarchyObjects)
OnHierarchyObjectUnregistered(GameManager, hierarchyObject);
GameManager.OnHierarchyObjectRegistered -= OnHierarchyObjectRegistered;
GameManager.OnHierarchyObjectUnRegistered -= OnHierarchyObjectUnregistered;
GameManager = null!;
OnUnassigned?.Invoke(this);
return true;
}
public IEnumerator<T> GetEnumerator() => activeBehaviours.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => activeBehaviours.GetEnumerator();
}

View File

@ -0,0 +1,19 @@
using System;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Core;
public class ActiveBehaviourCollectorSorted<T> : ActiveBehaviourCollector<T> where T : class, IBehaviour
{
public Comparison<T>? SortBy { get; set; } = null;
protected override void OnBehaviourAdd(IBehaviour behaviour)
{
if (SortBy is not null)
activeBehaviours.Sort(SortBy);
}
public ActiveBehaviourCollectorSorted() { }
public ActiveBehaviourCollectorSorted(IGameManager gameManager) : base(gameManager) { }
}

View File

@ -4,13 +4,13 @@ namespace Syntriax.Engine.Core.Abstract;
public abstract class BaseEntity : IEntity
{
public event IEntity.OnIdChangedEventHandler? OnIdChanged = null;
public event IEntity.IdChangedEventHandler? OnIdChanged = null;
public event IInitializable.OnInitializedEventHandler? OnInitialized = null;
public event IInitializable.OnFinalizedEventHandler? OnFinalized = null;
public event IInitializable.InitializedEventHandler? OnInitialized = null;
public event IInitializable.FinalizedEventHandler? OnFinalized = null;
public event IHasStateEnable.OnStateEnableAssignedEventHandler? OnStateEnableAssigned = null;
public event IAssignable.OnUnassignedEventHandler? OnUnassigned = null;
public event IHasStateEnable.StateEnableAssignedEventHandler? OnStateEnableAssigned = null;
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
private IStateEnable _stateEnable = null!;
@ -19,8 +19,6 @@ public abstract class BaseEntity : IEntity
public virtual IStateEnable StateEnable => _stateEnable;
public virtual bool IsActive => StateEnable.Enabled;
public string Id
{
get => _id;
@ -55,6 +53,7 @@ public abstract class BaseEntity : IEntity
}
}
protected virtual void OnAssign(IStateEnable stateEnable) { }
public bool Assign(IStateEnable stateEnable)
{
if (IsInitialized)
@ -62,6 +61,7 @@ public abstract class BaseEntity : IEntity
_stateEnable = stateEnable;
_stateEnable.Assign(this);
OnAssign(stateEnable);
OnStateEnableAssigned?.Invoke(this);
return true;
}
@ -86,7 +86,7 @@ public abstract class BaseEntity : IEntity
if (IsInitialized)
return false;
_stateEnable ??= new Factory.StateEnableFactory().Instantiate(this);
_stateEnable ??= Factory.StateEnableFactory.Instantiate(this);
InitializeInternal();

View File

@ -6,18 +6,14 @@ namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("{GetType().Name, nq}, Priority: {Priority}, Initialized: {Initialized}")]
public abstract class BehaviourBase : BaseEntity, IBehaviour
{
public event IHasBehaviourController.OnBehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned = null;
public event IBehaviour.OnPriorityChangedEventHandler? OnPriorityChanged = null;
public event IHasBehaviourController.BehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned = null;
public event IBehaviour.PriorityChangedEventHandler? OnPriorityChanged = null;
public event IActive.ActiveChangedEventHandler? OnActiveChanged = null;
private IBehaviourController _behaviourController = null!;
private int _priority = 0;
public IBehaviourController BehaviourController => _behaviourController;
public override bool IsActive => base.IsActive && BehaviourController.HierarchyObject.StateEnable.Enabled;
private int _priority = 0;
public int Priority
{
get => _priority;
@ -32,18 +28,41 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
}
}
private bool _isActive = false;
public bool IsActive => _isActive;
protected virtual void OnAssign(IBehaviourController behaviourController) { }
public bool Assign(IBehaviourController behaviourController)
{
if (IsInitialized)
return false;
_behaviourController = behaviourController;
OnAssign(behaviourController);
behaviourController.OnHierarchyObjectAssigned += OnHierarchyObjectAssigned;
if (behaviourController.HierarchyObject is not null)
OnHierarchyObjectAssigned(behaviourController);
OnBehaviourControllerAssigned?.Invoke(this);
return true;
}
private void OnHierarchyObjectAssigned(IHasHierarchyObject sender)
{
sender.HierarchyObject.OnActiveChanged += OnHierarchyObjectActiveChanged;
UpdateActive();
}
protected override void OnAssign(IStateEnable stateEnable)
{
base.OnAssign(stateEnable);
stateEnable.OnEnabledChanged += OnStateEnabledChanged;
}
protected override void UnassignInternal()
{
StateEnable.OnEnabledChanged -= OnStateEnabledChanged;
BehaviourController.OnHierarchyObjectAssigned -= OnHierarchyObjectAssigned;
base.UnassignInternal();
_behaviourController = null!;
}
@ -54,4 +73,16 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
NotAssignedException.Check(this, _behaviourController);
NotAssignedException.Check(this, StateEnable);
}
private void OnStateEnabledChanged(IStateEnable sender, bool previousState) => UpdateActive();
private void OnHierarchyObjectActiveChanged(IActive sender, bool previousState) => UpdateActive();
private void UpdateActive()
{
bool previousActive = IsActive;
_isActive = StateEnable.Enabled && _behaviourController.HierarchyObject.IsActive;
if (previousActive != IsActive)
OnActiveChanged?.Invoke(this, previousActive);
}
}

View File

@ -8,18 +8,18 @@ namespace Syntriax.Engine.Core;
public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
{
public event IAssignable.OnUnassignedEventHandler? OnUnassigned = null;
public event IHasGameManager.OnGameManagerAssignedEventHandler? OnGameManagerAssigned = null;
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
public event IHasGameManager.GameManagerAssignedEventHandler? OnGameManagerAssigned = null;
public event IBehaviourCollector<T>.OnCollectedEventHandler? OnCollected = null;
public event IBehaviourCollector<T>.OnRemovedEventHandler? OnRemoved = null;
public event IBehaviourCollector<T>.CollectedEventHandler? OnCollected = null;
public event IBehaviourCollector<T>.RemovedEventHandler? OnRemoved = null;
protected readonly List<T> _behaviours = new(32);
protected readonly List<T> behaviours = new(32);
public IReadOnlyList<T> Behaviours => _behaviours;
public IReadOnlyList<T> Behaviours => behaviours;
public IGameManager GameManager { get; private set; } = null!;
public T this[Index index] => _behaviours[index];
public T this[Index index] => behaviours[index];
public BehaviourCollector() { }
public BehaviourCollector(IGameManager gameManager) => Assign(gameManager);
@ -48,7 +48,7 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
if (behaviour is not T tBehaviour)
return;
_behaviours.Add(tBehaviour);
behaviours.Add(tBehaviour);
OnBehaviourAdd(behaviour);
OnCollected?.Invoke(this, tBehaviour);
}
@ -59,13 +59,14 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
if (behaviour is not T tBehaviour)
return;
if (!_behaviours.Remove(tBehaviour))
if (!behaviours.Remove(tBehaviour))
return;
OnBehaviourRemove(behaviour);
OnRemoved?.Invoke(this, tBehaviour);
}
protected virtual void OnAssign(IGameManager gameManager) { }
public bool Assign(IGameManager gameManager)
{
if (GameManager is not null)
@ -78,6 +79,7 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
gameManager.OnHierarchyObjectUnRegistered += OnHierarchyObjectUnregistered;
GameManager = gameManager;
OnAssign(gameManager);
OnGameManagerAssigned?.Invoke(this);
return true;
@ -99,6 +101,6 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
return true;
}
public IEnumerator<T> GetEnumerator() => _behaviours.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _behaviours.GetEnumerator();
public IEnumerator<T> GetEnumerator() => behaviours.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => behaviours.GetEnumerator();
}

View File

@ -11,7 +11,7 @@ public class BehaviourCollectorSorted<T> : BehaviourCollector<T> where T : class
protected override void OnBehaviourAdd(IBehaviour behaviour)
{
if (SortBy is not null)
_behaviours.Sort(SortBy);
behaviours.Sort(SortBy);
}
public BehaviourCollectorSorted() { }

View File

@ -11,18 +11,18 @@ namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count}")]
public class BehaviourController : IBehaviourController
{
public event IBehaviourController.OnPreUpdateEventHandler? OnPreUpdate = null;
public event IBehaviourController.OnUpdateEventHandler? OnUpdate = null;
public event IBehaviourController.OnPreDrawEventHandler? OnPreDraw = null;
public event IBehaviourController.PreUpdateEventHandler? OnPreUpdate = null;
public event IBehaviourController.UpdateEventHandler? OnUpdate = null;
public event IBehaviourController.PreDrawEventHandler? OnPreDraw = null;
public event IBehaviourController.OnBehaviourAddedEventHandler? OnBehaviourAdded = null;
public event IBehaviourController.OnBehaviourRemovedEventHandler? OnBehaviourRemoved = null;
public event IHasHierarchyObject.OnHierarchyObjectAssignedEventHandler? OnHierarchyObjectAssigned = null;
public event IBehaviourController.BehaviourAddedEventHandler? OnBehaviourAdded = null;
public event IBehaviourController.BehaviourRemovedEventHandler? OnBehaviourRemoved = null;
public event IHasHierarchyObject.HierarchyObjectAssignedEventHandler? OnHierarchyObjectAssigned = null;
public event IInitializable.OnInitializedEventHandler? OnInitialized = null;
public event IInitializable.OnFinalizedEventHandler? OnFinalized = null;
public event IInitializable.InitializedEventHandler? OnInitialized = null;
public event IInitializable.FinalizedEventHandler? OnFinalized = null;
public event IAssignable.OnUnassignedEventHandler? OnUnassigned = null;
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
private readonly IList<IBehaviour> behaviours = new List<IBehaviour>(Constants.BEHAVIOURS_SIZE_INITIAL);
@ -52,7 +52,7 @@ public class BehaviourController : IBehaviourController
InsertBehaviourByPriority(behaviour);
behaviour.Assign(this);
behaviour.Assign(HierarchyObject.StateEnable);
behaviour.Assign(Factory.StateEnableFactory.Instantiate(behaviour));
behaviour.Initialize();
behaviour.OnPriorityChanged += OnPriorityChange;
@ -61,7 +61,7 @@ public class BehaviourController : IBehaviourController
}
public T AddBehaviour<T>(params object?[]? args) where T : class, IBehaviour
=> AddBehaviour(new Factory.BehaviourFactory().Instantiate<T>(_hierarchyObject, args));
=> AddBehaviour(Factory.BehaviourFactory.Instantiate<T>(_hierarchyObject, args));
public T? GetBehaviour<T>()
{
@ -124,12 +124,14 @@ public class BehaviourController : IBehaviourController
OnBehaviourRemoved?.Invoke(this, behaviour);
}
protected virtual void OnAssign(IHierarchyObject hierarchyObject) { }
public bool Assign(IHierarchyObject hierarchyObject)
{
if (HierarchyObject is not null && HierarchyObject.IsInitialized)
return false;
_hierarchyObject = hierarchyObject;
OnAssign(hierarchyObject);
OnHierarchyObjectAssigned?.Invoke(this);
return true;
}

View File

@ -4,5 +4,7 @@
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>false</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Syntriax.Engine.Core</RootNamespace>
</PropertyGroup>
</Project>

View File

@ -2,10 +2,10 @@ using System;
namespace Syntriax.Engine.Core;
public readonly struct EngineTime(TimeSpan Total, TimeSpan Elapsed)
public readonly struct EngineTime(TimeSpan TimeSinceStart, TimeSpan TimeDelta)
{
public readonly TimeSpan Total = Total;
public readonly TimeSpan Elapsed = Elapsed;
public readonly TimeSpan TimeSinceStart = TimeSinceStart;
public readonly TimeSpan DeltaSpan = TimeDelta;
public readonly float DeltaTimeFrame = (float)Elapsed.TotalMilliseconds * .001f;
public readonly float DeltaTime = (float)TimeDelta.TotalSeconds;
}

View File

@ -0,0 +1,9 @@
using System;
namespace Syntriax.Engine.Core;
public static class EnumExtensions
{
public static bool CheckFlag(this Enum left, Enum right)
=> ((int)(object)left & (int)(object)right) != 0;
}

View File

@ -13,7 +13,7 @@ public static class HierarchyObjectExtensions
return hierarchyObject;
}
public static T? FindObject<T>(this IEnumerable<IHierarchyObject> hierarchyObjects) where T : class
public static T? FindHierarchyObject<T>(this IEnumerable<IHierarchyObject> hierarchyObjects) where T : class
{
foreach (IHierarchyObject hierarchyObject in hierarchyObjects)
if (hierarchyObject is T @object)
@ -22,13 +22,13 @@ public static class HierarchyObjectExtensions
return default;
}
public static bool TryFindObject<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
public static bool TryFindHierarchyObject<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
{
behaviour = FindObject<T>(hierarchyObjects);
behaviour = FindHierarchyObject<T>(hierarchyObjects);
return behaviour is not null;
}
public static void FindObjects<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, List<T> behaviours) where T : class
public static void FindHierarchyObjects<T>(this IEnumerable<IHierarchyObject> hierarchyObjects, List<T> behaviours) where T : class
{
behaviours.Clear();
foreach (IHierarchyObject hierarchyObject in hierarchyObjects)

View File

@ -5,14 +5,17 @@ namespace Syntriax.Engine.Core.Factory;
public class BehaviourControllerFactory
{
public IBehaviourController Instantiate(IHierarchyObject hierarchyObject)
public static IBehaviourController Instantiate(IHierarchyObject hierarchyObject)
=> Instantiate<BehaviourController>(hierarchyObject);
public T Instantiate<T>(IHierarchyObject hierarchyObject, params object?[]? args)
public static T Instantiate<T>(IHierarchyObject hierarchyObject, params object?[]? args)
where T : class, IBehaviourController
{
T behaviourController = TypeFactory.Get<T>(args);
if (!hierarchyObject.Assign(behaviourController))
throw AssignException.From(hierarchyObject, behaviourController);
if (!behaviourController.Assign(hierarchyObject))
throw AssignException.From(behaviourController, hierarchyObject);

View File

@ -5,10 +5,10 @@ namespace Syntriax.Engine.Core.Factory;
public class BehaviourFactory
{
public T Instantiate<T>(IHierarchyObject hierarchyObject, params object?[]? args) where T : class, IBehaviour
public static T Instantiate<T>(IHierarchyObject hierarchyObject, params object?[]? args) where T : class, IBehaviour
=> Instantiate<T>(hierarchyObject, stateEnable: null, args);
public T Instantiate<T>(IHierarchyObject hierarchyObject, IStateEnable? stateEnable, params object?[]? args)
public static T Instantiate<T>(IHierarchyObject hierarchyObject, IStateEnable? stateEnable, params object?[]? args)
where T : class, IBehaviour
{
T behaviour = TypeFactory.Get<T>(args);
@ -17,10 +17,10 @@ public class BehaviourFactory
if (!stateEnable.Assign(behaviour))
throw AssignException.From(stateEnable, behaviour);
if (!behaviour.Assign(hierarchyObject.BehaviourController))
throw AssignException.From(behaviour, hierarchyObject.BehaviourController);
if (!behaviour.Assign(stateEnable))
throw AssignException.From(behaviour, stateEnable);
if (!behaviour.Assign(hierarchyObject.BehaviourController))
throw AssignException.From(behaviour, hierarchyObject.BehaviourController);
return behaviour;
}

View File

@ -5,10 +5,10 @@ namespace Syntriax.Engine.Core.Factory;
public class HierarchyObjectFactory
{
public T Instantiate<T>(params object?[]? args) where T : class, IHierarchyObject
public static T Instantiate<T>(params object?[]? args) where T : class, IHierarchyObject
=> Instantiate<T>(behaviourController: null, stateEnable: null, args);
public T Instantiate<T>(
public static T Instantiate<T>(
IBehaviourController? behaviourController = null,
IStateEnable? stateEnable = null,
params object?[]? args

View File

@ -5,12 +5,15 @@ namespace Syntriax.Engine.Core.Factory;
public class StateEnableFactory
{
public IStateEnable Instantiate(IEntity entity) => Instantiate<StateEnable>(entity);
public static IStateEnable Instantiate(IEntity entity) => Instantiate<StateEnable>(entity);
public T Instantiate<T>(IEntity entity, params object?[]? args) where T : class, IStateEnable
public static T Instantiate<T>(IEntity entity, params object?[]? args) where T : class, IStateEnable
{
T stateEnable = TypeFactory.Get<T>(args);
if (!entity.Assign(stateEnable))
throw AssignException.From(entity, stateEnable);
if (!stateEnable.Assign(entity))
throw AssignException.From(stateEnable, entity);

View File

@ -4,7 +4,7 @@ namespace Syntriax.Engine.Core.Factory;
public class TransformFactory
{
public ITransform2D Instantiate() => TypeFactory.Get<Transform2D>();
public T Instantiate<T>(params object?[]? args) where T : class, ITransform2D
public static ITransform2D Instantiate() => TypeFactory.Get<Transform2D>();
public static T Instantiate<T>(params object?[]? args) where T : class, ITransform2D
=> TypeFactory.Get<T>(args);
}

View File

@ -1,34 +1,23 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Syntriax.Engine.Core.Abstract;
using Syntriax.Engine.Core.Exceptions;
using Syntriax.Engine.Core.Factory;
namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("HierarchyObject Count: {_hierarchyObjects.Count}")]
public class GameManager : BaseEntity, IGameManager
{
public event IGameManager.OnUpdateEventHandler? OnUpdate = null;
public event IGameManager.OnPreDawEventHandler? OnPreDraw = null;
public event IGameManager.UpdateEventHandler? OnUpdate = null;
public event IGameManager.PreDawEventHandler? OnPreDraw = null;
public event IGameManager.OnHierarchyObjectRegisteredEventHandler? OnHierarchyObjectRegistered = null;
public event IGameManager.OnHierarchyObjectUnRegisteredEventHandler? OnHierarchyObjectUnRegistered = null;
public event IGameManager.HierarchyObjectRegisteredEventHandler? OnHierarchyObjectRegistered = null;
public event IGameManager.HierarchyObjectUnRegisteredEventHandler? OnHierarchyObjectUnRegistered = null;
private readonly List<IHierarchyObject> _hierarchyObjects = new(Constants.GAME_OBJECTS_SIZE_INITIAL);
private HierarchyObjectFactory _hierarchyObjectFactory = null!;
private HierarchyObjectFactory HierarchyObjectFactory
{
get
{
_hierarchyObjectFactory ??= new HierarchyObjectFactory();
return _hierarchyObjectFactory;
}
}
public IReadOnlyList<IHierarchyObject> HierarchyObjects => _hierarchyObjects;
public override IStateEnable StateEnable
@ -37,7 +26,7 @@ public class GameManager : BaseEntity, IGameManager
{
if (base.StateEnable is null)
{
Assign(new StateEnableFactory().Instantiate(this));
Assign(Factory.StateEnableFactory.Instantiate(this));
if (base.StateEnable is null)
throw NotAssignedException.From(this, base.StateEnable);
}
@ -46,6 +35,8 @@ public class GameManager : BaseEntity, IGameManager
}
}
public EngineTime Time { get; private set; } = new();
public void Register(IHierarchyObject hierarchyObject)
{
if (_hierarchyObjects.Contains(hierarchyObject))
@ -70,7 +61,7 @@ public class GameManager : BaseEntity, IGameManager
public T InstantiateHierarchyObject<T>(params object?[]? args) where T : class, IHierarchyObject
{
T hierarchyObject = HierarchyObjectFactory.Instantiate<T>(args);
T hierarchyObject = Factory.HierarchyObjectFactory.Instantiate<T>(args);
Register(hierarchyObject);
return hierarchyObject;
}
@ -113,13 +104,14 @@ public class GameManager : BaseEntity, IGameManager
HierarchyObjects[i].Finalize();
}
public void Update(EngineTime time)
public void Update(EngineTime engineTime)
{
Time.SetTime(time);
Time = engineTime;
for (int i = 0; i < HierarchyObjects.Count; i++)
HierarchyObjects[i].BehaviourController.Update();
OnUpdate?.Invoke(this, time);
OnUpdate?.Invoke(this, engineTime);
}
public void PreDraw()
@ -130,8 +122,6 @@ public class GameManager : BaseEntity, IGameManager
OnPreDraw?.Invoke(this);
}
/////////////////////////////////////////////////////////////////
private void OnHierarchyObjectFinalize(IInitializable initializable)
{
if (initializable is IHierarchyObject hierarchyObject)
@ -143,4 +133,7 @@ public class GameManager : BaseEntity, IGameManager
if (sender is IHierarchyObject hierarchyObject)
Remove(hierarchyObject);
}
public IEnumerator<IHierarchyObject> GetEnumerator() => _hierarchyObjects.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _hierarchyObjects.GetEnumerator();
}

View File

@ -8,23 +8,27 @@ namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("Name: {Name}, Initialized: {Initialized}")]
public class HierarchyObject : BaseEntity, IHierarchyObject
{
public event IHierarchyObject.OnEnteredHierarchyEventHandler? OnEnteredHierarchy = null;
public event IHierarchyObject.OnExitedHierarchyEventHandler? OnExitedHierarchy = null;
public event IHierarchyObject.OnParentChangedEventHandler? OnParentChanged = null;
public event IHierarchyObject.OnChildrenAddedEventHandler? OnChildrenAdded = null;
public event IHierarchyObject.OnChildrenRemovedEventHandler? OnChildrenRemoved = null;
public event IHasBehaviourController.OnBehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned = null;
public event INameable.OnNameChangedEventHandler? OnNameChanged = null;
public event IHierarchyObject.EnteredHierarchyEventHandler? OnEnteredHierarchy = null;
public event IHierarchyObject.ExitedHierarchyEventHandler? OnExitedHierarchy = null;
public event IHierarchyObject.ParentChangedEventHandler? OnParentChanged = null;
public event IHierarchyObject.ChildrenAddedEventHandler? OnChildrenAdded = null;
public event IHierarchyObject.ChildrenRemovedEventHandler? OnChildrenRemoved = null;
public event IHasBehaviourController.BehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned = null;
public event INameable.NameChangedEventHandler? OnNameChanged = null;
public event IActive.ActiveChangedEventHandler? OnActiveChanged = null;
private string _name = nameof(HierarchyObject);
private IGameManager _gameManager = null!;
private IBehaviourController _behaviourController = null!;
private bool _isActive = false;
private readonly List<IHierarchyObject> _children = [];
public IHierarchyObject? Parent { get; private set; } = null;
public IReadOnlyList<IHierarchyObject> Children => _children;
public IBehaviourController BehaviourController => _behaviourController;
public IGameManager GameManager => _gameManager;
public bool IsInHierarchy => _gameManager is not null;
public bool IsActive => _isActive;
public string Name
{
@ -39,8 +43,6 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
}
}
public IBehaviourController BehaviourController => _behaviourController;
protected virtual void OnEnteringHierarchy(IGameManager gameManager) { }
bool IHierarchyObject.EnterHierarchy(IGameManager gameManager)
{
@ -48,6 +50,7 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
return false;
_gameManager = gameManager;
UpdateActive();
OnEnteringHierarchy(gameManager);
OnEnteredHierarchy?.Invoke(this, gameManager);
return true;
@ -74,7 +77,7 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
if (previousParent is not null)
{
previousParent.RemoveChild(this);
previousParent.OnParentChanged -= NotifyChildrenOnParentChange;
previousParent.OnActiveChanged -= OnParentActiveChanged;
}
Parent = parent;
@ -82,9 +85,10 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
if (parent is not null)
{
parent.AddChild(this);
parent.OnParentChanged += NotifyChildrenOnParentChange;
parent.OnActiveChanged += OnParentActiveChanged;
}
UpdateActive();
OnParentChanged?.Invoke(this, previousParent, parent);
}
@ -107,28 +111,47 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
OnChildrenRemoved?.Invoke(this, parent);
}
private void NotifyChildrenOnParentChange(IHierarchyObject sender, IHierarchyObject? previousParent, IHierarchyObject? newParent)
{
// 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.parentTransform);` but seems an unnecessary call too?
foreach (IHierarchyObject child in Children) // TODO CHECK ERRORS
child.SetParent(this);
}
protected virtual void OnAssign(IBehaviourController behaviourController) { }
public bool Assign(IBehaviourController behaviourController)
{
if (IsInitialized)
return false;
_behaviourController = behaviourController;
OnAssign(behaviourController);
OnBehaviourControllerAssigned?.Invoke(this);
return true;
}
protected override void OnAssign(IStateEnable stateEnable)
{
base.OnAssign(stateEnable);
stateEnable.OnEnabledChanged += OnStateEnabledChanged;
}
private void OnParentActiveChanged(IActive sender, bool previousState) => UpdateActive();
private void OnStateEnabledChanged(IStateEnable sender, bool previousState) => UpdateActive();
private void UpdateActive()
{
bool previousActive = IsActive;
_isActive = StateEnable.Enabled && (Parent?.IsActive ?? true);
if (previousActive != IsActive)
OnActiveChanged?.Invoke(this, previousActive);
}
protected override void UnassignInternal()
{
base.UnassignInternal();
StateEnable.OnEnabledChanged -= OnStateEnabledChanged;
}
protected override void InitializeInternal()
{
base.InitializeInternal();
_behaviourController ??= new Factory.BehaviourControllerFactory().Instantiate(this);
_behaviourController ??= Factory.BehaviourControllerFactory.Instantiate(this);
}
public IEnumerator<IHierarchyObject> GetEnumerator() => _children.GetEnumerator();

View File

@ -4,9 +4,9 @@ namespace Syntriax.Engine.Core;
public class StateEnable : IStateEnable
{
public event IAssignable.OnUnassignedEventHandler? OnUnassigned = null;
public event IHasEntity.OnEntityAssignedEventHandler? OnEntityAssigned = null;
public event IStateEnable.OnNameChangedEventHandler? OnEnabledChanged = null;
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
public event IHasEntity.EntityAssignedEventHandler? OnEntityAssigned = null;
public event IStateEnable.EnabledChangedEventHandler? OnEnabledChanged = null;
private bool _enabled = true;
private IEntity _entity = null!;
@ -27,12 +27,14 @@ public class StateEnable : IStateEnable
}
}
protected virtual void OnAssign(IEntity entity) { }
public bool Assign(IEntity entity)
{
if (_entity is not null && _entity.IsInitialized)
return false;
_entity = entity;
OnAssign(entity);
OnEntityAssigned?.Invoke(this);
return true;
}

View File

@ -1,15 +0,0 @@
using System;
namespace Syntriax.Engine.Core;
public static class Time
{
private static EngineTime _engineTime = new(TimeSpan.Zero, TimeSpan.Zero);
public static TimeSpan Total => _engineTime.Total;
public static TimeSpan Elapsed => _engineTime.Elapsed;
public static float DeltaTimeFrame => _engineTime.DeltaTimeFrame;
public static void SetTime(EngineTime engineTime) => _engineTime = engineTime;
}

View File

@ -5,9 +5,9 @@ namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("Name: {HierarchyObject.Name, nq} Position: {Position.ToString(), nq}, Scale: {Scale.ToString(), nq}, Rotation: {Rotation}")]
public class Transform2D : Behaviour, ITransform2D
{
public event ITransform2D.OnPositionChangedEventHandler? OnPositionChanged = null;
public event ITransform2D.OnScaleChangedEventHandler? OnScaleChanged = null;
public event ITransform2D.OnRotationChangedEventHandler? OnRotationChanged = null;
public event ITransform2D.PositionChangedEventHandler? OnPositionChanged = null;
public event ITransform2D.ScaleChangedEventHandler? OnScaleChanged = null;
public event ITransform2D.RotationChangedEventHandler? OnRotationChanged = null;
private Vector2D _position = Vector2D.Zero;
private Vector2D _scale = Vector2D.One;

View File

@ -1,484 +0,0 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from `dotnet new gitignore`
# dotenv files
.env
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET
project.lock.json
project.fragment.lock.json
artifacts/
# Tye
.tye/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
.idea
##
## Visual studio for Mac
##
# globs
Makefile.in
*.userprefs
*.usertasks
config.make
config.status
aclocal.m4
install-sh
autom4te.cache/
*.tar.gz
tarballs/
test-results/
# Mac bundle stuff
*.dmg
*.app
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# Vim temporary swap files
*.swp

View File

@ -10,17 +10,17 @@ public interface ICollider2D : IBehaviour
/// <summary>
/// Event triggered when a collision is detected.
/// </summary>
event OnCollisionDetectedEventHandler? OnCollisionDetected;
event CollisionDetectedEventHandler? OnCollisionDetected;
/// <summary>
/// Event triggered when a collision is resolved.
/// </summary>
event OnCollisionResolvedEventHandler? OnCollisionResolved;
event CollisionResolvedEventHandler? OnCollisionResolved;
/// <summary>
/// Event triggered when another <see cref="ICollider2D"/> triggers this <see cref="ICollider2D"/>.
/// </summary>
event OnTriggeredEventHandler? OnTriggered;
event TriggeredEventHandler? OnTriggered;
/// <inheritdoc cref="ITransform2D" />
ITransform2D Transform { get; }
@ -44,7 +44,7 @@ public interface ICollider2D : IBehaviour
void Resolve(CollisionDetectionInformation collisionDetectionInformation);
void Trigger(ICollider2D initiator);
delegate void OnCollisionDetectedEventHandler(ICollider2D sender, CollisionDetectionInformation collisionDetectionInformation);
delegate void OnCollisionResolvedEventHandler(ICollider2D sender, CollisionDetectionInformation collisionDetectionInformation);
delegate void OnTriggeredEventHandler(ICollider2D sender, ICollider2D initiatorCollider);
delegate void CollisionDetectedEventHandler(ICollider2D sender, CollisionDetectionInformation collisionDetectionInformation);
delegate void CollisionResolvedEventHandler(ICollider2D sender, CollisionDetectionInformation collisionDetectionInformation);
delegate void TriggeredEventHandler(ICollider2D sender, ICollider2D initiatorCollider);
}

View File

@ -8,12 +8,12 @@ public interface IPhysicsEngine2D
/// <summary>
/// Event triggered when the <see cref="IPhysicsEngine2D"/> has done a single physics iteration.
/// </summary>
event OnPhysicsIterationEventHandler? OnPhysicsIteration;
event PhysicsIterationEventHandler? OnPhysicsIteration;
/// <summary>
/// Event triggered when the <see cref="IPhysicsEngine2D"/> has done a full physics step/>.
/// </summary>
event OnPhysicsStepEventHandler? OnPhysicsStep;
event PhysicsStepEventHandler? OnPhysicsStep;
/// <summary>
/// The number of iterations the <see cref="IPhysicsEngine2D"/> performs per step.
@ -26,6 +26,6 @@ public interface IPhysicsEngine2D
/// <param name="deltaTime">The time step.</param>
void Step(float deltaTime);
delegate void OnPhysicsIterationEventHandler(IPhysicsEngine2D sender, float iterationDeltaTime);
delegate void OnPhysicsStepEventHandler(IPhysicsEngine2D sender, float stepDeltaTime);
delegate void PhysicsIterationEventHandler(IPhysicsEngine2D sender, float iterationDeltaTime);
delegate void PhysicsStepEventHandler(IPhysicsEngine2D sender, float stepDeltaTime);
}

View File

@ -6,9 +6,9 @@ namespace Syntriax.Engine.Physics2D;
public abstract class Collider2DBehaviourBase : Behaviour2D, ICollider2D
{
public event ICollider2D.OnCollisionDetectedEventHandler? OnCollisionDetected = null;
public event ICollider2D.OnCollisionResolvedEventHandler? OnCollisionResolved = null;
public event ICollider2D.OnTriggeredEventHandler? OnTriggered = null;
public event ICollider2D.CollisionDetectedEventHandler? OnCollisionDetected = null;
public event ICollider2D.CollisionResolvedEventHandler? OnCollisionResolved = null;
public event ICollider2D.TriggeredEventHandler? OnTriggered = null;
protected bool NeedsRecalculation { get; private set; } = true;
protected IRigidBody2D? _rigidBody2D = null;

View File

@ -1,13 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\Engine.Core\Engine.Core.csproj" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Syntriax.Engine.Physics2D</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Engine.Core\Engine.Core.csproj" />
</ItemGroup>
</Project>

View File

@ -25,7 +25,7 @@ public class PhysicsCoroutineManager : HierarchyObject
protected override void OnEnteringHierarchy(IGameManager gameManager)
{
physicsEngine = gameManager.HierarchyObjects.FindObject<IPhysicsEngine2D>();
physicsEngine = gameManager.FindHierarchyObject<IPhysicsEngine2D>();
if (physicsEngine is IPhysicsEngine2D foundPhysicsEngine)
foundPhysicsEngine.OnPhysicsStep += OnPhysicsStep;
else
@ -51,12 +51,12 @@ public class PhysicsCoroutineManager : HierarchyObject
gameManager.OnUpdate -= OnUpdate;
}
private void OnUpdate(IGameManager sender, EngineTime time)
private void OnUpdate(IGameManager sender, EngineTime engineTime)
{
if (GameManager is not IGameManager gameManager)
return;
physicsEngine = gameManager.HierarchyObjects.FindObject<IPhysicsEngine2D>();
physicsEngine = gameManager.FindHierarchyObject<IPhysicsEngine2D>();
if (physicsEngine is IPhysicsEngine2D foundPhysicsEngine)
{
foundPhysicsEngine.OnPhysicsStep += OnPhysicsStep;

View File

@ -7,8 +7,8 @@ namespace Syntriax.Engine.Physics2D;
public class PhysicsEngine2D : IPhysicsEngine2D
{
public event IPhysicsEngine2D.OnPhysicsIterationEventHandler? OnPhysicsIteration = null;
public event IPhysicsEngine2D.OnPhysicsStepEventHandler? OnPhysicsStep = null;
public event IPhysicsEngine2D.PhysicsIterationEventHandler? OnPhysicsIteration = null;
public event IPhysicsEngine2D.PhysicsStepEventHandler? OnPhysicsStep = null;
private readonly List<IRigidBody2D> rigidBodies = new(32);
private readonly List<ICollider2D> colliders = new(64);

View File

@ -6,8 +6,8 @@ namespace Syntriax.Engine.Physics2D;
public class PhysicsEngine2DCollector : HierarchyObject, IPhysicsEngine2D
{
public event IPhysicsEngine2D.OnPhysicsIterationEventHandler? OnPhysicsIteration = null;
public event IPhysicsEngine2D.OnPhysicsStepEventHandler? OnPhysicsStep = null;
public event IPhysicsEngine2D.PhysicsIterationEventHandler? OnPhysicsIteration = null;
public event IPhysicsEngine2D.PhysicsStepEventHandler? OnPhysicsStep = null;
private int _iterationPerStep = 1;

View File

@ -1,12 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Engine.Core\Engine.Core.csproj" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Syntriax.Engine.Systems</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Engine.Core\Engine.Core.csproj" />
</ItemGroup>
</Project>

View File

@ -1,6 +1,6 @@
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Input;
namespace Syntriax.Engine.Systems.Input;
public interface IButtonInputs<T> : IHasStateEnable
{

View File

@ -0,0 +1,22 @@
namespace Syntriax.Engine.StateMachine;
public interface IState
{
event StateUpdateEventHandler? OnStateUpdate;
event StateTransitionedFromEventHandler? OnStateTransitionedFrom;
event StateTransitionedToEventHandler? OnStateTransitionedTo;
event StateTransitionReadyEventHandler? OnStateTransitionReady;
string Name { get; }
IState? GetNextState();
void Update();
void TransitionTo(IState from);
void TransitionFrom(IState to);
delegate void StateUpdateEventHandler(IState sender);
delegate void StateTransitionedFromEventHandler(IState sender, IState toState);
delegate void StateTransitionedToEventHandler(IState sender, IState fromState);
delegate void StateTransitionReadyEventHandler(IState sender, IState toState);
}

View File

@ -0,0 +1,52 @@
namespace Syntriax.Engine.StateMachine;
public class State : IState
{
public event IState.StateUpdateEventHandler? OnStateUpdate = null;
public event IState.StateTransitionedFromEventHandler? OnStateTransitionedFrom = null;
public event IState.StateTransitionedToEventHandler? OnStateTransitionedTo = null;
public event IState.StateTransitionReadyEventHandler? OnStateTransitionReady = null;
private readonly List<StateTransition> transitions = [];
private readonly Dictionary<string, StateTransition> possibleTransitions = [];
public string Name { get; set; } = "Default State Name";
public IReadOnlyList<StateTransition> Transitions => transitions;
public IReadOnlyDictionary<string, StateTransition> PossibleTransitions => possibleTransitions;
public void RemoveTransition(string name)
{
if (!possibleTransitions.TryGetValue(name, out StateTransition stateTransition))
return;
transitions.Remove(stateTransition);
possibleTransitions.Remove(name);
}
public void AddTransition(string name, StateTransition stateTransition)
{
if (transitions.Contains(stateTransition))
return;
transitions.Add(stateTransition);
possibleTransitions.Add(name, stateTransition);
}
public void Update()
{
if (GetNextState() is IState transitionState)
OnStateTransitionReady?.Invoke(this, transitionState);
OnStateUpdate?.Invoke(this);
}
public void TransitionTo(IState from) => OnStateTransitionedTo?.Invoke(this, from);
public void TransitionFrom(IState to) => OnStateTransitionedFrom?.Invoke(this, to);
public IState? GetNextState()
{
foreach (StateTransition stateTransition in transitions)
if (stateTransition.CanTransition)
return stateTransition.State;
return null;
}
}

View File

@ -0,0 +1,37 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.StateMachine;
public abstract class StateBehaviourBase : Behaviour, IState
{
public event IState.StateUpdateEventHandler? OnStateUpdate = null;
public event IState.StateTransitionedFromEventHandler? OnStateTransitionedFrom = null;
public event IState.StateTransitionedToEventHandler? OnStateTransitionedTo = null;
public abstract event IState.StateTransitionReadyEventHandler? OnStateTransitionReady;
public abstract string Name { get; }
protected virtual void OnUpdateState() { }
public void Update()
{
OnUpdateState();
OnStateUpdate?.Invoke(this);
}
protected virtual void OnTransitionedToState(IState from) { }
public void TransitionTo(IState from)
{
OnTransitionedToState(from);
OnStateTransitionedTo?.Invoke(this, from);
}
protected virtual void OnTransitionedFromState(IState to) { }
public void TransitionFrom(IState to)
{
OnTransitionedFromState(to);
OnStateTransitionedFrom?.Invoke(this, to);
}
public abstract IState? GetNextState();
}

View File

@ -0,0 +1,49 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.StateMachine;
public class StateMachine : Behaviour
{
public event StateChangedEventHandler? OnStateChanged = null;
private IState _state = new State();
public IState State
{
get => _state;
set
{
if (_state == value)
return;
IState previousState = _state;
previousState.OnStateTransitionReady -= OnStateTransitionReady;
_state = value;
previousState.TransitionFrom(value);
value.TransitionTo(_state);
OnStateChanged?.Invoke(this, previousState, value);
value.OnStateTransitionReady += OnStateTransitionReady;
}
}
private void OnStateTransitionReady(IState sender, IState toState)
{
State = toState;
while (State.GetNextState() is IState nextState)
State = nextState;
}
protected override void OnUpdate()
{
if (State is null)
return;
while (State.GetNextState() is IState nextState)
State = nextState;
State.Update();
}
public delegate void StateChangedEventHandler(StateMachine sender, IState previousState, IState newState);
}

View File

@ -0,0 +1,11 @@
namespace Syntriax.Engine.StateMachine;
public readonly record struct StateTransition(IState State, IReadOnlyList<Func<bool>> Conditions)
{
public static implicit operator (IState state, IReadOnlyList<Func<bool>> conditions)(StateTransition value)
=> (value.State, value.Conditions);
public static implicit operator StateTransition((IState state, IReadOnlyList<Func<bool>> conditions) value)
=> new(value.state, value.conditions);
public bool CanTransition => !Conditions.Any(c => !c.Invoke());
}

View File

@ -0,0 +1,18 @@
namespace Syntriax.Engine.Systems.Time;
public interface IReadOnlyStopwatch
{
event StopwatchEventHandler? OnStarted;
event StopwatchDeltaEventHandler? OnDelta;
event StopwatchEventHandler? OnStopped;
double Time { get; }
TimerState State { get; }
event StopwatchEventHandler? OnPaused;
event StopwatchEventHandler? OnResumed;
delegate void StopwatchEventHandler(IReadOnlyStopwatch sender);
delegate void StopwatchDeltaEventHandler(IReadOnlyStopwatch sender, double delta);
}

View File

@ -0,0 +1,11 @@
namespace Syntriax.Engine.Systems.Time;
public interface IReadOnlyTicker : IReadOnlyStopwatch
{
event TickerTickEventHandler? OnTick;
int TickCounter { get; }
double TickPeriod { get; set; }
delegate void TickerTickEventHandler(IReadOnlyTicker sender);
}

View File

@ -0,0 +1,22 @@
namespace Syntriax.Engine.Systems.Time;
public interface IReadOnlyTimer
{
event TimerEventHandler? OnStarted;
event TimerDeltaEventHandler? OnDelta;
event TimerEventHandler? OnStopped;
event TimerEventHandler? OnPaused;
event TimerEventHandler? OnResumed;
double StartTime { get; }
double Remaining { get; }
float Percentage { get; }
TimerState State { get; }
delegate void TimerEventHandler(IReadOnlyTimer sender);
delegate void TimerDeltaEventHandler(IReadOnlyTimer sender, double delta);
}

View File

@ -0,0 +1,10 @@
namespace Syntriax.Engine.Systems.Time;
public interface IStopwatch : IReadOnlyStopwatch
{
void StopwatchStart();
void StopwatchStop();
void StopwatchPause();
void StopwatchResume();
}

View File

@ -0,0 +1,11 @@
namespace Syntriax.Engine.Systems.Time;
public interface ITicker : IStopwatch
{
event TickerTickEventHandler? OnTick;
int TickCounter { get; }
double TickPeriod { get; set; }
delegate void TickerTickEventHandler(ITicker sender);
}

View File

@ -0,0 +1,10 @@
namespace Syntriax.Engine.Systems.Time;
public interface ITimer : IReadOnlyTimer
{
void TimerStart(double time);
void TimerStop();
void TimerPause();
void TimerResume();
}

View File

@ -0,0 +1,99 @@
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Systems.Time;
public class StopwatchBehaviour : Behaviour, IStopwatch
{
public event IReadOnlyStopwatch.StopwatchEventHandler? OnStarted = null;
public event IReadOnlyStopwatch.StopwatchDeltaEventHandler? OnDelta = null;
public event IReadOnlyStopwatch.StopwatchEventHandler? OnStopped = null;
public event IReadOnlyStopwatch.StopwatchEventHandler? OnPaused = null;
public event IReadOnlyStopwatch.StopwatchEventHandler? OnResumed = null;
public double Time { get; protected set; } = 0f;
public TimerState State { get; protected set; } = TimerState.Idle;
private bool shouldBeTicking = false;
private bool hasStartedTickingBefore = false;
public virtual void StopwatchStart()
{
Time = 0f;
shouldBeTicking = true;
hasStartedTickingBefore = false;
if (IsActive)
StartStopwatch();
}
public virtual void StopwatchStop()
{
if (!shouldBeTicking)
return;
shouldBeTicking = false;
State = TimerState.Stopped;
OnStopped?.Invoke(this);
}
protected override void OnUpdate()
{
if (State is not TimerState.Ticking)
return;
double delta = GameManager.Time.DeltaSpan.TotalSeconds;
Time += delta;
OnDelta?.Invoke(this, delta);
}
protected override void OnEnteredHierarchy(IGameManager gameManager)
{
if (!shouldBeTicking || State is TimerState.Ticking)
return;
if (hasStartedTickingBefore)
StopwatchResume();
else
StartStopwatch();
}
protected override void OnExitedHierarchy(IGameManager gameManager)
{
if (!shouldBeTicking || State is not TimerState.Ticking)
return;
StopwatchPause();
}
public virtual void StopwatchPause()
{
State = TimerState.Paused;
OnPaused?.Invoke(this);
}
public virtual void StopwatchResume()
{
State = TimerState.Ticking;
OnResumed?.Invoke(this);
}
private void StartStopwatch()
{
hasStartedTickingBefore = true;
State = TimerState.Ticking;
OnStarted?.Invoke(this);
}
protected override void OnFinalize()
{
Time = 0f;
State = TimerState.Idle;
shouldBeTicking = false;
hasStartedTickingBefore = false;
}
}

View File

@ -0,0 +1,38 @@
namespace Syntriax.Engine.Systems.Time;
public class TickerBehaviour : StopwatchBehaviour, ITicker
{
public event ITicker.TickerTickEventHandler? OnTick = null;
public double TickPeriod { get; set; } = 1f;
public int TickCounter { get; private set; } = 0;
private double nextTick = 0f;
public override void StopwatchStart()
{
TickCounter = 0;
base.StopwatchStart();
nextTick = Time + TickPeriod;
}
protected override void OnUpdate()
{
base.OnUpdate();
if (Time < nextTick)
return;
nextTick += TickPeriod;
TickCounter++;
OnTick?.Invoke(this);
}
protected override void OnFinalize()
{
base.OnFinalize();
TickCounter = 0;
nextTick = 0f;
}
}

View File

@ -0,0 +1,118 @@
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Systems.Time;
public class TimerBehaviour : Behaviour, ITimer
{
public event IReadOnlyTimer.TimerEventHandler? OnStarted = null;
public event IReadOnlyTimer.TimerDeltaEventHandler? OnDelta = null;
public event IReadOnlyTimer.TimerEventHandler? OnStopped = null;
public event IReadOnlyTimer.TimerEventHandler? OnPaused = null;
public event IReadOnlyTimer.TimerEventHandler? OnResumed = null;
public TimerState State { get; protected set; } = TimerState.Idle;
public double StartTime { get; protected set; } = 0f;
public float Percentage => (float)(1f - (Remaining / StartTime));
private double _remaining = 0f;
public double Remaining
{
get => _remaining;
protected set
{
if (value < .0f)
value = .0f;
_remaining = value;
}
}
private bool shouldBeTicking = false;
private bool hasStartedTickingBefore = false;
public virtual void TimerStart(double time)
{
StartTime = time;
Remaining = time;
shouldBeTicking = true;
hasStartedTickingBefore = false;
if (IsActive)
StartTimer();
}
public virtual void TimerStop()
{
if (!shouldBeTicking)
return;
shouldBeTicking = false;
State = TimerState.Stopped;
OnStopped?.Invoke(this);
}
protected override void OnUpdate()
{
if (State is not TimerState.Ticking)
return;
double delta = GameManager.Time.DeltaSpan.TotalSeconds;
Remaining -= delta;
OnDelta?.Invoke(this, delta);
if (Remaining <= .0f)
TimerStop();
}
protected override void OnEnteredHierarchy(IGameManager gameManager)
{
if (!shouldBeTicking || State is TimerState.Ticking)
return;
if (hasStartedTickingBefore)
TimerResume();
else
StartTimer();
}
protected override void OnExitedHierarchy(IGameManager gameManager)
{
if (!shouldBeTicking || State is not TimerState.Ticking)
return;
TimerPause();
}
public virtual void TimerPause()
{
State = TimerState.Paused;
OnPaused?.Invoke(this);
}
public virtual void TimerResume()
{
State = TimerState.Ticking;
OnResumed?.Invoke(this);
}
private void StartTimer()
{
hasStartedTickingBefore = true;
State = TimerState.Ticking;
OnStarted?.Invoke(this);
}
protected override void OnFinalize()
{
StartTime = 0f;
Remaining = 0f;
State = TimerState.Idle;
shouldBeTicking = false;
hasStartedTickingBefore = false;
}
}

View File

@ -0,0 +1,9 @@
namespace Syntriax.Engine.Systems.Time;
public enum TimerState
{
Idle = 0b0001,
Ticking = 0b0010,
Paused = 0b0100,
Stopped = 0b1000
}

View File

@ -0,0 +1,72 @@
// Reference: https://easings.net
namespace Syntriax.Engine.Systems.Tween;
internal static class EaseConstants
{
internal const float c1 = 1.70158f;
internal const float c2 = c1 * 1.525f;
internal const float c3 = c1 + 1f;
internal const float c4 = 2f * Core.Math.PI / 3;
internal const float c5 = 2f * Core.Math.PI / 4.5f;
}
public readonly struct EaseLinear : IEasing { public readonly float Evaluate(float value) => value; }
public readonly struct EaseInQuad : IEasing { public readonly float Evaluate(float value) => value * value; }
public readonly struct EaseOutQuad : IEasing { public readonly float Evaluate(float value) => 1f - (1f - value) * (1f - value); }
public readonly struct EaseInOutQuad : IEasing { public readonly float Evaluate(float value) => value < 0.5f ? 2f * value * value : 1f - Core.Math.Pow(-2f * value + 2f, 2f) * .5f; }
public readonly struct EaseInCubic : IEasing { public readonly float Evaluate(float value) => value * value * value; }
public readonly struct EaseOutCubic : IEasing { public readonly float Evaluate(float value) => 1f - Core.Math.Pow(1f - value, 3); }
public readonly struct EaseInOutCubic : IEasing { public readonly float Evaluate(float value) => value < 0.5f ? 4f * value * value * value : 1f - Core.Math.Pow(-2f * value + 2f, 3) * .5f; }
public readonly struct EaseInQuart : IEasing { public readonly float Evaluate(float value) => value * value * value * value; }
public readonly struct EaseOutQuart : IEasing { public readonly float Evaluate(float value) => 1f - Core.Math.Pow(1f - value, 4f); }
public readonly struct EaseInOutQuart : IEasing { public readonly float Evaluate(float value) => value < 0.5f ? 8 * value * value * value * value : 1f - Core.Math.Pow(-2f * value + 2f, 4f) * .5f; }
public readonly struct EaseInQuint : IEasing { public readonly float Evaluate(float value) => value * value * value * value * value; }
public readonly struct EaseOutQuint : IEasing { public readonly float Evaluate(float value) => 1f - Core.Math.Pow(1f - value, 5); }
public readonly struct EaseInOutQuint : IEasing { public readonly float Evaluate(float value) => value < 0.5f ? 16 * value * value * value * value * value : 1f - Core.Math.Pow(-2f * value + 2f, 5) * .5f; }
public readonly struct EaseInSine : IEasing { public readonly float Evaluate(float value) => 1f - Core.Math.Cos(value * Core.Math.PI * .5f); }
public readonly struct EaseOutSine : IEasing { public readonly float Evaluate(float value) => Core.Math.Sin(value * Core.Math.PI * .5f); }
public readonly struct EaseInOutSine : IEasing { public readonly float Evaluate(float value) => -(Core.Math.Cos(Core.Math.PI * value) - 1f) * .5f; }
public readonly struct EaseInExpo : IEasing { public readonly float Evaluate(float value) => value == 0 ? 0 : Core.Math.Pow(2f, 10f * value - 10f); }
public readonly struct EaseOutExpo : IEasing { public readonly float Evaluate(float value) => value == 1f ? 1f : 1f - Core.Math.Pow(2f, -10f * value); }
public readonly struct EaseInOutExpo : IEasing { public readonly float Evaluate(float value) => value == 0 ? 0 : value == 1f ? 1f : value < 0.5f ? Core.Math.Pow(2f, 20 * value - 10f) * .5f : (2f - Core.Math.Pow(2f, -20 * value + 10f)) * .5f; }
public readonly struct EaseInCirc : IEasing { public readonly float Evaluate(float value) => 1f - Core.Math.Sqrt(1f - Core.Math.Pow(value, 2f)); }
public readonly struct EaseOutCirc : IEasing { public readonly float Evaluate(float value) => Core.Math.Sqrt(1f - Core.Math.Pow(value - 1f, 2f)); }
public readonly struct EaseInOutCirc : IEasing { public readonly float Evaluate(float value) => value < 0.5f ? (1f - Core.Math.Sqrt(1f - Core.Math.Pow(2f * value, 2f))) * .5f : (Core.Math.Sqrt(1f - Core.Math.Pow(-2f * value + 2f, 2f)) + 1f) * .5f; }
public readonly struct EaseInBack : IEasing { public readonly float Evaluate(float value) => EaseConstants.c3 * value * value * value - EaseConstants.c1 * value * value; }
public readonly struct EaseOutBack : IEasing { public readonly float Evaluate(float value) => 1f + EaseConstants.c3 * Core.Math.Pow(value - 1f, 3) + EaseConstants.c1 * Core.Math.Pow(value - 1f, 2f); }
public readonly struct EaseInOutBack : IEasing { public readonly float Evaluate(float value) => value < 0.5f ? Core.Math.Pow(2f * value, 2f) * ((EaseConstants.c2 + 1f) * 2f * value - EaseConstants.c2) * .5f : (Core.Math.Pow(2f * value - 2f, 2f) * ((EaseConstants.c2 + 1f) * (value * 2f - 2f) + EaseConstants.c2) + 2f) * .5f; }
public readonly struct EaseInElastic : IEasing { public readonly float Evaluate(float value) => value == 0 ? 0 : value == 1f ? 1f : -Core.Math.Pow(2f, 10f * value - 10f) * Core.Math.Sin((value * 10f - 10.75f) * EaseConstants.c4); }
public readonly struct EaseOutElastic : IEasing { public readonly float Evaluate(float value) => value == 0 ? 0 : value == 1f ? 1f : Core.Math.Pow(2f, -10f * value) * Core.Math.Sin((value * 10f - 0.75f) * EaseConstants.c4) + 1f; }
public readonly struct EaseInOutElastic : IEasing { public readonly float Evaluate(float value) => value == 0 ? 0 : value == 1f ? 1f : value < 0.5f ? -(Core.Math.Pow(2f, 20 * value - 10f) * Core.Math.Sin((20 * value - 11.125f) * EaseConstants.c5)) * .5f : Core.Math.Pow(2f, -20 * value + 10f) * Core.Math.Sin((20 * value - 11.125f) * EaseConstants.c5) * .5f + 1f; }
public readonly struct EaseInBounce : IEasing { public readonly float Evaluate(float value) => 1f - new EaseOutBounce().Evaluate(1f - value); }
public readonly struct EaseOutBounce : IEasing
{
public readonly float Evaluate(float value)
{
const float n1 = 7.5625f;
const float d1 = 2.75f;
if (value < 1 / d1)
return n1 * value * value;
if (value < 2 / d1)
return n1 * (value -= 1.5f / d1) * value + 0.75f;
if (value < 2.5 / d1)
return n1 * (value -= 2.25f / d1) * value + 0.9375f;
return n1 * (value -= 2.625f / d1) * value + 0.984375f;
}
}
public readonly struct EaseInOutBounce : IEasing { public readonly float Evaluate(float value) => value < 0.5f ? (1f - new EaseOutBounce().Evaluate(1f - 2f * value)) * .5f : (1f + new EaseOutBounce().Evaluate(2f * value - 1f)) * .5f; }

View File

@ -0,0 +1,7 @@
namespace Syntriax.Engine.Systems.Tween;
public interface IEasing
{
float Evaluate(float value);
}

View File

@ -0,0 +1,25 @@
namespace Syntriax.Engine.Systems.Tween;
public interface ITween
{
event TweenEventHandler? OnStarted;
event TweenEventHandler? OnPaused;
event TweenEventHandler? OnResumed;
event TweenEventHandler? OnCancelled;
event TweenEventHandler? OnCompleted;
event TweenEventHandler? OnEnded;
event TweenEventHandler? OnUpdated;
event TweenDeltaEventHandler? OnDeltaUpdated;
TweenState State { get; set; }
float Counter { get; }
float Duration { get; }
float Progress { get; }
float Value { get; }
delegate void TweenEventHandler(ITween sender);
delegate void TweenDeltaEventHandler(ITween sender, float delta);
}

View File

@ -0,0 +1,82 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
internal class Tween : ITween
{
public event ITween.TweenEventHandler? OnStarted = null;
public event ITween.TweenEventHandler? OnPaused = null;
public event ITween.TweenEventHandler? OnResumed = null;
public event ITween.TweenEventHandler? OnCancelled = null;
public event ITween.TweenEventHandler? OnCompleted = null;
public event ITween.TweenEventHandler? OnEnded = null;
public event ITween.TweenEventHandler? OnUpdated = null;
public event ITween.TweenDeltaEventHandler? OnDeltaUpdated = null;
private TweenState _state = TweenState.Idle;
public TweenState State
{
get => _state;
set
{
if (value == _state)
return;
TweenState previousState = _state;
_state = value;
switch (value)
{
case TweenState.Completed: OnCompleted?.Invoke(this); OnEnded?.Invoke(this); break;
case TweenState.Cancelled: OnCancelled?.Invoke(this); OnEnded?.Invoke(this); break;
case TweenState.Paused: OnPaused?.Invoke(this); break;
case TweenState.Playing:
if (previousState == TweenState.Idle)
OnStarted?.Invoke(this);
else
OnResumed?.Invoke(this);
break;
}
}
}
public float Duration { get; internal set; } = 1f;
public float Progress { get; internal set; } = 0f;
private float _counter = 0f;
public IEasing Easing { get; set; } = new EaseLinear();
public float Value => Easing.Evaluate(Progress);
public float Counter
{
get => _counter;
set
{
if (value < _counter)
return;
float previousProgress = Progress;
_counter = value.Min(Duration).Max(0f);
Progress = Counter / Duration;
OnUpdated?.Invoke(this);
OnDeltaUpdated?.Invoke(this, Easing.Evaluate(Progress) - Easing.Evaluate(previousProgress));
if (_counter >= Duration)
State = TweenState.Completed;
}
}
internal void Reset()
{
_counter = 0f;
Progress = 0f;
State = TweenState.Idle;
}
public Tween() { }
public Tween(float duration)
{
Duration = duration;
}
}

View File

@ -0,0 +1,90 @@
namespace Syntriax.Engine.Systems.Tween;
public static class TweenExtensions
{
public static ITween Loop(this ITween tween, int count)
{
Tween tweenConcrete = (Tween)tween;
int counter = count;
tweenConcrete.OnCompleted += _ =>
{
if (counter-- <= 0)
return;
tweenConcrete.Reset();
tweenConcrete.State = TweenState.Playing;
};
return tween;
}
public static ITween LoopInfinitely(this ITween tween)
{
Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnCompleted += _ =>
{
tweenConcrete.Reset();
tweenConcrete.State = TweenState.Playing;
};
return tween;
}
public static ITween Ease(this ITween tween, IEasing easing)
{
Tween tweenConcrete = (Tween)tween;
tweenConcrete.Easing = easing;
return tween;
}
public static ITween OnStart(this ITween tween, Action callback)
{
Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnStarted += _ => callback.Invoke();
return tween;
}
public static ITween OnPause(this ITween tween, Action callback)
{
Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnPaused += _ => callback.Invoke();
return tween;
}
public static ITween OnResume(this ITween tween, Action callback)
{
Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnResumed += _ => callback.Invoke();
return tween;
}
public static ITween OnCancel(this ITween tween, Action callback)
{
Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnCancelled += _ => callback.Invoke();
return tween;
}
public static ITween OnComplete(this ITween tween, Action callback)
{
Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnCompleted += _ => callback.Invoke();
return tween;
}
public static ITween OnEnd(this ITween tween, Action callback)
{
Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnEnded += _ => callback.Invoke();
return tween;
}
public static ITween OnUpdate(this ITween tween, Action callback)
{
Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnUpdated += _ => callback.Invoke();
return tween;
}
public static ITween OnDeltaUpdate(this ITween tween, Action<float> callback)
{
Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnDeltaUpdated += (_, delta) => callback.Invoke(delta);
return tween;
}
}

View File

@ -0,0 +1,66 @@
using System.Collections;
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Abstract;
namespace Syntriax.Engine.Systems.Tween;
public class TweenManager : HierarchyObject
{
private CoroutineManager coroutineManager = null!;
private readonly Dictionary<ITween, IEnumerator> runningCoroutines = [];
public ITween StartTween(float duration, TweenSetCallback? setCallback = null)
{
Tween tween = new(duration);
tween.OnUpdated += tween => setCallback?.Invoke(tween.Value);
runningCoroutines.Add(tween, coroutineManager.StartCoroutine(RunTween(tween)));
return tween;
}
private IEnumerator RunTween(Tween tween)
{
tween.State = TweenState.Playing;
yield return null;
while (true)
{
if (tween.State.CheckFlag(TweenState.Cancelled | TweenState.Completed))
break;
if (tween.State.CheckFlag(TweenState.Paused))
{
yield return null;
continue;
}
tween.Counter += GameManager.Time.DeltaTime;
yield return null;
}
runningCoroutines.Remove(tween);
}
public void CancelTween(ITween tween)
{
if (!runningCoroutines.TryGetValue(tween, out IEnumerator? runningCoroutine))
return;
tween.State = TweenState.Cancelled;
coroutineManager.StopCoroutine(runningCoroutine);
runningCoroutines.Remove(tween);
}
protected override void OnEnteringHierarchy(IGameManager gameManager)
{
coroutineManager = gameManager.FindHierarchyObject<CoroutineManager>() ?? throw new($"No {nameof(CoroutineManager)} was found in the game manager");
}
protected override void OnExitingHierarchy(IGameManager gameManager)
{
coroutineManager = null!;
}
public delegate void TweenSetCallback(float t);
}

View File

@ -0,0 +1,10 @@
namespace Syntriax.Engine.Systems.Tween;
public enum TweenState
{
Idle = 0b00001,
Playing = 0b00010,
Paused = 0b00100,
Cancelled = 0b01000,
Completed = 0b10000,
}

View File

@ -0,0 +1,5 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public class WaitForTweenCompleteCoroutineYield(ITween tween) : CoroutineYield(() => tween.State == TweenState.Completed);

View File

@ -0,0 +1,5 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public class WaitWhileTweenActiveCoroutineYield(ITween tween) : CoroutineYield(() => tween.State.CheckFlag(TweenState.Completed | TweenState.Cancelled));

View File

@ -5,10 +5,12 @@ VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Core", "Engine.Core\Engine.Core.csproj", "{71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Input", "Engine.Input\Engine.Input.csproj", "{12149E55-1EE8-45B4-A82E-15BA981B0C6A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Physics2D", "Engine.Physics2D\Engine.Physics2D.csproj", "{3B3C3332-07E3-4A00-9898-0A5410BCB08C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Systems", "Engine.Systems\Engine.Systems.csproj", "{8452323D-99EF-43B1-8E7B-123E02279674}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine", "Engine\Engine.csproj", "{58AE79C1-9203-44AE-8022-AA180F0A71DC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -22,13 +24,17 @@ Global
{71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}.Release|Any CPU.Build.0 = Release|Any CPU
{12149E55-1EE8-45B4-A82E-15BA981B0C6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{12149E55-1EE8-45B4-A82E-15BA981B0C6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{12149E55-1EE8-45B4-A82E-15BA981B0C6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{12149E55-1EE8-45B4-A82E-15BA981B0C6A}.Release|Any CPU.Build.0 = Release|Any CPU
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Release|Any CPU.Build.0 = Release|Any CPU
{8452323D-99EF-43B1-8E7B-123E02279674}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8452323D-99EF-43B1-8E7B-123E02279674}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8452323D-99EF-43B1-8E7B-123E02279674}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8452323D-99EF-43B1-8E7B-123E02279674}.Release|Any CPU.Build.0 = Release|Any CPU
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

15
Engine/Engine.csproj Normal file
View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Engine.Core\Engine.Core.csproj" />
<ProjectReference Include="..\Engine.Systems\Engine.Systems.csproj" />
<ProjectReference Include="..\Engine.Physics2D\Engine.Physics2D.csproj" />
</ItemGroup>
</Project>