Skip to content

Commit

Permalink
Add feature to run during physics process
Browse files Browse the repository at this point in the history
- Added a new feature that allows the Update() method of a coroutine to be run in physics frames instead of regular process frames
- Refactored the code of the CoroutineManager (DRY)
  • Loading branch information
Inspiaaa committed Sep 4, 2024
1 parent 8433d34 commit 04404fa
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 36 deletions.
4 changes: 4 additions & 0 deletions src/Co.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
21 changes: 21 additions & 0 deletions src/CoProcessMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace HCoroutines;

/// <summary>
/// The ProcessMode determines when the Update() method of a coroutine is called.
/// </summary>
public enum CoProcessMode {
/// <summary>
/// The ProcessMode is inherited from the parent coroutine. If there is no parent, then the Normal mode is used.
/// </summary>
Inherit,

/// <summary>
/// The Update() method is called during each process frame (like the _Process() method).
/// </summary>
Normal,

/// <summary>
/// The Update() method is called during each physics frame (like the _PhysicsProcess() method).
/// </summary>
Physics
}
8 changes: 7 additions & 1 deletion src/CoroutineBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -34,7 +37,10 @@ public void StartCoroutine(CoroutineBase coroutine)
/// </summary>
public virtual void OnEnter()
{
ResumeUpdates();
if (this.ProcessMode == CoProcessMode.Inherit)
{
this.ProcessMode = Parent?.ProcessMode ?? CoProcessMode.Normal;
}
}

/// <summary>
Expand Down
77 changes: 42 additions & 35 deletions src/CoroutineManager.cs
Original file line number Diff line number Diff line change
@@ -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<CoroutineBase> activeCoroutines = new();
private HashSet<CoroutineBase> coroutinesToDeactivate = new();
private HashSet<CoroutineBase> coroutinesToActivate = new();

private DeferredHashSet<CoroutineBase> activeProcessCoroutines = new();
private DeferredHashSet<CoroutineBase> activePhysicsProcessCoroutines = new();

public void StartCoroutine(CoroutineBase coroutine)
{
coroutine.Manager = this;
Expand All @@ -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;
}
}

Expand All @@ -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<CoroutineBase> coroutines)
{
coroutines.Lock();

foreach (CoroutineBase coroutine in activeCoroutines)
foreach (CoroutineBase coroutine in coroutines.Items)
{
if (coroutine.IsAlive && coroutine.IsPlaying)
{
Expand All @@ -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();
}
}
67 changes: 67 additions & 0 deletions src/Util/DeferredHashSet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System.Collections.Generic;

namespace HCoroutines.Util;

/// <summary>
/// HashSet implementation that defers Add() and Remove() calls during iteration.
/// </summary>
public class DeferredHashSet<T>
{
public HashSet<T> Items = new();

private bool isIterating = false;
private HashSet<T> itemsToAdd = new();
private HashSet<T> 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();
}
}

0 comments on commit 04404fa

Please sign in to comment.