diff --git a/Engine.Core/Abstract/IActive.cs b/Engine.Core/Abstract/IActive.cs new file mode 100644 index 0000000..34c7dd3 --- /dev/null +++ b/Engine.Core/Abstract/IActive.cs @@ -0,0 +1,19 @@ +namespace Syntriax.Engine.Core.Abstract; + +/// +/// Represents an entity which can be active or not. +/// +public interface IActive +{ + /// + /// Event triggered when the state of the changes. + /// + event ActiveChangedEventHandler? OnActiveChanged; + + /// + /// The value indicating whether the is enabled. + /// + bool IsActive { get; } + + delegate void ActiveChangedEventHandler(IActive sender, bool previousState); +} diff --git a/Engine.Core/Abstract/IBehaviour.cs b/Engine.Core/Abstract/IBehaviour.cs index 9fc2d30..6858d0a 100644 --- a/Engine.Core/Abstract/IBehaviour.cs +++ b/Engine.Core/Abstract/IBehaviour.cs @@ -3,7 +3,7 @@ namespace Syntriax.Engine.Core.Abstract; /// /// Represents a behaviour that any object in the game might use to interact with itself or other objects. /// -public interface IBehaviour : IEntity, IHasBehaviourController, IHasStateEnable +public interface IBehaviour : IEntity, IActive, IHasBehaviourController, IHasStateEnable { /// /// Event triggered when the priority of the changes. @@ -15,10 +15,5 @@ public interface IBehaviour : IEntity, IHasBehaviourController, IHasStateEnable /// int Priority { get; set; } - /// - /// The value indicating whether the is active. - /// - bool IsActive { get; } - delegate void PriorityChangedEventHandler(IBehaviour sender, int previousPriority); } diff --git a/Engine.Core/Abstract/IHierarchyObject.cs b/Engine.Core/Abstract/IHierarchyObject.cs index aa5d7c3..e3909b5 100644 --- a/Engine.Core/Abstract/IHierarchyObject.cs +++ b/Engine.Core/Abstract/IHierarchyObject.cs @@ -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. /// -public interface IHierarchyObject : IEntity, INameable, IHasBehaviourController, IEnumerable +public interface IHierarchyObject : IEntity, IActive, INameable, IHasBehaviourController, IEnumerable { /// /// Event triggered when the enters the hierarchy. diff --git a/Engine.Core/Abstract/IStateEnable.cs b/Engine.Core/Abstract/IStateEnable.cs index 8debf8c..132b9bd 100644 --- a/Engine.Core/Abstract/IStateEnable.cs +++ b/Engine.Core/Abstract/IStateEnable.cs @@ -8,12 +8,12 @@ public interface IStateEnable : IHasEntity /// /// Event triggered when the state of the changes. /// - event NameChangedEventHandler? OnEnabledChanged; + event EnabledChangedEventHandler? OnEnabledChanged; /// /// The value indicating whether the is enabled. /// bool Enabled { get; set; } - delegate void NameChangedEventHandler(IStateEnable sender, bool previousState); + delegate void EnabledChangedEventHandler(IStateEnable sender, bool previousState); } diff --git a/Engine.Core/ActiveBehaviourCollector.cs b/Engine.Core/ActiveBehaviourCollector.cs index 70e48d8..f7472aa 100644 --- a/Engine.Core/ActiveBehaviourCollector.cs +++ b/Engine.Core/ActiveBehaviourCollector.cs @@ -16,7 +16,7 @@ public class ActiveBehaviourCollector : IBehaviourCollector where T : clas private readonly List monitoringBehaviours = new(32); protected readonly List activeBehaviours = new(32); - protected readonly Dictionary monitoringStateToBehaviour = new(32); + protected readonly Dictionary monitoringActiveToBehaviour = new(32); public IReadOnlyList Behaviours => activeBehaviours; public IGameManager GameManager { get; private set; } = null!; @@ -51,23 +51,22 @@ public class ActiveBehaviourCollector : IBehaviourCollector 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 : IBehaviourCollector 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) diff --git a/Engine.Core/Abstract/BaseEntity.cs b/Engine.Core/BaseEntity.cs similarity index 96% rename from Engine.Core/Abstract/BaseEntity.cs rename to Engine.Core/BaseEntity.cs index 02a1ec4..66db6b0 100644 --- a/Engine.Core/Abstract/BaseEntity.cs +++ b/Engine.Core/BaseEntity.cs @@ -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; } diff --git a/Engine.Core/BehaviourBase.cs b/Engine.Core/BehaviourBase.cs index 8a7bbca..a89f88f 100644 --- a/Engine.Core/BehaviourBase.cs +++ b/Engine.Core/BehaviourBase.cs @@ -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); + } } diff --git a/Engine.Core/BehaviourCollector.cs b/Engine.Core/BehaviourCollector.cs index 83f6de3..b586523 100644 --- a/Engine.Core/BehaviourCollector.cs +++ b/Engine.Core/BehaviourCollector.cs @@ -66,6 +66,7 @@ public class BehaviourCollector : IBehaviourCollector 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 : IBehaviourCollector where T : class gameManager.OnHierarchyObjectUnRegistered += OnHierarchyObjectUnregistered; GameManager = gameManager; + OnAssign(gameManager); OnGameManagerAssigned?.Invoke(this); return true; diff --git a/Engine.Core/BehaviourController.cs b/Engine.Core/BehaviourController.cs index 361ff08..341262d 100644 --- a/Engine.Core/BehaviourController.cs +++ b/Engine.Core/BehaviourController.cs @@ -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; } diff --git a/Engine.Core/Factory/BehaviourFactory.cs b/Engine.Core/Factory/BehaviourFactory.cs index 7191d1f..23bf214 100644 --- a/Engine.Core/Factory/BehaviourFactory.cs +++ b/Engine.Core/Factory/BehaviourFactory.cs @@ -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; } diff --git a/Engine.Core/HierarchyObject.cs b/Engine.Core/HierarchyObject.cs index 8247e76..b25bb26 100644 --- a/Engine.Core/HierarchyObject.cs +++ b/Engine.Core/HierarchyObject.cs @@ -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(); diff --git a/Engine.Core/StateEnable.cs b/Engine.Core/StateEnable.cs index f00391b..1bfa884 100644 --- a/Engine.Core/StateEnable.cs +++ b/Engine.Core/StateEnable.cs @@ -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; }