diff --git a/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj b/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj
index 88924f4..3921c34 100644
--- a/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj
+++ b/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj
@@ -16,11 +16,11 @@
-
+
all
runtime; build; native; contentfiles; analyzers
-
+
all
runtime; build; native; contentfiles; analyzers
diff --git a/GFramework.Game.Abstractions/GFramework.Game.Abstractions.csproj b/GFramework.Game.Abstractions/GFramework.Game.Abstractions.csproj
index b383b5d..8d399cc 100644
--- a/GFramework.Game.Abstractions/GFramework.Game.Abstractions.csproj
+++ b/GFramework.Game.Abstractions/GFramework.Game.Abstractions.csproj
@@ -18,11 +18,11 @@
-
+
all
runtime; build; native; contentfiles; analyzers
-
+
all
runtime; build; native; contentfiles; analyzers
diff --git a/GFramework.Game.Abstractions/coroutine/ICoroutineScheduler.cs b/GFramework.Game.Abstractions/coroutine/ICoroutineScheduler.cs
new file mode 100644
index 0000000..3e00dd1
--- /dev/null
+++ b/GFramework.Game.Abstractions/coroutine/ICoroutineScheduler.cs
@@ -0,0 +1,13 @@
+namespace GFramework.Game.Abstractions.coroutine;
+
+///
+/// 协程调度器接口,定义了协程系统的基本调度方法
+///
+public interface ICoroutineScheduler
+{
+ ///
+ /// 更新协程调度器,处理等待中的协程
+ ///
+ /// 自上一帧以来的时间间隔(以秒为单位)
+ void Update(float deltaTime);
+}
\ No newline at end of file
diff --git a/GFramework.Game.Abstractions/coroutine/ICoroutineScope.cs b/GFramework.Game.Abstractions/coroutine/ICoroutineScope.cs
new file mode 100644
index 0000000..9896a14
--- /dev/null
+++ b/GFramework.Game.Abstractions/coroutine/ICoroutineScope.cs
@@ -0,0 +1,17 @@
+namespace GFramework.Game.Abstractions.coroutine;
+
+///
+/// 协程作用域接口,用于管理协程的生命周期
+///
+public interface ICoroutineScope
+{
+ ///
+ /// 获取协程是否处于活动状态
+ ///
+ bool IsActive { get; }
+
+ ///
+ /// 取消协程执行
+ ///
+ void Cancel();
+}
\ No newline at end of file
diff --git a/GFramework.Game.Abstractions/coroutine/ICoroutineSystem.cs b/GFramework.Game.Abstractions/coroutine/ICoroutineSystem.cs
new file mode 100644
index 0000000..f3a3c61
--- /dev/null
+++ b/GFramework.Game.Abstractions/coroutine/ICoroutineSystem.cs
@@ -0,0 +1,15 @@
+using GFramework.Core.Abstractions.system;
+
+namespace GFramework.Game.Abstractions.coroutine;
+
+///
+/// 协程系统接口,继承自ISystem,用于管理游戏中的协程执行
+///
+public interface ICoroutineSystem : ISystem
+{
+ ///
+ /// 更新协程系统,在每一帧调用以处理协程逻辑
+ ///
+ /// 距离上一帧的时间间隔(秒)
+ void OnUpdate(float deltaTime);
+}
\ No newline at end of file
diff --git a/GFramework.Game.Abstractions/coroutine/IYieldInstruction.cs b/GFramework.Game.Abstractions/coroutine/IYieldInstruction.cs
new file mode 100644
index 0000000..46bbb17
--- /dev/null
+++ b/GFramework.Game.Abstractions/coroutine/IYieldInstruction.cs
@@ -0,0 +1,18 @@
+namespace GFramework.Game.Abstractions.coroutine;
+
+///
+/// 表示一个可等待的指令接口,用于协程中的等待操作
+///
+public interface IYieldInstruction
+{
+ ///
+ /// 获取当前等待指令是否已完成
+ ///
+ bool IsDone { get; }
+
+ ///
+ /// 更新等待指令的状态
+ ///
+ /// 自上次更新以来的时间间隔(以秒为单位)
+ void Update(float deltaTime);
+}
\ No newline at end of file
diff --git a/GFramework.Game/coroutine/CoroutineContext.cs b/GFramework.Game/coroutine/CoroutineContext.cs
new file mode 100644
index 0000000..c5b8700
--- /dev/null
+++ b/GFramework.Game/coroutine/CoroutineContext.cs
@@ -0,0 +1,27 @@
+using GFramework.Game.Abstractions.coroutine;
+
+namespace GFramework.Game.coroutine;
+
+///
+/// 协程上下文类,用于封装协程执行所需的环境信息
+///
+/// 协程作用域接口实例
+/// 协程调度器实例
+/// 协程的所有者对象,默认为null
+public class CoroutineContext(ICoroutineScope scope, CoroutineScheduler scheduler, object? owner = null)
+{
+ ///
+ /// 获取协程作用域
+ ///
+ public ICoroutineScope Scope { get; } = scope;
+
+ ///
+ /// 获取协程调度器
+ ///
+ public CoroutineScheduler Scheduler { get; } = scheduler;
+
+ ///
+ /// 获取协程所有者对象
+ ///
+ public object? Owner { get; } = owner;
+}
\ No newline at end of file
diff --git a/GFramework.Game/coroutine/CoroutineHandle.cs b/GFramework.Game/coroutine/CoroutineHandle.cs
new file mode 100644
index 0000000..0fce2b9
--- /dev/null
+++ b/GFramework.Game/coroutine/CoroutineHandle.cs
@@ -0,0 +1,118 @@
+using System.Collections;
+using GFramework.Game.Abstractions.coroutine;
+
+namespace GFramework.Game.coroutine;
+
+public class CoroutineHandle : IYieldInstruction
+{
+ private readonly Stack _stack = new();
+ private IYieldInstruction? _waitingInstruction;
+
+ internal CoroutineHandle(IEnumerator routine, CoroutineContext context, IYieldInstruction? waitingInstruction)
+ {
+ _stack.Push(routine);
+ Context = context;
+ _waitingInstruction = waitingInstruction;
+ }
+
+ public CoroutineContext Context { get; }
+ public bool IsCancelled { get; private set; }
+ public bool IsDone { get; private set; }
+
+ void IYieldInstruction.Update(float deltaTime)
+ {
+ InternalUpdate(deltaTime);
+ }
+
+ public event Action? OnComplete;
+ public event Action? OnError;
+
+ private bool InternalUpdate(float deltaTime)
+ {
+ if (IsDone) return false;
+
+ if (_waitingInstruction != null)
+ {
+ _waitingInstruction.Update(deltaTime);
+ if (!_waitingInstruction.IsDone) return true;
+ _waitingInstruction = null;
+ }
+
+ if (_stack.Count == 0)
+ {
+ Complete();
+ return false;
+ }
+
+ try
+ {
+ var current = _stack.Peek();
+ if (current.MoveNext())
+ {
+ ProcessYieldValue(current.Current);
+ return true;
+ }
+
+ _stack.Pop();
+ return _stack.Count > 0 || !CompleteCheck();
+ }
+ catch (Exception ex)
+ {
+ HandleError(ex);
+ return false;
+ }
+ }
+
+ private void ProcessYieldValue(object yielded)
+ {
+ switch (yielded)
+ {
+ case null:
+ break;
+ case IEnumerator nested:
+ _stack.Push(nested);
+ break;
+ // ✅ 将更具体的类型放在前面
+ case CoroutineHandle otherHandle:
+ _waitingInstruction = otherHandle;
+ break;
+ case IYieldInstruction instruction:
+ _waitingInstruction = instruction;
+ break;
+ default:
+ throw new InvalidOperationException($"Unsupported yield type: {yielded.GetType()}");
+ }
+ }
+
+ private bool CompleteCheck()
+ {
+ if (_stack.Count == 0) Complete();
+ return IsDone;
+ }
+
+ private void Complete()
+ {
+ if (IsDone) return;
+ IsDone = true;
+ _stack.Clear();
+ _waitingInstruction = null;
+ OnComplete?.Invoke();
+ }
+
+ private void HandleError(Exception ex)
+ {
+ IsDone = true;
+ _stack.Clear();
+ _waitingInstruction = null;
+ OnError?.Invoke(ex);
+ }
+
+ public void Cancel()
+ {
+ if (IsDone) return;
+ IsDone = true;
+ IsCancelled = true;
+ _stack.Clear();
+ _waitingInstruction = null;
+ }
+}
\ No newline at end of file
diff --git a/GFramework.Game/coroutine/CoroutineScheduler.cs b/GFramework.Game/coroutine/CoroutineScheduler.cs
new file mode 100644
index 0000000..6b4fdef
--- /dev/null
+++ b/GFramework.Game/coroutine/CoroutineScheduler.cs
@@ -0,0 +1,52 @@
+using System.Collections;
+using GFramework.Game.Abstractions.coroutine;
+
+namespace GFramework.Game.coroutine;
+
+public class CoroutineScheduler : ICoroutineScheduler
+{
+ private readonly List _active = new();
+ private readonly List _toAdd = new();
+ private readonly HashSet _toRemove = new();
+
+ public int ActiveCount => _active.Count;
+
+ public void Update(float deltaTime)
+ {
+ if (_toAdd.Count > 0)
+ {
+ _active.AddRange(_toAdd);
+ _toAdd.Clear();
+ }
+
+ for (var i = _active.Count - 1; i >= 0; i--)
+ {
+ var c = _active[i];
+
+ if (!c.Context.Scope.IsActive)
+ {
+ c.Cancel();
+ _toRemove.Add(c);
+ continue;
+ }
+
+ if (!c.Update(deltaTime))
+ _toRemove.Add(c);
+ }
+
+ if (_toRemove.Count <= 0) return;
+ {
+ _active.RemoveAll(c => _toRemove.Contains(c));
+ _toRemove.Clear();
+ }
+ }
+
+ internal CoroutineHandle StartCoroutine(IEnumerator routine, CoroutineContext context)
+ {
+ var handle = new CoroutineHandle(routine, context);
+ _toAdd.Add(handle);
+ return handle;
+ }
+
+ internal void RemoveCoroutine(CoroutineHandle handle) => _toRemove.Add(handle);
+}
\ No newline at end of file
diff --git a/GFramework.Game/coroutine/CoroutineScope.cs b/GFramework.Game/coroutine/CoroutineScope.cs
new file mode 100644
index 0000000..1c7f16e
--- /dev/null
+++ b/GFramework.Game/coroutine/CoroutineScope.cs
@@ -0,0 +1,57 @@
+using System.Collections;
+using GFramework.Game.Abstractions.coroutine;
+
+namespace GFramework.Game.coroutine;
+
+public class CoroutineScope : ICoroutineScope, IDisposable
+{
+ private readonly List _children = new();
+ private readonly CoroutineScope _parent;
+ private readonly HashSet _runningCoroutines = new();
+ private readonly CoroutineScheduler _scheduler;
+
+ private bool _isActive = true;
+
+ public CoroutineScope(CoroutineScheduler scheduler, string name = null, CoroutineScope parent = null)
+ {
+ _scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler));
+ _parent = parent;
+ _parent?._children.Add(this);
+ Name = name ?? $"Scope_{GetHashCode()}";
+ }
+
+ public string Name { get; }
+ public bool IsActive => _isActive;
+
+ public void Cancel()
+ {
+ if (!_isActive) return;
+
+ _isActive = false;
+
+ foreach (var child in _children.ToList())
+ child.Cancel();
+
+ foreach (var handle in _runningCoroutines.ToList())
+ handle.Cancel();
+
+ _runningCoroutines.Clear();
+ }
+
+ public void Dispose() => Cancel();
+
+ public CoroutineHandle Launch(IEnumerator routine)
+ {
+ if (!_isActive)
+ throw new InvalidOperationException($"Scope '{Name}' is not active");
+
+ var context = new CoroutineContext(this, _scheduler, this);
+ var handle = _scheduler.StartCoroutine(routine, context);
+
+ _runningCoroutines.Add(handle);
+ handle.OnComplete += () => _runningCoroutines.Remove(handle);
+ handle.OnError += (ex) => _runningCoroutines.Remove(handle);
+
+ return handle;
+ }
+}
\ No newline at end of file
diff --git a/GFramework.Game/coroutine/CoroutineScopeExtensions.cs b/GFramework.Game/coroutine/CoroutineScopeExtensions.cs
new file mode 100644
index 0000000..c610ef5
--- /dev/null
+++ b/GFramework.Game/coroutine/CoroutineScopeExtensions.cs
@@ -0,0 +1,57 @@
+using System.Collections;
+using GFramework.Game.Abstractions.coroutine;
+
+namespace GFramework.Game.coroutine;
+
+///
+/// 为协程作用域提供扩展方法,支持延迟执行和重复执行功能
+///
+public static class CoroutineScopeExtensions
+{
+ ///
+ /// 启动一个延迟执行的协程
+ ///
+ /// 协程作用域
+ /// 延迟时间(秒)
+ /// 延迟后要执行的动作
+ /// 协程句柄,可用于控制协程的生命周期
+ public static CoroutineHandle LaunchDelayed(this ICoroutineScope scope, float delay, Action action)
+ => ((CoroutineScope)scope).Launch(DelayedRoutine(delay, action));
+
+ ///
+ /// 创建延迟执行的协程迭代器
+ ///
+ /// 延迟时间(秒)
+ /// 要执行的动作
+ /// 协程迭代器
+ private static IEnumerator DelayedRoutine(float delay, Action? action)
+ {
+ yield return new WaitForSeconds(delay);
+ action?.Invoke();
+ }
+
+ ///
+ /// 启动一个重复执行的协程
+ ///
+ /// 协程作用域
+ /// 重复间隔时间(秒)
+ /// 每次重复时要执行的动作
+ /// 协程句柄,可用于控制协程的生命周期
+ public static CoroutineHandle LaunchRepeating(this ICoroutineScope scope, float interval, Action action)
+ => ((CoroutineScope)scope).Launch(RepeatingRoutine(interval, action));
+
+ ///
+ /// 创建重复执行的协程迭代器
+ ///
+ /// 重复间隔时间(秒)
+ /// 要执行的动作
+ /// 协程迭代器
+ private static IEnumerator RepeatingRoutine(float interval, Action action)
+ {
+ while (true)
+ {
+ action?.Invoke();
+ yield return new WaitForSeconds(interval);
+ }
+ }
+}
\ No newline at end of file
diff --git a/GFramework.Game/coroutine/CoroutineSystem.cs b/GFramework.Game/coroutine/CoroutineSystem.cs
new file mode 100644
index 0000000..6bd87e2
--- /dev/null
+++ b/GFramework.Game/coroutine/CoroutineSystem.cs
@@ -0,0 +1,28 @@
+using GFramework.Core.system;
+using GFramework.Game.Abstractions.coroutine;
+
+namespace GFramework.Game.coroutine;
+
+///
+/// 协程系统类,负责管理和更新协程调度器
+///
+/// 协程调度器实例
+public class CoroutineSystem(CoroutineScheduler scheduler) : AbstractSystem, ICoroutineSystem
+{
+ ///
+ /// 更新协程系统,驱动协程调度器执行协程逻辑
+ ///
+ /// 时间间隔,表示自上一帧以来经过的时间(秒)
+ public void OnUpdate(float deltaTime)
+ {
+ // 更新协程调度器,处理等待中的协程
+ scheduler.Update(deltaTime);
+ }
+
+ ///
+ /// 初始化协程系统
+ ///
+ protected override void OnInit()
+ {
+ }
+}
\ No newline at end of file
diff --git a/GFramework.Game/coroutine/GlobalCoroutineScope.cs b/GFramework.Game/coroutine/GlobalCoroutineScope.cs
new file mode 100644
index 0000000..189a4d0
--- /dev/null
+++ b/GFramework.Game/coroutine/GlobalCoroutineScope.cs
@@ -0,0 +1,31 @@
+using System.Collections;
+
+namespace GFramework.Game.coroutine;
+
+///
+/// 全局协程作用域管理器,提供全局唯一的协程执行环境
+///
+public static class GlobalCoroutineScope
+{
+ private static CoroutineScope? _instance;
+
+ ///
+ /// 获取全局协程作用域实例,如果未初始化则抛出异常
+ ///
+ private static CoroutineScope Instance =>
+ _instance ?? throw new InvalidOperationException("GlobalScope not initialized");
+
+ ///
+ /// 初始化全局协程作用域
+ ///
+ /// 协程调度器实例
+ public static void Initialize(CoroutineScheduler scheduler) =>
+ _instance = new CoroutineScope(scheduler, "GlobalScope");
+
+ ///
+ /// 在全局作用域中启动一个协程
+ ///
+ /// 要执行的协程枚举器
+ /// 协程句柄,用于控制和管理协程生命周期
+ public static CoroutineHandle Launch(IEnumerator routine) => Instance.Launch(routine);
+}
\ No newline at end of file
diff --git a/GFramework.Game/coroutine/WaitForSeconds.cs b/GFramework.Game/coroutine/WaitForSeconds.cs
new file mode 100644
index 0000000..9b887f7
--- /dev/null
+++ b/GFramework.Game/coroutine/WaitForSeconds.cs
@@ -0,0 +1,24 @@
+using GFramework.Game.Abstractions.coroutine;
+
+namespace GFramework.Game.coroutine;
+
+///
+/// 表示一个等待指定秒数的时间延迟指令
+///
+/// 需要等待的秒数
+public class WaitForSeconds(float seconds) : IYieldInstruction
+{
+ private float _elapsed;
+ public bool IsDone { get; private set; }
+
+ ///
+ /// 更新时间进度,当累计时间达到指定秒数时标记完成
+ ///
+ /// 自上次更新以来经过的时间(秒)
+ public void Update(float deltaTime)
+ {
+ if (IsDone) return;
+ _elapsed += deltaTime;
+ if (_elapsed >= seconds) IsDone = true;
+ }
+}
\ No newline at end of file
diff --git a/GFramework.Game/coroutine/WaitUntil.cs b/GFramework.Game/coroutine/WaitUntil.cs
new file mode 100644
index 0000000..3025b49
--- /dev/null
+++ b/GFramework.Game/coroutine/WaitUntil.cs
@@ -0,0 +1,24 @@
+using GFramework.Game.Abstractions.coroutine;
+
+namespace GFramework.Game.coroutine;
+
+///
+/// 等待直到指定条件满足的协程等待指令
+///
+/// 用于判断等待条件是否满足的布尔函数委托
+public class WaitUntil(Func predicate) : IYieldInstruction
+{
+ ///
+ /// 获取等待指令是否已完成
+ ///
+ public bool IsDone { get; private set; }
+
+ ///
+ /// 更新等待状态,在每一帧调用以检查条件是否满足
+ ///
+ /// 自上一帧以来的时间间隔
+ public void Update(float deltaTime)
+ {
+ if (!IsDone) IsDone = predicate();
+ }
+}
\ No newline at end of file
diff --git a/GFramework.Game/coroutine/WaitWhile.cs b/GFramework.Game/coroutine/WaitWhile.cs
new file mode 100644
index 0000000..682c58a
--- /dev/null
+++ b/GFramework.Game/coroutine/WaitWhile.cs
@@ -0,0 +1,24 @@
+using GFramework.Game.Abstractions.coroutine;
+
+namespace GFramework.Game.coroutine;
+
+///
+/// 等待条件为假的等待指令,当指定的谓词条件变为false时完成等待
+///
+/// 用于判断是否继续等待的条件函数,返回true表示继续等待,返回false表示等待结束
+public class WaitWhile(Func predicate) : IYieldInstruction
+{
+ ///
+ /// 获取等待指令是否已完成
+ ///
+ public bool IsDone { get; private set; }
+
+ ///
+ /// 更新等待状态,检查谓词条件是否满足结束等待的要求
+ ///
+ /// 自上次更新以来的时间间隔
+ public void Update(float deltaTime)
+ {
+ if (!IsDone) IsDone = !predicate();
+ }
+}
\ No newline at end of file
diff --git a/GFramework.Godot.SourceGenerators.Abstractions/GFramework.Godot.SourceGenerators.Abstractions.csproj b/GFramework.Godot.SourceGenerators.Abstractions/GFramework.Godot.SourceGenerators.Abstractions.csproj
index 002c951..811a1b9 100644
--- a/GFramework.Godot.SourceGenerators.Abstractions/GFramework.Godot.SourceGenerators.Abstractions.csproj
+++ b/GFramework.Godot.SourceGenerators.Abstractions/GFramework.Godot.SourceGenerators.Abstractions.csproj
@@ -19,11 +19,11 @@
-
+
all
runtime; build; native; contentfiles; analyzers
-
+
all
runtime; build; native; contentfiles; analyzers
diff --git a/GFramework.SourceGenerators.Abstractions/GFramework.SourceGenerators.Abstractions.csproj b/GFramework.SourceGenerators.Abstractions/GFramework.SourceGenerators.Abstractions.csproj
index dd0fb77..3aa8fd9 100644
--- a/GFramework.SourceGenerators.Abstractions/GFramework.SourceGenerators.Abstractions.csproj
+++ b/GFramework.SourceGenerators.Abstractions/GFramework.SourceGenerators.Abstractions.csproj
@@ -18,11 +18,11 @@
-
+
all
runtime; build; native; contentfiles; analyzers
-
+
all
runtime; build; native; contentfiles; analyzers
diff --git a/GFramework.SourceGenerators.Common/GFramework.SourceGenerators.Common.csproj b/GFramework.SourceGenerators.Common/GFramework.SourceGenerators.Common.csproj
index 65193bf..05cbd7b 100644
--- a/GFramework.SourceGenerators.Common/GFramework.SourceGenerators.Common.csproj
+++ b/GFramework.SourceGenerators.Common/GFramework.SourceGenerators.Common.csproj
@@ -22,11 +22,11 @@
runtime; build; native; contentfiles; analyzers
-
+
all
runtime; build; native; contentfiles; analyzers
-
+
all
runtime; build; native; contentfiles; analyzers