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> /// <summary>
/// Represents a behaviour that any object in the game might use to interact with itself or other objects. /// Represents a behaviour that any object in the game might use to interact with itself or other objects.
/// </summary> /// </summary>
public interface IBehaviour : IEntity, IHasBehaviourController, IHasStateEnable public interface IBehaviour : IEntity, IActive, IHasBehaviourController, IHasStateEnable
{ {
/// <summary> /// <summary>
/// Event triggered when the priority of the <see cref="IBehaviour"/> changes. /// Event triggered when the priority of the <see cref="IBehaviour"/> changes.
@ -15,10 +15,5 @@ public interface IBehaviour : IEntity, IHasBehaviourController, IHasStateEnable
/// </summary> /// </summary>
int Priority { get; set; } 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); 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 /// 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. /// for notifying when the see enters or exits the hierarchy.
/// </summary> /// </summary>
public interface IHierarchyObject : IEntity, INameable, IHasBehaviourController, IEnumerable<IHierarchyObject> public interface IHierarchyObject : IEntity, IActive, INameable, IHasBehaviourController, IEnumerable<IHierarchyObject>
{ {
/// <summary> /// <summary>
/// Event triggered when the <see cref="IHierarchyObject"/> enters the hierarchy. /// Event triggered when the <see cref="IHierarchyObject"/> enters the hierarchy.

View File

@ -8,12 +8,12 @@ public interface IStateEnable : IHasEntity
/// <summary> /// <summary>
/// Event triggered when the <see cref="Enabled"/> state of the <see cref="IStateEnable"/> changes. /// Event triggered when the <see cref="Enabled"/> state of the <see cref="IStateEnable"/> changes.
/// </summary> /// </summary>
event NameChangedEventHandler? OnEnabledChanged; event EnabledChangedEventHandler? OnEnabledChanged;
/// <summary> /// <summary>
/// The value indicating whether the <see cref="IStateEnable"/> is enabled. /// The value indicating whether the <see cref="IStateEnable"/> is enabled.
/// </summary> /// </summary>
bool Enabled { get; set; } 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); private readonly List<T> monitoringBehaviours = new(32);
protected readonly List<T> activeBehaviours = 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 IReadOnlyList<T> Behaviours => activeBehaviours;
public IGameManager GameManager { get; private set; } = null!; public IGameManager GameManager { get; private set; } = null!;
@ -51,23 +51,22 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
return; return;
monitoringBehaviours.Add(tBehaviour); monitoringBehaviours.Add(tBehaviour);
monitoringStateToBehaviour.Add(tBehaviour.StateEnable, tBehaviour); monitoringActiveToBehaviour.Add(tBehaviour, tBehaviour);
tBehaviour.StateEnable.OnEnabledChanged += OnBehaviourStateChanged; tBehaviour.OnActiveChanged += OnBehaviourStateChanged;
OnBehaviourStateChanged(tBehaviour.StateEnable, !tBehaviour.StateEnable.Enabled); OnBehaviourStateChanged(tBehaviour, !tBehaviour.IsActive);
} }
private void OnBehaviourStateChanged(IStateEnable sender, bool previousState) private void OnBehaviourStateChanged(IActive sender, bool previousState)
{ {
T behaviour = monitoringStateToBehaviour[sender]; T behaviour = monitoringActiveToBehaviour[sender];
if (sender.Enabled) if (sender.IsActive)
{ {
activeBehaviours.Add(behaviour); activeBehaviours.Add(behaviour);
OnBehaviourAdd(behaviour); OnBehaviourAdd(behaviour);
OnCollected?.Invoke(this, behaviour); OnCollected?.Invoke(this, behaviour);
} }
else if (activeBehaviours.Contains(behaviour)) else if (activeBehaviours.Remove(behaviour))
{ {
activeBehaviours.Remove(behaviour);
OnBehaviourRemove(behaviour); OnBehaviourRemove(behaviour);
OnRemoved?.Invoke(this, behaviour); OnRemoved?.Invoke(this, behaviour);
} }
@ -79,10 +78,15 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
if (behaviour is not T tBehaviour) if (behaviour is not T tBehaviour)
return; return;
if (!monitoringBehaviours.Remove(tBehaviour) || !monitoringStateToBehaviour.Remove(tBehaviour.StateEnable)) if (!monitoringBehaviours.Remove(tBehaviour) || !monitoringActiveToBehaviour.Remove(tBehaviour))
return; return;
tBehaviour.StateEnable.OnEnabledChanged -= OnBehaviourStateChanged; tBehaviour.OnActiveChanged -= OnBehaviourStateChanged;
if (activeBehaviours.Remove(tBehaviour))
{
OnBehaviourRemove(tBehaviour);
OnRemoved?.Invoke(this, tBehaviour);
}
} }
public bool Assign(IGameManager gameManager) public bool Assign(IGameManager gameManager)

