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 { public partial class MapManager : Singleton { public MapBaseCollection baseCollection; public ZoneData currentZoneData; public ZoneManager currentZoneManager; /// /// 加载指定 ZoneData 对应的场景,完成后回调 onComplete。 /// 首次加载时跳过淡出(画面已是黑屏),后续切换时先淡出再卸载旧场景。 /// /// 目标 Zone 数据。 /// 场景完全就绪后的回调。 /// /// 为 true 时跳过敌人生成和 CombatRoomSm.StartRoom(), /// 用于重入已完成的战斗节点(只需重新加载场景环境,无需重新开始战斗)。 /// public void LoadZone(ZoneData nextZoneData, Action onComplete = null, bool skipBattleSetup = false) { StartCoroutine(SwitchRoomRoutine(nextZoneData, onComplete, skipBattleSetup)); } } public partial class MapManager { public static MapBaseCollection BaseCollection => Instance.baseCollection; } public partial class MapManager { private IEnumerator SwitchRoomRoutine(ZoneData nextZoneData, Action onComplete, bool skipBattleSetup = false) { bool isFirstLoad = currentZoneData == null; // 0. 禁用玩家输入,防止切换期间误操作 PlayerInputSubcontroller playerInput = MainGameManager.Player.inputSc; playerInput.inputActions.Player.Disable(); // MODIFIED: 在开始异步加载前禁用 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; } // 1. 淡出画面(首次加载时画面已经是黑屏,无需再 FadeToBlack) if (!isFirstLoad) { bool fadeOutComplete = false; ScreenFader.Instance.FadeToBlack(onComplete: () => fadeOutComplete = true).Play(); yield return new WaitUntil(() => fadeOutComplete); } // 2. 异步卸载旧场景 if (!isFirstLoad) { yield return SceneManager.UnloadSceneAsync(currentZoneData.sceneName); } currentZoneData = nextZoneData; // 3. 异步加载新场景(Additive) 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; // 4. 设为活跃场景 Scene nextScene = SceneManager.GetSceneByName(nextSceneName); SceneManager.SetActiveScene(nextScene); yield return new WaitForEndOfFrame(); // 5. 重置战斗状态、初始化 Zone CombatManager.AttackAreaSm.Reset(); CombatManager.CombatRoomSm.Reset(); // 6. 将玩家放置到 playerSpawns 指定的出生点(无论是否跳过战斗均需执行) SpawnPlayerAtZone(nextZoneData); if (skipBattleSetup) { // 已完成节点:仅生成交互物体(ExitGate 等),跳过敌人生成和战斗启动 ZoneManager.instance.SetupZoneWithoutEnemies(currentZoneData); } else { ZoneManager.instance.SetupZone(currentZoneData); // 等待一帧,让所有 Instantiate 出的敌人执行 Start(), // 将自身 Add 进 activeEnemiesList,再启动战斗房间判定。 yield return null; CombatManager.CombatRoomSm.StartRoom(); } // 7. 淡入画面 yield return new WaitForSeconds(0.5f); ScreenFader.Instance?.FadeToClear().Play(); // 8. 恢复玩家输入 playerInput.inputActions.Player.Enable(); // 9. 回调通知调用方(RunManager) onComplete?.Invoke(); } /// /// 将玩家传送到 ZoneData.playerSpawns 中随机选择的出生点。 /// 若无配置则回退到原点。 /// private void SpawnPlayerAtZone(ZoneData zoneData) { Transform playerTransform = MainGameManager.Player.transform; // MODIFIED: 传送前必须禁用 CharacterController,否则位置设置可能会被物理系统忽略 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; // MODIFIED: 彻底重置重力、跳跃等缓存速度,避免传送后向下冲破地板 ResetPlayerMovementVelocity(); // MODIFIED: 传送及重置速度完成后,重新启用 CharacterController if (MainGameManager.Player.collisionSc != null && MainGameManager.Player.collisionSc.useCharacterController) { MainGameManager.Player.collisionSc.characterController.enabled = true; } } // MODIFIED: 新增清空玩家移动控制状态的方法,确保重力、跳跃速度全部归零 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; } } } }