diff --git a/addons/GDTask/Autoload/GDTaskPlayerLoopAutoload.cs b/addons/GDTask/Autoload/GDTaskPlayerLoopAutoload.cs
index 9bd83af..a699c4a 100644
--- a/addons/GDTask/Autoload/GDTaskPlayerLoopAutoload.cs
+++ b/addons/GDTask/Autoload/GDTaskPlayerLoopAutoload.cs
@@ -1,8 +1,6 @@
-using System;
-using System.Linq;
-using Fractural.Tasks.Internal;
-using System.Threading;
+using Fractural.Tasks.Internal;
using Godot;
+using System;
namespace Fractural.Tasks
{
@@ -16,31 +14,8 @@ public enum PlayerLoopTiming
{
Process = 0,
PhysicsProcess = 1,
- }
-
- [Flags]
- public enum InjectPlayerLoopTimings
- {
- ///
- /// Preset: All loops(default).
- ///
- All = Process | PhysicsProcess,
-
- ///
- /// Preset: All without last except LastPostLateUpdate.
- ///
- Standard = Process | PhysicsProcess,
-
- ///
- /// Preset: Minimum pattern, Update | PhysicsProcess | LastPostLateUpdate
- ///
- Minimum =
- Process | PhysicsProcess,
-
- // PlayerLoopTiming
-
- PhysicsProcess = 1,
- Process = 2,
+ PauseProcess = 2,
+ PausePhysicsProcess = 3,
}
public interface IPlayerLoopItem
@@ -85,7 +60,7 @@ public static GDTaskPlayerLoopAutoload Global
get
{
if (s_Global != null) return s_Global;
-
+
var newInstance = new GDTaskPlayerLoopAutoload();
newInstance.Initialize();
var currentScene = ((SceneTree)Engine.GetMainLoop()).CurrentScene;
@@ -104,6 +79,7 @@ public static GDTaskPlayerLoopAutoload Global
private int mainThreadId;
private ContinuationQueue[] yielders;
private PlayerLoopRunner[] runners;
+ private ProcessListener processListener;
public override void _Ready()
{
@@ -118,15 +94,25 @@ public override void _Ready()
private void Initialize()
{
+ ProcessMode = ProcessModeEnum.Pausable;
mainThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
yielders = new[] {
new ContinuationQueue(PlayerLoopTiming.Process),
new ContinuationQueue(PlayerLoopTiming.PhysicsProcess),
+ new ContinuationQueue(PlayerLoopTiming.PauseProcess),
+ new ContinuationQueue(PlayerLoopTiming.PausePhysicsProcess),
};
runners = new[] {
new PlayerLoopRunner(PlayerLoopTiming.Process),
new PlayerLoopRunner(PlayerLoopTiming.PhysicsProcess),
+ new PlayerLoopRunner(PlayerLoopTiming.PauseProcess),
+ new PlayerLoopRunner(PlayerLoopTiming.PausePhysicsProcess),
};
+ processListener = new ProcessListener();
+ AddChild(processListener);
+ processListener.ProcessMode = ProcessModeEnum.Always;
+ processListener.OnProcess += PauseProcess;
+ processListener.OnPhysicsProcess += PausePhysicsProcess;
}
public override void _Notification(int what)
@@ -147,14 +133,26 @@ public override void _Notification(int what)
public override void _Process(double delta)
{
- yielders[(int) PlayerLoopTiming.Process].Run();
- runners[(int) PlayerLoopTiming.Process].Run();
+ yielders[(int)PlayerLoopTiming.Process].Run();
+ runners[(int)PlayerLoopTiming.Process].Run();
}
public override void _PhysicsProcess(double delta)
{
- yielders[(int) PlayerLoopTiming.PhysicsProcess].Run();
- runners[(int) PlayerLoopTiming.PhysicsProcess].Run();
+ yielders[(int)PlayerLoopTiming.PhysicsProcess].Run();
+ runners[(int)PlayerLoopTiming.PhysicsProcess].Run();
+ }
+
+ private void PauseProcess(double delta)
+ {
+ yielders[(int)PlayerLoopTiming.PauseProcess].Run();
+ runners[(int)PlayerLoopTiming.PauseProcess].Run();
+ }
+
+ private void PausePhysicsProcess(double delta)
+ {
+ yielders[(int)PlayerLoopTiming.PausePhysicsProcess].Run();
+ runners[(int)PlayerLoopTiming.PausePhysicsProcess].Run();
}
}
}
diff --git a/addons/GDTask/Autoload/ProcessListener.cs b/addons/GDTask/Autoload/ProcessListener.cs
new file mode 100644
index 0000000..142533b
--- /dev/null
+++ b/addons/GDTask/Autoload/ProcessListener.cs
@@ -0,0 +1,21 @@
+using Godot;
+using System;
+
+namespace Fractural.Tasks
+{
+ public partial class ProcessListener : Node
+ {
+ public event Action OnProcess;
+ public event Action OnPhysicsProcess;
+
+ public override void _Process(double delta)
+ {
+ OnProcess?.Invoke(delta);
+ }
+
+ public override void _PhysicsProcess(double delta)
+ {
+ OnPhysicsProcess?.Invoke(delta);
+ }
+ }
+}
diff --git a/addons/GDTask/GDTask.Delay.cs b/addons/GDTask/GDTask.Delay.cs
index 35e4fd5..8bf5599 100644
--- a/addons/GDTask/GDTask.Delay.cs
+++ b/addons/GDTask/GDTask.Delay.cs
@@ -1,9 +1,8 @@
using Fractural.Tasks.Internal;
+using Godot;
using System;
-using System.Collections;
using System.Runtime.CompilerServices;
using System.Threading;
-using Godot;
namespace Fractural.Tasks
{
@@ -82,7 +81,7 @@ public static GDTask WaitForEndOfFrame(CancellationToken cancellationToken)
}
///
- /// Same as GDTask.Yield(PlayerLoopTiming.LastPhysicsProcess).
+ /// Same as GDTask.Yield(PlayerLoopTiming.PhysicsProcess).
///
public static YieldAwaitable WaitForPhysicsProcess()
{
@@ -90,7 +89,7 @@ public static YieldAwaitable WaitForPhysicsProcess()
}
///
- /// Same as GDTask.Yield(PlayerLoopTiming.LastPhysicsProcess, cancellationToken).
+ /// Same as GDTask.Yield(PlayerLoopTiming.PhysicsProcess, cancellationToken).
///
public static GDTask WaitForPhysicsProcess(CancellationToken cancellationToken)
{
@@ -138,7 +137,6 @@ public static GDTask WaitForPhysicsProcess(CancellationToken cancellationToken)
delayType = DelayType.Realtime;
}
#endif
-
switch (delayType)
{
case DelayType.Realtime:
@@ -566,11 +564,11 @@ public bool MoveNext()
}
}
- if (timing == PlayerLoopTiming.Process)
+ if (timing == PlayerLoopTiming.Process || timing == PlayerLoopTiming.PauseProcess)
elapsed += GDTaskPlayerLoopAutoload.Global.DeltaTime;
else
elapsed += GDTaskPlayerLoopAutoload.Global.PhysicsDeltaTime;
-
+
if (elapsed >= delayTimeSpan)
{
core.TrySetResult(null);
diff --git a/addons/GDTask/Internal/ContinuationQueue.cs b/addons/GDTask/Internal/ContinuationQueue.cs
index 65bed06..b35736b 100644
--- a/addons/GDTask/Internal/ContinuationQueue.cs
+++ b/addons/GDTask/Internal/ContinuationQueue.cs
@@ -95,7 +95,11 @@ public void Run()
case PlayerLoopTiming.Process:
Process();
break;
- default:
+ case PlayerLoopTiming.PausePhysicsProcess:
+ PausePhysicsProcess();
+ break;
+ case PlayerLoopTiming.PauseProcess:
+ PauseProcess();
break;
}
#else
@@ -105,6 +109,8 @@ public void Run()
void PhysicsProcess() => RunCore();
void Process() => RunCore();
+ void PausePhysicsProcess() => RunCore();
+ void PauseProcess() => RunCore();
[System.Diagnostics.DebuggerHidden]
void RunCore()
diff --git a/addons/GDTask/Internal/PlayerLoopRunner.cs b/addons/GDTask/Internal/PlayerLoopRunner.cs
index 7baacb9..0c344ec 100644
--- a/addons/GDTask/Internal/PlayerLoopRunner.cs
+++ b/addons/GDTask/Internal/PlayerLoopRunner.cs
@@ -82,6 +82,12 @@ public void Run()
case PlayerLoopTiming.Process:
Process();
break;
+ case PlayerLoopTiming.PausePhysicsProcess:
+ PausePhysicsProcess();
+ break;
+ case PlayerLoopTiming.PauseProcess:
+ PauseProcess();
+ break;
}
#else
RunCore();
@@ -90,6 +96,8 @@ public void Run()
void PhysicsProcess() => RunCore();
void Process() => RunCore();
+ void PausePhysicsProcess() => RunCore();
+ void PauseProcess() => RunCore();
[System.Diagnostics.DebuggerHidden]
diff --git a/tests/manual/Test.cs b/tests/manual/Test.cs
index d2171c0..b2528c7 100644
--- a/tests/manual/Test.cs
+++ b/tests/manual/Test.cs
@@ -5,12 +5,14 @@
namespace Tests.Manual
{
- public partial class Test : Node2D
+ public partial class Test : Node
{
[Export]
private bool runTestOnReady;
[Export]
private NodePath spritePath;
+ [Export]
+ private Label pauseLabel;
public Sprite2D sprite;
public override void _Ready()
@@ -18,43 +20,54 @@ public override void _Ready()
sprite = GetNode(spritePath);
if (runTestOnReady)
Run().Forget();
+ ProcessMode = ProcessModeEnum.Always;
+ pauseLabel.Text = GetTree().Paused ? "Paused" : "Unpaused";
}
public override void _Input(InputEvent @event)
{
- if (@event.IsActionReleased("ui_select"))
+ if (@event.IsActionReleased("ui_left"))
{
Run().Forget();
}
+ else if (@event.IsActionReleased("ui_right"))
+ {
+ RunPause().Forget();
+ }
+ else if (@event.IsActionReleased("ui_up"))
+ {
+ GetTree().Paused = !GetTree().Paused;
+ pauseLabel.Text = GetTree().Paused ? "Paused" : "Unpaused";
+ }
}
private async GDTaskVoid Run()
{
- GD.Print("Pre delay");
+ GD.Print("Run: Pre delay");
sprite.Visible = false;
await GDTask.Delay(TimeSpan.FromSeconds(3));
sprite.Visible = true;
- GD.Print("Post delay after 3 seconds");
+ GD.Print("Run: Post delay after 3 seconds");
- GD.Print("Pre RunWithResult");
+ GD.Print("Run: Pre RunWithResult");
string result = await RunWithResult();
- GD.Print($"Post got result: {result}");
+ GD.Print($"Run: Post got result: {result}");
- GD.Print("LongTask started");
+ GD.Print("Run: LongTask started");
var cts = new CancellationTokenSource();
CancellableReallyLongTask(cts.Token).Forget();
await GDTask.Delay(TimeSpan.FromSeconds(3));
cts.Cancel();
- GD.Print("LongTask cancelled");
+ GD.Print("Run: LongTask cancelled");
await GDTask.WaitForEndOfFrame();
- GD.Print("WaitForEndOfFrame");
+ GD.Print("Run: WaitForEndOfFrame");
await GDTask.WaitForPhysicsProcess();
- GD.Print("WaitForPhysicsProcess");
+ GD.Print("Run: WaitForPhysicsProcess");
await GDTask.NextFrame();
- GD.Print("NextFrame");
+ GD.Print("Run: NextFrame");
}
private async GDTask RunWithResult()
@@ -66,13 +79,60 @@ private async GDTask RunWithResult()
private async GDTaskVoid CancellableReallyLongTask(CancellationToken cancellationToken)
{
int seconds = 10;
- GD.Print($"Starting long task ({seconds} seconds long).");
+ GD.Print($"Run: Starting long task ({seconds} seconds long).");
for (int i = 0; i < seconds; i++)
{
- GD.Print($"Working on long task for {i} seconds...");
+ GD.Print($"Run: Working on long task for {i} seconds...");
await GDTask.Delay(TimeSpan.FromSeconds(1), cancellationToken: cancellationToken);
}
- GD.Print("Finished long task.");
+ GD.Print("Run: Finished long task.");
+ }
+
+ private async GDTaskVoid RunPause()
+ {
+ GD.Print("RunPause: Pre delay");
+ sprite.Visible = false;
+ await GDTask.Delay(TimeSpan.FromSeconds(3), PlayerLoopTiming.PauseProcess);
+ sprite.Visible = true;
+ GD.Print("RunPause: Post delay after 3 seconds");
+
+ GD.Print("RunPause: Pre RunWithResult");
+ string result = await RunWithResultPause();
+ GD.Print($"RunPause: Post got result: {result}");
+
+ GD.Print("RunPause: LongTask started");
+ var cts = new CancellationTokenSource();
+
+ CancellableReallyLongTaskPause(cts.Token).Forget();
+
+ await GDTask.Delay(TimeSpan.FromSeconds(3), PlayerLoopTiming.PauseProcess);
+ cts.Cancel();
+ GD.Print("RunPause: LongTask cancelled");
+
+ await GDTask.Yield(PlayerLoopTiming.PauseProcess);
+ GD.Print("RunPause: Yield(PlayerLoopTiming.PauseProcess)");
+ await GDTask.Yield(PlayerLoopTiming.PausePhysicsProcess);
+ GD.Print("RunPause: Yield(PlayerLoopTiming.PausePhysicsProcess)");
+ await GDTask.NextFrame(PlayerLoopTiming.PauseProcess);
+ GD.Print("RunPause: NextFrame");
+ }
+
+ private async GDTask RunWithResultPause()
+ {
+ await GDTask.Delay(TimeSpan.FromSeconds(2), PlayerLoopTiming.PauseProcess);
+ return "Hello";
+ }
+
+ private async GDTaskVoid CancellableReallyLongTaskPause(CancellationToken cancellationToken)
+ {
+ int seconds = 10;
+ GD.Print($"RunPause: Starting long task ({seconds} seconds long).");
+ for (int i = 0; i < seconds; i++)
+ {
+ GD.Print($"RunPause: Working on long task for {i} seconds...");
+ await GDTask.Delay(TimeSpan.FromSeconds(1), PlayerLoopTiming.PauseProcess, cancellationToken);
+ }
+ GD.Print("RunPause: Finished long task.");
}
}
}
diff --git a/tests/manual/Test.tscn b/tests/manual/Test.tscn
index ea67b9e..d7a3a0b 100644
--- a/tests/manual/Test.tscn
+++ b/tests/manual/Test.tscn
@@ -3,11 +3,32 @@
[ext_resource type="Script" path="res://tests/manual/Test.cs" id="1"]
[ext_resource type="Texture2D" uid="uid://cw01ge0bgos8m" path="res://icon.png" id="2"]
-[node name="Test" type="Node2D"]
+[node name="Test" type="Node" node_paths=PackedStringArray("pauseLabel")]
script = ExtResource("1")
-spritePath = NodePath("Sprite2D")
+spritePath = NodePath("Node2D/Sprite2D")
+pauseLabel = NodePath("UI/Control/PauseLabel")
-[node name="Sprite2D" type="Sprite2D" parent="."]
+[node name="Node2D" type="Node2D" parent="."]
+
+[node name="Sprite2D" type="Sprite2D" parent="Node2D"]
texture = ExtResource("2")
-[node name="Camera2D" type="Camera2D" parent="."]
+[node name="Camera2D" type="Camera2D" parent="Node2D"]
+
+[node name="UI" type="CanvasLayer" parent="."]
+
+[node name="Control" type="Control" parent="UI"]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+
+[node name="PauseLabel" type="Label" parent="UI/Control"]
+layout_mode = 0
+offset_left = 18.0
+offset_top = 18.0
+offset_right = 75.0
+offset_bottom = 41.0
+text = "Paused"