From 65d56d069655e98e9f4e624b83c30382cbd6a921 Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 15 Feb 2026 12:59:15 +0800 Subject: [PATCH] =?UTF-8?q?refactor(scene):=20=E9=87=8D=E6=9E=84=E5=9C=BA?= =?UTF-8?q?=E6=99=AF=E8=B7=AF=E7=94=B1=E7=B3=BB=E7=BB=9F=E5=B9=B6=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=E8=B5=84=E6=BA=90=E6=B3=A8=E5=86=8C=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将场景路由基类从同步改为异步实现,支持场景栈管理 - 添加场景行为接口定义及相关的进入参数接口 - 实现场景的压入、弹出、替换和清空等栈操作功能 - 迁移UI资源注册接口到资产模块下 - 移除缓存淘汰策略枚举并更新UI切换阶段命名空间引用 - 优化日志记录器创建方式和统一命名空间规范 --- .../{ui => asset}/IAssetRegistry.cs | 2 +- .../enums/CacheEvictionPolicy.cs | 17 -- .../enums/UITransitionPhases.cs | 2 +- GFramework.Game.Abstractions/scene/IScene.cs | 64 ++++++ .../scene/ISceneBehavior.cs | 88 ++++++++ .../scene/ISceneBehaviorProvider.cs | 27 +++ .../scene/ISceneEnterParam.cs | 20 ++ .../scene/ISceneFactory.cs | 30 +++ .../scene/ISceneRoot.cs | 68 +++++- .../scene/ISceneRouter.cs | 72 ++++++- .../ui/IUiTransitionHandler.cs | 4 +- GFramework.Game/scene/SceneRouterBase.cs | 198 ++++++++++++++---- GFramework.Game/ui/UiRouterBase.cs | 6 +- GFramework.Game/ui/UiTransitionPipeline.cs | 4 +- .../ui/handler/LoggingTransitionHandler.cs | 4 +- .../ui/handler/UiTransitionHandlerBase.cs | 4 +- GFramework.Godot/scene/IGodotSceneRegistry.cs | 2 +- GFramework.Godot/ui/IGodotUiRegistry.cs | 2 +- 18 files changed, 520 insertions(+), 94 deletions(-) rename GFramework.Game.Abstractions/{ui => asset}/IAssetRegistry.cs (87%) delete mode 100644 GFramework.Game.Abstractions/enums/CacheEvictionPolicy.cs create mode 100644 GFramework.Game.Abstractions/scene/IScene.cs create mode 100644 GFramework.Game.Abstractions/scene/ISceneBehavior.cs create mode 100644 GFramework.Game.Abstractions/scene/ISceneBehaviorProvider.cs create mode 100644 GFramework.Game.Abstractions/scene/ISceneEnterParam.cs create mode 100644 GFramework.Game.Abstractions/scene/ISceneFactory.cs diff --git a/GFramework.Game.Abstractions/ui/IAssetRegistry.cs b/GFramework.Game.Abstractions/asset/IAssetRegistry.cs similarity index 87% rename from GFramework.Game.Abstractions/ui/IAssetRegistry.cs rename to GFramework.Game.Abstractions/asset/IAssetRegistry.cs index fcf3a7e..1c35089 100644 --- a/GFramework.Game.Abstractions/ui/IAssetRegistry.cs +++ b/GFramework.Game.Abstractions/asset/IAssetRegistry.cs @@ -1,7 +1,7 @@ using GFramework.Core.Abstractions.registries; using GFramework.Core.Abstractions.utility; -namespace GFramework.Game.Abstractions.ui; +namespace GFramework.Game.Abstractions.asset; /// /// 资源注册表接口,用于管理指定类型T的资源注册和查找 diff --git a/GFramework.Game.Abstractions/enums/CacheEvictionPolicy.cs b/GFramework.Game.Abstractions/enums/CacheEvictionPolicy.cs deleted file mode 100644 index 030ebd5..0000000 --- a/GFramework.Game.Abstractions/enums/CacheEvictionPolicy.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace GFramework.Game.Abstractions.enums; - -/// -/// 缓存淘汰策略枚举 -/// -public enum CacheEvictionPolicy -{ - /// - /// 最近最少使用 - /// - Lru, - - /// - /// 最少使用频率 - /// - Lfu -} \ No newline at end of file diff --git a/GFramework.Game.Abstractions/enums/UITransitionPhases.cs b/GFramework.Game.Abstractions/enums/UITransitionPhases.cs index e35bfca..c87bf72 100644 --- a/GFramework.Game.Abstractions/enums/UITransitionPhases.cs +++ b/GFramework.Game.Abstractions/enums/UITransitionPhases.cs @@ -4,7 +4,7 @@ namespace GFramework.Game.Abstractions.enums; /// UI切换阶段枚举,定义UI切换过程中的不同阶段 /// [Flags] -public enum UITransitionPhases +public enum UiTransitionPhases { /// /// UI切换前阶段,在此阶段执行的Handler可以阻塞UI切换流程 diff --git a/GFramework.Game.Abstractions/scene/IScene.cs b/GFramework.Game.Abstractions/scene/IScene.cs new file mode 100644 index 0000000..887a403 --- /dev/null +++ b/GFramework.Game.Abstractions/scene/IScene.cs @@ -0,0 +1,64 @@ +// Copyright (c) 2026 GeWuYou +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace GFramework.Game.Abstractions.scene; + +/// +/// 场景接口,定义了场景生命周期管理的核心方法。 +/// 实现此接口的类需要处理场景从加载到卸载的完整生命周期。 +/// +public interface IScene +{ + /// + /// 异步加载场景所需资源。 + /// 在场景正式进入前调用,负责预加载场景所需的各类资源。 + /// + /// 场景进入参数,可能包含初始化数据或上下文信息。 + /// 表示加载操作完成的ValueTask。 + ValueTask OnLoadAsync(ISceneEnterParam? param); + + /// + /// 异步处理场景正式进入逻辑。 + /// 在资源加载完成后调用,启动场景的主要运行逻辑。 + /// + /// 表示进入操作完成的ValueTask。 + ValueTask OnEnterAsync(); + + /// + /// 异步处理场景暂停逻辑。 + /// 当场景被其他场景覆盖或失去焦点时调用。 + /// + /// 表示暂停操作完成的ValueTask。 + ValueTask OnPauseAsync(); + + /// + /// 异步处理场景恢复逻辑。 + /// 当场景重新获得焦点或从暂停状态恢复时调用。 + /// + /// 表示恢复操作完成的ValueTask。 + ValueTask OnResumeAsync(); + + /// + /// 异步处理场景退出逻辑。 + /// 在场景即将被替换或关闭时调用,执行清理工作。 + /// + /// 表示退出操作完成的ValueTask。 + ValueTask OnExitAsync(); + + /// + /// 异步卸载场景资源。 + /// 在场景完全退出后调用,释放占用的内存和资源。 + /// + /// 表示卸载操作完成的ValueTask。 + ValueTask OnUnloadAsync(); +} \ No newline at end of file diff --git a/GFramework.Game.Abstractions/scene/ISceneBehavior.cs b/GFramework.Game.Abstractions/scene/ISceneBehavior.cs new file mode 100644 index 0000000..820324f --- /dev/null +++ b/GFramework.Game.Abstractions/scene/ISceneBehavior.cs @@ -0,0 +1,88 @@ +// Copyright (c) 2026 GeWuYou +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace GFramework.Game.Abstractions.scene; + +/// +/// 场景行为接口,定义了场景生命周期管理的标准方法。 +/// 实现此接口的类需要处理场景的加载、激活、暂停、恢复和卸载等核心操作。 +/// +public interface ISceneBehavior +{ + /// + /// 获取场景的唯一标识符。 + /// 用于区分不同的场景实例。 + /// + string Key { get; } + + /// + /// 获取场景是否已加载完成的状态。 + /// true表示场景资源已加载,false表示未加载或正在加载中。 + /// + bool IsLoaded { get; } + + /// + /// 获取场景是否处于活跃运行状态。 + /// true表示场景正在前台运行,false表示场景已暂停或未激活。 + /// + bool IsActive { get; } + + /// + /// 获取场景是否正在进行切换操作。 + /// true表示场景正在加载、卸载或过渡中,false表示场景状态稳定。 + /// + bool IsTransitioning { get; } + + /// + /// 异步加载场景所需资源。 + /// 在场景正式进入前调用,负责预加载场景所需的各类资源。 + /// + /// 场景进入参数,可能包含初始化数据或上下文信息。 + /// 表示加载操作完成的ValueTask。 + ValueTask OnLoadAsync(ISceneEnterParam? param); + + /// + /// 异步处理场景正式进入逻辑。 + /// 在资源加载完成后调用,启动场景的主要运行逻辑。 + /// + /// 表示进入操作完成的ValueTask。 + ValueTask OnEnterAsync(); + + /// + /// 异步处理场景暂停逻辑。 + /// 当场景被其他场景覆盖或失去焦点时调用。 + /// + /// 表示暂停操作完成的ValueTask。 + ValueTask OnPauseAsync(); + + /// + /// 异步处理场景恢复逻辑。 + /// 当场景重新获得焦点或从暂停状态恢复时调用。 + /// + /// 表示恢复操作完成的ValueTask。 + ValueTask OnResumeAsync(); + + /// + /// 异步处理场景退出逻辑。 + /// 在场景即将被替换或关闭时调用,执行清理工作。 + /// + /// 表示退出操作完成的ValueTask。 + ValueTask OnExitAsync(); + + /// + /// 异步卸载场景资源。 + /// 在场景完全退出后调用,释放占用的内存和资源。 + /// + /// 表示卸载操作完成的ValueTask。 + ValueTask OnUnloadAsync(); +} \ No newline at end of file diff --git a/GFramework.Game.Abstractions/scene/ISceneBehaviorProvider.cs b/GFramework.Game.Abstractions/scene/ISceneBehaviorProvider.cs new file mode 100644 index 0000000..121e11d --- /dev/null +++ b/GFramework.Game.Abstractions/scene/ISceneBehaviorProvider.cs @@ -0,0 +1,27 @@ +// Copyright (c) 2026 GeWuYou +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace GFramework.Game.Abstractions.scene; + +/// +/// 场景行为提供者接口,用于获取场景行为实例。 +/// 实现此接口的类负责创建和提供具体的场景行为对象。 +/// +public interface ISceneBehaviorProvider +{ + /// + /// 获取场景行为实例。 + /// + /// 场景行为对象,包含场景的具体实现逻辑。 + ISceneBehavior GetScene(); +} \ No newline at end of file diff --git a/GFramework.Game.Abstractions/scene/ISceneEnterParam.cs b/GFramework.Game.Abstractions/scene/ISceneEnterParam.cs new file mode 100644 index 0000000..901facf --- /dev/null +++ b/GFramework.Game.Abstractions/scene/ISceneEnterParam.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2026 GeWuYou +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace GFramework.Game.Abstractions.scene; + +/// +/// 场景进入参数接口 +/// 该接口用于定义场景跳转时传递的参数数据结构 +/// +public interface ISceneEnterParam; \ No newline at end of file diff --git a/GFramework.Game.Abstractions/scene/ISceneFactory.cs b/GFramework.Game.Abstractions/scene/ISceneFactory.cs new file mode 100644 index 0000000..b1d61e1 --- /dev/null +++ b/GFramework.Game.Abstractions/scene/ISceneFactory.cs @@ -0,0 +1,30 @@ +// Copyright (c) 2026 GeWuYou +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using GFramework.Core.Abstractions.utility; + +namespace GFramework.Game.Abstractions.scene; + +/// +/// 场景工厂接口,继承自上下文工具接口。 +/// 负责根据场景键值创建对应的场景行为实例,是场景管理系统的核心工厂模式实现。 +/// +public interface ISceneFactory : IContextUtility +{ + /// + /// 根据指定的场景键值创建场景行为实例。 + /// + /// 场景的唯一标识符键值。 + /// 创建的场景行为对象,如果无法创建则返回null。 + ISceneBehavior Create(string sceneKy); +} \ No newline at end of file diff --git a/GFramework.Game.Abstractions/scene/ISceneRoot.cs b/GFramework.Game.Abstractions/scene/ISceneRoot.cs index 4947608..22673b1 100644 --- a/GFramework.Game.Abstractions/scene/ISceneRoot.cs +++ b/GFramework.Game.Abstractions/scene/ISceneRoot.cs @@ -14,18 +14,74 @@ namespace GFramework.Game.Abstractions.scene; /// -/// 定义场景根接口,用于管理场景的加载和卸载操作。 +/// 场景根接口,定义了场景管理系统的核心功能。 +/// 负责管理场景的生命周期、场景栈操作以及场景间的切换控制。 /// public interface ISceneRoot { /// - /// 替换当前场景为指定键对应的场景。 + /// 获取当前活动的场景行为对象。 + /// 返回null表示当前没有活动场景。 /// - /// 场景的唯一标识符,用于定位要加载的场景。 - void Replace(string key); + ISceneBehavior? Current { get; } /// - /// 卸载当前场景。 + /// 获取所有已加载场景的行为对象列表。 + /// 列表采用栈式结构,索引0为栈底场景,最后一个元素为当前活动场景。 /// - void Unload(); + IReadOnlyList Stack { get; } + + /// + /// 获取场景系统是否正在进行切换操作。 + /// true表示正在执行场景加载、卸载或切换,false表示系统空闲。 + /// + bool IsTransitioning { get; } + + /// + /// 异步替换当前场景,清空整个场景栈并加载新场景。 + /// 此操作会卸载所有现有场景,然后加载指定的新场景。 + /// + /// 要加载的场景唯一标识符。 + /// 可选的场景进入参数,用于传递初始化数据。 + /// 表示替换操作完成的ValueTask。 + ValueTask ReplaceAsync(string key, ISceneEnterParam? param = null); + + /// + /// 异步压入新场景到场景栈顶部。 + /// 当前场景会被暂停,新场景成为活动场景。 + /// + /// 要加载的场景唯一标识符。 + /// 可选的场景进入参数,用于传递初始化数据。 + /// 表示压入操作完成的ValueTask。 + ValueTask PushAsync(string key, ISceneEnterParam? param = null); + + /// + /// 异步弹出当前场景并恢复下一个场景。 + /// 当前场景会被卸载,栈中的下一个场景变为活动场景。 + /// + /// 表示弹出操作完成的ValueTask。 + ValueTask PopAsync(); + + /// + /// 异步清空所有已加载的场景。 + /// 卸载场景栈中的所有场景,使系统回到无场景状态。 + /// + /// 表示清空操作完成的ValueTask。 + ValueTask ClearAsync(); + + /// + /// 异步加载指定场景并返回场景行为实例。 + /// 此方法仅加载场景资源但不激活场景,通常用于预加载或后台加载场景。 + /// + /// 要加载的场景唯一标识符。 + /// 表示加载操作完成的ValueTask,包含加载成功的场景行为对象。 + ValueTask LoadAsync(string sceneKey); + + /// + /// 异步卸载指定的场景行为实例。 + /// 释放场景占用的资源并从系统中移除该场景实例。 + /// + /// 要卸载的场景行为实例。 + /// 表示卸载操作完成的ValueTask。 + ValueTask UnloadAsync(ISceneBehavior scene); } \ No newline at end of file diff --git a/GFramework.Game.Abstractions/scene/ISceneRouter.cs b/GFramework.Game.Abstractions/scene/ISceneRouter.cs index ee896f0..d7708a5 100644 --- a/GFramework.Game.Abstractions/scene/ISceneRouter.cs +++ b/GFramework.Game.Abstractions/scene/ISceneRouter.cs @@ -16,30 +16,82 @@ using GFramework.Core.Abstractions.system; namespace GFramework.Game.Abstractions.scene; /// -/// 定义场景路由接口,用于管理场景的切换、卸载以及根节点绑定。 +/// 场景路由接口,继承自系统接口。 +/// 负责管理场景的导航、切换、生命周期控制以及与场景根节点的绑定操作。 +/// 提供完整的场景栈管理和路由功能。 /// public interface ISceneRouter : ISystem { /// - /// 获取当前场景的唯一标识符(键)。 + /// 获取当前活动的场景行为对象。 + /// 返回null表示当前没有活动场景。 + /// + ISceneBehavior? Current { get; } + + /// + /// 获取当前活动场景的唯一标识键值。 + /// 返回null表示当前没有活动场景。 /// - /// 当前场景的键,如果未加载任何场景则返回 null。 string? CurrentKey { get; } /// - /// 替换当前场景为指定键对应的场景。 + /// 获取场景行为对象的只读列表,表示当前的场景栈结构。 + /// 列表中第一个元素为栈底场景,最后一个元素为当前活动场景。 /// - /// 目标场景的唯一标识符(键)。 - void Replace(string sceneKey); + IReadOnlyList Stack { get; } /// - /// 卸载当前场景。 + /// 获取场景路由器是否正在进行场景切换操作。 + /// true表示正在执行场景加载、卸载或切换,false表示系统空闲。 /// - void Unload(); + bool IsTransitioning { get; } /// - /// 将指定的场景根节点与当前路由进行绑定。 + /// 绑定场景根节点,建立路由与场景管理器的连接。 /// - /// 需要绑定的场景根节点。 + /// 要绑定的场景根节点实例。 void BindRoot(ISceneRoot root); + + /// + /// 异步替换当前所有场景,清空整个场景栈并加载新的场景。 + /// 此操作会卸载所有现有场景,然后加载指定的新场景。 + /// + /// 要加载的场景唯一标识符。 + /// 可选的场景进入参数,用于传递初始化数据。 + /// 表示替换操作完成的ValueTask。 + ValueTask ReplaceAsync( + string sceneKey, + ISceneEnterParam? param = null); + + /// + /// 异步压入新场景到场景栈顶部。 + /// 当前场景会被暂停,新场景成为活动场景。 + /// + /// 要加载的场景唯一标识符。 + /// 可选的场景进入参数,用于传递初始化数据。 + /// 表示压入操作完成的ValueTask。 + ValueTask PushAsync( + string sceneKey, + ISceneEnterParam? param = null); + + /// + /// 异步弹出当前场景并恢复栈中的下一个场景。 + /// 当前场景会被卸载,栈中的下一个场景变为活动场景。 + /// + /// 表示弹出操作完成的ValueTask。 + ValueTask PopAsync(); + + /// + /// 异步清空所有已加载的场景。 + /// 卸载场景栈中的所有场景,使系统回到无场景状态。 + /// + /// 表示清空操作完成的ValueTask。 + ValueTask ClearAsync(); + + /// + /// 检查指定场景是否存在于当前场景栈中。 + /// + /// 要检查的场景唯一标识符。 + /// true表示场景在栈中存在,false表示不存在。 + bool Contains(string sceneKey); } \ No newline at end of file diff --git a/GFramework.Game.Abstractions/ui/IUiTransitionHandler.cs b/GFramework.Game.Abstractions/ui/IUiTransitionHandler.cs index 69c0f3e..4177849 100644 --- a/GFramework.Game.Abstractions/ui/IUiTransitionHandler.cs +++ b/GFramework.Game.Abstractions/ui/IUiTransitionHandler.cs @@ -16,7 +16,7 @@ public interface IUiTransitionHandler /// 处理器适用的阶段,默认为所有阶段 /// 可以使用Flags枚举指定多个阶段 /// - UITransitionPhases Phases { get; } + UiTransitionPhases Phases { get; } /// /// 判断是否应该处理当前事件 @@ -25,7 +25,7 @@ public interface IUiTransitionHandler /// UI切换事件 /// 当前阶段 /// 是否处理 - bool ShouldHandle(UiTransitionEvent @event, UITransitionPhases phases); + bool ShouldHandle(UiTransitionEvent @event, UiTransitionPhases phases); /// /// 处理UI切换事件 diff --git a/GFramework.Game/scene/SceneRouterBase.cs b/GFramework.Game/scene/SceneRouterBase.cs index 88af0c2..9b884a5 100644 --- a/GFramework.Game/scene/SceneRouterBase.cs +++ b/GFramework.Game/scene/SceneRouterBase.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2026 GeWuYou +// Copyright (c) 2026 GeWuYou // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -11,6 +11,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +using GFramework.Core.Abstractions.logging; +using GFramework.Core.logging; using GFramework.Core.system; using GFramework.Game.Abstractions.scene; @@ -23,70 +25,174 @@ namespace GFramework.Game.scene; public abstract class SceneRouterBase : AbstractSystem, ISceneRouter { - /// - /// 当前绑定的场景根节点。 - /// + private static readonly ILogger Log = + LoggerFactoryResolver.Provider.CreateLogger(nameof(SceneRouterBase)); + + private readonly Stack _stack = new(); + private readonly SemaphoreSlim _transitionLock = new(1, 1); + protected ISceneRoot? Root; - /// - /// 当前激活场景的键值。 - /// - public string? CurrentKey { get; private set; } + public ISceneBehavior? Current => _stack.Count > 0 ? _stack.Peek() : null; + + public string? CurrentKey => Current?.Key; + + public IReadOnlyList Stack => + _stack.Reverse().ToList(); + + public bool IsTransitioning { get; private set; } - /// - /// 绑定场景根节点。 - /// - /// 要绑定的场景根节点。 public void BindRoot(ISceneRoot root) { Root = root; + Log.Debug("Bind Scene Root: {0}", root.GetType().Name); } - /// - /// 替换当前场景为指定键值的新场景。 - /// 在替换前后会调用相应的虚方法以支持扩展逻辑。 - /// - /// 目标场景的键值。 - public void Replace(string sceneKey) + #region Replace + + public async ValueTask ReplaceAsync( + string sceneKey, + ISceneEnterParam? param = null) { - // 调用替换前的钩子方法 - OnBeforeReplace(sceneKey); + await _transitionLock.WaitAsync(); + try + { + IsTransitioning = true; - // 执行场景替换操作 - Root!.Replace(sceneKey); + Log.Debug("Replace Scene: {0}", sceneKey); - // 更新当前场景键值 - CurrentKey = sceneKey; - - // 调用替换后的钩子方法 - OnAfterReplace(sceneKey); + await ClearInternalAsync(); + await PushInternalAsync(sceneKey, param); + } + finally + { + IsTransitioning = false; + _transitionLock.Release(); + } } - /// - /// 卸载当前场景,并将当前场景键值置为空。 - /// - public void Unload() + #endregion + + #region Query + + public bool Contains(string sceneKey) { - // 执行场景卸载操作 - Root!.Unload(); - - // 清空当前场景键值 - CurrentKey = null; + return _stack.Any(s => s.Key == sceneKey); } - /// - /// 场景替换前的虚方法,可在子类中重写以实现自定义逻辑。 - /// - /// 即将被替换的场景键值。 - protected virtual void OnBeforeReplace(string key) + #endregion + + #region Push + + public async ValueTask PushAsync( + string sceneKey, + ISceneEnterParam? param = null) { + await _transitionLock.WaitAsync(); + try + { + IsTransitioning = true; + await PushInternalAsync(sceneKey, param); + } + finally + { + IsTransitioning = false; + _transitionLock.Release(); + } } - /// - /// 场景替换后的虚方法,可在子类中重写以实现自定义逻辑。 - /// - /// 已替换的场景键值。 - protected virtual void OnAfterReplace(string key) + private async ValueTask PushInternalAsync( + string sceneKey, + ISceneEnterParam? param) { + if (Contains(sceneKey)) + { + Log.Warn("Scene already in stack: {0}", sceneKey); + return; + } + + var scene = await Root!.LoadAsync(sceneKey); + + if (_stack.Count > 0) + { + var current = _stack.Peek(); + await current.OnPauseAsync(); + } + + _stack.Push(scene); + + await scene.OnEnterAsync(param); + await scene.OnShowAsync(); + + Log.Debug("Push Scene: {0}, stackCount={1}", + sceneKey, _stack.Count); } + + #endregion + + #region Pop + + public async ValueTask PopAsync() + { + await _transitionLock.WaitAsync(); + try + { + IsTransitioning = true; + await PopInternalAsync(); + } + finally + { + IsTransitioning = false; + _transitionLock.Release(); + } + } + + private async ValueTask PopInternalAsync() + { + if (_stack.Count == 0) + return; + + var top = _stack.Pop(); + + await top.OnExitAsync(); + await Root!.UnloadAsync(top); + + if (_stack.Count > 0) + { + var next = _stack.Peek(); + await next.OnResumeAsync(); + await next.OnShowAsync(); + } + + Log.Debug("Pop Scene, stackCount={0}", _stack.Count); + } + + #endregion + + #region Clear + + public async ValueTask ClearAsync() + { + await _transitionLock.WaitAsync(); + try + { + IsTransitioning = true; + await ClearInternalAsync(); + } + finally + { + IsTransitioning = false; + _transitionLock.Release(); + } + } + + private async ValueTask ClearInternalAsync() + { + while (_stack.Count > 0) + { + await PopInternalAsync(); + } + } + + #endregion } \ No newline at end of file diff --git a/GFramework.Game/ui/UiRouterBase.cs b/GFramework.Game/ui/UiRouterBase.cs index 6c0b794..7be98ee 100644 --- a/GFramework.Game/ui/UiRouterBase.cs +++ b/GFramework.Game/ui/UiRouterBase.cs @@ -12,7 +12,7 @@ namespace GFramework.Game.ui; /// public abstract class UiRouterBase : AbstractSystem, IUiRouter { - private static readonly ILogger Log = LoggerFactoryResolver.Provider.CreateLogger("UiRouterBase"); + private static readonly ILogger Log = LoggerFactoryResolver.Provider.CreateLogger(nameof(UiRouterBase)); /// /// 路由守卫列表 @@ -519,7 +519,7 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter private void BeforeChange(UiTransitionEvent @event) { Log.Debug("BeforeChange phases started: {0}", @event.TransitionType); - _pipeline.ExecuteAsync(@event, UITransitionPhases.BeforeChange).GetAwaiter().GetResult(); + _pipeline.ExecuteAsync(@event, UiTransitionPhases.BeforeChange).GetAwaiter().GetResult(); Log.Debug("BeforeChange phases completed: {0}", @event.TransitionType); } @@ -530,7 +530,7 @@ public abstract class UiRouterBase : AbstractSystem, IUiRouter { try { - await _pipeline.ExecuteAsync(@event, UITransitionPhases.AfterChange).ConfigureAwait(false); + await _pipeline.ExecuteAsync(@event, UiTransitionPhases.AfterChange).ConfigureAwait(false); Log.Debug("AfterChange phases completed: {0}", @event.TransitionType); } catch (Exception ex) diff --git a/GFramework.Game/ui/UiTransitionPipeline.cs b/GFramework.Game/ui/UiTransitionPipeline.cs index 2e2d459..c5bd9e4 100644 --- a/GFramework.Game/ui/UiTransitionPipeline.cs +++ b/GFramework.Game/ui/UiTransitionPipeline.cs @@ -62,7 +62,7 @@ public class UiTransitionPipeline /// 异步任务 public async Task ExecuteAsync( UiTransitionEvent @event, - UITransitionPhases phases, + UiTransitionPhases phases, CancellationToken cancellationToken = default ) { @@ -102,7 +102,7 @@ public class UiTransitionPipeline private List FilterAndSortHandlers( UiTransitionEvent @event, - UITransitionPhases phases) + UiTransitionPhases phases) { return _handlers .Where(h => h.Phases.HasFlag(phases) && h.ShouldHandle(@event, phases)) diff --git a/GFramework.Game/ui/handler/LoggingTransitionHandler.cs b/GFramework.Game/ui/handler/LoggingTransitionHandler.cs index e3f799e..39d81fa 100644 --- a/GFramework.Game/ui/handler/LoggingTransitionHandler.cs +++ b/GFramework.Game/ui/handler/LoggingTransitionHandler.cs @@ -13,7 +13,7 @@ public sealed class LoggingTransitionHandler : UiTransitionHandlerBase /// /// 日志记录器实例,用于记录UI切换相关信息 /// - private static readonly ILogger Log = LoggerFactoryResolver.Provider.CreateLogger("LoggingTransitionHandler"); + private static readonly ILogger Log = LoggerFactoryResolver.Provider.CreateLogger(nameof(LoggingTransitionHandler)); /// /// 获取处理器优先级,数值越大优先级越高 @@ -23,7 +23,7 @@ public sealed class LoggingTransitionHandler : UiTransitionHandlerBase /// /// 获取处理器处理的UI切换阶段,处理所有阶段 /// - public override UITransitionPhases Phases => UITransitionPhases.All; + public override UiTransitionPhases Phases => UiTransitionPhases.All; /// /// 处理UI切换事件的异步方法 diff --git a/GFramework.Game/ui/handler/UiTransitionHandlerBase.cs b/GFramework.Game/ui/handler/UiTransitionHandlerBase.cs index 98394af..b868996 100644 --- a/GFramework.Game/ui/handler/UiTransitionHandlerBase.cs +++ b/GFramework.Game/ui/handler/UiTransitionHandlerBase.cs @@ -11,7 +11,7 @@ public abstract class UiTransitionHandlerBase : IUiTransitionHandler /// /// 处理器适用的阶段,默认为所有阶段 /// - public virtual UITransitionPhases Phases => UITransitionPhases.All; + public virtual UiTransitionPhases Phases => UiTransitionPhases.All; /// /// 优先级,需要在子类中实现 @@ -21,7 +21,7 @@ public abstract class UiTransitionHandlerBase : IUiTransitionHandler /// /// 判断是否应该处理当前事件,默认返回true /// - public virtual bool ShouldHandle(UiTransitionEvent @event, UITransitionPhases phases) + public virtual bool ShouldHandle(UiTransitionEvent @event, UiTransitionPhases phases) { return true; } diff --git a/GFramework.Godot/scene/IGodotSceneRegistry.cs b/GFramework.Godot/scene/IGodotSceneRegistry.cs index 760db28..b28ceed 100644 --- a/GFramework.Godot/scene/IGodotSceneRegistry.cs +++ b/GFramework.Godot/scene/IGodotSceneRegistry.cs @@ -1,4 +1,4 @@ -using GFramework.Game.Abstractions.ui; +using GFramework.Game.Abstractions.asset; using Godot; namespace GFramework.Godot.scene; diff --git a/GFramework.Godot/ui/IGodotUiRegistry.cs b/GFramework.Godot/ui/IGodotUiRegistry.cs index 3b90918..a67b411 100644 --- a/GFramework.Godot/ui/IGodotUiRegistry.cs +++ b/GFramework.Godot/ui/IGodotUiRegistry.cs @@ -1,4 +1,4 @@ -using GFramework.Game.Abstractions.ui; +using GFramework.Game.Abstractions.asset; using Godot; namespace GFramework.Godot.ui;