using System; using System.Collections; using System.Collections.Generic; using System.Linq; using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Engines; using Common; using DamageNumbersPro; using DragonLi.Core; using Mirror; using UnityEngine; using UnityEngine.PlayerLoop; using XPlugin.Data.JsonLiteDB; using static XPlugin.Data.JsonLiteDB.JsonLiteDB; using LitJson; using Pathfinding; using DarkTonic.MasterAudio; using NaughtyAttributes; using Unity.Mathematics; using UnityEngine.UIElements; using EventDispatcher = DragonLi.Core.EventDispatcher; using Random = UnityEngine.Random; /// /// 游戏状态 /// public enum GameState { None = 0, Playing = 1, /// /// 胜利 /// Victory = 2, /// /// 失败 /// Failur = 3, Settle, Waiting = 5, } public class GameManager : NetworkBehaviour { public static GameManager Ins { get; private set; } #region 预制体 public GameObject redDoorPre; public GameObject blueDoorPre; public GameObject AirdropPlanePre;//空投飞机 public GameObject[] airdropItems;//空头道具 public GameObject startGameItemPre; public GameObject damageNumberPre; public GameObject redDamageNumberPre; // 怪物ui预制体 public GameObject EnemyUIPre; public GameObject roundPlaceDoorPre; #endregion #region 实例化后物体 public GameObject redDoor; public GameObject blueDoor; #endregion // json数据库 private JsonLiteDB DB; // 怪物信息集合 public Dictionary> EnemyInfos = new Dictionary>(); // 枪械信息集合 public Dictionary> GunInfos = new Dictionary>(); /// /// 所有敌人UI /// public Dictionary EnemyUIList = new Dictionary(); public Dictionary SettleInfos = new Dictionary(); public List allPlayers = new List(); // 游玩结束时间 [NonSerialized] [SyncVar] public long vistEnd = 0; [NonSerialized] [SyncVar] public long vistRoundEnd = 0; // 总游玩时长 private int vistAllTime = (int)(60 * 15f); [NonSerialized] [SyncVar] private int roundAllTime; [SyncVar] public string settleData = ""; [SyncVar] [NonSerialized] public int CurRoundIndex; [SyncVar] public GameState gameState = GameState.None; [NonSerialized] [SyncVar] public int BlueScore = 0; [NonSerialized] [SyncVar] public int RedScore = 0; [NonSerialized] [SyncVar] public int RedRoundScore = 0; [NonSerialized] [SyncVar] public int BlueRoundScore = 0; [Header("指引系统")] public GameObject GuideArrowPre; // 指引箭头预制体 private GameObject guideArrowInstanceRed; // 指向红门的指引箭头实例 private GameObject guideArrowInstanceBlue; // 指向蓝门的指引箭头实例 private GuideArrowPath guideArrowComponentRed; // 红门指引箭头组件 private GuideArrowPath guideArrowComponentBlue; // 蓝门指引箭头组件 private bool isGuideArrowActive = false; // 优化:使用协程更新路径,避免每帧计算 private Coroutine pathUpdateCoroutine; // 优化:减少更新频率 private float updatePathInterval = 0.5f; // 路径更新间隔(秒) private float lastPathUpdateTime = 0f; // 优化:简化路径计算 private Vector3 lastPlayerPosition = Vector3.zero; private float updatePositionThreshold = 1.0f; // 玩家移动超过这个距离才更新路径 private int pathSegments = 10; // 路径分段数(使用Lerp插值) // 指引系统目标位置 private Vector3 guideTargetPositionRed = Vector3.zero; private Vector3 guideTargetPositionBlue = Vector3.zero; // 是否已初始化指引系统 private bool isGuideInitialized = false; // 是否已经同步门位置 private bool isDoorPositionSynced = false; // 调试用:强制显示指引 public bool forceShowGuide = false; void Awake() { Ins = this; } void Start() { // 延迟初始化指引系统,等待网络连接 CoroutineTaskManager.Instance.WaitSecondTodo(() => { InitializeGuideArrow(); }, 2f); if (isClient) { UpdateConf(); } if (isServer) { CreateDoor(GameLocal.Ins.redDoorPos, GameLocal.Ins.blueDoorPos, GameLocal.Ins.startGameItemPos); CoroutineTaskManager.Instance.WaitSecondTodo(() => { StartGuide(); }, 2f); roundAllTime = vistAllTime / 3; } } /// /// 创建门 /// [Server] public void CreateDoor(Transform redPos, Transform bluePos, Transform startGamePos) { SetGameState(GameState.Waiting); if (!isServer) return; //创建红色门 redDoor = Instantiate(redDoorPre, redPos.position, Quaternion.identity); NetworkServer.Spawn(redDoor); //创建蓝色门 blueDoor = Instantiate(blueDoorPre, bluePos.position, Quaternion.identity); NetworkServer.Spawn(blueDoor); // 等待一帧确保物体同步,然后同步门位置 CoroutineTaskManager.Instance.WaitSecondTodo(() => { // 同步门的位置给所有客户端 RpcSyncDoorPosition(redPos.position, bluePos.position); Debug.Log("服务器:门位置已同步到客户端"); // 服务器端也初始化指引 CoroutineTaskManager.Instance.WaitSecondTodo(() => { StartGuide(); }, 0.5f); }, 0.5f); } /// /// 同步门的位置给所有客户端 /// [ClientRpc] public void RpcSyncDoorPosition(Vector3 redDoorPos, Vector3 blueDoorPos) { // 客户端接收门的位置 guideTargetPositionRed = redDoorPos; guideTargetPositionBlue = blueDoorPos; isDoorPositionSynced = true; Debug.Log("客户端:收到门位置同步,红门: " + guideTargetPositionRed + ",蓝门: " + guideTargetPositionBlue); // 如果指引系统已经初始化,立即开始指引 if (isGuideInitialized) { CoroutineTaskManager.Instance.WaitSecondTodo(() => { StartGuide(); }, 0.3f); } } [ClientRpc] public void RpcShowDamageNumber(float damage, Vector3 pos, bool isCritical) { GameObject prefab = isCritical ? redDamageNumberPre : damageNumberPre; if (prefab == null) return; GameObject obj = Instantiate(prefab, pos, Quaternion.identity); var dmg = obj.GetComponent(); if (dmg != null) { dmg.worldPos = pos; dmg.damageValue = isCritical ? damage * 2 : damage; dmg.OnStartClient(); } Destroy(obj, 10f); } [Server] public void GameStart() { Debug.Log("开始游戏"); vistEnd = (long)(DateTime.Now.Subtract(new DateTime(1970, 1, 1))).TotalSeconds + vistAllTime; vistRoundEnd = (long)(DateTime.Now.Subtract(new DateTime(1970, 1, 1))).TotalSeconds + roundAllTime; _airdropTime = 25; _curAirdropTime = _airdropTime; ChangeBgmRpc(1); StartAirDrop(); // 停止所有客户端的指引 RpcStopAllGuide(); // 延迟1s后给所有人发放武器 CoroutineTaskManager.Instance.WaitSecondTodo(() => { foreach (var player in allPlayers) { if (player != null) { player.HideInvincible(); ShowTeamUI(player.connectionToClient, (int)player.teamType); } } RpcShowSettle(); }, 1f); StartRoundTime(); } [TargetRpc] public void ShowTeamUI(NetworkConnectionToClient conn, int teamId) { EventDispatcher.TriggerEvent("ChangeTeam", teamId); } [Server] public void StartRoundTime() { CurRoundIndex++; CoroutineTaskManager.Instance.WaitSecondTodo(() => { if (gameState == GameState.Waiting) return; StopRound(); }, roundAllTime); } [Server] public void RegisterPlayer(Player p) { if (!allPlayers.Contains(p)) allPlayers.Add(p); } // ★ 玩家离开游戏时调用(在Player.OnStopServer里调用) [Server] public void UnRegisterPlayer(Player p) { if (allPlayers.Contains(p)) allPlayers.Remove(p); } // ★ 游戏开始时调用 → 自动分配队伍 [Server] public void AutoAssignTeams() { int redCount = 0; int blueCount = 0; // 统计已选队伍玩家 foreach (var p in allPlayers) { if (p.teamType == TeamType.Red) redCount++; else if (p.teamType == TeamType.Blue) blueCount++; } int totalPlayers = allPlayers.Count; // ----------------------------- // ★ 第一步:强制平衡队伍(如果人数>=2 且某队==0) // ----------------------------- if (totalPlayers >= 2) { if (redCount == 0 && blueCount > 0) { // 需要从蓝队分一半人到红队 int need = Mathf.Max(1, blueCount / 2); foreach (var p in allPlayers) { if (need <= 0) break; if (p.teamType == TeamType.Blue) { p.teamType = TeamType.Red; need--; redCount++; blueCount--; } } } else if (blueCount == 0 && redCount > 0) { // 需要从红队分一半人到蓝队 int need = Mathf.Max(1, redCount / 2); foreach (var p in allPlayers) { if (need <= 0) break; if (p.teamType == TeamType.Red) { p.teamType = TeamType.Blue; need--; blueCount++; redCount--; } } } } // ----------------------------- // ★ 第二步:给未分配队伍(NONE)的玩家自动分配 // ----------------------------- foreach (var p in allPlayers) { if (p.teamType != TeamType.None) continue; if (redCount < blueCount) { p.teamType = TeamType.Red; redCount++; } else if (blueCount < redCount) { p.teamType = TeamType.Blue; blueCount++; } else { // 平局 → 随机 if (Random.value > 0.5f) { p.teamType = TeamType.Red; redCount++; } else { p.teamType = TeamType.Blue; blueCount++; } } // 给队伍分配武器 GivePistol(p.index); } } [Server] public void CheckGameStart() { int curTeamIndex = 0; bool isStartGame = false; foreach (var player in allPlayers) { if (player.teamType != TeamType.None) curTeamIndex++; } isStartGame = curTeamIndex >= GameLocal.Ins.addPlayerIndex; if (isStartGame) { if (gameState == GameState.Playing) return; AutoAssignTeams(); gameState = GameState.Playing; BlueRoundScore = 0; RedRoundScore = 0; // 停止所有客户端的指引 RpcStopAllGuide(); NetworkServer.Destroy(redDoor); NetworkServer.Destroy(blueDoor); RpcShowHUD(); MonoSingleton.Instance.WaitSecondTodo(() => { GameStart(); }, 3f, this); } } [ClientRpc] public void RpcShowSettle() { GameLocal.Ins.Settle.SetActive(true); } [ClientRpc] public void GivePistol(int index) { Debug.Log("创建武器中..."); foreach (var item in NetworkServer.connections.Values) { Player player = item.identity.GetComponent(); if (player != null && player.index == index) { player.GivePistol(); player.GiveArmor(); } } } [Server] public void SetGameState(GameState state) { gameState = state; } [Server] public void GameOver() { gameState = GameState.Settle; } [ClientRpc] public void RpcShow() { SettlePanel.Show(); } [ClientRpc] public void ChangeBgmRpc(int i) { GameLocal.Ins.BGMState.StateChange(i); } [Server] public void CreateAirdropPlane(Vector3 startPos, Vector3 endPos) { GameObject airdropPlane = Instantiate(AirdropPlanePre, startPos, quaternion.identity); NetworkServer.Spawn(airdropPlane); AirdropPlane airdropPlaneScript = airdropPlane.GetComponent(); airdropPlaneScript.OnSpawn(endPos); } [Server] public void CreateAirDropItem(int id, Vector3 startPos) { GameObject airdropItem = Instantiate(airdropItems[id], startPos, quaternion.identity); NetworkServer.Spawn(airdropItem); } /// /// 痛击场上所有敌方单位 /// [Command(requiresAuthority = false)] public void DamageAllEnemy() { } /// /// 更新配置表 /// public void UpdateConf() { string text = Resources.Load("Data").text; if (text != null) { ParseGameJson(text); } } public void ParseGameJson(string text) { DB = new JsonLiteDB(); DB.Load(text); TableReader infoReader = DB["GunsInfo"].GetReader(); while (infoReader.Read()) { GunInfo info = new GunInfo(infoReader); GunInfos.TryGetValue((GunType)info.Type, out Dictionary infoList); if (infoList == null) { Dictionary list = new Dictionary(); list.Add(info.Lvl, info); GunInfos.Add((GunType)info.Type, list); } else { infoList.Add(info.Lvl, info); } } Debug.Log("游戏数值更新 -> complete"); } [Server] public void CreateEnemyUI(Enemy enemy) { GameObject enemyUI = Instantiate(EnemyUIPre); NetworkServer.Spawn(enemyUI); PlayerUI enemyUIScript = enemyUI.GetComponent(); //enemyUIScript.Init(enemy); EnemyUIList.Add(enemy.id, enemyUIScript); } [Server] public void DeleteEnemyUI(int id) { GameObject enemyUI = EnemyUIList[id].gameObject; EnemyUIList.Remove(id); NetworkServer.Destroy(enemyUI); } [ClientRpc] public void RpcShowHUD() { HUDPanel.Show(); } [Server] public void AddData(int playerIndex) { SettleInfos.Add(playerIndex.ToString(), new SettleInfo()); } #region 工具 public string GetLessTimeStr() { string res = ""; if (gameState == GameState.Playing) { res = FormatTime((int)(vistEnd - DateTime.Now.Subtract(new DateTime(1970, 1, 1)).TotalSeconds)); } return res; } public string GetRoundLessTimeStr() { string res = ""; if (gameState == GameState.Playing) { res = FormatTime((int)(vistRoundEnd - DateTime.Now.Subtract(new DateTime(1970, 1, 1)).TotalSeconds)); } return res; } public int GetLessTimeSeconds() { if (isServer) { return (int)(vistEnd - NetworkTime.time); } else { return (int)(vistEnd - NetworkClient.connection.remoteTimeStamp); } } // 时分秒 public string FormatTime(int totalSeconds) { //确保时间不为负 totalSeconds = Mathf.Max(0, totalSeconds); int hours = totalSeconds / 3600; // string hh = hours < 10 ? "0" + hours : hours.ToString(); int minutes = (totalSeconds - hours * 3600) / 60; string mm = minutes < 10f ? "0" + minutes : minutes.ToString(); int seconds = totalSeconds - hours * 3600 - minutes * 60; string ss = seconds < 10 ? "0" + seconds : seconds.ToString(); return string.Format("{0}:{1}", mm, ss); } #endregion [TargetRpc] public void PlaySound2DRPC(NetworkConnection target, string sound) { MasterAudio.PlaySound(sound); } public void PlaySound2D(string sound) { foreach (var item in allPlayers) { PlaySound2DRPC(item.connectionToClient, sound); } } [TargetRpc] public void PlaySound3DRPC(NetworkConnection target, string sound, Transform pos) { MasterAudio.PlaySound3DAtTransform(sound, pos); } public void PlaySound3D(string sound, Transform target) { foreach (var item in allPlayers) { PlaySound3DRPC(item.connectionToClient, sound, target); } } [SyncVar] private int syncRemainingTime; private float _airdropTime;//空投时间 private float _curAirdropTime;//当前空头时间 private bool _isStartAirdrop;//是否开始倒计时空投 public void StartAirDrop() { _isStartAirdrop = true; } public void StopAirDrop() { _isStartAirdrop = false; } void Update() { if (isServer && gameState == GameState.Playing && _isStartAirdrop) { //服务器计算剩余时间并同步 //syncRemainingTime = GetLessTimeSeconds(); _curAirdropTime += Time.deltaTime; if (_curAirdropTime >= _airdropTime) { int airdropPosId = Random.Range(0, 4); int endAirdropPosId = 0; if (airdropPosId == 0) endAirdropPosId = 1; if (airdropPosId == 1) endAirdropPosId = 0; if (airdropPosId == 2) endAirdropPosId = 3; if (airdropPosId == 3) endAirdropPosId = 2; CreateAirdropPlane(GameLocal.Ins.startPlanePos[airdropPosId].position, GameLocal.Ins.startPlanePos[endAirdropPosId].position); _curAirdropTime = 0; StopAirDrop(); } } if (isServer) { if (Input.GetKeyDown(KeyCode.Space)) { StopRound(); } } // 调试:强制显示指引(测试用) if (forceShowGuide && Input.GetKeyDown(KeyCode.G) && isGuideInitialized && !isGuideArrowActive) { Debug.Log("强制显示指引"); StartGuide(); } } [Server] public void StartRound() { gameState = GameState.Playing; ChangeBgmRpc(CurRoundIndex + 1); vistRoundEnd = (long)(DateTime.Now.Subtract(new DateTime(1970, 1, 1))).TotalSeconds + roundAllTime; foreach (var item in allPlayers) { item.HideInvincible(); } StartRoundTime(); } [Server] public void StopRound() { gameState = GameState.Waiting; ChangeBgmRpc(0); if (CurRoundIndex >= 3) gameState = GameState.Settle; if (BlueRoundScore > RedRoundScore) { BlueScore += 1; } if (BlueRoundScore < RedRoundScore) { RedScore += 1; } if (BlueRoundScore == RedRoundScore && BlueRoundScore != 0 && RedRoundScore != 0) { RedScore += 1; BlueScore += 1; } foreach (var item in allPlayers) { item.ClearGun(); } MonoSingleton.Instance.WaitSecondTodo(() => { foreach (var item in allPlayers) { RpcPlayerRoundEnd(item.connectionToClient); } }, 1f, this); CreateRoundPlaceDoorPre(); MonoSingleton.Instance.WaitSecondTodo(() => { if (gameState == GameState.Settle) return; BlueRoundScore = 0; RedRoundScore = 0; foreach (var player in allPlayers) { RpcPlayerWaitRoundEnd(player.connectionToClient); } }, 10f, this); MonoSingleton.Instance.WaitSecondTodo(() => { if (gameState == GameState.Settle) return; StartRound(); }, 13f, this); } [TargetRpc] public void RpcPlayerRoundEnd(NetworkConnection target) { EventDispatcher.TriggerEvent("NewRoundStart", CurRoundIndex == 3 ? BlueScore : BlueRoundScore, CurRoundIndex == 3 ? RedScore : RedRoundScore, CurRoundIndex); } [TargetRpc] public void RpcPlayerWaitRoundEnd(NetworkConnection target) { GameLocal.Ins.RoundUI.SetActive(false); EventDispatcher.TriggerEvent("NewWaveStart"); } public Player GetPlayerByIndex(int index) { return allPlayers.FirstOrDefault(p => p.index == index); } #region 回合地图 [Server] public void CreateRoundPlaceDoorPre() { //创建吸收场地黑洞 var roundPlaceDoor = Instantiate(roundPlaceDoorPre, GameLocal.Ins.roundPlaceDoorPos.position, Quaternion.identity); roundPlaceDoor.transform.eulerAngles = GameLocal.Ins.roundPlaceDoorPos.eulerAngles; NetworkServer.Spawn(roundPlaceDoor); foreach (var player in allPlayers) { PlaceFly(player.connectionToClient, roundPlaceDoor.transform.position); } MonoSingleton.Instance.WaitSecondTodo(() => { if (roundPlaceDoor != null) NetworkServer.Destroy(roundPlaceDoor); if (gameState == GameState.Settle) return; // RPC:通知客户端执行地图升起动画 foreach (var player in allPlayers) { RpcMapRise(player.connectionToClient); } }, 8f, this); } [TargetRpc] public void PlaceFly(NetworkConnection target, Vector3 blackHolePos) { GameObject curPlace = GameLocal.Ins.CurPlace(CurRoundIndex); foreach (Transform child in curPlace.GetComponentsInChildren()) { if (child == curPlace.transform) continue; // 跳过根节点 Collider childCollider = child.GetComponent(); if (childCollider != null) childCollider.enabled = false; GameLocal.Ins.AddObject(child, blackHolePos); } } [TargetRpc] public void RpcMapRise(NetworkConnection target) { StartCoroutine(MapRiseCoroutine()); } private IEnumerator MapRiseCoroutine() { GameLocal.Ins.ShowPlace(CurRoundIndex + 1); GameObject newMap = GameLocal.Ins.CurPlace(CurRoundIndex + 1); // 新地图预先放在场景里 Vector3 startPos = newMap.transform.position; Vector3 endPos = new Vector3(startPos.x, 0f, startPos.z); float duration = 2f; float t = 0f; while (t < duration) { t += Time.deltaTime; float progress = t / duration; // 使用平滑插值(可改成 easeOut) float smooth = Mathf.SmoothStep(0, 1, progress); newMap.transform.position = Vector3.Lerp(startPos, endPos, smooth); yield return null; } newMap.transform.position = endPos; } #endregion #region 分数系统 /// /// 红色方加分 /// /// public void AddRedScore(int score) { RedRoundScore += score; } /// /// 蓝色方加分 /// /// public void AddBlueScore(int score) { BlueRoundScore += score; } #endregion #region 指引系统(优化版)- 双线版本,使用Vector3.Lerp /// /// 初始化指引箭头(每个客户端独立) /// private void InitializeGuideArrow() { // 检查预制体是否存在 if (GuideArrowPre == null) { Debug.LogError("GuideArrowPre 预制体未分配!"); return; } // 实例化两个指引箭头预制体(本地实例,不会网络同步) guideArrowInstanceRed = Instantiate(GuideArrowPre); guideArrowInstanceRed.name = "GuideArrow_Red"; guideArrowInstanceBlue = Instantiate(GuideArrowPre); guideArrowInstanceBlue.name = "GuideArrow_Blue"; // 获取指引箭头组件 guideArrowComponentRed = guideArrowInstanceRed.GetComponent(); guideArrowComponentBlue = guideArrowInstanceBlue.GetComponent(); if (guideArrowComponentRed == null || guideArrowComponentBlue == null) { Debug.LogError("GuideArrowPre 预制体上没有找到 GuideArrowPath 组件!"); if (guideArrowInstanceRed != null) Destroy(guideArrowInstanceRed); if (guideArrowInstanceBlue != null) Destroy(guideArrowInstanceBlue); guideArrowInstanceRed = null; guideArrowInstanceBlue = null; return; } // 初始化玩家位置 if (GameLocal.Ins.self != null) { lastPlayerPosition = GameLocal.Ins.self.transform.position; } isGuideInitialized = true; Debug.Log("指引系统初始化完成,创建了2个指引箭头,等待门位置同步..."); // 如果已经收到门位置同步,立即开始指引 if (isDoorPositionSynced && guideTargetPositionRed != Vector3.zero && guideTargetPositionBlue != Vector3.zero) { CoroutineTaskManager.Instance.WaitSecondTodo(() => { StartGuide(); }, 0.5f); } // 如果是客户端,尝试查找网络上的门 else if (!isServer) { CoroutineTaskManager.Instance.WaitSecondTodo(() => { TryFindDoorsAndStartGuide(); }, 1f); } } /// /// 尝试查找门并开始指引(客户端使用) /// private void TryFindDoorsAndStartGuide() { if (isGuideInitialized && !isGuideArrowActive) { Vector3[] doorPositions = FindDoorPositions(); if (doorPositions[0] != Vector3.zero && doorPositions[1] != Vector3.zero) { guideTargetPositionRed = doorPositions[0]; guideTargetPositionBlue = doorPositions[1]; StartGuide(); } else { Debug.Log("客户端:未找到两个门,等待门位置同步..."); } } } /// /// 查找门的位置(客户端使用)- 优化版,减少查找频率 /// private Vector3[] FindDoorPositions() { Vector3[] doorPositions = new Vector3[2] { Vector3.zero, Vector3.zero }; // 使用缓存的门对象,避免频繁查找 if (redDoor != null && redDoor.activeInHierarchy) { doorPositions[0] = redDoor.transform.position; } else if (blueDoor != null && blueDoor.activeInHierarchy) { // 注意:这里需要区分红门和蓝门 // 假设红门和蓝门都有对应的变量 } // 如果没有缓存,则尝试查找 if (doorPositions[0] == Vector3.zero) { GameObject redDoorObj = GameObject.FindWithTag("RedDoor"); if (redDoorObj != null) doorPositions[0] = redDoorObj.transform.position; } if (doorPositions[1] == Vector3.zero) { GameObject blueDoorObj = GameObject.FindWithTag("BlueDoor"); if (blueDoorObj != null) doorPositions[1] = blueDoorObj.transform.position; } // 备用查找方案 if (doorPositions[0] == Vector3.zero || doorPositions[1] == Vector3.zero) { GameObject[] allDoors = GameObject.FindGameObjectsWithTag("Door"); foreach (var door in allDoors) { if (door.name.Contains("Red") && doorPositions[0] == Vector3.zero) { doorPositions[0] = door.transform.position; } else if (door.name.Contains("Blue") && doorPositions[1] == Vector3.zero) { doorPositions[1] = door.transform.position; } } } return doorPositions; } /// /// 开始指引(在创建门后调用) /// public void StartGuide() { if (!isGuideInitialized) { Debug.LogWarning("指引系统未初始化,无法开始指引"); return; } if (GameLocal.Ins.self == null) { Debug.LogWarning("玩家对象未找到,无法开始指引"); return; } // 获取门位置 Vector3 doorPositionRed = guideTargetPositionRed; Vector3 doorPositionBlue = guideTargetPositionBlue; if (doorPositionRed == Vector3.zero || doorPositionBlue == Vector3.zero) { Vector3[] doorPositions = FindDoorPositions(); if (doorPositions[0] == Vector3.zero || doorPositions[1] == Vector3.zero) { Debug.LogWarning("未找到两个门位置,指引无法开始。"); return; } else { guideTargetPositionRed = doorPositions[0]; guideTargetPositionBlue = doorPositions[1]; } } Debug.Log("开始指引,红门位置: " + guideTargetPositionRed + ",蓝门位置: " + guideTargetPositionBlue + ",玩家位置: " + GameLocal.Ins.self.transform.position); // 记录初始位置 lastPlayerPosition = GameLocal.Ins.self.transform.position; // 初始计算路径 UpdateGuidePaths(); // 启动路径更新协程 if (pathUpdateCoroutine != null) { StopCoroutine(pathUpdateCoroutine); } pathUpdateCoroutine = StartCoroutine(UpdatePathsCoroutine()); // 显示指引 ShowGuideArrow(); } /// /// 路径更新协程 - 优化性能,减少更新频率 /// private IEnumerator UpdatePathsCoroutine() { while (isGuideArrowActive && isGuideInitialized) { // 检查时间间隔 if (Time.time - lastPathUpdateTime >= updatePathInterval) { UpdateGuidePaths(); lastPathUpdateTime = Time.time; } // 等待下一帧 yield return null; } } /// /// 更新指引路径 - 使用Vector3.Lerp优化计算 /// private void UpdateGuidePaths() { if (!isGuideArrowActive || GameLocal.Ins.self == null || !isGuideInitialized) return; // 获取玩家当前位置 Vector3 currentPlayerPosition = GameLocal.Ins.self.transform.position; // 检查玩家是否移动了足够远的距离 float distanceMoved = Vector3.Distance(currentPlayerPosition, lastPlayerPosition); // 如果移动距离小于阈值,且门位置未变化,则跳过更新 if (distanceMoved < updatePositionThreshold) { return; } // 更新玩家位置 lastPlayerPosition = currentPlayerPosition; // 计算两条路径 List pathToRed = CalculateSimplePath(currentPlayerPosition, guideTargetPositionRed); List pathToBlue = CalculateSimplePath(currentPlayerPosition, guideTargetPositionBlue); // 设置路径 if (pathToRed != null && pathToRed.Count > 1) { guideArrowComponentRed.SetPath(pathToRed); } if (pathToBlue != null && pathToBlue.Count > 1) { guideArrowComponentBlue.SetPath(pathToBlue); } } /// /// 计算简单路径 - 使用Vector3.Lerp插值 /// private List CalculateSimplePath(Vector3 start, Vector3 end) { if (start == Vector3.zero || end == Vector3.zero) return null; List path = new List(); // 添加起点 path.Add(start); // 使用Vector3.Lerp生成中间点 for (int i = 1; i < pathSegments; i++) { float t = (float)i / pathSegments; Vector3 point = Vector3.Lerp(start, end, t); // 轻微调整高度,形成自然曲线 float height = Mathf.Sin(t * Mathf.PI) * 0.5f; point.y += height; path.Add(point); } // 添加终点 path.Add(end); return path; } /// /// 显示指引箭头(本地调用) /// public void ShowGuideArrow() { if (guideArrowComponentRed != null && guideArrowComponentBlue != null && GameLocal.Ins.self != null && isGuideInitialized) { guideArrowComponentRed.ShowPath(); guideArrowComponentBlue.ShowPath(); isGuideArrowActive = true; Debug.Log("指引箭头已显示(2条线)"); } } /// /// 隐藏指引箭头(本地调用) /// public void HideGuideArrow() { if (guideArrowComponentRed != null && guideArrowComponentBlue != null && isGuideInitialized) { guideArrowComponentRed.ClosePath(); guideArrowComponentBlue.ClosePath(); isGuideArrowActive = false; // 停止协程 if (pathUpdateCoroutine != null) { StopCoroutine(pathUpdateCoroutine); pathUpdateCoroutine = null; } Debug.Log("指引箭头已隐藏(2条线)"); } } /// /// 清理指引箭头实例 /// public void CleanupGuideArrow() { // 停止协程 if (pathUpdateCoroutine != null) { StopCoroutine(pathUpdateCoroutine); pathUpdateCoroutine = null; } if (guideArrowInstanceRed != null) { Destroy(guideArrowInstanceRed); guideArrowInstanceRed = null; } if (guideArrowInstanceBlue != null) { Destroy(guideArrowInstanceBlue); guideArrowInstanceBlue = null; } guideArrowComponentRed = null; guideArrowComponentBlue = null; isGuideArrowActive = false; isGuideInitialized = false; } /// /// RPC调用:停止所有客户端的指引 /// [ClientRpc] public void RpcStopAllGuide() { StopGuide(); } /// /// 停止指引(在游戏开始或删除门时调用) /// public void StopGuide() { HideGuideArrow(); } #endregion }