// 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.Logging; using GFramework.Core.Extensions; using GFramework.Core.Logging; using GFramework.Game.Abstractions.Enums; using GFramework.Game.Abstractions.Scene; using GFramework.Game.Routing; namespace GFramework.Game.Scene; /// /// 场景路由基类,提供场景切换和卸载的基础功能。 /// 实现了 接口,用于管理场景的加载、替换和卸载操作。 /// public abstract class SceneRouterBase : RouterBase, ISceneRouter { private static readonly ILogger Log = LoggerFactoryResolver.Provider.CreateLogger(nameof(SceneRouterBase)); private readonly SceneTransitionPipeline _pipeline = new(); private readonly SemaphoreSlim _transitionLock = new(1, 1); private ISceneFactory _factory = null!; /// /// 场景根节点 /// protected ISceneRoot? Root; /// /// 获取当前场景行为对象。 /// public new ISceneBehavior? Current => Stack.Count > 0 ? Stack.Peek() : null; /// /// 获取当前场景的键名。 /// public new string? CurrentKey => Current?.Key; /// /// 获取场景栈的只读视图,按压入顺序排列(从栈底到栈顶)。 /// IEnumerable ISceneRouter.Stack => base.Stack.Reverse(); /// /// 获取是否正在进行场景转换。 /// public bool IsTransitioning { get; private set; } /// /// 绑定场景根节点。 /// /// 场景根节点实例。 public void BindRoot(ISceneRoot root) { Root = root; Log.Debug("Bind Scene Root: {0}", root.GetType().Name); } #region Replace /// /// 替换当前场景为指定场景。 /// /// 目标场景键名。 /// 场景进入参数。 /// 异步任务。 public async ValueTask ReplaceAsync( string sceneKey, ISceneEnterParam? param = null) { await _transitionLock.WaitAsync(); try { IsTransitioning = true; var @event = CreateEvent(sceneKey, SceneTransitionType.Replace, param); await _pipeline.ExecuteAroundAsync(@event, async () => { await BeforeChangeAsync(@event); await ClearInternalAsync(); await PushInternalAsync(sceneKey, param); await AfterChangeAsync(@event); }); } finally { IsTransitioning = false; _transitionLock.Release(); } } #endregion #region Query /// /// 检查指定场景是否在栈中。 /// /// 场景键名。 /// 如果场景在栈中返回true,否则返回false。 public new bool Contains(string sceneKey) { return Stack.Any(s => s.Key == sceneKey); } #endregion /// /// 注册场景过渡处理器。 /// /// 处理器实例。 /// 执行选项。 public void RegisterHandler(ISceneTransitionHandler handler, SceneTransitionHandlerOptions? options = null) { _pipeline.RegisterHandler(handler, options); } /// /// 注销场景过渡处理器。 /// /// 处理器实例。 public void UnregisterHandler(ISceneTransitionHandler handler) { _pipeline.UnregisterHandler(handler); } /// /// 注册环绕场景过渡处理器。 /// /// 环绕处理器实例。 /// 处理器选项配置。 public void RegisterAroundHandler( ISceneAroundTransitionHandler handler, SceneTransitionHandlerOptions? options = null) { _pipeline.RegisterAroundHandler(handler, options); } /// /// 注销环绕场景过渡处理器。 /// /// 环绕处理器实例。 public void UnregisterAroundHandler( ISceneAroundTransitionHandler handler) { _pipeline.UnregisterAroundHandler(handler); } /// /// 注册场景过渡处理器的抽象方法,由子类实现。 /// protected override abstract void RegisterHandlers(); /// /// 系统初始化方法,获取场景工厂并注册处理器。 /// protected override void OnInit() { _factory = this.GetUtility()!; Log.Debug("SceneRouterBase initialized. Factory={0}", _factory.GetType().Name); RegisterHandlers(); } #region Push /// /// 将指定场景推入栈顶。 /// /// 目标场景键名。 /// 场景进入参数。 /// 异步任务。 public async ValueTask PushAsync( string sceneKey, ISceneEnterParam? param = null) { await _transitionLock.WaitAsync(); try { IsTransitioning = true; var @event = CreateEvent(sceneKey, SceneTransitionType.Push, param); await _pipeline.ExecuteAroundAsync(@event, async () => { await BeforeChangeAsync(@event); await PushInternalAsync(sceneKey, param); await AfterChangeAsync(@event); }); } finally { IsTransitioning = false; _transitionLock.Release(); } } /// /// 内部推送场景实现方法。 /// 执行守卫检查、场景创建、添加到场景树、加载资源、暂停当前场景、压入栈等操作。 /// /// 场景键名。 /// 场景进入参数。 /// 异步任务。 private async ValueTask PushInternalAsync( string sceneKey, ISceneEnterParam? param) { if (Contains(sceneKey)) { Log.Warn("Scene already in stack: {0}", sceneKey); return; } // 守卫检查 if (!await ExecuteEnterGuardsAsync(sceneKey, param)) { Log.Warn("Push blocked by guard: {0}", sceneKey); return; } // 通过 Factory 创建场景实例 var scene = _factory.Create(sceneKey); // 添加到场景树 Root!.AddScene(scene); // 加载资源 await scene.OnLoadAsync(param); // 暂停当前场景 if (Stack.Count > 0) { var current = Stack.Peek(); await current.OnPauseAsync(); } // 压入栈 Stack.Push(scene); // 进入场景 await scene.OnEnterAsync(); Log.Debug("Push Scene: {0}, stackCount={1}", sceneKey, Stack.Count); } #endregion #region Pop /// /// 弹出栈顶场景。 /// /// 异步任务。 public async ValueTask PopAsync() { await _transitionLock.WaitAsync(); try { IsTransitioning = true; var @event = CreateEvent(null, SceneTransitionType.Pop); await _pipeline.ExecuteAroundAsync(@event, async () => { await BeforeChangeAsync(@event); await PopInternalAsync(); await AfterChangeAsync(@event); }); } finally { IsTransitioning = false; _transitionLock.Release(); } } /// /// 内部弹出场景实现方法。 /// 执行守卫检查、退出场景、卸载资源、从场景树移除、恢复下一个场景等操作。 /// /// 异步任务。 private async ValueTask PopInternalAsync() { if (Stack.Count == 0) return; var top = Stack.Peek(); // 守卫检查 if (!await ExecuteLeaveGuardsAsync(top.Key)) { Log.Warn("Pop blocked by guard: {0}", top.Key); return; } Stack.Pop(); // 退出场景 await top.OnExitAsync(); // 卸载资源 await top.OnUnloadAsync(); // 从场景树移除 Root!.RemoveScene(top); // 恢复下一个场景 if (Stack.Count > 0) { var next = Stack.Peek(); await next.OnResumeAsync(); } Log.Debug("Pop Scene, stackCount={0}", Stack.Count); } #endregion #region Clear /// /// 清空所有场景栈。 /// /// 异步任务。 public async ValueTask ClearAsync() { await _transitionLock.WaitAsync(); try { IsTransitioning = true; var @event = CreateEvent(null, SceneTransitionType.Clear); await _pipeline.ExecuteAroundAsync(@event, async () => { await BeforeChangeAsync(@event); await ClearInternalAsync(); await AfterChangeAsync(@event); }); } finally { IsTransitioning = false; _transitionLock.Release(); } } /// /// 内部清空场景栈实现方法。 /// 循环调用弹出操作直到栈为空。 /// /// 异步任务。 private async ValueTask ClearInternalAsync() { while (Stack.Count > 0) { await PopInternalAsync(); } } #endregion #region Helper Methods /// /// 创建场景转换事件对象。 /// /// 目标场景键名。 /// 转换类型。 /// 进入参数。 /// 场景转换事件实例。 private SceneTransitionEvent CreateEvent( string? toSceneKey, SceneTransitionType type, ISceneEnterParam? param = null) { return new SceneTransitionEvent { FromSceneKey = CurrentKey, ToSceneKey = toSceneKey, TransitionType = type, EnterParam = param }; } /// /// 执行转换前阶段的处理逻辑。 /// /// 场景转换事件。 /// 异步任务。 private async Task BeforeChangeAsync(SceneTransitionEvent @event) { Log.Debug("BeforeChange phases started: {0}", @event.TransitionType); await _pipeline.ExecuteAsync(@event, SceneTransitionPhases.BeforeChange); Log.Debug("BeforeChange phases completed: {0}", @event.TransitionType); } /// /// 执行转换后阶段的处理逻辑。 /// /// 场景转换事件。 private async Task AfterChangeAsync(SceneTransitionEvent @event) { Log.Debug("AfterChange phases started: {0}", @event.TransitionType); await _pipeline.ExecuteAsync(@event, SceneTransitionPhases.AfterChange); Log.Debug("AfterChange phases completed: {0}", @event.TransitionType); } #endregion }