View File

@ -19,8 +19,6 @@ public abstract class BaseEntity : IEntity
public virtual IStateEnable StateEnable => _stateEnable; public virtual IStateEnable StateEnable => _stateEnable;
public virtual bool IsActive => StateEnable.Enabled;
public string Id public string Id
{ {
get => _id; get => _id;
@ -55,6 +53,7 @@ public abstract class BaseEntity : IEntity
} }
} }
protected virtual void OnAssign(IStateEnable stateEnable) { }
public bool Assign(IStateEnable stateEnable) public bool Assign(IStateEnable stateEnable)
{ {
if (IsInitialized) if (IsInitialized)
@ -62,6 +61,7 @@ public abstract class BaseEntity : IEntity
_stateEnable = stateEnable; _stateEnable = stateEnable;
_stateEnable.Assign(this); _stateEnable.Assign(this);
OnAssign(stateEnable);
OnStateEnableAssigned?.Invoke(this); OnStateEnableAssigned?.Invoke(this);
return true; return true;
} }

View File

@ -7,17 +7,14 @@ namespace Syntriax.Engine.Core;
public abstract class BehaviourBase : BaseEntity, IBehaviour public abstract class BehaviourBase : BaseEntity, IBehaviour
{ {
public event IHasBehaviourController.BehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned = null; public event IHasBehaviourController.BehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned = null;
public event IBehaviour.PriorityChangedEventHandler? OnPriorityChanged = null; public event IBehaviour.PriorityChangedEventHandler? OnPriorityChanged = null;
public event IActive.ActiveChangedEventHandler? OnActiveChanged = null;
private IBehaviourController _behaviourController = null!; private IBehaviourController _behaviourController = null!;
private int _priority = 0;
public IBehaviourController BehaviourController => _behaviourController; public IBehaviourController BehaviourController => _behaviourController;
public override bool IsActive => base.IsActive && BehaviourController.HierarchyObject.StateEnable.Enabled; private int _priority = 0;
public int Priority public int Priority
{ {
get => _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) public bool Assign(IBehaviourController behaviourController)
{ {
if (IsInitialized) if (IsInitialized)
return false; return false;
_behaviourController = behaviourController; _behaviourController = behaviourController;
OnAssign(behaviourController);
behaviourController.OnHierarchyObjectAssigned += OnHierarchyObjectAssigned;
if (behaviourController.HierarchyObject is not null)
OnHierarchyObjectAssigned(behaviourController);
OnBehaviourControllerAssigned?.Invoke(this); OnBehaviourControllerAssigned?.Invoke(this);
return true; 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() protected override void UnassignInternal()
{ {
StateEnable.OnEnabledChanged -= OnStateEnabledChanged;
BehaviourController.OnHierarchyObjectAssigned -= OnHierarchyObjectAssigned;
base.UnassignInternal(); base.UnassignInternal();
_behaviourController = null!; _behaviourController = null!;
} }
@ -54,4 +73,16 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
NotAssignedException.Check(this, _behaviourController); NotAssignedException.Check(this, _behaviourController);
NotAssignedException.Check(this, StateEnable); 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); OnRemoved?.Invoke(this, tBehaviour);
} }
protected virtual void OnAssign(IGameManager gameManager) { }
public bool Assign(IGameManager gameManager) public bool Assign(IGameManager gameManager)
{ {
if (GameManager is not null) if (GameManager is not null)
@ -78,6 +79,7 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
gameManager.OnHierarchyObjectUnRegistered += OnHierarchyObjectUnregistered; gameManager.OnHierarchyObjectUnRegistered += OnHierarchyObjectUnregistered;
GameManager = gameManager; GameManager = gameManager;
OnAssign(gameManager);
OnGameManagerAssigned?.Invoke(this); OnGameManagerAssigned?.Invoke(this);
return true; return true;

View File

@ -124,12 +124,14 @@ public class BehaviourController : IBehaviourController
OnBehaviourRemoved?.Invoke(this, behaviour); OnBehaviourRemoved?.Invoke(this, behaviour);
} }
protected virtual void OnAssign(IHierarchyObject hierarchyObject) { }
public bool Assign(IHierarchyObject hierarchyObject) public bool Assign(IHierarchyObject hierarchyObject)
{ {
if (HierarchyObject is not null && HierarchyObject.IsInitialized) if (HierarchyObject is not null && HierarchyObject.IsInitialized)
return false; return false;
_hierarchyObject = hierarchyObject; _hierarchyObject = hierarchyObject;
OnAssign(hierarchyObject);
OnHierarchyObjectAssigned?.Invoke(this); OnHierarchyObjectAssigned?.Invoke(this);
return true; return true;
} }

View File

@ -17,10 +17,10 @@ public class BehaviourFactory
if (!stateEnable.Assign(behaviour)) if (!stateEnable.Assign(behaviour))
throw AssignException.From(stateEnable, behaviour); throw AssignException.From(stateEnable, behaviour);
if (!behaviour.Assign(hierarchyObject.BehaviourController))
throw AssignException.From(behaviour, hierarchyObject.BehaviourController);
if (!behaviour.Assign(stateEnable)) if (!behaviour.Assign(stateEnable))
throw AssignException.From(behaviour, stateEnable); throw AssignException.From(behaviour, stateEnable);
if (!behaviour.Assign(hierarchyObject.BehaviourController))
throw AssignException.From(behaviour, hierarchyObject.BehaviourController);
return behaviour; return behaviour;
} }

