using System; using System.Collections; using Cielonos.Core.UI; using Cielonos.MainGame.Characters; using Cielonos.MainGame.Map; using DG.Tweening; using SLSUtilities.General; using UnityEngine; using UnityEngine.SceneManagement; namespace Cielonos.MainGame { /// /// Zone 加载后的初始化模式。 /// EnvironmentOnly 用于回到已完成节点或起点等非战斗场景,只恢复场景和交互物; /// CombatEncounter 用于正式进入战斗节点,会生成敌人并启动 CombatRoomSubmodule。 /// public enum ZoneSetupMode { EnvironmentOnly, CombatEncounter, } public partial class MapManager : Singleton { public MapBaseCollection baseCollection; public ZoneData currentZoneData; public ZoneManager currentZoneManager; /// /// 加载指定 ZoneData 对应的场景。保留 bool 参数是为了兼容旧调用点; /// 新代码优先使用 ZoneSetupMode 重载,让调用语义更清晰。 /// public void LoadZone(ZoneData nextZoneData, Action onComplete = null, bool skipBattleSetup = false) { ZoneSetupMode setupMode = skipBattleSetup ? ZoneSetupMode.EnvironmentOnly : ZoneSetupMode.CombatEncounter; LoadZone(nextZoneData, onComplete, setupMode); } /// /// 使用明确的初始化模式加载 Zone,并在场景、出生点、战斗初始化全部完成后回调调用方。 /// public void LoadZone(ZoneData nextZoneData, Action onComplete, ZoneSetupMode setupMode) { StartCoroutine(SwitchRoomRoutine(nextZoneData, onComplete, setupMode)); } } public partial class MapManager { public static MapBaseCollection BaseCollection => Instance.baseCollection; } public partial class MapManager { private IEnumerator SwitchRoomRoutine(ZoneData nextZoneData, Action onComplete, ZoneSetupMode setupMode) { bool isFirstLoad = currentZoneData == null; // 切换场景期间禁止玩家输入,避免玩家在卸载/加载窗口里触发移动或攻击。 PlayerInputSubcontroller playerInput = MainGameManager.Player.inputSc; playerInput.inputActions.Player.Disable(); // 加载前禁用 CharacterController,并清空下落速度,避免玩家在异步加载期间掉入虚空。 if (MainGameManager.Player.collisionSc != null && MainGameManager.Player.collisionSc.useCharacterController) { MainGameManager.Player.collisionSc.characterController.enabled = false; } if (MainGameManager.Player.movementSc != null) { MainGameManager.Player.movementSc.gravitationalMovement = Vector3.zero; MainGameManager.Player.movementSc.jumpVelocity = 0f; } // 首次进入时画面本来就是黑屏;后续切换需要先淡出再卸载旧场景。 if (!isFirstLoad) { bool fadeOutComplete = false; ScreenFader.Instance.FadeToBlack(onComplete: () => fadeOutComplete = true).Play(); yield return new WaitUntil(() => fadeOutComplete); } if (!isFirstLoad) { yield return SceneManager.UnloadSceneAsync(currentZoneData.sceneName); } currentZoneData = nextZoneData; string nextSceneName = nextZoneData.sceneName; AsyncOperation op = SceneManager.LoadSceneAsync(nextSceneName, LoadSceneMode.Additive); op.allowSceneActivation = false; while (op.progress < 0.9f) { yield return null; } op.allowSceneActivation = true; yield return op; Scene nextScene = SceneManager.GetSceneByName(nextSceneName); SceneManager.SetActiveScene(nextScene); yield return new WaitForEndOfFrame(); // 每次切换 Zone 都先清理上一场战斗遭遇的运行时缓存。 CombatManager.ResetCurrentEncounter(); // 出生点放置独立于战斗模式:回到已完成节点时也需要正确传送玩家。 SpawnPlayerAtZone(nextZoneData); if (setupMode == ZoneSetupMode.EnvironmentOnly) { // 已完成节点或非战斗入口:只生成 ExitGate 等交互物,不生成敌人,也不启动房间结算。 ZoneManager.instance.SetupZoneWithoutEnemies(currentZoneData); } else { ZoneManager.instance.SetupZone(currentZoneData); // 等待一帧,让 Instantiate 出的敌人完成 Start() 并注册到 EnemySubmodule。 yield return null; CombatManager.CombatRoomSm.StartRoom(); } yield return new WaitForSeconds(0.5f); ScreenFader.Instance?.FadeToClear().Play(); playerInput.inputActions.Player.Enable(); onComplete?.Invoke(); } /// /// 将玩家传送到 ZoneData.playerSpawns 中随机选择的出生点;若配置缺失则回退到原点。 /// private void SpawnPlayerAtZone(ZoneData zoneData) { Transform playerTransform = MainGameManager.Player.transform; // CharacterController 启用时直接设置 Transform 可能被物理状态覆盖,所以传送前先禁用。 if (MainGameManager.Player.collisionSc != null && MainGameManager.Player.collisionSc.useCharacterController) { MainGameManager.Player.collisionSc.characterController.enabled = false; } Vector3 targetPosition = Vector3.zero; Quaternion targetRotation = Quaternion.identity; bool foundSpawn = false; if (zoneData.playerSpawns != null && zoneData.playerSpawns.Count > 0) { ZoneData.SpawnPointKey spawnKey = zoneData.playerSpawns.Count == 1 ? zoneData.playerSpawns[0] : zoneData.playerSpawns[UnityEngine.Random.Range(0, zoneData.playerSpawns.Count)]; if (ZoneManager.instance.spawnPoints.TryGetValue(spawnKey.group, out var points) && spawnKey.index < points.Count) { SpawnPoint point = points[spawnKey.index]; point.GetTransform(out targetPosition, out targetRotation); foundSpawn = true; Debug.Log($"[MapManager] 玩家放置在出生点 {spawnKey.group}_{spawnKey.index}。"); } } if (!foundSpawn) { Debug.LogWarning("[MapManager] ZoneData 未配置 playerSpawns 或出生点不存在,玩家将放置在原点。"); } playerTransform.position = targetPosition; playerTransform.rotation = targetRotation; ResetPlayerMovementVelocity(); if (MainGameManager.Player.collisionSc != null && MainGameManager.Player.collisionSc.useCharacterController) { MainGameManager.Player.collisionSc.characterController.enabled = true; } } /// /// 清空玩家移动控制器里的速度缓存,避免传送后继承上一帧的重力、跳跃或水平位移。 /// private void ResetPlayerMovementVelocity() { var movementSc = MainGameManager.Player.movementSc; if (movementSc != null) { movementSc.gravitationalMovement = Vector3.zero; movementSc.jumpVelocity = 0f; movementSc.horizontalMovement = Vector3.zero; movementSc.verticalMovement = Vector3.zero; movementSc.finalMovementVelocity = Vector3.zero; movementSc.jumpMovement = Vector3.zero; movementSc.movementModifier = Vector3.zero; } } } }