mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
feat(coroutine): 添加完整的协程系统实现
- 实现了协程调度器和句柄管理机制 - 添加了多种等待指令包括延时、帧数、条件等待等 - 创建了协程辅助方法和扩展功能 - 集成了Godot引擎的时间源和生命周期管理 - 实现了协程的暂停、恢复、终止等控制功能 - 添加了协程标签管理和按标签批量操作功能 - 提供了与Godot节点生命周期绑定的取消机制
This commit is contained in:
parent
16a72e85af
commit
ddbf7af572
32
GFramework.Core.Abstractions/coroutine/CoroutineState.cs
Normal file
32
GFramework.Core.Abstractions/coroutine/CoroutineState.cs
Normal file
@ -0,0 +1,32 @@
|
||||
namespace GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 表示协程的执行状态枚举
|
||||
/// </summary>
|
||||
public enum CoroutineState
|
||||
{
|
||||
/// <summary>
|
||||
/// 协程正在运行中
|
||||
/// </summary>
|
||||
Running,
|
||||
|
||||
/// <summary>
|
||||
/// 协程已暂停
|
||||
/// </summary>
|
||||
Paused,
|
||||
|
||||
/// <summary>
|
||||
/// 协程被锁定或等待其他协程完成
|
||||
/// </summary>
|
||||
Held,
|
||||
|
||||
/// <summary>
|
||||
/// 协程已完成执行
|
||||
/// </summary>
|
||||
Completed,
|
||||
|
||||
/// <summary>
|
||||
/// 协程已被取消
|
||||
/// </summary>
|
||||
Cancelled,
|
||||
}
|
||||
22
GFramework.Core.Abstractions/coroutine/ITimeSource.cs
Normal file
22
GFramework.Core.Abstractions/coroutine/ITimeSource.cs
Normal file
@ -0,0 +1,22 @@
|
||||
namespace GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 时间源接口,提供当前时间、时间增量以及更新功能
|
||||
/// </summary>
|
||||
public interface ITimeSource
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取当前时间
|
||||
/// </summary>
|
||||
double CurrentTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取时间增量(上一帧到当前帧的时间差)
|
||||
/// </summary>
|
||||
double DeltaTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新时间源的状态
|
||||
/// </summary>
|
||||
void Update();
|
||||
}
|
||||
18
GFramework.Core.Abstractions/coroutine/IYieldInstruction.cs
Normal file
18
GFramework.Core.Abstractions/coroutine/IYieldInstruction.cs
Normal file
@ -0,0 +1,18 @@
|
||||
namespace GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 定义一个可等待指令的接口,用于协程系统中的异步操作控制
|
||||
/// </summary>
|
||||
public interface IYieldInstruction
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取当前等待指令是否已完成执行
|
||||
/// </summary>
|
||||
bool IsDone { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 每帧由调度器调用,用于更新当前等待指令的状态
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">自上一帧以来的时间间隔(以秒为单位)</param>
|
||||
void Update(double deltaTime);
|
||||
}
|
||||
94
GFramework.Core/coroutine/CoroutineHandle.cs
Normal file
94
GFramework.Core/coroutine/CoroutineHandle.cs
Normal file
@ -0,0 +1,94 @@
|
||||
namespace GFramework.Core.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 协程句柄
|
||||
/// 用于唯一标识和管理协程实例的结构体,通过ID系统实现协程的跟踪和比较功能
|
||||
/// </summary>
|
||||
public readonly struct CoroutineHandle : IEquatable<CoroutineHandle>
|
||||
{
|
||||
/// <summary>
|
||||
/// 预留空间常量,用于ID分配的基数
|
||||
/// </summary>
|
||||
private const byte ReservedSpace = 0x0F;
|
||||
|
||||
/// <summary>
|
||||
/// 下一个索引数组,用于跟踪每个实例ID的下一个可用索引位置
|
||||
/// 索引范围:0-15,对应16个不同的实例槽位
|
||||
/// </summary>
|
||||
private static readonly int[] NextIndex = new int[16];
|
||||
|
||||
/// <summary>
|
||||
/// 协程句柄的内部ID,用于唯一标识协程实例
|
||||
/// </summary>
|
||||
private readonly int _id;
|
||||
|
||||
/// <summary>
|
||||
/// 静态构造函数,初始化NextIndex数组的默认值
|
||||
/// 将索引0的下一个可用位置设置为ReservedSpace + 1
|
||||
/// </summary>
|
||||
static CoroutineHandle()
|
||||
{
|
||||
NextIndex[0] = ReservedSpace + 1;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前协程句柄的键值(低4位)
|
||||
/// </summary>
|
||||
public byte Key => (byte)(_id & ReservedSpace);
|
||||
|
||||
/// <summary>
|
||||
/// 判断当前协程句柄是否有效
|
||||
/// 有效性通过Key是否为0来判断
|
||||
/// </summary>
|
||||
public bool IsValid => Key != 0;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数,创建一个新的协程句柄
|
||||
/// </summary>
|
||||
/// <param name="instanceId">实例ID,用于区分不同的协程实例槽位</param>
|
||||
public CoroutineHandle(byte instanceId)
|
||||
{
|
||||
if (instanceId > ReservedSpace)
|
||||
instanceId -= ReservedSpace;
|
||||
|
||||
_id = NextIndex[instanceId] + instanceId;
|
||||
NextIndex[instanceId] += ReservedSpace + 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 比较当前协程句柄与另一个协程句柄是否相等
|
||||
/// </summary>
|
||||
/// <param name="other">要比较的协程句柄</param>
|
||||
/// <returns>如果两个句柄的ID相同则返回true,否则返回false</returns>
|
||||
public bool Equals(CoroutineHandle other) => _id == other._id;
|
||||
|
||||
/// <summary>
|
||||
/// 比较当前对象与指定对象是否相等
|
||||
/// </summary>
|
||||
/// <param name="obj">要比较的对象</param>
|
||||
/// <returns>如果对象是协程句柄且ID相同则返回true,否则返回false</returns>
|
||||
public override bool Equals(object? obj) => obj is CoroutineHandle handle && Equals(handle);
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前协程句柄的哈希码
|
||||
/// </summary>
|
||||
/// <returns>基于内部ID计算的哈希码</returns>
|
||||
public override int GetHashCode() => _id;
|
||||
|
||||
/// <summary>
|
||||
/// 比较两个协程句柄是否相等
|
||||
/// </summary>
|
||||
/// <param name="a">第一个协程句柄</param>
|
||||
/// <param name="b">第二个协程句柄</param>
|
||||
/// <returns>如果两个句柄的ID相同则返回true,否则返回false</returns>
|
||||
public static bool operator ==(CoroutineHandle a, CoroutineHandle b) => a._id == b._id;
|
||||
|
||||
/// <summary>
|
||||
/// 比较两个协程句柄是否不相等
|
||||
/// </summary>
|
||||
/// <param name="a">第一个协程句柄</param>
|
||||
/// <param name="b">第二个协程句柄</param>
|
||||
/// <returns>如果两个句柄的ID不同则返回true,否则返回false</returns>
|
||||
public static bool operator !=(CoroutineHandle a, CoroutineHandle b) => a._id != b._id;
|
||||
}
|
||||
101
GFramework.Core/coroutine/CoroutineHelper.cs
Normal file
101
GFramework.Core/coroutine/CoroutineHelper.cs
Normal file
@ -0,0 +1,101 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 协程辅助方法
|
||||
/// </summary>
|
||||
public static class CoroutineHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 等待指定秒数
|
||||
/// </summary>
|
||||
/// <param name="seconds">要等待的秒数</param>
|
||||
/// <returns>延迟等待指令</returns>
|
||||
public static Delay WaitForSeconds(double seconds)
|
||||
{
|
||||
return new Delay(seconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 等待一帧
|
||||
/// </summary>
|
||||
/// <returns>等待一帧的指令</returns>
|
||||
public static WaitOneFrame WaitForOneFrame()
|
||||
{
|
||||
return new WaitOneFrame();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 等待指定帧数
|
||||
/// </summary>
|
||||
/// <param name="frames">要等待的帧数</param>
|
||||
/// <returns>等待帧数指令</returns>
|
||||
public static WaitForFrames WaitForFrames(int frames)
|
||||
{
|
||||
return new WaitForFrames(frames);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 等待直到条件满足
|
||||
/// </summary>
|
||||
/// <param name="predicate">条件判断函数</param>
|
||||
/// <returns>等待条件指令</returns>
|
||||
public static WaitUntil WaitUntil(Func<bool> predicate)
|
||||
{
|
||||
return new WaitUntil(predicate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 等待当条件为真时持续等待
|
||||
/// </summary>
|
||||
/// <param name="predicate">条件判断函数</param>
|
||||
/// <returns>等待条件指令</returns>
|
||||
public static WaitWhile WaitWhile(Func<bool> predicate)
|
||||
{
|
||||
return new WaitWhile(predicate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 延迟调用指定的委托
|
||||
/// </summary>
|
||||
/// <param name="delay">延迟时间(秒)</param>
|
||||
/// <param name="action">要执行的动作委托</param>
|
||||
/// <returns>返回一个枚举器,用于协程执行</returns>
|
||||
public static IEnumerator<IYieldInstruction> DelayedCall(double delay, Action? action)
|
||||
{
|
||||
yield return new Delay(delay);
|
||||
action?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重复调用指定的委托指定次数
|
||||
/// </summary>
|
||||
/// <param name="interval">每次调用之间的间隔时间(秒)</param>
|
||||
/// <param name="count">调用次数</param>
|
||||
/// <param name="action">要执行的动作委托</param>
|
||||
/// <returns>返回一个枚举器,用于协程执行</returns>
|
||||
public static IEnumerator<IYieldInstruction> RepeatCall(double interval, int count, Action? action)
|
||||
{
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
action?.Invoke();
|
||||
yield return new Delay(interval);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 无限重复调用指定的委托
|
||||
/// </summary>
|
||||
/// <param name="interval">每次调用之间的间隔时间(秒)</param>
|
||||
/// <param name="action">要执行的动作委托</param>
|
||||
/// <returns>返回一个枚举器,用于协程执行</returns>
|
||||
public static IEnumerator<IYieldInstruction> RepeatCallForever(double interval, Action? action)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
action?.Invoke();
|
||||
yield return new Delay(interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
32
GFramework.Core/coroutine/CoroutineMetadata.cs
Normal file
32
GFramework.Core/coroutine/CoroutineMetadata.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 存储协程元数据信息的内部类,包含协程的状态、枚举器、标签等信息
|
||||
/// </summary>
|
||||
internal class CoroutineMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// 协程在调度器中的槽位索引
|
||||
/// </summary>
|
||||
public int SlotIndex;
|
||||
|
||||
/// <summary>
|
||||
/// 协程当前的执行状态
|
||||
/// </summary>
|
||||
public CoroutineState State;
|
||||
|
||||
/// <summary>
|
||||
/// 协程的标签标识符,用于协程的分类和查找
|
||||
/// </summary>
|
||||
public string? Tag;
|
||||
|
||||
/// <summary>
|
||||
/// 判断协程是否处于活跃状态(运行中、暂停或挂起)
|
||||
/// </summary>
|
||||
public bool IsActive =>
|
||||
State is CoroutineState.Running
|
||||
or CoroutineState.Paused
|
||||
or CoroutineState.Held;
|
||||
}
|
||||
392
GFramework.Core/coroutine/CoroutineScheduler.cs
Normal file
392
GFramework.Core/coroutine/CoroutineScheduler.cs
Normal file
@ -0,0 +1,392 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 协程调度器,用于管理和执行协程
|
||||
/// </summary>
|
||||
/// <param name="timeSource">时间源接口,提供时间相关数据</param>
|
||||
/// <param name="instanceId">实例ID,默认为1</param>
|
||||
/// <param name="initialCapacity">初始容量,默认为256</param>
|
||||
public sealed class CoroutineScheduler(
|
||||
ITimeSource timeSource,
|
||||
byte instanceId = 1,
|
||||
int initialCapacity = 256)
|
||||
{
|
||||
private readonly Dictionary<CoroutineHandle, CoroutineMetadata> _metadata = new();
|
||||
private readonly Dictionary<string, HashSet<CoroutineHandle>> _tagged = new();
|
||||
private readonly ITimeSource _timeSource = timeSource ?? throw new ArgumentNullException(nameof(timeSource));
|
||||
private readonly Dictionary<CoroutineHandle, HashSet<CoroutineHandle>> _waiting = new();
|
||||
private int _activeCount;
|
||||
private int _nextSlot;
|
||||
|
||||
private CoroutineSlot?[] _slots = new CoroutineSlot?[initialCapacity];
|
||||
|
||||
/// <summary>
|
||||
/// 获取时间差值
|
||||
/// </summary>
|
||||
public double DeltaTime => _timeSource.DeltaTime;
|
||||
|
||||
/// <summary>
|
||||
/// 获取活跃协程数量
|
||||
/// </summary>
|
||||
public int ActiveCoroutineCount => _activeCount;
|
||||
|
||||
#region Run / Update
|
||||
|
||||
/// <summary>
|
||||
/// 运行协程
|
||||
/// </summary>
|
||||
/// <param name="coroutine">要运行的协程枚举器</param>
|
||||
/// <param name="tag">协程标签,可选</param>
|
||||
/// <returns>协程句柄</returns>
|
||||
public CoroutineHandle Run(
|
||||
IEnumerator<IYieldInstruction>? coroutine,
|
||||
string? tag = null)
|
||||
{
|
||||
if (coroutine == null)
|
||||
return default;
|
||||
|
||||
if (_nextSlot >= _slots.Length)
|
||||
Expand();
|
||||
|
||||
var handle = new CoroutineHandle(instanceId);
|
||||
var slotIndex = _nextSlot++;
|
||||
|
||||
var slot = new CoroutineSlot
|
||||
{
|
||||
Enumerator = coroutine,
|
||||
State = CoroutineState.Running
|
||||
};
|
||||
|
||||
_slots[slotIndex] = slot;
|
||||
_metadata[handle] = new CoroutineMetadata
|
||||
{
|
||||
SlotIndex = slotIndex,
|
||||
State = CoroutineState.Running,
|
||||
Tag = tag
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(tag))
|
||||
AddTag(tag, handle);
|
||||
|
||||
Prewarm(slotIndex);
|
||||
_activeCount++;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新所有协程状态
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
_timeSource.Update();
|
||||
var delta = _timeSource.DeltaTime;
|
||||
|
||||
// 遍历所有槽位并更新协程状态
|
||||
for (var i = 0; i < _nextSlot; i++)
|
||||
{
|
||||
var slot = _slots[i];
|
||||
if (slot == null || slot.State != CoroutineState.Running)
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
// 1️⃣ 处理等待指令
|
||||
if (slot.Waiting != null)
|
||||
{
|
||||
slot.Waiting.Update(delta);
|
||||
if (!slot.Waiting.IsDone)
|
||||
continue;
|
||||
|
||||
slot.Waiting = null;
|
||||
}
|
||||
|
||||
// 2️⃣ 推进协程
|
||||
if (!slot.Enumerator.MoveNext())
|
||||
{
|
||||
Complete(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
slot.Waiting = slot.Enumerator.Current;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnError(i, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Pause / Resume / Kill
|
||||
|
||||
/// <summary>
|
||||
/// 暂停指定协程
|
||||
/// </summary>
|
||||
/// <param name="handle">协程句柄</param>
|
||||
/// <returns>是否成功暂停</returns>
|
||||
public bool Pause(CoroutineHandle handle)
|
||||
{
|
||||
if (!_metadata.TryGetValue(handle, out var meta))
|
||||
return false;
|
||||
|
||||
var slot = _slots[meta.SlotIndex];
|
||||
if (slot == null || slot.State != CoroutineState.Running)
|
||||
return false;
|
||||
|
||||
slot.State = CoroutineState.Paused;
|
||||
meta.State = CoroutineState.Paused;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 恢复指定协程
|
||||
/// </summary>
|
||||
/// <param name="handle">协程句柄</param>
|
||||
/// <returns>是否成功恢复</returns>
|
||||
public bool Resume(CoroutineHandle handle)
|
||||
{
|
||||
if (!_metadata.TryGetValue(handle, out var meta))
|
||||
return false;
|
||||
|
||||
var slot = _slots[meta.SlotIndex];
|
||||
if (slot == null || slot.State != CoroutineState.Paused)
|
||||
return false;
|
||||
|
||||
slot.State = CoroutineState.Running;
|
||||
meta.State = CoroutineState.Running;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 终止指定协程
|
||||
/// </summary>
|
||||
/// <param name="handle">协程句柄</param>
|
||||
/// <returns>是否成功终止</returns>
|
||||
public bool Kill(CoroutineHandle handle)
|
||||
{
|
||||
if (!_metadata.TryGetValue(handle, out var meta))
|
||||
return false;
|
||||
|
||||
Complete(meta.SlotIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Wait / Tag / Clear
|
||||
|
||||
/// <summary>
|
||||
/// 让当前协程等待目标协程完成
|
||||
/// </summary>
|
||||
/// <param name="current">当前协程句柄</param>
|
||||
/// <param name="target">目标协程句柄</param>
|
||||
public void WaitForCoroutine(
|
||||
CoroutineHandle current,
|
||||
CoroutineHandle target)
|
||||
{
|
||||
if (current == target)
|
||||
throw new InvalidOperationException("Coroutine cannot wait for itself.");
|
||||
|
||||
if (!_metadata.ContainsKey(target))
|
||||
return;
|
||||
|
||||
if (_metadata.TryGetValue(current, out var meta))
|
||||
{
|
||||
var slot = _slots[meta.SlotIndex];
|
||||
if (slot != null)
|
||||
{
|
||||
slot.State = CoroutineState.Held;
|
||||
meta.State = CoroutineState.Held;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_waiting.TryGetValue(target, out var set))
|
||||
{
|
||||
set = new HashSet<CoroutineHandle>();
|
||||
_waiting[target] = set;
|
||||
}
|
||||
|
||||
set.Add(current);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据标签终止协程
|
||||
/// </summary>
|
||||
/// <param name="tag">协程标签</param>
|
||||
/// <returns>被终止的协程数量</returns>
|
||||
public int KillByTag(string tag)
|
||||
{
|
||||
if (!_tagged.TryGetValue(tag, out var handles))
|
||||
return 0;
|
||||
|
||||
var copy = handles.ToArray();
|
||||
var count = 0;
|
||||
|
||||
foreach (var h in copy)
|
||||
if (Kill(h))
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空所有协程
|
||||
/// </summary>
|
||||
/// <returns>被清除的协程数量</returns>
|
||||
public int Clear()
|
||||
{
|
||||
var count = _activeCount;
|
||||
|
||||
Array.Clear(_slots);
|
||||
_metadata.Clear();
|
||||
_tagged.Clear();
|
||||
_waiting.Clear();
|
||||
|
||||
_nextSlot = 0;
|
||||
_activeCount = 0;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal
|
||||
|
||||
/// <summary>
|
||||
/// 预热协程槽位,执行协程的第一步
|
||||
/// </summary>
|
||||
/// <param name="slotIndex">槽位索引</param>
|
||||
private void Prewarm(int slotIndex)
|
||||
{
|
||||
var slot = _slots[slotIndex];
|
||||
if (slot == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (!slot.Enumerator.MoveNext())
|
||||
{
|
||||
Complete(slotIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
slot.Waiting = slot.Enumerator.Current;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnError(slotIndex, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 完成指定槽位的协程
|
||||
/// </summary>
|
||||
/// <param name="slotIndex">槽位索引</param>
|
||||
private void Complete(int slotIndex)
|
||||
{
|
||||
var slot = _slots[slotIndex];
|
||||
if (slot == null)
|
||||
return;
|
||||
|
||||
_slots[slotIndex] = null;
|
||||
_activeCount--;
|
||||
|
||||
CoroutineHandle handle = default;
|
||||
foreach (var kv in _metadata)
|
||||
{
|
||||
if (kv.Value.SlotIndex == slotIndex)
|
||||
{
|
||||
handle = kv.Key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!handle.IsValid)
|
||||
return;
|
||||
|
||||
RemoveTag(handle);
|
||||
_metadata.Remove(handle);
|
||||
|
||||
// 唤醒等待者
|
||||
if (_waiting.TryGetValue(handle, out var waiters))
|
||||
{
|
||||
foreach (var waiter in waiters)
|
||||
{
|
||||
if (_metadata.TryGetValue(waiter, out var meta))
|
||||
{
|
||||
var s = _slots[meta.SlotIndex];
|
||||
if (s != null)
|
||||
{
|
||||
s.State = CoroutineState.Running;
|
||||
meta.State = CoroutineState.Running;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_waiting.Remove(handle);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理协程执行中的错误
|
||||
/// </summary>
|
||||
/// <param name="slotIndex">槽位索引</param>
|
||||
/// <param name="ex">异常对象</param>
|
||||
private void OnError(int slotIndex, Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine(ex);
|
||||
Complete(slotIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 扩展协程槽位数组容量
|
||||
/// </summary>
|
||||
private void Expand()
|
||||
{
|
||||
Array.Resize(ref _slots, _slots.Length * 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为协程添加标签
|
||||
/// </summary>
|
||||
/// <param name="tag">标签名称</param>
|
||||
/// <param name="handle">协程句柄</param>
|
||||
private void AddTag(string tag, CoroutineHandle handle)
|
||||
{
|
||||
if (!_tagged.TryGetValue(tag, out var set))
|
||||
{
|
||||
set = new HashSet<CoroutineHandle>();
|
||||
_tagged[tag] = set;
|
||||
}
|
||||
|
||||
set.Add(handle);
|
||||
_metadata[handle].Tag = tag;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除协程标签
|
||||
/// </summary>
|
||||
/// <param name="handle">协程句柄</param>
|
||||
private void RemoveTag(CoroutineHandle handle)
|
||||
{
|
||||
if (!_metadata.TryGetValue(handle, out var meta) || meta.Tag == null)
|
||||
return;
|
||||
|
||||
if (_tagged.TryGetValue(meta.Tag, out var set))
|
||||
{
|
||||
set.Remove(handle);
|
||||
if (set.Count == 0)
|
||||
_tagged.Remove(meta.Tag);
|
||||
}
|
||||
|
||||
meta.Tag = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
24
GFramework.Core/coroutine/CoroutineSlot.cs
Normal file
24
GFramework.Core/coroutine/CoroutineSlot.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 协程槽位类,用于管理单个协程的执行状态和调度信息
|
||||
/// </summary>
|
||||
internal sealed class CoroutineSlot
|
||||
{
|
||||
/// <summary>
|
||||
/// 协程枚举器,包含协程的执行逻辑
|
||||
/// </summary>
|
||||
public required IEnumerator<IYieldInstruction> Enumerator;
|
||||
|
||||
/// <summary>
|
||||
/// 协程当前状态
|
||||
/// </summary>
|
||||
public CoroutineState State;
|
||||
|
||||
/// <summary>
|
||||
/// 当前等待的指令,用于控制协程的暂停和恢复
|
||||
/// </summary>
|
||||
public IYieldInstruction? Waiting;
|
||||
}
|
||||
29
GFramework.Core/coroutine/Delay.cs
Normal file
29
GFramework.Core/coroutine/Delay.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 延迟等待指令,实现IYieldInstruction接口,用于协程中的时间延迟
|
||||
/// </summary>
|
||||
/// <param name="seconds">需要延迟的秒数</param>
|
||||
public sealed class Delay(double seconds) : IYieldInstruction
|
||||
{
|
||||
/// <summary>
|
||||
/// 剩余等待时间
|
||||
/// </summary>
|
||||
private double _remaining = Math.Max(0, seconds);
|
||||
|
||||
/// <summary>
|
||||
/// 更新延迟计时器
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">时间增量</param>
|
||||
public void Update(double deltaTime)
|
||||
{
|
||||
_remaining -= deltaTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取延迟是否完成
|
||||
/// </summary>
|
||||
public bool IsDone => _remaining <= 0;
|
||||
}
|
||||
29
GFramework.Core/coroutine/WaitForCoroutine.cs
Normal file
29
GFramework.Core/coroutine/WaitForCoroutine.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 等待协程完成的指令类,实现IYieldInstruction接口
|
||||
/// </summary>
|
||||
public sealed class WaitForCoroutine : IYieldInstruction
|
||||
{
|
||||
private bool _done;
|
||||
|
||||
/// <summary>
|
||||
/// 更新方法,用于处理时间更新逻辑
|
||||
/// </summary>
|
||||
/// <param name="delta">时间增量</param>
|
||||
public void Update(double delta)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取协程是否已完成的状态
|
||||
/// </summary>
|
||||
public bool IsDone => _done;
|
||||
|
||||
/// <summary>
|
||||
/// 内部方法,用于标记协程完成状态
|
||||
/// </summary>
|
||||
internal void Complete() => _done = true;
|
||||
}
|
||||
29
GFramework.Core/coroutine/WaitForFrames.cs
Normal file
29
GFramework.Core/coroutine/WaitForFrames.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 等待指定帧数的等待指令类
|
||||
/// </summary>
|
||||
/// <param name="frames">需要等待的帧数,最小值为1</param>
|
||||
public sealed class WaitForFrames(int frames) : IYieldInstruction
|
||||
{
|
||||
/// <summary>
|
||||
/// 剩余等待帧数
|
||||
/// </summary>
|
||||
private int _remaining = Math.Max(1, frames);
|
||||
|
||||
/// <summary>
|
||||
/// 更新方法,在每一帧调用时减少剩余帧数
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">时间间隔(秒)</param>
|
||||
public void Update(double deltaTime)
|
||||
{
|
||||
_remaining--;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取等待是否完成的状态
|
||||
/// </summary>
|
||||
public bool IsDone => _remaining <= 0;
|
||||
}
|
||||
26
GFramework.Core/coroutine/WaitOneFrame.cs
Normal file
26
GFramework.Core/coroutine/WaitOneFrame.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 表示等待一帧的等待指令实现
|
||||
/// 实现IYieldInstruction接口,用于协程中等待一个游戏帧的执行
|
||||
/// </summary>
|
||||
public sealed class WaitOneFrame : IYieldInstruction
|
||||
{
|
||||
private bool _done;
|
||||
|
||||
/// <summary>
|
||||
/// 更新方法,在每一帧被调用时将完成状态设置为true
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">时间间隔,表示当前帧与上一帧的时间差</param>
|
||||
public void Update(double deltaTime)
|
||||
{
|
||||
_done = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前等待指令是否已完成
|
||||
/// </summary>
|
||||
public bool IsDone => _done;
|
||||
}
|
||||
26
GFramework.Core/coroutine/WaitUntil.cs
Normal file
26
GFramework.Core/coroutine/WaitUntil.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个等待直到指定条件满足的协程指令
|
||||
/// </summary>
|
||||
/// <param name="predicate">用于判断条件是否满足的函数委托</param>
|
||||
public sealed class WaitUntil(Func<bool> predicate) : IYieldInstruction
|
||||
{
|
||||
private readonly Func<bool> _predicate = predicate ?? throw new ArgumentNullException(nameof(predicate));
|
||||
|
||||
/// <summary>
|
||||
/// 更新协程状态(此实现中不需要处理时间)
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">时间增量</param>
|
||||
public void Update(double deltaTime)
|
||||
{
|
||||
// 不需要时间
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取协程指令是否已完成
|
||||
/// </summary>
|
||||
public bool IsDone => _predicate();
|
||||
}
|
||||
26
GFramework.Core/coroutine/WaitWhile.cs
Normal file
26
GFramework.Core/coroutine/WaitWhile.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个等待条件为假时才完成的协程指令
|
||||
/// </summary>
|
||||
/// <param name="predicate">用于判断是否继续等待的条件函数,当返回true时继续等待,返回false时完成</param>
|
||||
public sealed class WaitWhile(Func<bool> predicate) : IYieldInstruction
|
||||
{
|
||||
private readonly Func<bool> _predicate = predicate ?? throw new ArgumentNullException(nameof(predicate));
|
||||
|
||||
/// <summary>
|
||||
/// 更新协程状态(此实现中为空方法)
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">时间增量</param>
|
||||
public void Update(double deltaTime)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取协程指令是否已完成
|
||||
/// 当谓词函数返回false时,表示条件不再满足,指令完成
|
||||
/// </summary>
|
||||
public bool IsDone => !_predicate();
|
||||
}
|
||||
66
GFramework.Godot/coroutine/CoroutineExtensions.cs
Normal file
66
GFramework.Godot/coroutine/CoroutineExtensions.cs
Normal file
@ -0,0 +1,66 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
using GFramework.Core.coroutine;
|
||||
using Godot;
|
||||
|
||||
namespace GFramework.Godot.coroutine;
|
||||
|
||||
public static class CoroutineExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动协程的扩展方法
|
||||
/// </summary>
|
||||
public static CoroutineHandle RunCoroutine(
|
||||
this IEnumerator<IYieldInstruction> coroutine,
|
||||
Segment segment = Segment.Process,
|
||||
string? tag = null)
|
||||
{
|
||||
return Timing.RunCoroutine(coroutine, segment, tag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 让协程在指定节点被销毁时自动取消
|
||||
/// </summary>
|
||||
public static IEnumerator<IYieldInstruction> CancelWith(
|
||||
this IEnumerator<IYieldInstruction> coroutine,
|
||||
Node node)
|
||||
{
|
||||
while (Timing.IsNodeAlive(node) && coroutine.MoveNext())
|
||||
yield return coroutine.Current;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 让协程在任一节点被销毁时自动取消
|
||||
/// </summary>
|
||||
public static IEnumerator<IYieldInstruction> CancelWith(
|
||||
this IEnumerator<IYieldInstruction> coroutine,
|
||||
Node node1,
|
||||
Node node2)
|
||||
{
|
||||
while (Timing.IsNodeAlive(node1) &&
|
||||
Timing.IsNodeAlive(node2) &&
|
||||
coroutine.MoveNext())
|
||||
yield return coroutine.Current;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 让协程在多个节点都被销毁时自动取消
|
||||
/// </summary>
|
||||
public static IEnumerator<IYieldInstruction> CancelWith(
|
||||
this IEnumerator<IYieldInstruction> coroutine,
|
||||
params Node[] nodes)
|
||||
{
|
||||
while (AllNodesAlive(nodes) && coroutine.MoveNext())
|
||||
yield return coroutine.Current;
|
||||
}
|
||||
|
||||
private static bool AllNodesAlive(Node[] nodes)
|
||||
{
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
if (!Timing.IsNodeAlive(node))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
44
GFramework.Godot/coroutine/GodotTimeSource.cs
Normal file
44
GFramework.Godot/coroutine/GodotTimeSource.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Godot.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// Godot时间源实现,用于提供基于Godot引擎的时间信息
|
||||
/// </summary>
|
||||
/// <param name="getDeltaFunc">获取增量时间的函数委托</param>
|
||||
public class GodotTimeSource(Func<double> getDeltaFunc) : ITimeSource
|
||||
{
|
||||
private readonly Func<double> _getDeltaFunc = getDeltaFunc ?? throw new ArgumentNullException(nameof(getDeltaFunc));
|
||||
private double _currentTime;
|
||||
private double _deltaTime;
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前累计时间
|
||||
/// </summary>
|
||||
public double CurrentTime => _currentTime;
|
||||
|
||||
/// <summary>
|
||||
/// 获取上一帧的时间增量
|
||||
/// </summary>
|
||||
public double DeltaTime => _deltaTime;
|
||||
|
||||
/// <summary>
|
||||
/// 更新时间源,计算新的增量时间和累计时间
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
// 调用外部提供的函数获取当前帧的时间增量
|
||||
_deltaTime = _getDeltaFunc();
|
||||
// 累加到总时间中
|
||||
_currentTime += _deltaTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置时间源到初始状态
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
_currentTime = 0;
|
||||
_deltaTime = 0;
|
||||
}
|
||||
}
|
||||
22
GFramework.Godot/coroutine/Segment.cs
Normal file
22
GFramework.Godot/coroutine/Segment.cs
Normal file
@ -0,0 +1,22 @@
|
||||
namespace GFramework.Godot.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 定义协程执行的不同时间段枚举
|
||||
/// </summary>
|
||||
public enum Segment
|
||||
{
|
||||
/// <summary>
|
||||
/// 普通处理阶段,在每一帧的常规处理过程中执行
|
||||
/// </summary>
|
||||
Process,
|
||||
|
||||
/// <summary>
|
||||
/// 物理处理阶段,在物理更新循环中执行,通常用于需要与物理引擎同步的操作
|
||||
/// </summary>
|
||||
PhysicsProcess,
|
||||
|
||||
/// <summary>
|
||||
/// 延迟处理阶段,在当前帧结束后延迟执行,通常用于需要等待当前帧完成后再执行的操作
|
||||
/// </summary>
|
||||
DeferredProcess
|
||||
}
|
||||
374
GFramework.Godot/coroutine/Timing.cs
Normal file
374
GFramework.Godot/coroutine/Timing.cs
Normal file
@ -0,0 +1,374 @@
|
||||
using System.Reflection;
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
using GFramework.Core.coroutine;
|
||||
using Godot;
|
||||
|
||||
namespace GFramework.Godot.coroutine;
|
||||
|
||||
public partial class Timing : Node
|
||||
{
|
||||
private static Timing? _instance;
|
||||
private static readonly Timing?[] ActiveInstances = new Timing?[16];
|
||||
private CoroutineScheduler _deferredScheduler;
|
||||
private GodotTimeSource? _deferredTimeSource;
|
||||
private ushort _frameCounter;
|
||||
|
||||
private byte _instanceId = 1;
|
||||
private CoroutineScheduler _physicsScheduler;
|
||||
private GodotTimeSource? _physicsTimeSource;
|
||||
|
||||
private CoroutineScheduler _processScheduler;
|
||||
|
||||
private GodotTimeSource? _processTimeSource;
|
||||
|
||||
#region 单例
|
||||
|
||||
public static Timing Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance != null)
|
||||
return _instance;
|
||||
|
||||
var tree = (SceneTree)Engine.GetMainLoop();
|
||||
_instance = tree.Root.GetNodeOrNull<Timing>(nameof(Timing));
|
||||
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new Timing
|
||||
{
|
||||
Name = nameof(Timing)
|
||||
};
|
||||
tree.Root.AddChild(_instance);
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Debug 信息
|
||||
|
||||
public int ProcessCoroutines => _processScheduler?.ActiveCoroutineCount ?? 0;
|
||||
|
||||
public int PhysicsCoroutines => _physicsScheduler?.ActiveCoroutineCount ?? 0;
|
||||
|
||||
public int DeferredCoroutines => _deferredScheduler?.ActiveCoroutineCount ?? 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 生命周期
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
ProcessPriority = -1;
|
||||
|
||||
TrySetPhysicsPriority(-1);
|
||||
|
||||
InitializeSchedulers();
|
||||
RegisterInstance();
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
if (_instanceId < ActiveInstances.Length)
|
||||
ActiveInstances[_instanceId] = null;
|
||||
|
||||
CleanupInstanceIfNecessary();
|
||||
}
|
||||
|
||||
private static void CleanupInstanceIfNecessary()
|
||||
{
|
||||
_instance = null;
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
_processScheduler?.Update();
|
||||
_frameCounter++;
|
||||
|
||||
CallDeferred(nameof(ProcessDeferred));
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
_physicsScheduler?.Update();
|
||||
}
|
||||
|
||||
private void ProcessDeferred()
|
||||
{
|
||||
_deferredScheduler?.Update();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 初始化
|
||||
|
||||
private void InitializeSchedulers()
|
||||
{
|
||||
_processTimeSource = new GodotTimeSource(GetProcessDeltaTime);
|
||||
_physicsTimeSource = new GodotTimeSource(GetPhysicsProcessDeltaTime);
|
||||
_deferredTimeSource = new GodotTimeSource(GetProcessDeltaTime);
|
||||
|
||||
_processScheduler = new CoroutineScheduler(
|
||||
_processTimeSource,
|
||||
_instanceId,
|
||||
initialCapacity: 256
|
||||
);
|
||||
|
||||
_physicsScheduler = new CoroutineScheduler(
|
||||
_physicsTimeSource,
|
||||
_instanceId,
|
||||
initialCapacity: 128
|
||||
);
|
||||
|
||||
_deferredScheduler = new CoroutineScheduler(
|
||||
_deferredTimeSource,
|
||||
_instanceId,
|
||||
initialCapacity: 64
|
||||
);
|
||||
}
|
||||
|
||||
private void RegisterInstance()
|
||||
{
|
||||
if (ActiveInstances[_instanceId] == null)
|
||||
{
|
||||
ActiveInstances[_instanceId] = this;
|
||||
return;
|
||||
}
|
||||
|
||||
for (byte i = 1; i < ActiveInstances.Length; i++)
|
||||
{
|
||||
if (ActiveInstances[i] == null)
|
||||
{
|
||||
_instanceId = i;
|
||||
ActiveInstances[i] = this;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new OverflowException("最多只能存在 15 个 Timing 实例");
|
||||
}
|
||||
|
||||
private static void TrySetPhysicsPriority(int priority)
|
||||
{
|
||||
try
|
||||
{
|
||||
typeof(Node)
|
||||
.GetProperty(
|
||||
"ProcessPhysicsPriority",
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.Public)
|
||||
?.SetValue(Instance, priority);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 协程启动 API
|
||||
|
||||
public static CoroutineHandle RunCoroutine(
|
||||
IEnumerator<IYieldInstruction> coroutine,
|
||||
Segment segment = Segment.Process,
|
||||
string? tag = null)
|
||||
{
|
||||
return Instance.RunCoroutineOnInstance(coroutine, segment, tag);
|
||||
}
|
||||
|
||||
public CoroutineHandle RunCoroutineOnInstance(
|
||||
IEnumerator<IYieldInstruction>? coroutine,
|
||||
Segment segment = Segment.Process,
|
||||
string? tag = null)
|
||||
{
|
||||
if (coroutine == null)
|
||||
return default;
|
||||
|
||||
return segment switch
|
||||
{
|
||||
Segment.Process => _processScheduler.Run(coroutine, tag),
|
||||
Segment.PhysicsProcess => _physicsScheduler.Run(coroutine, tag),
|
||||
Segment.DeferredProcess => _deferredScheduler.Run(coroutine, tag),
|
||||
_ => default
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 协程控制 API
|
||||
|
||||
public static bool PauseCoroutine(CoroutineHandle handle)
|
||||
{
|
||||
return GetInstance(handle.Key)?.PauseOnInstance(handle) ?? false;
|
||||
}
|
||||
|
||||
public static bool ResumeCoroutine(CoroutineHandle handle)
|
||||
{
|
||||
return GetInstance(handle.Key)?.ResumeOnInstance(handle) ?? false;
|
||||
}
|
||||
|
||||
public static bool KillCoroutine(CoroutineHandle handle)
|
||||
{
|
||||
return GetInstance(handle.Key)?.KillOnInstance(handle) ?? false;
|
||||
}
|
||||
|
||||
public static int KillCoroutines(string tag)
|
||||
{
|
||||
return Instance.KillByTagOnInstance(tag);
|
||||
}
|
||||
|
||||
public static int KillAllCoroutines()
|
||||
{
|
||||
return Instance.ClearOnInstance();
|
||||
}
|
||||
|
||||
private bool PauseOnInstance(CoroutineHandle handle)
|
||||
{
|
||||
return _processScheduler.Pause(handle)
|
||||
|| _physicsScheduler.Pause(handle)
|
||||
|| _deferredScheduler.Pause(handle);
|
||||
}
|
||||
|
||||
private bool ResumeOnInstance(CoroutineHandle handle)
|
||||
{
|
||||
return _processScheduler.Resume(handle)
|
||||
|| _physicsScheduler.Resume(handle)
|
||||
|| _deferredScheduler.Resume(handle);
|
||||
}
|
||||
|
||||
private bool KillOnInstance(CoroutineHandle handle)
|
||||
{
|
||||
return _processScheduler.Kill(handle)
|
||||
|| _physicsScheduler.Kill(handle)
|
||||
|| _deferredScheduler.Kill(handle);
|
||||
}
|
||||
|
||||
private int KillByTagOnInstance(string tag)
|
||||
{
|
||||
int count = 0;
|
||||
count += _processScheduler.KillByTag(tag);
|
||||
count += _physicsScheduler.KillByTag(tag);
|
||||
count += _deferredScheduler.KillByTag(tag);
|
||||
return count;
|
||||
}
|
||||
|
||||
private int ClearOnInstance()
|
||||
{
|
||||
int count = 0;
|
||||
count += _processScheduler.Clear();
|
||||
count += _physicsScheduler.Clear();
|
||||
count += _deferredScheduler.Clear();
|
||||
return count;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 工具方法
|
||||
|
||||
public static Timing? GetInstance(byte id)
|
||||
{
|
||||
return id < ActiveInstances.Length ? ActiveInstances[id] : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建等待指定秒数的指令
|
||||
/// </summary>
|
||||
public static Delay WaitForSeconds(double seconds)
|
||||
{
|
||||
return new Delay(seconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建等待一帧的指令
|
||||
/// </summary>
|
||||
public static WaitOneFrame WaitForOneFrame()
|
||||
{
|
||||
return new WaitOneFrame();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建等待指定帧数的指令
|
||||
/// </summary>
|
||||
public static WaitForFrames WaitForFrames(int frames)
|
||||
{
|
||||
return new WaitForFrames(frames);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建等待直到条件满足的指令
|
||||
/// </summary>
|
||||
public static WaitUntil WaitUntil(Func<bool> predicate)
|
||||
{
|
||||
return new WaitUntil(predicate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建等待当条件为真时持续等待的指令
|
||||
/// </summary>
|
||||
public static WaitWhile WaitWhile(Func<bool> predicate)
|
||||
{
|
||||
return new WaitWhile(predicate);
|
||||
}
|
||||
|
||||
public static bool IsNodeAlive(Node? node)
|
||||
{
|
||||
return node != null
|
||||
&& IsInstanceValid(node)
|
||||
&& !node.IsQueuedForDeletion()
|
||||
&& node.IsInsideTree();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 延迟调用
|
||||
|
||||
public static CoroutineHandle CallDelayed(
|
||||
double delay,
|
||||
Action? action,
|
||||
Segment segment = Segment.Process)
|
||||
{
|
||||
if (action == null)
|
||||
return default;
|
||||
|
||||
return RunCoroutine(DelayedCallCoroutine(delay, action), segment);
|
||||
}
|
||||
|
||||
public static CoroutineHandle CallDelayed(
|
||||
double delay,
|
||||
Action? action,
|
||||
Node cancelWith,
|
||||
Segment segment = Segment.Process)
|
||||
{
|
||||
if (action == null)
|
||||
return default;
|
||||
|
||||
return RunCoroutine(
|
||||
DelayedCallWithCancelCoroutine(delay, action, cancelWith),
|
||||
segment);
|
||||
}
|
||||
|
||||
private static IEnumerator<IYieldInstruction> DelayedCallCoroutine(
|
||||
double delay,
|
||||
Action action)
|
||||
{
|
||||
yield return new Delay(delay);
|
||||
action();
|
||||
}
|
||||
|
||||
private static IEnumerator<IYieldInstruction> DelayedCallWithCancelCoroutine(
|
||||
double delay,
|
||||
Action action,
|
||||
Node cancelWith)
|
||||
{
|
||||
yield return new Delay(delay);
|
||||
|
||||
if (IsNodeAlive(cancelWith))
|
||||
action();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user