mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-06 16:16:44 +08:00
- 补齐 docs/zh-CN 多个栏目页面的 title 与 description frontmatter,清空完全缺 frontmatter 的历史页面 - 修复 multiplayer、source-generators 与 troubleshooting 触达页面暴露的 Markdown 结构和站内链接问题 - 更新 documentation-full-coverage-governance 的恢复点、验证结果与下一批 metadata 热点
4.1 KiB
4.1 KiB
title, description
| title | description |
|---|---|
| Godot 协程系统 | 说明 GFramework.Godot.Coroutine 的宿主集成能力、阶段语义与使用方式。 |
Godot 协程系统
概述
GFramework.Godot.Coroutine 在 Core 协程内核之上提供 Godot 宿主集成,负责把 Godot 的不同更新循环映射为真实的协程阶段语义:
Segment.ProcessSegment.ProcessIgnorePauseSegment.PhysicsProcessSegment.DeferredProcess
它同时补充了以下宿主能力:
- 节点归属协程运行入口
- 节点退树自动终止
- Godot 真实时间源
- 句柄控制与快照查询
启动协程
直接运行枚举器
using GFramework.Core.Abstractions.Coroutine;
using GFramework.Core.Coroutine.Instructions;
using GFramework.Godot.Coroutine;
public partial class DemoNode : Node
{
public override void _Ready()
{
Demo().RunCoroutine();
}
private IEnumerator<IYieldInstruction> Demo()
{
GD.Print("start");
yield return new Delay(1.0);
yield return new WaitForEndOfFrame();
GD.Print("done");
}
}
默认情况下,RunCoroutine() 会在 Segment.Process 上运行。
以 Node 作为生命周期所有者运行
更推荐的方式是以节点为入口运行协程:
public override void _Ready()
{
this.RunCoroutine(LongRunningTask(), Segment.Process, tag: "ui-blink");
}
这会自动把协程登记为该节点归属协程,并在节点退出场景树时终止它。
你仍然可以继续使用 CancelWith(...) 包装已有枚举器;它适合把一个协程显式绑定到多个节点生命周期。
Segment 与阶段语义
Godot 层会把不同 segment 映射为不同的 CoroutineExecutionStage:
Segment.Process- 对应默认更新阶段
- 场景树暂停时不会推进
Segment.ProcessIgnorePause- 同样对应默认更新阶段
- 场景树暂停时仍会推进
Segment.PhysicsProcess- 对应固定更新阶段
WaitForFixedUpdate会在这里真实完成
Segment.DeferredProcess- 对应帧结束阶段
WaitForEndOfFrame会在这里真实完成
示例:
this.RunCoroutine(PhysicsRoutine(), Segment.PhysicsProcess);
this.RunCoroutine(UiAnimation(), Segment.ProcessIgnorePause);
时间等待语义
Godot 集成层为每个调度器同时提供了两套时间源:
- 缩放时间
- 来自
_Process/_PhysicsProcess的帧增量
- 来自
- 真实时间
- 来自 Godot 单调时钟,不受时间缩放和暂停影响
因此:
Delay/WaitForSecondsScaled使用宿主帧增量WaitForSecondsRealtime使用真实时间
这意味着 UI 或暂停菜单中的协程可以安全使用 WaitForSecondsRealtime 保持真实计时。
生命周期管理
自动归属
var handle = this.RunCoroutine(LoadAvatar(), tag: "avatar");
手动绑定多个节点
LongRunningTask()
.CancelWith(this, panelNode)
.RunCoroutine();
主动清理
Timing.KillCoroutine(handle);
Timing.KillCoroutines(this);
Timing.KillCoroutines("avatar");
Timing.KillAllCoroutines();
调试与查询
if (Timing.TryGetCoroutineSnapshot(handle, out var snapshot))
{
GD.Print(snapshot.ExecutionStage);
GD.Print(snapshot.WaitingInstructionType);
}
var ownedCount = Timing.GetOwnedCoroutineCount(this);
实例级计数器:
Timing.Instance.ProcessCoroutinesTiming.Instance.ProcessIgnorePauseCoroutinesTiming.Instance.PhysicsCoroutinesTiming.Instance.DeferredCoroutines
延迟调用
Timing.CallDelayed(1.0, () => GD.Print("1 秒后执行"));
Timing.CallDelayed(1.0, () => GD.Print("节点仍然有效时执行"), this);
第二个重载内部使用节点归属语义,因此节点退树后不会再触发动作。
与 IContextAware 集成
Godot 层还提供以下扩展方法,用于把命令、查询和通知直接包装成协程并交给 Timing 调度:
RunCommandCoroutine(...)RunCommandCoroutine<TResponse>(...)RunQueryCoroutine<TResponse>(...)RunPublishCoroutine(...)
这些 API 仍然可以与 Segment、节点归属和标签控制一起使用。