mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-26 06:16:43 +08:00
- 新增 Core API 参考文档,涵盖架构与模块、数据模型与系统、命令与查询等核心组件 - 添加事件系统接口详细文档,包括 IEvent、IEventBus、IUnRegister 等接口说明 - 提供完整的 API 使用示例路径、最佳实践与性能建议 - 包含架构图、依赖关系图与故障排查指南 - 添加测试用例参考与扩展方法说明 - [skip ci]
21 KiB
21 KiB
等待指令系统
**本文档引用的文件** - [IYieldInstruction.cs](file://GFramework.Core.Abstractions/coroutine/IYieldInstruction.cs) - [ITimeSource.cs](file://GFramework.Core.Abstractions/coroutine/ITimeSource.cs) - [Delay.cs](file://GFramework.Core/coroutine/Delay.cs) - [WaitOneFrame.cs](file://GFramework.Core/coroutine/WaitOneFrame.cs) - [WaitForFrames.cs](file://GFramework.Core/coroutine/WaitForFrames.cs) - [WaitUntil.cs](file://GFramework.Core/coroutine/WaitUntil.cs) - [WaitWhile.cs](file://GFramework.Core/coroutine/WaitWhile.cs) - [WaitForCoroutine.cs](file://GFramework.Core/coroutine/WaitForCoroutine.cs) - [CoroutineScheduler.cs](file://GFramework.Core/coroutine/CoroutineScheduler.cs) - [CoroutineSlot.cs](file://GFramework.Core/coroutine/CoroutineSlot.cs) - [CoroutineMetadata.cs](file://GFramework.Core/coroutine/CoroutineMetadata.cs) - [CoroutineHandle.cs](file://GFramework.Core/coroutine/CoroutineHandle.cs) - [CoroutineHelper.cs](file://GFramework.Core/coroutine/CoroutineHelper.cs) - [GodotTimeSource.cs](file://GFramework.Godot/coroutine/GodotTimeSource.cs) - [Timing.cs](file://GFramework.Godot/coroutine/Timing.cs) - [YieldInstructionTests.cs](file://GFramework.Core.Tests/coroutine/YieldInstructionTests.cs)目录
简介
本文件面向游戏开发中的协程等待指令系统,提供完整的API文档与使用指南。内容涵盖等待指令接口设计、各等待指令的实现原理与使用场景、调度器工作流、时间源抽象、在Godot平台的集成方式、性能对比与选择建议、复杂等待组合的最佳实践,以及调试技巧与常见陷阱。
项目结构
等待指令系统由“抽象接口层”“核心实现层”“平台集成层”“测试验证层”构成:
- 抽象接口层:定义等待指令与时间源的契约,确保跨平台可替换性
- 核心实现层:提供具体等待指令与调度器、句柄、元数据、槽位等
- 平台集成层:Godot时间源与Timing节点,提供三段式(Process/PhysicsProcess/Deferred)协程调度
- 测试验证层:对各等待指令行为进行单元测试,覆盖边界与正确性
graph TB
subgraph "抽象层"
IYield["IYieldInstruction 接口"]
ITime["ITimeSource 接口"]
end
subgraph "核心实现层"
DelayC["Delay"]
WOne["WaitOneFrame"]
WFrm["WaitForFrames"]
WUnt["WaitUntil"]
WWhl["WaitWhile"]
WCro["WaitForCoroutine"]
Sch["CoroutineScheduler"]
Slot["CoroutineSlot"]
Meta["CoroutineMetadata"]
Hdl["CoroutineHandle"]
Help["CoroutineHelper"]
end
subgraph "平台集成层"
GTime["GodotTimeSource"]
TimingN["Timing 节点"]
end
subgraph "测试层"
Tests["YieldInstructionTests"]
end
IYield --> DelayC
IYield --> WOne
IYield --> WFrm
IYield --> WUnt
IYield --> WWhl
IYield --> WCro
ITime --> GTime
GTime --> Sch
Sch --> Slot
Sch --> Meta
Sch --> Hdl
Sch --> Help
TimingN --> Sch
TimingN --> GTime
Tests --> DelayC
Tests --> WOne
Tests --> WFrm
Tests --> WUnt
Tests --> WWhl
Tests --> WCro
图表来源
- IYieldInstruction.cs
- ITimeSource.cs
- Delay.cs
- WaitOneFrame.cs
- WaitForFrames.cs
- WaitUntil.cs
- WaitWhile.cs
- WaitForCoroutine.cs
- CoroutineScheduler.cs
- CoroutineSlot.cs
- CoroutineMetadata.cs
- CoroutineHandle.cs
- CoroutineHelper.cs
- GodotTimeSource.cs
- Timing.cs
- YieldInstructionTests.cs
章节来源
核心组件
- IYieldInstruction:等待指令契约,定义每帧更新与完成判定
- ITimeSource:时间源契约,提供当前时间与时间增量
- Delay:基于时间的延迟等待
- WaitOneFrame:单帧等待
- WaitForFrames:按帧数等待
- WaitUntil/WaitWhile:基于布尔谓词的条件等待
- WaitForCoroutine:等待另一个协程完成
- CoroutineScheduler:协程调度器,驱动等待指令与推进协程
- CoroutineSlot/Metadata/Handle:调度器内部数据结构
- CoroutineHelper:等待指令工厂与常用组合
- GodotTimeSource/Timing:Godot平台的时间源与三段式调度入口
章节来源
- IYieldInstruction.cs
- ITimeSource.cs
- CoroutineScheduler.cs
- CoroutineSlot.cs
- CoroutineMetadata.cs
- CoroutineHandle.cs
- CoroutineHelper.cs
- GodotTimeSource.cs
- Timing.cs
架构总览
等待指令系统采用“接口抽象 + 可插拔时间源 + 调度器驱动”的架构。调度器在每帧读取时间源增量,驱动当前等待指令更新,若完成则推进协程枚举器;若遇到新的等待指令则切换等待对象。
sequenceDiagram
participant T as "Timing 节点"
participant S as "CoroutineScheduler"
participant TS as "ITimeSource/GodotTimeSource"
participant E as "IEnumerator<IYieldInstruction>"
participant Y as "IYieldInstruction"
T->>S : "Update()"
S->>TS : "Update()"
TS-->>S : "CurrentTime, DeltaTime"
loop 遍历所有协程槽位
S->>E : "MoveNext()"
alt 枚举器结束
S->>S : "Complete(slot)"
else 返回新等待指令
S->>Y : "Waiting = Current"
S->>Y : "Update(DeltaTime)"
alt 等待完成
S->>S : "Waiting = null"
else 仍在等待
S->>S : "跳过该协程本轮推进"
end
end
end
图表来源
详细组件分析
IYieldInstruction 接口与实现规范
- 设计要点
- 每帧由调度器调用 Update(deltaTime),用于更新内部状态
- IsDone 用于表达等待是否完成,调度器据此决定是否推进协程
- 实现应保持幂等与无副作用,避免在 Update 中产生不可预期的外部状态变化
- 实现约定
- 对于时间型等待,应在 Update 中消耗剩余时间并更新完成标志
- 对于帧型等待,应在 Update 中减少剩余帧数或标记完成
- 对于条件等待,应在 Update 中保持空实现,完成标志由 IsDone 的谓词决定
章节来源
Delay 时间延迟机制
- 功能概述
- 基于秒数的绝对时间等待,内部维护剩余时间并在每帧递减
- 关键行为
- 初始剩余时间为最大值(秒数取非负),Update 递减剩余时间
- 完成条件为剩余时间小于等于 0
- 精确度与性能
- 精确度受 DeltaTime 影响,建议在高帧率场景下注意累积误差
- 性能开销极小,仅需一次浮点减法与一次比较
- 使用建议
- 适合通用延迟、节流、倒计时等场景
- 与 RepeatCall/RepeatCallForever 组合可实现周期性任务
flowchart TD
Start(["进入 Update"]) --> Dec["剩余时间 -= deltaTime"]
Dec --> Check{"剩余时间 <= 0 ?"}
Check --> |否| End(["返回未完成"])
Check --> |是| Done(["标记完成"])
图表来源
章节来源
WaitOneFrame 单帧等待
- 功能概述
- 等待恰好一帧,常用于“下一帧再执行”的语义
- 关键行为
- Update 第一次调用即标记完成
- 适合需要严格帧序控制的场景
- 性能
- 开销极低,仅一次布尔赋值
- 使用建议
- 与动画同步、UI刷新节奏配合
- 注意与其他按帧等待指令叠加时的语义
flowchart TD
Enter(["Update 调用"]) --> SetDone["标记完成"]
SetDone --> Exit(["返回完成"])
图表来源
章节来源
WaitForFrames 帧等待
- 功能概述
- 等待固定帧数,内部维护剩余帧数并在每帧递减
- 关键行为
- 最小等待帧数为 1,0 或负数均视为 1 帧
- 每次 Update 减少 1 帧,完成条件为剩余帧数 ≤ 0
- 性能
- 每帧一次整数递减与比较,开销极低
- 使用建议
- 适合与渲染/物理更新节奏对齐的任务
- 与 Delay 组合可实现“先延迟 N 秒,再等待 M 帧”的复合效果
flowchart TD
Start(["Update 调用"]) --> Dec["剩余帧数--"]
Dec --> Check{"剩余帧数 <= 0 ?"}
Check --> |否| End(["返回未完成"])
Check --> |是| Done(["标记完成"])
图表来源
章节来源
WaitUntil 与 WaitWhile 条件等待
- 功能概述
- WaitUntil:当谓词返回 true 时完成
- WaitWhile:当谓词返回 false 时完成(即“条件为真时继续等待”)
- 关键行为
- Update 为空实现,完成标志直接由谓词结果决定
- 谓词必须稳定、无副作用,避免外部状态在 Update 期间被修改
- 性能
- 每帧一次谓词调用与一次布尔比较
- 建议谓词逻辑轻量,避免阻塞或昂贵运算
- 使用建议
- WaitUntil 适合“等待某事件发生”
- WaitWhile 适合“持续等待某条件不再满足”
flowchart TD
Enter(["Update 调用"]) --> Eval["读取谓词结果"]
Eval --> Cond{"完成条件满足?"}
Cond --> |是| Done(["标记完成"])
Cond --> |否| End(["保持等待"])
图表来源
章节来源
WaitForCoroutine 等待协程完成
- 功能概述
- 等待另一个协程完成,通常通过调度器的等待机制实现
- 关键行为
- Update 不改变状态,完成标志由外部标记(内部方法)控制
- 一般通过调度器的“等待协程”机制唤醒当前协程
- 使用建议
- 适合协程间同步执行顺序
- 注意避免自等待(调度器会拒绝)
章节来源
调度器与时间源
- CoroutineScheduler
- 管理协程槽位、元数据与等待集合,负责推进协程与处理异常
- 每帧更新时先驱动等待指令,再推进协程枚举器
- 支持暂停/恢复/终止/按标签终止/清空等控制
- ITimeSource/GodotTimeSource
- 提供 DeltaTime 与 CurrentTime,GodotTimeSource 从引擎获取增量时间并累加总时间
- Timing(Godot)
- 提供三段式调度:Process/PhysicsProcess/Deferred
- 每帧驱动对应调度器,支持延迟调用与协程控制 API
classDiagram
class IYieldInstruction {
+bool IsDone
+void Update(deltaTime)
}
class ITimeSource {
+double CurrentTime
+double DeltaTime
+void Update()
}
class Delay {
-double _remaining
+void Update(deltaTime)
+bool IsDone
}
class WaitOneFrame {
-bool _done
+void Update(deltaTime)
+bool IsDone
}
class WaitForFrames {
-int _remaining
+void Update(deltaTime)
+bool IsDone
}
class WaitUntil {
-Func~bool~ _predicate
+void Update(deltaTime)
+bool IsDone
}
class WaitWhile {
-Func~bool~ _predicate
+void Update(deltaTime)
+bool IsDone
}
class CoroutineScheduler {
+double DeltaTime
+int ActiveCoroutineCount
+Run(coroutine, tag)
+Update()
+Pause(handle)
+Resume(handle)
+Kill(handle)
+KillByTag(tag)
+Clear()
}
class GodotTimeSource {
+double CurrentTime
+double DeltaTime
+void Update()
+void Reset()
}
IYieldInstruction <|.. Delay
IYieldInstruction <|.. WaitOneFrame
IYieldInstruction <|.. WaitForFrames
IYieldInstruction <|.. WaitUntil
IYieldInstruction <|.. WaitWhile
ITimeSource <|.. GodotTimeSource
CoroutineScheduler --> ITimeSource : "使用"
CoroutineScheduler --> IYieldInstruction : "驱动"
图表来源
- IYieldInstruction.cs
- ITimeSource.cs
- Delay.cs
- WaitOneFrame.cs
- WaitForFrames.cs
- WaitUntil.cs
- WaitWhile.cs
- CoroutineScheduler.cs
- GodotTimeSource.cs
章节来源
- CoroutineScheduler.cs
- CoroutineSlot.cs
- CoroutineMetadata.cs
- CoroutineHandle.cs
- CoroutineHelper.cs
- GodotTimeSource.cs
- Timing.cs
依赖关系分析
- 组件耦合
- IYieldInstruction 是所有等待指令的统一契约,降低调度器与具体等待逻辑的耦合
- ITimeSource 抽象时间源,使调度器可在不同平台复用
- Timing 将调度器与 Godot 的生命周期绑定,提供三段式调度入口
- 外部依赖
- GodotTimeSource 依赖引擎提供的增量时间函数
- 调度器内部使用字典与数组管理协程,注意扩容与内存占用
- 循环依赖
- 未发现直接循环依赖;WaitForCoroutine 通过调度器等待集合间接关联
graph LR
IYield["IYieldInstruction"] --> Impl["各等待指令实现"]
ITime["ITimeSource"] --> Sch["CoroutineScheduler"]
Sch --> Slot["CoroutineSlot"]
Sch --> Meta["CoroutineMetadata"]
Sch --> Hdl["CoroutineHandle"]
Sch --> Help["CoroutineHelper"]
GTime["GodotTimeSource"] --> Sch
TimingN["Timing"] --> Sch
TimingN --> GTime
图表来源
- IYieldInstruction.cs
- ITimeSource.cs
- CoroutineScheduler.cs
- CoroutineSlot.cs
- CoroutineMetadata.cs
- CoroutineHandle.cs
- CoroutineHelper.cs
- GodotTimeSource.cs
- Timing.cs
章节来源
性能考量
- 时间型 vs 帧型 vs 条件型
- Delay:每帧一次浮点减法与比较,开销极低
- WaitOneFrame/WaitForFrames:整数递减与比较,开销极低
- WaitUntil/WaitWhile:每帧一次谓词调用与比较,开销取决于谓词复杂度
- 选择建议
- 需要与帧率解耦的延迟:优先 Delay
- 需要与渲染/物理节奏对齐:优先 WaitForFrames
- 需要语义化条件:优先 WaitUntil/WaitWhile
- 复杂等待组合:建议拆分为多个简单指令,便于调试与性能分析
- 优化技巧
- 将谓词逻辑尽量轻量化,避免在 Update 中执行昂贵操作
- 合理使用标签与句柄,避免大规模遍历
- 控制并发协程数量,防止调度器扩容与内存压力
[本节为通用性能指导,无需特定文件引用]
故障排查指南
- 常见问题
- 条件等待永不完成:检查谓词是否可终止,避免无限循环
- 帧等待异常提前完成:确认传入帧数为正整数,注意最小帧数约束
- 协程卡住:检查是否有未完成的等待指令或异常被捕获并导致提前结束
- 自等待异常:WaitForCoroutine 不允许等待自身
- 调试技巧
- 使用测试用例验证边界行为(零/负数秒、零帧、谓词瞬时满足)
- 在 Timing 节点中观察各段活跃协程数量
- 通过标签批量终止协程,定位问题协程
- 常见陷阱
- 在 Update 中修改共享状态导致谓词不稳定
- 忽略异常处理,导致协程意外结束
- 过度依赖高频率轮询,造成性能问题
章节来源
结论
等待指令系统通过清晰的接口抽象与可插拔时间源,提供了高效、可组合的协程等待能力。在 Godot 平台上,Timing 节点进一步将调度器与引擎生命周期绑定,支持多段式调度。合理选择等待指令、遵循实现规范与性能建议,可显著提升游戏开发中的异步流程可控性与稳定性。
[本节为总结性内容,无需特定文件引用]
附录
API 一览与使用建议
- Delay(seconds)
- 适用:通用延迟、节流、倒计时
- 建议:与 RepeatCall/RepeatCallForever 组合
- WaitOneFrame()
- 适用:严格帧序控制
- 建议:与动画/UI刷新节奏配合
- WaitForFrames(frames)
- 适用:与渲染/物理节奏对齐
- 建议:最小帧数为 1,避免 0/- 帧
- WaitUntil(predicate)
- 适用:等待事件发生
- 建议:谓词稳定、无副作用
- WaitWhile(predicate)
- 适用:持续等待条件不再满足
- 建议:谓词稳定、无副作用
- WaitForCoroutine()
- 适用:协程间同步
- 建议:避免自等待,使用调度器等待机制
章节来源
典型应用场景
- 动画同步:使用 WaitForFrames 对齐关键帧
- 延迟执行:使用 Delay 实现延迟调用
- 条件触发:使用 WaitUntil/WaitWhile 实现事件驱动
- 复杂等待组合:将多个简单等待指令串联,形成清晰的流程图
[本节为概念性内容,无需特定文件引用]