mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
feat(coroutine): 扩展协程功能并重构指令结构
- 将协程等待指令移动到instructions命名空间下 - 添加WaitForProgress指令支持带进度回调的时间等待 - 添加WaitForAllCoroutines指令用于等待多个协程完成 - 添加AsyncOperation类用于桥接协程与async/await模型 - 添加协程扩展方法包括RepeatEvery、ExecuteAfter、Sequence等功能 - 添加Task与协程的转换扩展方法AsCoroutineInstruction - 添加协程调度器的ParallelCoroutines扩展方法 - 添加IsCoroutineAlive方法检查协程状态 - 更新相关测试文件以匹配新的命名空间结构
This commit is contained in:
parent
27bc481577
commit
01d9fefa1d
342
GFramework.Core.Tests/coroutine/AsyncOperationTests.cs
Normal file
342
GFramework.Core.Tests/coroutine/AsyncOperationTests.cs
Normal file
@ -0,0 +1,342 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
using GFramework.Core.coroutine.instructions;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// AsyncOperation的单元测试类
|
||||
/// 测试内容包括:
|
||||
/// - 初始化状态
|
||||
/// - 完成和状态检查
|
||||
/// - 异常处理
|
||||
/// - 延续操作
|
||||
/// - GetAwaiter
|
||||
/// - IsCompleted属性
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class AsyncOperationTests
|
||||
{
|
||||
/// <summary>
|
||||
/// 验证AsyncOperation初始状态为未完成
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AsyncOperation_Should_Not_Be_Done_Initially()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
|
||||
Assert.That(op.IsDone, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证AsyncOperation初始状态IsCompleted为false
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AsyncOperation_Should_Not_Be_Completed_Initially()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
|
||||
Assert.That(op.IsCompleted, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SetCompleted后IsDone应该为true
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SetCompleted_Should_Set_IsDone_To_True()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
|
||||
op.SetCompleted();
|
||||
|
||||
Assert.That(op.IsDone, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SetCompleted后IsCompleted应该为true
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SetCompleted_Should_Set_IsCompleted_To_True()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
|
||||
op.SetCompleted();
|
||||
|
||||
Assert.That(op.IsCompleted, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SetCompleted只能被调用一次
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SetCompleted_Should_Be_Idempotent()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
|
||||
op.SetCompleted();
|
||||
op.SetCompleted();
|
||||
op.SetCompleted();
|
||||
|
||||
Assert.That(op.IsDone, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SetException后IsDone应该为true
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SetException_Should_Set_IsDone_To_True()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
|
||||
op.SetException(new InvalidOperationException("Test exception"));
|
||||
|
||||
Assert.That(op.IsDone, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SetException后Task应该包含异常
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SetException_Should_Set_Exception_On_Task()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
var expectedException = new InvalidOperationException("Test exception");
|
||||
|
||||
op.SetException(expectedException);
|
||||
|
||||
Assert.That(async () => await op.Task, Throws.InstanceOf<InvalidOperationException>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证OnCompleted应该在已完成时立即执行延续
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnCompleted_Should_Execute_Immediately_When_Already_Completed()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
var continuationCalled = false;
|
||||
|
||||
op.SetCompleted();
|
||||
op.OnCompleted(() => continuationCalled = true);
|
||||
|
||||
Assert.That(continuationCalled, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证OnCompleted应该在未完成时不立即执行延续
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnCompleted_Should_Not_Execute_Immediately_When_Not_Completed()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
var continuationCalled = false;
|
||||
|
||||
op.OnCompleted(() => continuationCalled = true);
|
||||
|
||||
Assert.That(continuationCalled, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证延续应该在SetCompleted后被调用
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Continuation_Should_Be_Called_After_SetCompleted()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
var continuationCalled = false;
|
||||
|
||||
op.OnCompleted(() => continuationCalled = true);
|
||||
op.SetCompleted();
|
||||
|
||||
Assert.That(continuationCalled, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证多个延续应该都能被调用
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Multiple_Continuations_Should_All_Be_Called()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
var callCount = 0;
|
||||
|
||||
op.OnCompleted(() => callCount++);
|
||||
op.OnCompleted(() => callCount++);
|
||||
op.OnCompleted(() => callCount++);
|
||||
op.SetCompleted();
|
||||
|
||||
Assert.That(callCount, Is.EqualTo(3));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证延续应该在SetException后被调用
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Continuation_Should_Be_Called_After_SetException()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
var continuationCalled = false;
|
||||
|
||||
op.OnCompleted(() => continuationCalled = true);
|
||||
op.SetException(new InvalidOperationException("Test"));
|
||||
|
||||
Assert.That(continuationCalled, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SetCompleted后设置的延续也应该被调用
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Continuation_Registered_After_Completed_Should_Be_Called()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
var firstCalled = false;
|
||||
var secondCalled = false;
|
||||
|
||||
op.OnCompleted(() => firstCalled = true);
|
||||
op.SetCompleted();
|
||||
op.OnCompleted(() => secondCalled = true);
|
||||
|
||||
Assert.That(firstCalled, Is.True);
|
||||
Assert.That(secondCalled, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证GetAwaiter应该返回自身
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetAwaiter_Should_Return_Self()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
|
||||
var awaiter = op.GetAwaiter();
|
||||
|
||||
Assert.That(awaiter, Is.SameAs(op));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证Update方法不应该改变状态
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Update_Should_Not_Change_State()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
|
||||
op.Update(0.1);
|
||||
|
||||
Assert.That(op.IsDone, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证AsyncOperation实现IYieldInstruction接口
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AsyncOperation_Should_Implement_IYieldInstruction()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
|
||||
Assert.That(op, Is.InstanceOf<IYieldInstruction>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证Task属性应该返回有效的Task
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Task_Property_Should_Return_Valid_Task()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
|
||||
Assert.That(op.Task, Is.Not.Null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SetCompleted后Task应该完成
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Task_Should_Complete_After_SetCompleted()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
|
||||
op.SetCompleted();
|
||||
|
||||
await op.Task;
|
||||
|
||||
Assert.That(op.Task.IsCompleted, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SetException后Task应该失败
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Task_Should_Fault_After_SetException()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
|
||||
op.SetException(new InvalidOperationException("Test"));
|
||||
|
||||
Assert.That(op.Task.IsFaulted, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SetCompleted只能设置一次
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SetCompleted_Should_Only_Set_Once()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
var firstCallCompleted = false;
|
||||
var secondCallCompleted = false;
|
||||
|
||||
op.OnCompleted(() => firstCallCompleted = true);
|
||||
op.SetCompleted();
|
||||
|
||||
op.OnCompleted(() => secondCallCompleted = true);
|
||||
op.SetCompleted();
|
||||
|
||||
Assert.That(firstCallCompleted, Is.True);
|
||||
Assert.That(secondCallCompleted, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SetException只能在未完成时设置
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SetException_Should_Not_Work_After_SetCompleted()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
|
||||
op.SetCompleted();
|
||||
op.SetException(new InvalidOperationException("Test"));
|
||||
|
||||
Assert.That(op.Task.IsCompletedSuccessfully, Is.True);
|
||||
Assert.That(op.Task.IsFaulted, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证SetCompleted不能在SetException后设置
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SetCompleted_Should_Not_Work_After_SetException()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
|
||||
op.SetException(new InvalidOperationException("Test"));
|
||||
op.SetCompleted();
|
||||
|
||||
Assert.That(op.Task.IsFaulted, Is.True);
|
||||
Assert.That(op.Task.IsCompletedSuccessfully, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证延续抛出的异常应该被捕获
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Continuation_Exception_Should_Be_Caught()
|
||||
{
|
||||
var op = new AsyncOperation();
|
||||
|
||||
op.OnCompleted(() => throw new InvalidOperationException("Test exception"));
|
||||
|
||||
Assert.DoesNotThrow(() => op.SetCompleted());
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
using GFramework.Core.coroutine;
|
||||
using GFramework.Core.coroutine.instructions;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.coroutine;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
using GFramework.Core.coroutine;
|
||||
using GFramework.Core.coroutine.instructions;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.coroutine;
|
||||
|
||||
295
GFramework.Core.Tests/coroutine/TaskCoroutineExtensionsTests.cs
Normal file
295
GFramework.Core.Tests/coroutine/TaskCoroutineExtensionsTests.cs
Normal file
@ -0,0 +1,295 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
using GFramework.Core.coroutine;
|
||||
using GFramework.Core.coroutine.extensions;
|
||||
using GFramework.Core.coroutine.instructions;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// TaskCoroutineExtensions的单元测试类
|
||||
/// 测试内容包括:
|
||||
/// - AsCoroutineInstruction方法
|
||||
/// - StartTaskAsCoroutine方法
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class TaskCoroutineExtensionsTests
|
||||
{
|
||||
/// <summary>
|
||||
/// 验证AsCoroutineInstruction应该返回WaitForTask
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AsCoroutineInstruction_Should_Return_WaitForTask()
|
||||
{
|
||||
var task = Task.CompletedTask;
|
||||
var instruction = task.AsCoroutineInstruction();
|
||||
|
||||
Assert.That(instruction, Is.InstanceOf<WaitForTask>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证AsCoroutineInstruction<T>应该返回WaitForTask<T>
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AsCoroutineInstructionOfT_Should_Return_WaitForTaskOfT()
|
||||
{
|
||||
var task = Task.FromResult(42);
|
||||
var instruction = task.AsCoroutineInstruction<int>();
|
||||
|
||||
Assert.That(instruction, Is.InstanceOf<WaitForTask<int>>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证AsCoroutineInstruction可以处理已完成的Task
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AsCoroutineInstruction_Should_Handle_Completed_Task()
|
||||
{
|
||||
var task = Task.CompletedTask;
|
||||
var instruction = task.AsCoroutineInstruction();
|
||||
|
||||
Assert.That(instruction, Is.InstanceOf<WaitForTask>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证AsCoroutineInstruction<T>可以处理已完成的Task
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AsCoroutineInstructionOfT_Should_Handle_Completed_Task()
|
||||
{
|
||||
var task = Task.FromResult(42);
|
||||
var instruction = task.AsCoroutineInstruction<int>();
|
||||
|
||||
Assert.That(instruction, Is.InstanceOf<WaitForTask<int>>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证AsCoroutineInstruction<T>应该能够访问Task结果
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AsCoroutineInstructionOfT_Should_Access_Task_Result()
|
||||
{
|
||||
var task = Task.FromResult(42);
|
||||
var instruction = task.AsCoroutineInstruction<int>();
|
||||
|
||||
task.Wait();
|
||||
|
||||
Assert.That(instruction.Result, Is.EqualTo(42));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证AsCoroutineInstruction应该处理null Task(抛出异常)
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AsCoroutineInstruction_Should_Handle_Null_Task()
|
||||
{
|
||||
Task task = null!;
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => task.AsCoroutineInstruction());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证AsCoroutineInstruction<T>应该处理null Task(抛出异常)
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AsCoroutineInstructionOfT_Should_Handle_Null_Task()
|
||||
{
|
||||
Task<int> task = null!;
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => task.AsCoroutineInstruction<int>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证AsCoroutineInstruction应该处理失败的Task
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AsCoroutineInstruction_Should_Handle_Faulted_Task()
|
||||
{
|
||||
var task = Task.FromException(new InvalidOperationException("Test exception"));
|
||||
var instruction = task.AsCoroutineInstruction();
|
||||
|
||||
Assert.That(instruction, Is.InstanceOf<WaitForTask>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证AsCoroutineInstruction<T>应该处理失败的Task
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AsCoroutineInstructionOfT_Should_Handle_Faulted_Task()
|
||||
{
|
||||
var task = Task.FromException<int>(new InvalidOperationException("Test exception"));
|
||||
var instruction = task.AsCoroutineInstruction<int>();
|
||||
|
||||
Assert.That(instruction, Is.InstanceOf<WaitForTask<int>>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证StartTaskAsCoroutine应该返回有效的协程句柄
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void StartTaskAsCoroutine_Should_Return_Valid_Handle()
|
||||
{
|
||||
var timeSource = new TestTimeSource();
|
||||
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
|
||||
var task = Task.CompletedTask;
|
||||
|
||||
var handle = scheduler.StartTaskAsCoroutine(task);
|
||||
|
||||
Assert.That(handle.IsValid, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证StartTaskAsCoroutine<T>应该返回有效的协程句柄
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void StartTaskAsCoroutineOfT_Should_Return_Valid_Handle()
|
||||
{
|
||||
var timeSource = new TestTimeSource();
|
||||
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
|
||||
var task = Task.FromResult(42);
|
||||
|
||||
var handle = scheduler.StartTaskAsCoroutine(task);
|
||||
|
||||
Assert.That(handle.IsValid, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证StartTaskAsCoroutine应该等待Task完成
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void StartTaskAsCoroutine_Should_Wait_For_Task_Completion()
|
||||
{
|
||||
var timeSource = new TestTimeSource();
|
||||
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
|
||||
|
||||
var completed = false;
|
||||
var tcs = new TaskCompletionSource<object?>();
|
||||
|
||||
var handle = scheduler.StartTaskAsCoroutine(tcs.Task);
|
||||
|
||||
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(1));
|
||||
Assert.That(completed, Is.False);
|
||||
|
||||
tcs.SetResult(null);
|
||||
Task.Delay(50).Wait();
|
||||
|
||||
scheduler.Update();
|
||||
scheduler.Update();
|
||||
|
||||
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证StartTaskAsCoroutine<T>应该等待Task完成
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void StartTaskAsCoroutineOfT_Should_Wait_For_Task_Completion()
|
||||
{
|
||||
var timeSource = new TestTimeSource();
|
||||
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
|
||||
|
||||
var tcs = new TaskCompletionSource<int>();
|
||||
|
||||
var handle = scheduler.StartTaskAsCoroutine(tcs.Task);
|
||||
|
||||
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(1));
|
||||
|
||||
tcs.SetResult(42);
|
||||
Task.Delay(50).Wait();
|
||||
|
||||
scheduler.Update();
|
||||
scheduler.Update();
|
||||
|
||||
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证StartTaskAsCoroutine应该处理已完成的Task
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void StartTaskAsCoroutine_Should_Handle_Completed_Task()
|
||||
{
|
||||
var timeSource = new TestTimeSource();
|
||||
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
|
||||
var task = Task.CompletedTask;
|
||||
|
||||
var handle = scheduler.StartTaskAsCoroutine(task);
|
||||
|
||||
scheduler.Update();
|
||||
|
||||
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证StartTaskAsCoroutine应该处理失败的Task
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void StartTaskAsCoroutine_Should_Handle_Faulted_Task()
|
||||
{
|
||||
var timeSource = new TestTimeSource();
|
||||
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
|
||||
var task = Task.FromException(new InvalidOperationException("Test"));
|
||||
|
||||
var handle = scheduler.StartTaskAsCoroutine(task);
|
||||
|
||||
Assert.DoesNotThrow(() => scheduler.Update());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证StartTaskAsCoroutine<T>应该处理失败的Task
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void StartTaskAsCoroutineOfT_Should_Handle_Faulted_Task()
|
||||
{
|
||||
var timeSource = new TestTimeSource();
|
||||
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
|
||||
var task = Task.FromException<int>(new InvalidOperationException("Test"));
|
||||
|
||||
scheduler.StartTaskAsCoroutine(task);
|
||||
|
||||
Assert.DoesNotThrow(() => scheduler.Update());
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 验证StartTaskAsCoroutine应该与调度器正常协作
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void StartTaskAsCoroutine_Should_Work_With_Scheduler()
|
||||
{
|
||||
var timeSource = new TestTimeSource();
|
||||
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
|
||||
|
||||
var tcs = new TaskCompletionSource<object?>();
|
||||
scheduler.StartTaskAsCoroutine(tcs.Task);
|
||||
|
||||
var tcs2 = new TaskCompletionSource<int>();
|
||||
scheduler.StartTaskAsCoroutine(tcs2.Task);
|
||||
|
||||
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(2));
|
||||
|
||||
tcs.SetResult(null);
|
||||
tcs2.SetResult(42);
|
||||
|
||||
Task.Delay(50).Wait();
|
||||
scheduler.Update();
|
||||
scheduler.Update();
|
||||
|
||||
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试用时间源类
|
||||
/// </summary>
|
||||
private class TestTimeSource : ITimeSource
|
||||
{
|
||||
public double CurrentTime { get; private set; }
|
||||
public double DeltaTime { get; private set; }
|
||||
|
||||
public void Update()
|
||||
{
|
||||
DeltaTime = 0.1;
|
||||
CurrentTime += DeltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
294
GFramework.Core.Tests/coroutine/WaitForProgressTests.cs
Normal file
294
GFramework.Core.Tests/coroutine/WaitForProgressTests.cs
Normal file
@ -0,0 +1,294 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
using GFramework.Core.coroutine.instructions;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// WaitForProgress的单元测试类
|
||||
/// 测试内容包括:
|
||||
/// - 初始化和基本功能
|
||||
/// - 进度回调
|
||||
/// - 边界条件
|
||||
/// - 异常处理
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class WaitForProgressTests
|
||||
{
|
||||
/// <summary>
|
||||
/// 验证WaitForProgress初始状态为未完成
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForProgress_Should_Not_Be_Done_Initially()
|
||||
{
|
||||
var progressValues = new List<float>();
|
||||
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForProgress应该在指定时间后完成
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForProgress_Should_Be_Done_After_Duration()
|
||||
{
|
||||
var progressValues = new List<float>();
|
||||
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||
|
||||
wait.Update(0.5);
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
|
||||
wait.Update(0.5);
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForProgress应该在Update时调用进度回调
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForProgress_Should_Call_Progress_Callback_On_Update()
|
||||
{
|
||||
var progressValues = new List<float>();
|
||||
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||
|
||||
wait.Update(0.25);
|
||||
|
||||
Assert.That(progressValues.Count, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证进度值应该在0到1之间
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForProgress_Should_Have_Progress_Between_0_And_1()
|
||||
{
|
||||
var progressValues = new List<float>();
|
||||
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||
|
||||
while (!wait.IsDone)
|
||||
{
|
||||
wait.Update(0.1);
|
||||
}
|
||||
|
||||
foreach (var progress in progressValues)
|
||||
{
|
||||
Assert.That(progress, Is.GreaterThanOrEqualTo(0.0f));
|
||||
Assert.That(progress, Is.LessThanOrEqualTo(1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证进度值应该随着时间增加
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForProgress_Should_Increase_Progress_Over_Time()
|
||||
{
|
||||
var progressValues = new List<float>();
|
||||
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||
|
||||
wait.Update(0.25);
|
||||
Assert.That(progressValues[0], Is.EqualTo(0.25f).Within(0.01f));
|
||||
|
||||
wait.Update(0.25);
|
||||
Assert.That(progressValues[1], Is.EqualTo(0.5f).Within(0.01f));
|
||||
|
||||
wait.Update(0.25);
|
||||
Assert.That(progressValues[2], Is.EqualTo(0.75f).Within(0.01f));
|
||||
|
||||
wait.Update(0.25);
|
||||
Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForProgress应该抛出ArgumentNullException当回调为null
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForProgress_Should_Throw_ArgumentNullException_When_Callback_Is_Null()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>(() => new WaitForProgress(1.0, null!));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForProgress应该抛出ArgumentException当duration为0
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForProgress_Should_Throw_ArgumentException_When_Duration_Is_Zero()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => new WaitForProgress(0, _ => { }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForProgress应该抛出ArgumentException当duration为负数
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForProgress_Should_Throw_ArgumentException_When_Duration_Is_Negative()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => new WaitForProgress(-1.0, _ => { }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForProgress应该处理超过duration的更新
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForProgress_Should_Clamp_Progress_To_1_When_Exceeding_Duration()
|
||||
{
|
||||
var progressValues = new List<float>();
|
||||
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||
|
||||
wait.Update(0.5);
|
||||
wait.Update(0.5);
|
||||
wait.Update(0.5);
|
||||
|
||||
Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForProgress可以处理精确的duration
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForProgress_Should_Handle_Exact_Duration()
|
||||
{
|
||||
var progressValues = new List<float>();
|
||||
var duration = 1.0;
|
||||
var wait = new WaitForProgress(duration, progressValues.Add);
|
||||
|
||||
wait.Update(duration);
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForProgress可以处理不同的delta time
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForProgress_Should_Handle_Variable_Delta_Time()
|
||||
{
|
||||
var progressValues = new List<float>();
|
||||
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||
|
||||
wait.Update(0.1);
|
||||
wait.Update(0.05);
|
||||
wait.Update(0.15);
|
||||
wait.Update(0.2);
|
||||
wait.Update(0.5);
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForProgress可以处理多次Update
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForProgress_Should_Handle_Multiple_Updates()
|
||||
{
|
||||
var progressValues = new List<float>();
|
||||
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||
|
||||
var updateCount = 0;
|
||||
while (!wait.IsDone && updateCount < 100)
|
||||
{
|
||||
wait.Update(0.01);
|
||||
updateCount++;
|
||||
}
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
Assert.That(progressValues.Count, Is.GreaterThan(0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForProgress应该确保最后一个进度为1.0
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForProgress_Should_Ensure_Final_Progress_Is_1()
|
||||
{
|
||||
var progressValues = new List<float>();
|
||||
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||
|
||||
while (!wait.IsDone)
|
||||
{
|
||||
wait.Update(0.1);
|
||||
}
|
||||
|
||||
Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForProgress实现IYieldInstruction接口
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForProgress_Should_Implement_IYieldInstruction()
|
||||
{
|
||||
var progressValues = new List<float>();
|
||||
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||
|
||||
Assert.That(wait, Is.InstanceOf<IYieldInstruction>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForProgress可以处理很短的duration
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForProgress_Should_Handle_Short_Duration()
|
||||
{
|
||||
var progressValues = new List<float>();
|
||||
var wait = new WaitForProgress(0.001, progressValues.Add);
|
||||
|
||||
wait.Update(0.001);
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForProgress可以处理很长的duration
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForProgress_Should_Handle_Long_Duration()
|
||||
{
|
||||
var progressValues = new List<float>();
|
||||
var wait = new WaitForProgress(100.0, progressValues.Add);
|
||||
|
||||
wait.Update(50.0);
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
|
||||
wait.Update(50.0);
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForProgress在完成前不会超过1.0
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForProgress_Should_Not_Exceed_1_Before_Completion()
|
||||
{
|
||||
var progressValues = new List<float>();
|
||||
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||
|
||||
wait.Update(0.9);
|
||||
Assert.That(progressValues[^1], Is.LessThan(1.0f));
|
||||
|
||||
wait.Update(0.05);
|
||||
Assert.That(progressValues[^1], Is.LessThanOrEqualTo(1.0f));
|
||||
|
||||
wait.Update(0.05);
|
||||
Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForProgress的Update方法不影响未完成状态
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForProgress_Update_Should_Not_Affect_Before_Completion()
|
||||
{
|
||||
var progressValues = new List<float>();
|
||||
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||
|
||||
wait.Update(0.1);
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
|
||||
wait.Update(0.1);
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
}
|
||||
}
|
||||
311
GFramework.Core.Tests/coroutine/WaitForTaskTests.cs
Normal file
311
GFramework.Core.Tests/coroutine/WaitForTaskTests.cs
Normal file
@ -0,0 +1,311 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
using GFramework.Core.coroutine.instructions;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// WaitForTask的单元测试类
|
||||
/// 测试内容包括:
|
||||
/// - WaitForTask初始化和等待
|
||||
/// - WaitForTask<T>初始化、等待和结果获取
|
||||
/// - 异常处理
|
||||
/// - 边界条件
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class WaitForTaskTests
|
||||
{
|
||||
/// <summary>
|
||||
/// 验证WaitForTask初始状态为未完成
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForTask_Should_Not_Be_Done_Initially()
|
||||
{
|
||||
var task = Task.Run(() => { });
|
||||
var wait = new WaitForTask(task);
|
||||
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForTask应该在Task完成后完成
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForTask_Should_Be_Done_After_Task_Completes()
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object?>();
|
||||
var wait = new WaitForTask(tcs.Task);
|
||||
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
|
||||
tcs.SetResult(null);
|
||||
|
||||
Task.Delay(100).Wait();
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForTask应该处理已完成的Task
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForTask_Should_Handle_Already_Completed_Task()
|
||||
{
|
||||
var task = Task.CompletedTask;
|
||||
|
||||
Task.Delay(100).Wait();
|
||||
|
||||
var wait = new WaitForTask(task);
|
||||
|
||||
Task.Delay(100).Wait();
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForTask应该处理失败的Task
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForTask_Should_Handle_Faulted_Task()
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object?>();
|
||||
var wait = new WaitForTask(tcs.Task);
|
||||
|
||||
tcs.SetException(new InvalidOperationException("Test exception"));
|
||||
|
||||
Task.Delay(100).Wait();
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForTask应该处理取消的Task
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForTask_Should_Handle_Cancelled_Task()
|
||||
{
|
||||
var cts = new CancellationTokenSource();
|
||||
cts.Cancel();
|
||||
var task = Task.FromCanceled(cts.Token);
|
||||
|
||||
Task.Delay(100).Wait();
|
||||
|
||||
var wait = new WaitForTask(task);
|
||||
|
||||
Task.Delay(100).Wait();
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForTask应该抛出ArgumentNullException当task为null
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForTask_Should_Throw_ArgumentNullException_When_Task_Is_Null()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>(() => new WaitForTask(null!));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForTask<T>初始状态为未完成
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForTaskOfT_Should_Not_Be_Done_Initially()
|
||||
{
|
||||
var tcs = new TaskCompletionSource<int>();
|
||||
var wait = new WaitForTask<int>(tcs.Task);
|
||||
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForTask<T>应该在Task完成后完成
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForTaskOfT_Should_Be_Done_After_Task_Completes()
|
||||
{
|
||||
var tcs = new TaskCompletionSource<int>();
|
||||
var wait = new WaitForTask<int>(tcs.Task);
|
||||
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
|
||||
tcs.SetResult(42);
|
||||
|
||||
Task.Delay(100).Wait();
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForTask<T>应该返回Task的结果
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForTaskOfT_Should_Return_Task_Result()
|
||||
{
|
||||
var tcs = new TaskCompletionSource<int>();
|
||||
var wait = new WaitForTask<int>(tcs.Task);
|
||||
|
||||
tcs.SetResult(42);
|
||||
|
||||
Task.Delay(100).Wait();
|
||||
|
||||
Assert.That(wait.Result, Is.EqualTo(42));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForTask<T>应该处理已完成的Task
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForTaskOfT_Should_Handle_Already_Completed_Task()
|
||||
{
|
||||
var task = Task.FromResult(42);
|
||||
|
||||
Task.Delay(100).Wait();
|
||||
|
||||
var wait = new WaitForTask<int>(task);
|
||||
|
||||
Task.Delay(100).Wait();
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
Assert.That(wait.Result, Is.EqualTo(42));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForTask<T>应该处理失败的Task
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForTaskOfT_Should_Handle_Faulted_Task()
|
||||
{
|
||||
var tcs = new TaskCompletionSource<int>();
|
||||
var wait = new WaitForTask<int>(tcs.Task);
|
||||
|
||||
tcs.SetException(new InvalidOperationException("Test exception"));
|
||||
|
||||
Task.Delay(100).Wait();
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => { var _ = wait.Result; });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForTask<T>应该抛出ArgumentNullException当task为null
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForTaskOfT_Should_Throw_ArgumentNullException_When_Task_Is_Null()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>(() => new WaitForTask<int>(null!));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForTask实现IYieldInstruction接口
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForTask_Should_Implement_IYieldInstruction()
|
||||
{
|
||||
var task = Task.CompletedTask;
|
||||
var wait = new WaitForTask(task);
|
||||
|
||||
Assert.That(wait, Is.InstanceOf<IYieldInstruction>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForTask<T>实现IYieldInstruction接口
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForTaskOfT_Should_Implement_IYieldInstruction()
|
||||
{
|
||||
var task = Task.FromResult(42);
|
||||
var wait = new WaitForTask<int>(task);
|
||||
|
||||
Assert.That(wait, Is.InstanceOf<IYieldInstruction>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForTask的Update方法不影响状态
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForTask_Update_Should_Not_Affect_State()
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object?>();
|
||||
var wait = new WaitForTask(tcs.Task);
|
||||
|
||||
wait.Update(0.1);
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
|
||||
wait.Update(1.0);
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForTask<T>的Update方法不影响状态
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForTaskOfT_Update_Should_Not_Affect_State()
|
||||
{
|
||||
var tcs = new TaskCompletionSource<int>();
|
||||
var wait = new WaitForTask<int>(tcs.Task);
|
||||
|
||||
wait.Update(0.1);
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
|
||||
wait.Update(1.0);
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForTask在延迟完成后能够正确等待
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForTask_Should_Wait_For_Delayed_Task()
|
||||
{
|
||||
var delayMs = 100;
|
||||
var task = Task.Delay(delayMs);
|
||||
var wait = new WaitForTask(task);
|
||||
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
|
||||
Task.Delay(delayMs + 50).Wait();
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForTask<T>在异步操作完成后能够正确获取结果
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task WaitForTaskOfT_Should_Get_Result_From_Async_Operation()
|
||||
{
|
||||
var expectedValue = 123;
|
||||
var task = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(50);
|
||||
return expectedValue;
|
||||
});
|
||||
|
||||
var wait = new WaitForTask<int>(task);
|
||||
|
||||
await task;
|
||||
|
||||
Task.Delay(100).Wait();
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
Assert.That(wait.Result, Is.EqualTo(expectedValue));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证WaitForTask应该处理长时间运行的Task
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForTask_Should_Handle_Long_Running_Task()
|
||||
{
|
||||
var task = Task.Delay(200);
|
||||
var wait = new WaitForTask(task);
|
||||
|
||||
Assert.That(wait.IsDone, Is.False);
|
||||
|
||||
Task.Delay(250).Wait();
|
||||
|
||||
Assert.That(wait.IsDone, Is.True);
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
using GFramework.Core.coroutine;
|
||||
using GFramework.Core.coroutine.instructions;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.coroutine;
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
using GFramework.Core.coroutine.instructions;
|
||||
|
||||
namespace GFramework.Core.coroutine;
|
||||
|
||||
@ -59,6 +57,17 @@ public static class CoroutineHelper
|
||||
return new WaitWhile(predicate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 等待指定时间并提供进度回调
|
||||
/// </summary>
|
||||
/// <param name="duration">等待的持续时间(秒)</param>
|
||||
/// <param name="onProgress">进度回调函数,接收0-1之间的进度值</param>
|
||||
/// <returns>等待进度指令</returns>
|
||||
public static WaitForProgress WaitForProgress(double duration, Action<float> onProgress)
|
||||
{
|
||||
return new WaitForProgress(duration, onProgress);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 延迟调用指定的委托
|
||||
/// </summary>
|
||||
|
||||
@ -368,4 +368,8 @@ public sealed class CoroutineScheduler(
|
||||
}
|
||||
|
||||
#endregion
|
||||
public bool IsCoroutineAlive(CoroutineHandle handle)
|
||||
{
|
||||
return _metadata.ContainsKey(handle);
|
||||
}
|
||||
}
|
||||
131
GFramework.Core/coroutine/extensions/CoroutineExtensions.cs
Normal file
131
GFramework.Core/coroutine/extensions/CoroutineExtensions.cs
Normal file
@ -0,0 +1,131 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
using GFramework.Core.coroutine.instructions;
|
||||
|
||||
namespace GFramework.Core.coroutine.extensions;
|
||||
|
||||
/// <summary>
|
||||
/// 协程相关的扩展方法
|
||||
/// </summary>
|
||||
public static class CoroutineExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 在指定时间间隔内重复执行动作的协程
|
||||
/// </summary>
|
||||
/// <param name="interval">执行间隔时间(秒)</param>
|
||||
/// <param name="action">要重复执行的动作</param>
|
||||
/// <param name="count">重复次数,如果为null则无限重复</param>
|
||||
/// <returns>协程枚举器</returns>
|
||||
public static IEnumerator<IYieldInstruction> RepeatEvery(
|
||||
double interval,
|
||||
Action action,
|
||||
int? count = null)
|
||||
{
|
||||
if (count is < 0)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
var executedCount = 0;
|
||||
while (count == null || executedCount < count)
|
||||
{
|
||||
action?.Invoke();
|
||||
yield return new Delay(interval);
|
||||
executedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定延迟后执行动作的协程
|
||||
/// </summary>
|
||||
/// <param name="delay">延迟时间(秒)</param>
|
||||
/// <param name="action">要执行的动作</param>
|
||||
/// <returns>协程枚举器</returns>
|
||||
public static IEnumerator<IYieldInstruction> ExecuteAfter(
|
||||
double delay,
|
||||
Action? action)
|
||||
{
|
||||
if (delay < 0)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return new Delay(delay);
|
||||
action?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 顺序执行多个协程
|
||||
/// </summary>
|
||||
/// <param name="coroutines">要顺序执行的协程集合</param>
|
||||
/// <returns>协程枚举器</returns>
|
||||
public static IEnumerator<IYieldInstruction> Sequence(
|
||||
params IEnumerator<IYieldInstruction>[] coroutines)
|
||||
{
|
||||
foreach (var coroutine in coroutines)
|
||||
{
|
||||
while (coroutine.MoveNext())
|
||||
{
|
||||
yield return coroutine.Current;
|
||||
}
|
||||
|
||||
// 清理协程
|
||||
coroutine.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 并行执行多个协程(等待所有协程完成)
|
||||
/// 注意:这需要协程调度器的支持,这里提供一个包装器返回多个句柄
|
||||
/// </summary>
|
||||
/// <param name="scheduler">协程调度器</param>
|
||||
/// <param name="coroutines">要并行执行的协程集合</param>
|
||||
/// <returns>等待所有协程完成的协程</returns>
|
||||
public static IEnumerator<IYieldInstruction> ParallelCoroutines(
|
||||
this CoroutineScheduler scheduler,
|
||||
params IEnumerator<IYieldInstruction>[]? coroutines)
|
||||
{
|
||||
if (coroutines == null || coroutines.Length == 0)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
// 启动所有协程并收集句柄
|
||||
var handles = new List<CoroutineHandle>();
|
||||
foreach (var coroutine in coroutines)
|
||||
{
|
||||
var handle = scheduler.Run(coroutine);
|
||||
handles.Add(handle);
|
||||
}
|
||||
|
||||
// 等待所有协程完成
|
||||
yield return new WaitForAllCoroutines(scheduler,handles);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 带进度回调的等待协程
|
||||
/// </summary>
|
||||
/// <param name="totalTime">总等待时间(秒)</param>
|
||||
/// <param name="onProgress">进度回调,参数为0-1之间的进度值</param>
|
||||
/// <returns>协程枚举器</returns>
|
||||
public static IEnumerator<IYieldInstruction> WaitForSecondsWithProgress(
|
||||
double totalTime,
|
||||
Action<float>? onProgress)
|
||||
{
|
||||
if (totalTime <= 0)
|
||||
{
|
||||
onProgress?.Invoke(1.0f);
|
||||
yield break;
|
||||
}
|
||||
|
||||
onProgress?.Invoke(0.0f);
|
||||
|
||||
if (onProgress != null)
|
||||
{
|
||||
yield return new WaitForProgress(totalTime, onProgress);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return new Delay(totalTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
using GFramework.Core.coroutine.instructions;
|
||||
|
||||
namespace GFramework.Core.coroutine.extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Task与协程之间的扩展方法
|
||||
/// </summary>
|
||||
public static class TaskCoroutineExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 将Task转换为协程等待指令
|
||||
/// </summary>
|
||||
/// <param name="task">要等待的Task</param>
|
||||
/// <returns>等待Task的协程指令</returns>
|
||||
public static WaitForTask AsCoroutineInstruction(this Task task)
|
||||
{
|
||||
return new WaitForTask(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将泛型Task转换为协程等待指令
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Task返回值的类型</typeparam>
|
||||
/// <param name="task">要等待的Task</param>
|
||||
/// <returns>等待Task的协程指令</returns>
|
||||
public static WaitForTask<T> AsCoroutineInstruction<T>(this Task<T> task)
|
||||
{
|
||||
return new WaitForTask<T>(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在调度器中启动一个Task并等待其完成
|
||||
/// </summary>
|
||||
/// <param name="scheduler">协程调度器</param>
|
||||
/// <param name="task">要等待的Task</param>
|
||||
/// <returns>协程句柄</returns>
|
||||
public static CoroutineHandle StartTaskAsCoroutine(this CoroutineScheduler scheduler, Task task)
|
||||
{
|
||||
return scheduler.Run(CreateTaskCoroutine(task));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在调度器中启动一个泛型Task并等待其完成
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Task返回值的类型</typeparam>
|
||||
/// <param name="scheduler">协程调度器</param>
|
||||
/// <param name="task">要等待的Task</param>
|
||||
/// <returns>协程句柄</returns>
|
||||
public static CoroutineHandle StartTaskAsCoroutine<T>(this CoroutineScheduler scheduler, Task<T> task)
|
||||
{
|
||||
return scheduler.Run(CreateTaskCoroutine(task));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建等待Task的协程
|
||||
/// </summary>
|
||||
private static IEnumerator<IYieldInstruction> CreateTaskCoroutine(Task task)
|
||||
{
|
||||
yield return task.AsCoroutineInstruction();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建等待泛型Task的协程
|
||||
/// </summary>
|
||||
private static IEnumerator<IYieldInstruction> CreateTaskCoroutine<T>(Task<T> task)
|
||||
{
|
||||
yield return task.AsCoroutineInstruction();
|
||||
}
|
||||
}
|
||||
141
GFramework.Core/coroutine/instructions/AsyncOperation.cs
Normal file
141
GFramework.Core/coroutine/instructions/AsyncOperation.cs
Normal file
@ -0,0 +1,141 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine.instructions;
|
||||
|
||||
/// <summary>
|
||||
/// 异步操作包装器,用于桥接协程系统和async/await异步编程模型
|
||||
/// </summary>
|
||||
public class AsyncOperation : IYieldInstruction, INotifyCompletion
|
||||
{
|
||||
private readonly TaskCompletionSource<object?> _tcs = new();
|
||||
private volatile bool _completed;
|
||||
private volatile Action? _continuation;
|
||||
|
||||
/// <summary>
|
||||
/// 获取异步操作是否已完成
|
||||
/// </summary>
|
||||
public bool IsDone => _completed;
|
||||
|
||||
/// <summary>
|
||||
/// 获取异步操作的任务
|
||||
/// </summary>
|
||||
public Task Task => _tcs.Task;
|
||||
|
||||
/// <summary>
|
||||
/// 更新方法,用于处理时间更新逻辑
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">时间增量</param>
|
||||
public void Update(double deltaTime)
|
||||
{
|
||||
// 由外部调用SetCompleted来更新状态
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标记异步操作已完成
|
||||
/// </summary>
|
||||
public void SetCompleted()
|
||||
{
|
||||
if (_completed) return;
|
||||
|
||||
_completed = true;
|
||||
_tcs.SetResult(null);
|
||||
|
||||
var continuation = Interlocked.Exchange(ref _continuation, null);
|
||||
if (continuation != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
continuation.Invoke();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略延续中的异常
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标记异步操作因异常而失败
|
||||
/// </summary>
|
||||
/// <param name="exception">导致失败的异常</param>
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
if (_completed) return;
|
||||
|
||||
_completed = true;
|
||||
_tcs.SetException(exception);
|
||||
|
||||
var continuation = Interlocked.Exchange(ref _continuation, null);
|
||||
if (continuation != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
continuation.Invoke();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略延续中的异常
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置延续操作
|
||||
/// </summary>
|
||||
/// <param name="continuation">要执行的延续操作</param>
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
// 尝试添加延续
|
||||
var current = _continuation;
|
||||
var newContinuation = current == null ? continuation : current + continuation;
|
||||
|
||||
if (Interlocked.CompareExchange(ref _continuation, newContinuation, current) != current)
|
||||
{
|
||||
// 如果CAS失败,说明可能已经完成,直接执行
|
||||
if (_completed)
|
||||
{
|
||||
continuation();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 重试
|
||||
OnCompleted(continuation);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 双重检查:如果在设置延续后发现已完成,需要执行延续
|
||||
if (_completed)
|
||||
{
|
||||
var cont = Interlocked.Exchange(ref _continuation, null);
|
||||
if (cont != null)
|
||||
{
|
||||
cont();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取异步操作结果
|
||||
/// </summary>
|
||||
/// <returns>操作结果</returns>
|
||||
public object? GetResult()
|
||||
{
|
||||
return _tcs.Task.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取awaiter对象
|
||||
/// </summary>
|
||||
public AsyncOperation GetAwaiter()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否已完成
|
||||
/// </summary>
|
||||
public bool IsCompleted => _completed;
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine;
|
||||
namespace GFramework.Core.coroutine.instructions;
|
||||
|
||||
/// <summary>
|
||||
/// 延迟等待指令,实现IYieldInstruction接口,用于协程中的时间延迟
|
||||
@ -0,0 +1,29 @@
|
||||
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine.instructions;
|
||||
|
||||
/// <summary>
|
||||
/// 等待所有协程完成的等待指令
|
||||
/// </summary>
|
||||
public sealed class WaitForAllCoroutines(
|
||||
CoroutineScheduler scheduler,
|
||||
IReadOnlyList<CoroutineHandle> handles)
|
||||
: IYieldInstruction
|
||||
{
|
||||
private readonly CoroutineScheduler _scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler));
|
||||
private readonly IReadOnlyList<CoroutineHandle> _handles = handles ?? throw new ArgumentNullException(nameof(handles));
|
||||
|
||||
public void Update(double deltaTime)
|
||||
{
|
||||
// 不需要做任何事
|
||||
}
|
||||
|
||||
public bool IsDone
|
||||
{
|
||||
get
|
||||
{
|
||||
return _handles.All(handle => !_scheduler.IsCoroutineAlive(handle));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine;
|
||||
namespace GFramework.Core.coroutine.instructions;
|
||||
|
||||
/// <summary>
|
||||
/// 等待协程完成的指令类,实现IYieldInstruction接口
|
||||
@ -1,6 +1,6 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine;
|
||||
namespace GFramework.Core.coroutine.instructions;
|
||||
|
||||
/// <summary>
|
||||
/// 等待指定帧数的等待指令类
|
||||
60
GFramework.Core/coroutine/instructions/WaitForProgress.cs
Normal file
60
GFramework.Core/coroutine/instructions/WaitForProgress.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine.instructions;
|
||||
|
||||
/// <summary>
|
||||
/// 带进度回调的等待指令
|
||||
/// </summary>
|
||||
public class WaitForProgress : IYieldInstruction
|
||||
{
|
||||
private readonly double _duration;
|
||||
private readonly Action<float> _onProgress;
|
||||
private double _elapsed;
|
||||
private bool _progressCompleted;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化等待进度指令
|
||||
/// </summary>
|
||||
/// <param name="duration">总持续时间(秒)</param>
|
||||
/// <param name="onProgress">进度回调,参数为0-1之间的进度值</param>
|
||||
public WaitForProgress(double duration, Action<float> onProgress)
|
||||
{
|
||||
if (duration <= 0)
|
||||
throw new ArgumentException("Duration must be positive", nameof(duration));
|
||||
|
||||
_duration = duration;
|
||||
_onProgress = onProgress ?? throw new ArgumentNullException(nameof(onProgress));
|
||||
_elapsed = 0;
|
||||
_progressCompleted = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新方法
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">时间增量</param>
|
||||
public void Update(double deltaTime)
|
||||
{
|
||||
if (_progressCompleted)
|
||||
return;
|
||||
|
||||
_elapsed += deltaTime;
|
||||
|
||||
// 计算进度并回调
|
||||
if (_elapsed >= _duration)
|
||||
{
|
||||
_elapsed = _duration;
|
||||
_progressCompleted = true;
|
||||
_onProgress(1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
var progress = (float)(_elapsed / _duration);
|
||||
_onProgress(progress);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取等待是否已完成
|
||||
/// </summary>
|
||||
public bool IsDone => _progressCompleted;
|
||||
}
|
||||
60
GFramework.Core/coroutine/instructions/WaitForTask.T.cs
Normal file
60
GFramework.Core/coroutine/instructions/WaitForTask.T.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine.instructions;
|
||||
|
||||
/// <summary>
|
||||
/// 等待泛型Task完成的等待指令
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Task返回值的类型</typeparam>
|
||||
public sealed class WaitForTask<T> : IYieldInstruction
|
||||
{
|
||||
private readonly Task<T> _task;
|
||||
private volatile bool _done;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化等待泛型Task的指令
|
||||
/// </summary>
|
||||
/// <param name="task">要等待完成的Task</param>
|
||||
public WaitForTask(Task<T> task)
|
||||
{
|
||||
_task = task ?? throw new ArgumentNullException(nameof(task));
|
||||
|
||||
// 检查Task是否已经完成
|
||||
if (_task.IsCompleted)
|
||||
{
|
||||
_done = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 注册完成回调
|
||||
_task.ContinueWith(_ =>
|
||||
{
|
||||
_done = true;
|
||||
}, TaskContinuationOptions.ExecuteSynchronously);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新方法,用于处理时间更新逻辑
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">时间增量</param>
|
||||
public void Update(double deltaTime)
|
||||
{
|
||||
// Task的完成由ContinueWith回调设置
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取等待是否已完成
|
||||
/// </summary>
|
||||
public bool IsDone => _done;
|
||||
|
||||
/// <summary>
|
||||
/// 获取Task的结果值
|
||||
/// </summary>
|
||||
public T Result => _task.GetAwaiter().GetResult();
|
||||
|
||||
/// <summary>
|
||||
/// 获取Task的异常(如果有)
|
||||
/// </summary>
|
||||
public Exception? Exception => _task.Exception;
|
||||
}
|
||||
57
GFramework.Core/coroutine/instructions/WaitForTask.cs
Normal file
57
GFramework.Core/coroutine/instructions/WaitForTask.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine.instructions;
|
||||
|
||||
/// <summary>
|
||||
/// 等待Task完成的等待指令
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// 等待Task完成的等待指令
|
||||
/// </summary>
|
||||
public sealed class WaitForTask : IYieldInstruction
|
||||
{
|
||||
private readonly Task _task;
|
||||
private volatile bool _done;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化等待Task的指令
|
||||
/// </summary>
|
||||
/// <param name="task">要等待完成的Task</param>
|
||||
public WaitForTask(Task task)
|
||||
{
|
||||
_task = task ?? throw new ArgumentNullException(nameof(task));
|
||||
|
||||
// 检查Task是否已经完成
|
||||
if (_task.IsCompleted)
|
||||
{
|
||||
_done = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 注册完成回调
|
||||
_task.ContinueWith(_ =>
|
||||
{
|
||||
_done = true;
|
||||
}, TaskContinuationOptions.ExecuteSynchronously);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新方法,用于处理时间更新逻辑
|
||||
/// </summary>
|
||||
/// <param name="deltaTime">时间增量</param>
|
||||
public void Update(double deltaTime)
|
||||
{
|
||||
// Task的完成由ContinueWith回调设置
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取等待是否已完成
|
||||
/// </summary>
|
||||
public bool IsDone => _done;
|
||||
|
||||
/// <summary>
|
||||
/// 获取Task的异常(如果有)
|
||||
/// </summary>
|
||||
public Exception? Exception => _task.Exception;
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine;
|
||||
namespace GFramework.Core.coroutine.instructions;
|
||||
|
||||
/// <summary>
|
||||
/// 表示等待一帧的等待指令实现
|
||||
@ -1,6 +1,6 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine;
|
||||
namespace GFramework.Core.coroutine.instructions;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个等待直到指定条件满足的协程指令
|
||||
@ -1,6 +1,6 @@
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
|
||||
namespace GFramework.Core.coroutine;
|
||||
namespace GFramework.Core.coroutine.instructions;
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个等待条件为假时才完成的协程指令
|
||||
@ -1,6 +1,7 @@
|
||||
using System.Reflection;
|
||||
using GFramework.Core.Abstractions.coroutine;
|
||||
using GFramework.Core.coroutine;
|
||||
using GFramework.Core.coroutine.instructions;
|
||||
using Godot;
|
||||
|
||||
namespace GFramework.Godot.coroutine;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user