UI 系统

This commit is contained in:
2026-06-08 16:05:00 +08:00
parent b582317692
commit 247de307c6
150 changed files with 10945 additions and 158 deletions

View File

@@ -1,5 +1,6 @@
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
using BaseGames.Core.Assets;
using BaseGames.Core.Events;
using BaseGames.Core.States;
@@ -19,6 +20,14 @@ namespace BaseGames.Core
[SerializeField] private SettingsManager _settingsManager;
[SerializeField] private BootSequencer _bootSequencer;
#if UNITY_EDITOR
[Header("调试(仅编辑器,打包自动忽略)")]
[Tooltip("勾选后:从某个游玩场景直接按 Play 时,跳过 Splash 与 MainMenu\n" +
"直接以当前已打开的场景进入 GameplayHUD / 暂停 / 相机等照常工作)。\n" +
"若按 Play 时未打开任何游玩场景(仅 Persistent / MainMenu则自动回退正常启动流程。")]
[SerializeField] private bool _devSkipMainMenu;
#endif
[Header("Event Channels - Listen")]
[SerializeField] private VoidEventChannelSO _onPlayerDied;
[SerializeField] private VoidEventChannelSO _onPauseRequested;
@@ -72,6 +81,19 @@ namespace BaseGames.Core
/// </summary>
private IEnumerator BootCoroutine()
{
#if UNITY_EDITOR
// 开发直连:跳过 Splash/MainMenu直接以当前打开的游玩场景进入 Gameplay。
if (_devSkipMainMenu)
{
string devScene = FindOpenGameplaySceneName();
if (devScene != null)
{
yield return DevDirectPlayCoroutine(devScene);
yield break;
}
Debug.Log("[GameManager] 开发直连未检测到已打开的游玩场景回退正常启动Splash → MainMenu。");
}
#endif
if (_bootSequencer != null)
yield return StartCoroutine(_bootSequencer.RunBootSequenceCoroutine());
@@ -83,6 +105,59 @@ namespace BaseGames.Core
" 请检查 Persistent 场景的 GameServiceRegistrar 配置。");
}
#if UNITY_EDITOR
/// <summary>
/// 开发直连协程(仅编辑器):建立内存存档并把状态机推进到 Gameplay以当前已打开的场景直接开玩。
/// <para>
/// 不重新加载场景(当前场景已在 Hierarchy 中,重载会与流式管线产生重复实例),
/// 仅切换全局状态RoomController.Start() 冷启动接管相机,玩家按 Tag 绑定HUD 跟随 Gameplay 状态显示。
/// </para>
/// <para>
/// 关键:先 <c>yield return null</c> 等一帧。GameManager 执行序为 -1000其 Start 早于
/// InputReaderBootstrap.Start —— 后者会兜底调用 EnableUIInput()。若本帧内就切到 Gameplay
/// 输入会被随后执行的 EnableUIInput() 覆盖回 UI 模式,导致玩家输入全部失效。
/// 等一帧让所有 Start() 执行完毕后再切状态EnableGameplayInput 才是最终态。
/// </para>
/// </summary>
private IEnumerator DevDirectPlayCoroutine(string sceneName)
{
// 等一帧:让 Persistent 内所有组件的 Start() 跑完(尤其 InputReaderBootstrap 的 EnableUIInput 兜底)。
yield return null;
// 复刻新游戏初始状态:确保槽 0 存在内存存档,供存档点 / 世界状态等系统使用。
var save = ServiceLocator.GetOrDefault<ISaveService>();
if (save != null && !save.HasSave(0))
save.CreateSlot(0, false);
// 驱动状态机走完整合法链路Initializing → MainMenu → LoadingScene → Gameplay。
// 期间不加载任何场景,仅切换全局状态;末态 Gameplay 使 HUD 显示、暂停可用、输入切到 Gameplay。
RequestTransition(GameStates.MainMenu);
RequestTransition(GameStates.LoadingScene);
RequestTransition(GameStates.Gameplay);
Debug.Log($"[GameManager] 开发直连:已跳过 MainMenu直接以 '{sceneName}' 进入 Gameplay。");
}
/// <summary>
/// 返回当前已加载的、非系统场景(即游玩场景)的名称;没有则返回 null。
/// 仅按场景名约定判断,避免 Core 反向依赖 World 程序集的 RoomController 类型。
/// </summary>
private static string FindOpenGameplaySceneName()
{
for (int i = 0; i < SceneManager.sceneCount; i++)
{
var scene = SceneManager.GetSceneAt(i);
if (!scene.isLoaded) continue;
string n = scene.name;
if (string.IsNullOrEmpty(n)) continue;
if (n == AddressKeys.ScenePersistentName || n == AddressKeys.ScenePersistent) continue;
if (n == AddressKeys.SceneMainMenu) continue;
return n;
}
return null;
}
#endif
private void OnEnable()
{
_onPlayerDied? .Subscribe(HandlePlayerDied).AddTo(_subs);

View File

@@ -0,0 +1,12 @@
namespace BaseGames.Core
{
/// <summary>
/// 暴露「是否站在地面」的角色接口。定义于最底层 Core 程序集,
/// 供跨模块(如 Boss 击落计数)在不直接依赖 Player/Enemies 程序集的前提下查询地面状态。
/// </summary>
public interface IGroundedActor
{
/// <summary>当前是否站在地面false = 处于空中)。</summary>
bool IsGrounded { get; }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 24e27360e2a7ca6458cb3e994ab2d29f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: