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