mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-06 16:16:44 +08:00
feat(coroutine): 添加协程系统核心组件与Godot集成
- 实现CoroutineMetadata类存储协程元数据信息 - 创建CoroutineScheduler协程调度器管理协程生命周期 - 添加CoroutineSlot类管理单个协程执行状态 - 实现GodotTimeSource时间源支持缩放和真实时间 - 添加Timing类提供Godot协程管理功能 - 实现CoroutineNodeExtensions扩展方法支持节点生命周期管理 - 支持协程分组、标签、优先级等功能 - 提供协程暂停、恢复、终止等控制接口 - 实现协程统计和快照功能 - 添加等待指令处理机制支持多种等待类型
This commit is contained in:
parent
a22e522cf9
commit
1c41c57d72
@ -0,0 +1,28 @@
|
||||
namespace GFramework.Core.Abstractions.Coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 表示协程的最终完成结果。
|
||||
/// </summary>
|
||||
public enum CoroutineCompletionStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// 调度器无法确认该句柄的最终结果。
|
||||
/// 这通常意味着句柄无效,或者句柄对应的历史结果已经不可用。
|
||||
/// </summary>
|
||||
Unknown,
|
||||
|
||||
/// <summary>
|
||||
/// 协程自然执行结束。
|
||||
/// </summary>
|
||||
Completed,
|
||||
|
||||
/// <summary>
|
||||
/// 协程被外部终止、清空或取消令牌中断。
|
||||
/// </summary>
|
||||
Cancelled,
|
||||
|
||||
/// <summary>
|
||||
/// 协程在推进过程中抛出了异常。
|
||||
/// </summary>
|
||||
Faulted
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
namespace GFramework.Core.Abstractions.Coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 表示协程调度器当前所处的执行阶段。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 某些等待指令具有阶段语义,例如 <c>WaitForFixedUpdate</c> 和 <c>WaitForEndOfFrame</c>。
|
||||
/// 宿主应为这些语义提供匹配的调度器阶段,否则这类等待不会自然完成。
|
||||
/// </remarks>
|
||||
public enum CoroutineExecutionStage
|
||||
{
|
||||
/// <summary>
|
||||
/// 默认更新阶段。
|
||||
/// 普通时间等待、下一帧等待以及大多数条件等待都会在该阶段推进。
|
||||
/// </summary>
|
||||
Update,
|
||||
|
||||
/// <summary>
|
||||
/// 固定更新阶段。
|
||||
/// 仅与固定步相关的等待指令会在该阶段完成。
|
||||
/// </summary>
|
||||
FixedUpdate,
|
||||
|
||||
/// <summary>
|
||||
/// 帧结束阶段。
|
||||
/// 仅与帧尾或延迟执行相关的等待指令会在该阶段完成。
|
||||
/// </summary>
|
||||
EndOfFrame
|
||||
}
|
||||
@ -0,0 +1,174 @@
|
||||
using GFramework.Core.Abstractions.Coroutine;
|
||||
using GFramework.Core.Coroutine;
|
||||
using GFramework.Core.Coroutine.Instructions;
|
||||
|
||||
namespace GFramework.Core.Tests.Coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 协程调度器增强行为测试。
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public sealed class CoroutineSchedulerAdvancedTests
|
||||
{
|
||||
/// <summary>
|
||||
/// 验证 WaitForSecondsRealtime 使用独立的真实时间源推进。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForSecondsRealtime_Should_Use_Realtime_TimeSource_When_Provided()
|
||||
{
|
||||
var scaledTime = new FakeTimeSource();
|
||||
var realtimeTime = new FakeTimeSource();
|
||||
var scheduler = new CoroutineScheduler(
|
||||
scaledTime,
|
||||
realtimeTimeSource: realtimeTime);
|
||||
var completed = false;
|
||||
|
||||
IEnumerator<IYieldInstruction> Coroutine()
|
||||
{
|
||||
yield return new WaitForSecondsRealtime(1.0);
|
||||
completed = true;
|
||||
}
|
||||
|
||||
scheduler.Run(Coroutine());
|
||||
|
||||
scaledTime.Advance(0.1);
|
||||
realtimeTime.Advance(0.4);
|
||||
scheduler.Update();
|
||||
|
||||
Assert.That(completed, Is.False);
|
||||
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(1));
|
||||
|
||||
scaledTime.Advance(0.1);
|
||||
realtimeTime.Advance(0.6);
|
||||
scheduler.Update();
|
||||
|
||||
Assert.That(completed, Is.True);
|
||||
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证固定更新等待指令仅在固定阶段调度器中推进。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WaitForFixedUpdate_Should_Only_Advance_On_FixedUpdate_Scheduler()
|
||||
{
|
||||
var defaultTime = new FakeTimeSource();
|
||||
var fixedTime = new FakeTimeSource();
|
||||
var defaultScheduler = new CoroutineScheduler(defaultTime);
|
||||
var fixedScheduler = new CoroutineScheduler(
|
||||
fixedTime,
|
||||
executionStage: CoroutineExecutionStage.FixedUpdate);
|
||||
var defaultCompleted = false;
|
||||
var fixedCompleted = false;
|
||||
|
||||
IEnumerator<IYieldInstruction> DefaultCoroutine()
|
||||
{
|
||||
yield return new WaitForFixedUpdate();
|
||||
defaultCompleted = true;
|
||||
}
|
||||
|
||||
IEnumerator<IYieldInstruction> FixedCoroutine()
|
||||
{
|
||||
yield return new WaitForFixedUpdate();
|
||||
fixedCompleted = true;
|
||||
}
|
||||
|
||||
defaultScheduler.Run(DefaultCoroutine());
|
||||
fixedScheduler.Run(FixedCoroutine());
|
||||
|
||||
defaultTime.Advance(0.1);
|
||||
fixedTime.Advance(0.1);
|
||||
defaultScheduler.Update();
|
||||
fixedScheduler.Update();
|
||||
|
||||
Assert.That(defaultCompleted, Is.False);
|
||||
Assert.That(defaultScheduler.ActiveCoroutineCount, Is.EqualTo(1));
|
||||
Assert.That(fixedCompleted, Is.True);
|
||||
Assert.That(fixedScheduler.ActiveCoroutineCount, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证取消令牌会在下一次调度循环中终止协程并记录结果。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task CancellationToken_Should_Cancel_Coroutine_On_Next_Update()
|
||||
{
|
||||
var timeSource = new FakeTimeSource();
|
||||
var scheduler = new CoroutineScheduler(timeSource);
|
||||
using var cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
IEnumerator<IYieldInstruction> Coroutine()
|
||||
{
|
||||
yield return new Delay(10);
|
||||
}
|
||||
|
||||
var handle = scheduler.Run(Coroutine(), cancellationToken: cancellationTokenSource.Token);
|
||||
cancellationTokenSource.Cancel();
|
||||
|
||||
timeSource.Advance(0.1);
|
||||
scheduler.Update();
|
||||
|
||||
var status = await scheduler.WaitForCompletionAsync(handle);
|
||||
|
||||
Assert.That(scheduler.IsCoroutineAlive(handle), Is.False);
|
||||
Assert.That(status, Is.EqualTo(CoroutineCompletionStatus.Cancelled));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证调度器可以暴露活跃协程快照。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TryGetSnapshot_Should_Return_Current_Waiting_Instruction_And_Stage()
|
||||
{
|
||||
var timeSource = new FakeTimeSource();
|
||||
var scheduler = new CoroutineScheduler(
|
||||
timeSource,
|
||||
executionStage: CoroutineExecutionStage.EndOfFrame);
|
||||
|
||||
IEnumerator<IYieldInstruction> Coroutine()
|
||||
{
|
||||
yield return new WaitForEndOfFrame();
|
||||
}
|
||||
|
||||
var handle = scheduler.Run(Coroutine(), tag: "ui", group: "frame-end");
|
||||
|
||||
var found = scheduler.TryGetSnapshot(handle, out var snapshot);
|
||||
|
||||
Assert.That(found, Is.True);
|
||||
Assert.That(snapshot.Handle, Is.EqualTo(handle));
|
||||
Assert.That(snapshot.Tag, Is.EqualTo("ui"));
|
||||
Assert.That(snapshot.Group, Is.EqualTo("frame-end"));
|
||||
Assert.That(snapshot.IsWaiting, Is.True);
|
||||
Assert.That(snapshot.WaitingInstructionType, Is.EqualTo(typeof(WaitForEndOfFrame)));
|
||||
Assert.That(snapshot.ExecutionStage, Is.EqualTo(CoroutineExecutionStage.EndOfFrame));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证异常结束的协程会记录为 Faulted。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task WaitForCompletionAsync_Should_Return_Faulted_For_Failing_Coroutine()
|
||||
{
|
||||
var timeSource = new FakeTimeSource();
|
||||
var scheduler = new CoroutineScheduler(timeSource);
|
||||
|
||||
IEnumerator<IYieldInstruction> Coroutine()
|
||||
{
|
||||
yield return new WaitOneFrame();
|
||||
throw new InvalidOperationException("boom");
|
||||
#pragma warning disable CS0162
|
||||
yield break;
|
||||
#pragma warning restore CS0162
|
||||
}
|
||||
|
||||
var handle = scheduler.Run(Coroutine());
|
||||
timeSource.Advance(0.1);
|
||||
scheduler.Update();
|
||||
timeSource.Advance(0.1);
|
||||
scheduler.Update();
|
||||
|
||||
var status = await scheduler.WaitForCompletionAsync(handle);
|
||||
|
||||
Assert.That(status, Is.EqualTo(CoroutineCompletionStatus.Faulted));
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,12 @@ namespace GFramework.Core.Coroutine;
|
||||
/// </summary>
|
||||
internal class CoroutineMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// 协程所属调度器的执行阶段。
|
||||
/// 该值用于诊断等待语义是否与当前宿主阶段匹配。
|
||||
/// </summary>
|
||||
public CoroutineExecutionStage ExecutionStage;
|
||||
|
||||
/// <summary>
|
||||
/// 协程的分组标识符,用于批量管理协程
|
||||
/// </summary>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,12 @@ namespace GFramework.Core.Coroutine;
|
||||
/// </summary>
|
||||
internal sealed class CoroutineSlot
|
||||
{
|
||||
/// <summary>
|
||||
/// 由外部取消令牌创建的注册。
|
||||
/// 调度器在协程结束时必须释放该注册,避免泄漏取消回调。
|
||||
/// </summary>
|
||||
public CancellationTokenRegistration CancellationRegistration;
|
||||
|
||||
/// <summary>
|
||||
/// 协程枚举器,包含协程的执行逻辑
|
||||
/// </summary>
|
||||
|
||||
29
GFramework.Core/Coroutine/CoroutineSnapshot.cs
Normal file
29
GFramework.Core/Coroutine/CoroutineSnapshot.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using GFramework.Core.Abstractions.Coroutine;
|
||||
|
||||
namespace GFramework.Core.Coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// 表示某个活跃协程在调度器中的只读运行快照。
|
||||
/// </summary>
|
||||
/// <param name="Handle">协程句柄。</param>
|
||||
/// <param name="State">当前协程状态。</param>
|
||||
/// <param name="Priority">当前协程优先级。</param>
|
||||
/// <param name="Tag">可选标签。</param>
|
||||
/// <param name="Group">可选分组。</param>
|
||||
/// <param name="StartTimeMs">协程启动时间,单位为毫秒。</param>
|
||||
/// <param name="IsWaiting">当前是否正被等待指令阻塞。</param>
|
||||
/// <param name="WaitingInstructionType">
|
||||
/// 当前等待指令的具体类型。
|
||||
/// 若协程当前未处于等待状态,则该值为 <see langword="null" />。
|
||||
/// </param>
|
||||
/// <param name="ExecutionStage">所属调度器的执行阶段。</param>
|
||||
public readonly record struct CoroutineSnapshot(
|
||||
CoroutineHandle Handle,
|
||||
CoroutineState State,
|
||||
CoroutinePriority Priority,
|
||||
string? Tag,
|
||||
string? Group,
|
||||
double StartTimeMs,
|
||||
bool IsWaiting,
|
||||
Type? WaitingInstructionType,
|
||||
CoroutineExecutionStage ExecutionStage);
|
||||
52
GFramework.Godot.Tests/Coroutine/GodotTimeSourceTests.cs
Normal file
52
GFramework.Godot.Tests/Coroutine/GodotTimeSourceTests.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using System.Collections.Generic;
|
||||
using GFramework.Godot.Coroutine;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Godot.Tests.Coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// GodotTimeSource 的单元测试。
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public sealed class GodotTimeSourceTests
|
||||
{
|
||||
/// <summary>
|
||||
/// 验证增量模式会直接累加传入的 delta。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Update_Should_Accumulate_Delta_When_Using_Delta_Mode()
|
||||
{
|
||||
var values = new Queue<double>([0.1, 0.2]);
|
||||
var timeSource = new GodotTimeSource(() => values.Dequeue());
|
||||
|
||||
timeSource.Update();
|
||||
Assert.That(timeSource.DeltaTime, Is.EqualTo(0.1).Within(0.0001));
|
||||
Assert.That(timeSource.CurrentTime, Is.EqualTo(0.1).Within(0.0001));
|
||||
|
||||
timeSource.Update();
|
||||
Assert.That(timeSource.DeltaTime, Is.EqualTo(0.2).Within(0.0001));
|
||||
Assert.That(timeSource.CurrentTime, Is.EqualTo(0.3).Within(0.0001));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证绝对时间模式会根据前后两次采样计算 delta。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Update_Should_Calculate_Delta_When_Using_Absolute_Time_Mode()
|
||||
{
|
||||
var values = new Queue<double>([1.0, 1.25, 2.0]);
|
||||
var timeSource = new GodotTimeSource(() => values.Dequeue(), useAbsoluteTime: true);
|
||||
|
||||
timeSource.Update();
|
||||
Assert.That(timeSource.DeltaTime, Is.EqualTo(0).Within(0.0001));
|
||||
Assert.That(timeSource.CurrentTime, Is.EqualTo(1.0).Within(0.0001));
|
||||
|
||||
timeSource.Update();
|
||||
Assert.That(timeSource.DeltaTime, Is.EqualTo(0.25).Within(0.0001));
|
||||
Assert.That(timeSource.CurrentTime, Is.EqualTo(1.25).Within(0.0001));
|
||||
|
||||
timeSource.Update();
|
||||
Assert.That(timeSource.DeltaTime, Is.EqualTo(0.75).Within(0.0001));
|
||||
Assert.That(timeSource.CurrentTime, Is.EqualTo(2.0).Within(0.0001));
|
||||
}
|
||||
}
|
||||
23
GFramework.Godot.Tests/GFramework.Godot.Tests.csproj
Normal file
23
GFramework.Godot.Tests/GFramework.Godot.Tests.csproj
Normal file
@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TestTargetFrameworks Condition="'$(TestTargetFrameworks)' == ''">net10.0</TestTargetFrameworks>
|
||||
<TargetFrameworks>$(TestTargetFrameworks)</TargetFrameworks>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<WarningLevel>0</WarningLevel>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0"/>
|
||||
<PackageReference Include="NUnit" Version="4.5.1"/>
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GFramework.Godot\GFramework.Godot.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@ -35,6 +35,25 @@ public static class CoroutineNodeExtensions
|
||||
return Timing.RunCoroutine(coroutine, segment, tag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 以指定节点作为生命周期所有者运行协程。
|
||||
/// </summary>
|
||||
/// <param name="owner">拥有该协程生命周期的节点。</param>
|
||||
/// <param name="coroutine">要启动的协程枚举器。</param>
|
||||
/// <param name="segment">协程运行的时间段。</param>
|
||||
/// <param name="tag">协程标签。</param>
|
||||
/// <param name="cancellationToken">可选取消令牌。</param>
|
||||
/// <returns>返回协程句柄。</returns>
|
||||
public static CoroutineHandle RunCoroutine(
|
||||
this Node owner,
|
||||
IEnumerator<IYieldInstruction> coroutine,
|
||||
Segment segment = Segment.Process,
|
||||
string? tag = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Timing.RunOwnedCoroutine(owner, coroutine, segment, tag, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 让协程在指定节点被销毁时自动取消。
|
||||
/// </summary>
|
||||
|
||||
@ -1,42 +1,81 @@
|
||||
using GFramework.Core.Abstractions.Coroutine;
|
||||
using Godot;
|
||||
|
||||
namespace GFramework.Godot.Coroutine;
|
||||
|
||||
/// <summary>
|
||||
/// Godot时间源实现,用于提供基于Godot引擎的时间信息
|
||||
/// Godot 时间源实现,用于为协程调度器提供缩放时间或真实时间数据。
|
||||
/// </summary>
|
||||
/// <param name="getDeltaFunc">获取增量时间的函数委托</param>
|
||||
public class GodotTimeSource(Func<double> getDeltaFunc) : ITimeSource
|
||||
/// <param name="timeProvider">
|
||||
/// 时间提供函数。
|
||||
/// 在默认模式下该函数返回“本帧增量”;在绝对时间模式下该函数返回“当前绝对时间(秒)”。
|
||||
/// </param>
|
||||
/// <param name="useAbsoluteTime">
|
||||
/// 是否把 <paramref name="timeProvider" /> 返回值解释为绝对时间。
|
||||
/// 启用后,<see cref="Update" /> 会通过相邻两次读数计算 <see cref="DeltaTime" />。
|
||||
/// </param>
|
||||
public sealed class GodotTimeSource(Func<double> timeProvider, bool useAbsoluteTime = false) : ITimeSource
|
||||
{
|
||||
private readonly Func<double> _getDeltaFunc = getDeltaFunc ?? throw new ArgumentNullException(nameof(getDeltaFunc));
|
||||
private readonly Func<double> _timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||
private bool _initialized;
|
||||
private double _lastAbsoluteTime;
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前累计时间
|
||||
/// 获取当前累计时间。
|
||||
/// </summary>
|
||||
public double CurrentTime { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取上一帧的时间增量
|
||||
/// 获取上一帧的时间增量。
|
||||
/// </summary>
|
||||
public double DeltaTime { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新时间源,计算新的增量时间和累计时间
|
||||
/// 更新时间源,计算新的时间增量与累计时间。
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
// 调用外部提供的函数获取当前帧的时间增量
|
||||
DeltaTime = _getDeltaFunc();
|
||||
// 累加到总时间中
|
||||
var value = _timeProvider();
|
||||
if (useAbsoluteTime)
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
_initialized = true;
|
||||
_lastAbsoluteTime = value;
|
||||
CurrentTime = value;
|
||||
DeltaTime = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
DeltaTime = Math.Max(0, value - _lastAbsoluteTime);
|
||||
_lastAbsoluteTime = value;
|
||||
CurrentTime = value;
|
||||
return;
|
||||
}
|
||||
|
||||
DeltaTime = value;
|
||||
CurrentTime += DeltaTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置时间源到初始状态
|
||||
/// 创建基于 Godot 单调时钟的真实时间源。
|
||||
/// </summary>
|
||||
/// <returns>返回一个不受场景暂停与时间缩放影响的时间源实例。</returns>
|
||||
public static GodotTimeSource CreateRealtime()
|
||||
{
|
||||
return new GodotTimeSource(
|
||||
() => Time.GetTicksUsec() / 1_000_000.0,
|
||||
useAbsoluteTime: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置时间源到初始状态。
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
CurrentTime = 0;
|
||||
DeltaTime = 0;
|
||||
_initialized = false;
|
||||
_lastAbsoluteTime = 0;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -62,6 +62,7 @@
|
||||
<None Remove="GFramework.SourceGenerators.Attributes\**"/>
|
||||
<None Remove="Godot\**"/>
|
||||
<None Remove="GFramework.Game.Tests\**"/>
|
||||
<None Remove="GFramework.Godot.Tests\**"/>
|
||||
</ItemGroup>
|
||||
<!-- 聚合核心模块 -->
|
||||
<ItemGroup>
|
||||
@ -102,6 +103,7 @@
|
||||
<Compile Remove="GFramework.SourceGenerators.Attributes\**"/>
|
||||
<Compile Remove="Godot\**"/>
|
||||
<Compile Remove="GFramework.Game.Tests\**"/>
|
||||
<Compile Remove="GFramework.Godot.Tests\**"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Remove="GFramework.Core\**"/>
|
||||
@ -128,6 +130,7 @@
|
||||
<EmbeddedResource Remove="GFramework.SourceGenerators.Attributes\**"/>
|
||||
<EmbeddedResource Remove="Godot\**"/>
|
||||
<EmbeddedResource Remove="GFramework.Game.Tests\**"/>
|
||||
<EmbeddedResource Remove="GFramework.Godot.Tests\**"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Remove="AnalyzerReleases.Shipped.md"/>
|
||||
|
||||
@ -36,6 +36,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Game.Tests", "GF
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Godot.SourceGenerators.Tests", "GFramework.Godot.SourceGenerators.Tests\GFramework.Godot.SourceGenerators.Tests.csproj", "{E315489C-248A-4ABB-BD92-96F9F3AFE2C1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Godot.Tests", "GFramework.Godot.Tests\GFramework.Godot.Tests.csproj", "{576119E2-13D0-4ACF-A012-D01C320E8BF3}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -262,6 +264,18 @@ Global
|
||||
{E315489C-248A-4ABB-BD92-96F9F3AFE2C1}.Release|x64.Build.0 = Release|Any CPU
|
||||
{E315489C-248A-4ABB-BD92-96F9F3AFE2C1}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E315489C-248A-4ABB-BD92-96F9F3AFE2C1}.Release|x86.Build.0 = Release|Any CPU
|
||||
{576119E2-13D0-4ACF-A012-D01C320E8BF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{576119E2-13D0-4ACF-A012-D01C320E8BF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{576119E2-13D0-4ACF-A012-D01C320E8BF3}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{576119E2-13D0-4ACF-A012-D01C320E8BF3}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{576119E2-13D0-4ACF-A012-D01C320E8BF3}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{576119E2-13D0-4ACF-A012-D01C320E8BF3}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{576119E2-13D0-4ACF-A012-D01C320E8BF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{576119E2-13D0-4ACF-A012-D01C320E8BF3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{576119E2-13D0-4ACF-A012-D01C320E8BF3}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{576119E2-13D0-4ACF-A012-D01C320E8BF3}.Release|x64.Build.0 = Release|Any CPU
|
||||
{576119E2-13D0-4ACF-A012-D01C320E8BF3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{576119E2-13D0-4ACF-A012-D01C320E8BF3}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user