Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions CoroutineManager.tscn
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[gd_scene load_steps=3 format=2]
[gd_scene load_steps=3 format=3 uid="uid://3thn65kd8af7"]

[ext_resource path="res://HCoroutines/src/CoroutineManager.cs" type="Script" id=1]
[ext_resource path="res://HCoroutines/src/Util/TimeScheduler.cs" type="Script" id=2]
[ext_resource type="Script" path="res://HCoroutines/src/CoroutineManager.cs" id="1"]
[ext_resource type="Script" path="res://HCoroutines/src/Util/TimeScheduler.cs" id="2"]

[node name="CoroutineManager" type="Node"]
script = ExtResource( 1 )
script = ExtResource("1")

[node name="TimeScheduler" type="Node" parent="."]
script = ExtResource( 2 )
script = ExtResource("2")
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# HCoroutines

![Godot 3.5](https://img.shields.io/badge/Godot-3.5-blue?logo=godot-engine&logoColor=white&style=for-the-badge) ![](https://img.shields.io/github/license/Inspiaaa/HCoroutines?style=for-the-badge) ![](https://img.shields.io/github/v/release/Inspiaaa/HCoroutines?style=for-the-badge) ![](https://img.shields.io/badge/Godot-C%23-green?logo=csharp&style=for-the-badge)
![Godot 4.1.3](https://img.shields.io/badge/Godot-4.1.3-blue?logo=godot-engine&logoColor=white&style=for-the-badge) ![](https://img.shields.io/github/license/Inspiaaa/HCoroutines?style=for-the-badge) ![](https://img.shields.io/github/v/release/Inspiaaa/HCoroutines?style=for-the-badge) ![](https://img.shields.io/badge/Godot-C%23-green?logo=csharp&style=for-the-badge)

HCoroutines is a library that helps you write game logic in an **intuitive** way by bringing the concept of **hierarchical coroutines** to Godot (C#). Its built-in coroutine types are specifically designed for Godot, **reducing boilerplate** code and increasing **readability**. At the same time, **async methods** can also be seamlessly integrated with coroutines.

Expand Down Expand Up @@ -29,7 +29,7 @@ using System.Threading.Tasks;
// Import the library.
using HCoroutines;

public class Demo : Node2D {
public partial class Demo : Node2D {
public override void _Ready() {
// Spawn a new coroutine that is managed by
// the default CoroutineManager.
Expand Down Expand Up @@ -60,7 +60,7 @@ public class Demo : Node2D {
yield return Co.Await(Task.Delay(100));

// Await and use the result of an async task
var fetch = Co.Await<int>(FetchNumber());
var fetch = Co.Await(FetchNumber());
yield return fetch;
int number = fetch.Task.Result;

Expand All @@ -77,18 +77,17 @@ public class Demo : Node2D {
}

private IEnumerator GoTo(Vector2 target, float duration) {
Vector2 start = Position;
float speed = Position.DistanceTo(target) / duration;

while (Position.DistanceTo(target) > 0.01f) {
// delta time can be accessed via Co.DeltaTime.
Position = Position.MoveToward(target, duration * Co.DeltaTime);
Position = Position.MoveToward(target, speed * Co.DeltaTime);
yield return null;
}
}

private IEnumerator Turn(float duration) {
float fullRotation = (float)(2 * Math.PI);
float fullRotation = 2 * Mathf.Pi;
float angularSpeed = fullRotation / duration;
float angle = 0;

Expand All @@ -99,7 +98,7 @@ public class Demo : Node2D {
}
}

private async Task<int> FetchNumber() {
private static async Task<int> FetchNumber() {
await Task.Delay(100);
return 0;
}
Expand Down Expand Up @@ -151,6 +150,10 @@ To access the delta time from within a coroutine:
```csharp
float deltaTime = Co.DeltaTime;
```
or
```csharp
double deltaTime = Co.DeltaTimeDouble;
```

All coroutines inherit from the `CoroutineBase` class. To define a coroutine in the intuitive / standard way with `IEnumerators`, you can use the `Coroutine` class which wraps the `IEnumerator` (Either via `Co.Coroutine(...)` or `new Coroutine(...)`).

Expand Down
14 changes: 7 additions & 7 deletions demo/AnimatedIcon.tscn
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[gd_scene load_steps=3 format=2]
[gd_scene load_steps=3 format=3 uid="uid://cb2pyqnqkjpuc"]

[ext_resource path="res://HCoroutines/demo/Icon.png" type="Texture" id=1]
[ext_resource path="res://HCoroutines/demo/IconAnimation.cs" type="Script" id=2]
[ext_resource type="Texture2D" uid="uid://bu2485fdkbfkv" path="res://HCoroutines/demo/Icon.png" id="1"]
[ext_resource type="Script" path="res://HCoroutines/demo/IconAnimation.cs" id="2"]

[node name="Icon" type="Sprite"]
position = Vector2( 499, 297 )
texture = ExtResource( 1 )
script = ExtResource( 2 )
[node name="Icon" type="Sprite2D"]
position = Vector2(499, 297)
texture = ExtResource("1")
script = ExtResource("2")
21 changes: 8 additions & 13 deletions demo/Demo.tscn
Original file line number Diff line number Diff line change
@@ -1,27 +1,22 @@
[gd_scene load_steps=3 format=2]
[gd_scene load_steps=3 format=3 uid="uid://chu3ymn2s6v1s"]

[ext_resource path="res://HCoroutines/demo/AnimatedIcon.tscn" type="PackedScene" id=1]
[ext_resource path="res://HCoroutines/demo/IconSpawner.cs" type="Script" id=2]
[ext_resource type="PackedScene" uid="uid://cb2pyqnqkjpuc" path="res://HCoroutines/demo/AnimatedIcon.tscn" id="1"]
[ext_resource type="Script" path="res://HCoroutines/demo/IconSpawner.cs" id="2"]

[node name="Node2D" type="Node2D"]
script = ExtResource( 2 )
icon = ExtResource( 1 )
script = ExtResource("2")
icon = ExtResource("1")

[node name="Icon" parent="." instance=ExtResource( 1 )]
[node name="Icon" parent="." instance=ExtResource("1")]

[node name="Camera2D" type="Camera2D" parent="."]
current = true

[node name="UI" type="CanvasLayer" parent="."]

[node name="MarginContainer" type="MarginContainer" parent="UI"]
anchors_preset = 10
anchor_right = 1.0
margin_bottom = 14.0
custom_constants/margin_top = 20

[node name="Label" type="Label" parent="UI/MarginContainer"]
margin_top = 20.0
margin_right = 1024.0
margin_bottom = 34.0
layout_mode = 2
text = "Click to create a new animated icon"
align = 1
31 changes: 15 additions & 16 deletions demo/Icon.png.import
Original file line number Diff line number Diff line change
@@ -1,35 +1,34 @@
[remap]

importer="texture"
type="StreamTexture"
path="res://.import/Icon.png-68b487edcbe40feab18e741224dda706.stex"
type="CompressedTexture2D"
uid="uid://bu2485fdkbfkv"
path="res://.godot/imported/Icon.png-68b487edcbe40feab18e741224dda706.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://HCoroutines/demo/Icon.png"
dest_files=[ "res://.import/Icon.png-68b487edcbe40feab18e741224dda706.stex" ]
dest_files=["res://.godot/imported/Icon.png-68b487edcbe40feab18e741224dda706.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/hdr_compression=1
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
5 changes: 2 additions & 3 deletions demo/IconAnimation.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
using Godot;
using System;
using System.Collections;
using HCoroutines;
using System.Threading.Tasks;

public class IconAnimation : Node2D
public partial class IconAnimation : Node2D
{
public override void _Ready()
{
Expand Down Expand Up @@ -59,7 +58,7 @@ private IEnumerator ChangeColor(Color targetColor, float duration)
{
// Another way to do a tween

SceneTreeTween tween = CreateTween();
Tween tween = CreateTween();
tween
.TweenProperty(this, "modulate", targetColor, duration)
.SetTrans(Tween.TransitionType.Expo);
Expand Down
7 changes: 3 additions & 4 deletions demo/IconSpawner.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
using Godot;
using System;

public class IconSpawner : Node2D
public partial class IconSpawner : Node2D
{
[Export] private PackedScene icon;

public override void _Input(InputEvent e)
{
if (e is InputEventMouseButton mouseEvent)
{
if (mouseEvent.ButtonIndex == (int)ButtonList.Left && mouseEvent.Pressed)
if (mouseEvent.ButtonIndex == MouseButton.Left && mouseEvent.Pressed)
{
Node2D node = icon.Instance<Node2D>();
Node2D node = icon.Instantiate<Node2D>();
node.Position = GetLocalMousePosition();
AddChild(node);
}
Expand Down
Empty file added docs/.gdignore
Empty file.
6 changes: 4 additions & 2 deletions src/Co.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ private static Coroutine[] GetCoroutines(IEnumerator[] enumerators)

public static float DeltaTime => CoroutineManager.Instance.DeltaTime;

public static double DeltaTimeDouble => CoroutineManager.Instance.DeltaTimeDouble;


public static void Run(CoroutineBase coroutine)
=> CoroutineManager.Instance.StartCoroutine(coroutine);
Expand Down Expand Up @@ -82,7 +84,7 @@ public static WaitUntilCoroutine WaitUntil(Func<Boolean> condition)
=> new WaitUntilCoroutine(condition);


public static WaitForSignalCoroutine WaitForSignal(Godot.Object obj, string signal)
public static WaitForSignalCoroutine WaitForSignal(GodotObject obj, string signal)
=> new WaitForSignalCoroutine(obj, signal);


Expand Down Expand Up @@ -113,7 +115,7 @@ public static RepeatCoroutine RepeatInfinitely(Func<IEnumerator> creator)
=> new RepeatCoroutine(-1, coroutine => new Coroutine(creator()));


public static TweenCoroutine Tween(Action<SceneTreeTween> setupTween)
public static TweenCoroutine Tween(Action<Tween> setupTween)
=> new TweenCoroutine(setupTween);


Expand Down
8 changes: 5 additions & 3 deletions src/CoroutineManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

namespace HCoroutines
{
public class CoroutineManager : Node
public partial class CoroutineManager : Node
{
public static CoroutineManager Instance { get; private set; }
public float DeltaTime { get; set; }
public double DeltaTimeDouble { get; set; }

private bool isIteratingActiveCoroutines = false;
private HashSet<CoroutineBase> activeCoroutines = new HashSet<CoroutineBase>();
Expand Down Expand Up @@ -51,9 +52,10 @@ public override void _EnterTree()
Instance = this;
}

public override void _Process(float delta)
public override void _Process(double delta)
{
DeltaTime = delta;
DeltaTimeDouble = delta;
DeltaTime = (float)delta;

isIteratingActiveCoroutines = true;

Expand Down
2 changes: 0 additions & 2 deletions src/Coroutines/AwaitCoroutine.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System;
using System.Collections;
using System.Threading.Tasks;

namespace HCoroutines
Expand Down
1 change: 0 additions & 1 deletion src/Coroutines/Coroutine.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections;
using System.ComponentModel;

namespace HCoroutines
{
Expand Down
6 changes: 3 additions & 3 deletions src/Coroutines/TweenCoroutine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ namespace HCoroutines
/// </summary>
public class TweenCoroutine : CoroutineBase
{
private Action<SceneTreeTween> setupTween;
private SceneTreeTween tween;
private Action<Tween> setupTween;
private Tween tween;
private int schedulerId;

public TweenCoroutine(Action<SceneTreeTween> setupTween)
public TweenCoroutine(Action<Tween> setupTween)
{
this.setupTween = setupTween;
}
Expand Down
5 changes: 3 additions & 2 deletions src/Coroutines/WaitForSignalCoroutine.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Godot;
using HCoroutines.Util;

namespace HCoroutines
Expand All @@ -7,11 +8,11 @@ namespace HCoroutines
/// </summary>
public class WaitForSignalCoroutine : CoroutineBase
{
private Godot.Object targetObject;
private GodotObject targetObject;
private string targetSignal;
private int schedulerId;

public WaitForSignalCoroutine(Godot.Object obj, string signal)
public WaitForSignalCoroutine(GodotObject obj, string signal)
{
this.targetObject = obj;
this.targetSignal = signal;
Expand Down
14 changes: 6 additions & 8 deletions src/Util/TimeScheduler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

namespace HCoroutines.Util
{
public class TimeScheduler : Node
public partial class TimeScheduler : Node
{
public static TimeScheduler Instance { get; private set; }

private Dictionary<int, Action> actionsById = new Dictionary<int, Action>();
private Dictionary<int, Action> actionsById = new();
private int idCounter = 0;

public override void _EnterTree()
Expand All @@ -26,21 +26,19 @@ private int GetNextScheduleId()

public int Schedule(Action action, float delay)
{
SceneTreeTimer timer = GetTree().CreateTimer(delay, pauseModeProcess: false);
SceneTreeTimer timer = GetTree().CreateTimer(delay, processAlways: false);
return ScheduleOnSignal(action, timer, "timeout");
}

public int ScheduleOnSignal(Action action, Godot.Object obj, string signal)
public int ScheduleOnSignal(Action action, GodotObject obj, string signal)
{
int id = GetNextScheduleId();
actionsById[id] = action;

obj.Connect(
signal,
this,
nameof(CallCallback),
new Godot.Collections.Array(id),
(int)ConnectFlags.Oneshot
Callable.From(() => CallCallback(id)),
(int)ConnectFlags.OneShot
);

return id;
Expand Down