View File

@ -15,6 +15,7 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
public event IHierarchyObject.ChildrenRemovedEventHandler? OnChildrenRemoved = null; public event IHierarchyObject.ChildrenRemovedEventHandler? OnChildrenRemoved = null;
public event IHasBehaviourController.BehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned = null; public event IHasBehaviourController.BehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned = null;
public event INameable.NameChangedEventHandler? OnNameChanged = null; public event INameable.NameChangedEventHandler? OnNameChanged = null;
public event IActive.ActiveChangedEventHandler? OnActiveChanged = null;
private string _name = nameof(HierarchyObject); private string _name = nameof(HierarchyObject);
private IGameManager _gameManager = null!; private IGameManager _gameManager = null!;
@ -41,6 +42,8 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
public IBehaviourController BehaviourController => _behaviourController; public IBehaviourController BehaviourController => _behaviourController;
public bool IsActive { get; private set; } = false;
protected virtual void OnEnteringHierarchy(IGameManager gameManager) { } protected virtual void OnEnteringHierarchy(IGameManager gameManager) { }
bool IHierarchyObject.EnterHierarchy(IGameManager gameManager) bool IHierarchyObject.EnterHierarchy(IGameManager gameManager)
{ {
@ -48,6 +51,7 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
return false; return false;
_gameManager = gameManager; _gameManager = gameManager;
UpdateActive();
OnEnteringHierarchy(gameManager); OnEnteringHierarchy(gameManager);
OnEnteredHierarchy?.Invoke(this, gameManager); OnEnteredHierarchy?.Invoke(this, gameManager);
return true; return true;
@ -75,6 +79,7 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
{ {
previousParent.RemoveChild(this); previousParent.RemoveChild(this);
previousParent.OnParentChanged -= NotifyChildrenOnParentChange; previousParent.OnParentChanged -= NotifyChildrenOnParentChange;
previousParent.OnActiveChanged -= OnParentActiveChanged;
} }
Parent = parent; Parent = parent;
@ -83,8 +88,10 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
{ {
parent.AddChild(this); parent.AddChild(this);
parent.OnParentChanged += NotifyChildrenOnParentChange; parent.OnParentChanged += NotifyChildrenOnParentChange;
parent.OnActiveChanged += OnParentActiveChanged;
} }
UpdateActive();
OnParentChanged?.Invoke(this, previousParent, parent); OnParentChanged?.Invoke(this, previousParent, parent);
} }
@ -115,16 +122,43 @@ public class HierarchyObject : BaseEntity, IHierarchyObject
child.SetParent(this); child.SetParent(this);
} }
protected virtual void OnAssign(IBehaviourController behaviourController) { }
public bool Assign(IBehaviourController behaviourController) public bool Assign(IBehaviourController behaviourController)
{ {
if (IsInitialized) if (IsInitialized)
return false; return false;
_behaviourController = behaviourController; _behaviourController = behaviourController;
OnAssign(behaviourController);
OnBehaviourControllerAssigned?.Invoke(this); OnBehaviourControllerAssigned?.Invoke(this);
return true; 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() protected override void InitializeInternal()
{ {
base.InitializeInternal(); base.InitializeInternal();

View File

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