feat: IActive interface added for hierarchy active state

This commit is contained in:
Syntriax 2025-04-01 13:22:14 +03:00
parent d4c6288b38
commit 417ddca972
12 changed files with 119 additions and 30 deletions

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,7 +3,7 @@ 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.
@ -15,10 +15,5 @@ public interface IBehaviour : IEntity, IHasBehaviourController, IHasStateEnable
/// </summary>
int Priority { get; set; }
/// <summary>
/// The value indicating whether the <see cref="IBehaviour"/> is active.
/// </summary>
bool IsActive { get; }
delegate void PriorityChangedEventHandler(IBehaviour sender, int previousPriority);
}

View File

@ -7,7 +7,7 @@ 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.

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 NameChangedEventHandler? OnEnabledChanged;
event EnabledChangedEventHandler? OnEnabledChanged;
/// <summary>
/// The value indicating whether the <see cref="IStateEnable"/> is enabled.
/// </summary>
bool Enabled { get; set; }
delegate void NameChangedEventHandler(IStateEnable sender, bool previousState);
delegate void EnabledChangedEventHandler(IStateEnable sender, bool previousState);
}

View File

@ -16,7 +16,7 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
private readonly List<T> monitoringBehaviours = new(32);
protected readonly List<T> activeBehaviours = new(32);
protected readonly Dictionary<IStateEnable, T> monitoringStateToBehaviour = new(32);
protected readonly Dictionary<IActive, T> monitoringActiveToBehaviour = new(32);
public IReadOnlyList<T> Behaviours => activeBehaviours;
public IGameManager GameManager { get; private set; } = null!;
@ -51,23 +51,22 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
return;
monitoringBehaviours.Add(tBehaviour);
monitoringStateToBehaviour.Add(tBehaviour.StateEnable, tBehaviour);
tBehaviour.StateEnable.OnEnabledChanged += OnBehaviourStateChanged;
OnBehaviourStateChanged(tBehaviour.StateEnable, !tBehaviour.StateEnable.Enabled);
monitoringActiveToBehaviour.Add(tBehaviour, tBehaviour);
tBehaviour.OnActiveChanged += OnBehaviourStateChanged;
OnBehaviourStateChanged(tBehaviour, !tBehaviour.IsActive);
}
private void OnBehaviourStateChanged(IStateEnable sender, bool previousState)
private void OnBehaviourStateChanged(IActive sender, bool previousState)
{
T behaviour = monitoringStateToBehaviour[sender];
if (sender.Enabled)
T behaviour = monitoringActiveToBehaviour[sender];
if (sender.IsActive)
{
activeBehaviours.Add(behaviour);
OnBehaviourAdd(behaviour);
OnCollected?.Invoke(this, behaviour);
}
else if (activeBehaviours.Contains(behaviour))
else if (activeBehaviours.Remove(behaviour))
{
activeBehaviours.Remove(behaviour);
OnBehaviourRemove(behaviour);
OnRemoved?.Invoke(this, behaviour);
}
@ -79,10 +78,15 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
if (behaviour is not T tBehaviour)
return;
if (!monitoringBehaviours.Remove(tBehaviour) || !monitoringStateToBehaviour.Remove(tBehaviour.StateEnable))
if (!monitoringBehaviours.Remove(tBehaviour) || !monitoringActiveToBehaviour.Remove(tBehaviour))
return;
tBehaviour.StateEnable.OnEnabledChanged -= OnBehaviourStateChanged;
tBehaviour.OnActiveChanged -= OnBehaviourStateChanged;
if (activeBehaviours.Remove(tBehaviour))
{
OnBehaviourRemove(tBehaviour);
OnRemoved?.Invoke(this, tBehaviour);
}
}
public bool Assign(IGameManager gameManager)

View File

@ -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;
}

View File

@ -7,17 +7,14 @@ namespace Syntriax.Engine.Core;
public abstract class BehaviourBase : BaseEntity, IBehaviour
{
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 +29,40 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
}
}
public bool IsActive { get; private set; } = false;
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

@ -66,6 +66,7 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
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;

View File

@ -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

@ -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

@ -15,6 +15,7 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
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!;
@ -41,6 +42,8 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
public IBehaviourController BehaviourController => _behaviourController;
public bool IsActive { get; private set; } = false;
protected virtual void OnEnteringHierarchy(IGameManager gameManager) { }
bool IHierarchyObject.EnterHierarchy(IGameManager gameManager)
{
@ -48,6 +51,7 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
return false;
_gameManager = gameManager;
UpdateActive();
OnEnteringHierarchy(gameManager);
OnEnteredHierarchy?.Invoke(this, gameManager);
return true;
@ -75,6 +79,7 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
{
previousParent.RemoveChild(this);
previousParent.OnParentChanged -= NotifyChildrenOnParentChange;
previousParent.OnActiveChanged -= OnParentActiveChanged;
}
Parent = parent;
@ -83,8 +88,10 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
{
parent.AddChild(this);
parent.OnParentChanged += NotifyChildrenOnParentChange;
parent.OnActiveChanged += OnParentActiveChanged;
}
UpdateActive();
OnParentChanged?.Invoke(this, previousParent, parent);
}
@ -115,16 +122,43 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
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 senfder, 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();

View File

@ -6,7 +6,7 @@ public class StateEnable : IStateEnable
{
public event IAssignable.UnassignEventHandler? OnUnassigned = null;
public event IHasEntity.EntityAssignedEventHandler? OnEntityAssigned = null;
public event IStateEnable.NameChangedEventHandler? OnEnabledChanged = 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;
}