diff --git a/src/Co.cs b/src/Co.cs index 9909111..d888ce5 100644 --- a/src/Co.cs +++ b/src/Co.cs @@ -25,8 +25,12 @@ private static Coroutine[] GetCoroutines(IEnumerator[] enumerators) return coroutines; } + public static float DeltaTime => CoroutineManager.Instance.DeltaTime; public static double DeltaTimeDouble => CoroutineManager.Instance.DeltaTimeDouble; + + public static float PhysicsDeltaTime => CoroutineManager.Instance.PhysicsDeltaTime; + public static double PhysicsDeltaTimeDouble => CoroutineManager.Instance.PhysicsDeltaTimeDouble; public static void Run(CoroutineBase coroutine) diff --git a/src/CoProcessMode.cs b/src/CoProcessMode.cs new file mode 100644 index 0000000..fb01b90 --- /dev/null +++ b/src/CoProcessMode.cs @@ -0,0 +1,21 @@ +namespace HCoroutines; + +/// +/// The ProcessMode determines when the Update() method of a coroutine is called. +/// +public enum CoProcessMode { + /// + /// The ProcessMode is inherited from the parent coroutine. If there is no parent, then the Normal mode is used. + /// + Inherit, + + /// + /// The Update() method is called during each process frame (like the _Process() method). + /// + Normal, + + /// + /// The Update() method is called during each physics frame (like the _PhysicsProcess() method). + /// + Physics +} \ No newline at end of file diff --git a/src/CoroutineBase.cs b/src/CoroutineBase.cs index e99f724..07c136b 100644 --- a/src/CoroutineBase.cs +++ b/src/CoroutineBase.cs @@ -20,6 +20,9 @@ public class CoroutineBase public bool IsAlive = true; public bool IsPlaying = false; + // TODO: Add way to set this property. + public CoProcessMode ProcessMode { get; private set; } + public void StartCoroutine(CoroutineBase coroutine) { coroutine.Manager = Manager; @@ -34,7 +37,10 @@ public void StartCoroutine(CoroutineBase coroutine) /// public virtual void OnEnter() { - ResumeUpdates(); + if (this.ProcessMode == CoProcessMode.Inherit) + { + this.ProcessMode = Parent?.ProcessMode ?? CoProcessMode.Normal; + } } /// diff --git a/src/CoroutineManager.cs b/src/CoroutineManager.cs index f656a19..e915f0c 100644 --- a/src/CoroutineManager.cs +++ b/src/CoroutineManager.cs @@ -1,20 +1,22 @@ using Godot; using System; -using System.Collections.Generic; +using HCoroutines.Util; namespace HCoroutines; public partial class CoroutineManager : Node { public static CoroutineManager Instance { get; private set; } + public float DeltaTime { get; private set; } public double DeltaTimeDouble { get; private set; } + + public float PhysicsDeltaTime { get; private set; } + public double PhysicsDeltaTimeDouble { get; private set; } - private bool isIteratingActiveCoroutines = false; - private HashSet activeCoroutines = new(); - private HashSet coroutinesToDeactivate = new(); - private HashSet coroutinesToActivate = new(); - + private DeferredHashSet activeProcessCoroutines = new(); + private DeferredHashSet activePhysicsProcessCoroutines = new(); + public void StartCoroutine(CoroutineBase coroutine) { coroutine.Manager = this; @@ -23,27 +25,31 @@ public void StartCoroutine(CoroutineBase coroutine) public void ActivateCoroutine(CoroutineBase coroutine) { - if (isIteratingActiveCoroutines) - { - coroutinesToActivate.Add(coroutine); - coroutinesToDeactivate.Remove(coroutine); - } - else + switch (coroutine.ProcessMode) { - activeCoroutines.Add(coroutine); + case CoProcessMode.Normal: + case CoProcessMode.Inherit: + activeProcessCoroutines.Add(coroutine); + break; + + case CoProcessMode.Physics: + activePhysicsProcessCoroutines.Add(coroutine); + break; } } public void DeactivateCoroutine(CoroutineBase coroutine) { - if (isIteratingActiveCoroutines) - { - coroutinesToDeactivate.Add(coroutine); - coroutinesToActivate.Remove(coroutine); - } - else + switch (coroutine.ProcessMode) { - activeCoroutines.Remove(coroutine); + case CoProcessMode.Normal: + case CoProcessMode.Inherit: + activeProcessCoroutines.Remove(coroutine); + break; + + case CoProcessMode.Physics: + activePhysicsProcessCoroutines.Remove(coroutine); + break; } } @@ -57,9 +63,22 @@ public override void _Process(double delta) DeltaTime = (float)delta; DeltaTimeDouble = delta; - isIteratingActiveCoroutines = true; + UpdateCoroutines(activeProcessCoroutines); + } + + public override void _PhysicsProcess(double delta) + { + PhysicsDeltaTime = (float)delta; + PhysicsDeltaTimeDouble = delta; + + UpdateCoroutines(activePhysicsProcessCoroutines); + } + + private void UpdateCoroutines(DeferredHashSet coroutines) + { + coroutines.Lock(); - foreach (CoroutineBase coroutine in activeCoroutines) + foreach (CoroutineBase coroutine in coroutines.Items) { if (coroutine.IsAlive && coroutine.IsPlaying) { @@ -74,18 +93,6 @@ public override void _Process(double delta) } } - isIteratingActiveCoroutines = false; - - foreach (CoroutineBase coroutine in coroutinesToActivate) - { - activeCoroutines.Add(coroutine); - } - coroutinesToActivate.Clear(); - - foreach (CoroutineBase coroutine in coroutinesToDeactivate) - { - activeCoroutines.Remove(coroutine); - } - coroutinesToDeactivate.Clear(); + coroutines.Unlock(); } } \ No newline at end of file diff --git a/src/Util/DeferredHashSet.cs b/src/Util/DeferredHashSet.cs new file mode 100644 index 0000000..20fd20d --- /dev/null +++ b/src/Util/DeferredHashSet.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; + +namespace HCoroutines.Util; + +/// +/// HashSet implementation that defers Add() and Remove() calls during iteration. +/// +public class DeferredHashSet +{ + public HashSet Items = new(); + + private bool isIterating = false; + private HashSet itemsToAdd = new(); + private HashSet itemsToRemove = new(); + + public void Add(T item) + { + if (isIterating) + { + itemsToAdd.Add(item); + itemsToRemove.Remove(item); + } + else + { + Items.Add(item); + } + } + + public void Remove(T item) + { + if (isIterating) + { + itemsToRemove.Add(item); + itemsToAdd.Remove(item); + } + else + { + Items.Remove(item); + } + } + + public void Lock() + { + isIterating = true; + } + + public void Unlock() + { + isIterating = false; + ExecutePendingAddRemoves(); + } + + private void ExecutePendingAddRemoves() + { + foreach (T item in itemsToAdd) + { + Items.Add(item); + } + itemsToAdd.Clear(); + + foreach (T item in itemsToRemove) + { + Items.Remove(item); + } + itemsToRemove.Clear(); + } +} \ No newline at end of file