using BehaviorDesigner.Runtime; using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Engines; using Common; using DarkTonic.MasterAudio; using DragonLi.Core; using LitJson; using Mirror; using NaughtyAttributes; using Pathfinding; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.PlayerLoop; using UnityEngine.UIElements; using XPlugin.Data.JsonLiteDB; using static XPlugin.Data.JsonLiteDB.JsonLiteDB; using Random = UnityEngine.Random; /// /// 游戏状态 /// public enum GameState { None = 0, Playing = 1, /// /// 胜利 /// Victory = 2, /// /// 失败 /// Failur = 3, Settle=4, /// /// 准备阶段 /// Wave=5, /// /// 冲锋阶段 /// EndEvent, PKEvent, } public enum GameEnemyEventType { None=0,//普通事件 Snipe=1,//狙击事件 Mortar=2,//迫击炮事件 MachineGun=3,//机关枪事件 SubmachineGun=4,//冲锋枪事件 Grenade=5,//手榴弹事件 Biochemical=6,//生化兵事件 Tank=7,//坦克兵事件 Bomber=8,//轰炸机事件 HelpNpc=9,//拯救平民事件 BulletUp=10,//子弹拾取事件 CleanUp=11,//清场事件 LastGame=12,//最终冲锋事件 } public enum GameMode { OnePlayer=0, TwoPlayers=1, } public class GameManager : NetworkBehaviour { public static GameManager Ins { get; private set; } #region 预制体 public GameObject DoorPre; public GameObject itemPropPre; public GameObject mortarBulletPre; public GameObject gqBulletPre; public GameObject gqMuzzleBulletPre; // 炮塔预制体集合 //public GameObject[] TowerPres; // 怪物预制集合 public GameObject[] EnemyPres; public GameObject[] playerAiPres; public GameObject playerEndAiPres; public GameObject explosionPre; public GameObject enemyGrenadePre; public GameObject enemyGasBombPre; public GameObject enemyEndAiPre; public GameObject enemyPlanePre; public GameObject[] playerNpcPre; //public GameObject PathGuidePre; //public GuideArrowPath pathGuide;//引导路径 // 怪物ui预制体 public GameObject EnemyUIPre; #endregion // json数据库 private JsonLiteDB DB; // 怪物信息集合 //public Dictionary EnemyInfos = new Dictionary(); public Dictionary PlayerAiInfos = new Dictionary(); // 炮塔信息集合 public Dictionary> TowerInfos = new Dictionary>(); // 枪械信息集合 //public Dictionary GunInfos = new Dictionary(); // 子弹信息集合 public Dictionary BulletInfos = new Dictionary(); public Dictionary AiInfos = new Dictionary(); public Dictionary RoundInfos = new Dictionary(); // 剧情信息 public Dictionary CombatUnitInfos = new Dictionary(); private List curGunPropList = new List(); public Dictionary players = new Dictionary(); public GameMode gameMode = GameMode.OnePlayer; /// /// 敌人自增 /// private int enemyIndex = 0; /// /// 所有敌人 /// public Dictionary EnemyList = new Dictionary(); public Dictionary PlayerAiList = new Dictionary(); /// /// 所有敌人UI /// public Dictionary EnemyUIList = new Dictionary(); /// /// 所有炮塔 /// public Dictionary TowerList = new Dictionary(); public Dictionary> TowerList2 = new Dictionary>(); public Dictionary SettleInfos = new Dictionary(); //public List curEnemyList = new List(); [SyncVar] public bool isStart; /// /// 波次自增 /// [SyncVar] public int roundIndex = 0; // 游玩结束时间 [NonSerialized] [SyncVar] public long vistEnd = 0; // 总游玩时长 private int vistAllTime = (int)(60*12);//开启最后拼刺刀模式 [SyncVar] public string settleData = ""; [SyncVar] public GameState gameState = GameState.None; [SoundGroup] public string JointIn; [SoundGroup] public string roundSound; public int roundWaveTime; public int curRoundWaveTime; public GameObject player; [SyncVar] public bool isPkEvent; //添加 [Header("AI角色")] public GameObject aiCharacterPre;//AI角色预制体 private GameObject aiCharacter;//AI角色实例 private bool isAIIntroductionComplete = false; [Header("指引系统")] public GameObject GuideArrowPre; // 指引箭头预制体 private GameObject guideArrowInstance; // 指引箭头实例 private GuideArrowPath guideArrowComponent; // 指引箭头组件 private bool isGuideArrowActive = false; private Vector3 lastPlayerPosition; // 记录玩家上一次的位置 private float updatePathThreshold = 0.5f; // 更新路径的阈值(玩家移动超过这个距离才更新路径) private float updatePathCooldown = 0.3f; // 更新路径的冷却时间 private float lastPathUpdateTime = 0f; private Vector3[] lastPathPoints; // 上一次的路径点 private bool isPathSmoothed = false; private List smoothedPath = new List(); public void GamePlay() { //CreateAICharacter(); isAIIntroductionComplete = true; GameLocal .Ins .BGMState .gameObject .SetActive (true); if (isClient) { isStart = false; CreateDoor(); } } //修改处:添加AI介绍完成后的游戏开始方法 public void StartGameAfterIntroduction() { isAIIntroductionComplete = true; GameLocal .Ins .BGMState .gameObject .SetActive (true); if (isClient) { isStart = false; CreateDoor(); } } //修改处:添加创建AI角色的方法 private void CreateAICharacter() { //检查是否已经存在AI角色 if (aiCharacter != null) { Debug.Log("AI角色已经存在,不再创建新的"); return; } if (aiCharacterPre != null) { Debug.Log("创建AI角色"); //获取第一个玩家 //Transform player = players[0].transform; if (player == null) { Debug.LogError("找不到玩家,无法创建AI角色"); return; } //在玩家前方创建AI角色 Vector3 spawnPosition = player.transform.position + player.transform.forward * 3f; spawnPosition.y = 0f; // 固定Y坐标为0 aiCharacter = Instantiate(aiCharacterPre, spawnPosition, Quaternion.identity); //获取AIController并启动开场白 AIController aiController = aiCharacter.GetComponent(); if (aiController != null) { //注册AI介绍完成回调 aiController.OnIntroductionComplete += StartGameAfterIntroduction; // 新增:注册AI离开回调 // aiController.OnIntroductionComplete += CreateGuidancePathToDoor; //添加延迟,确保所有组件已经完成初始化 StartCoroutine(DelayedStartIntroduction(aiController)); } else { Debug.LogError("AI预制体中的AIController组件丢失"); } } else { Debug.LogError("AI预制体没有在GameManager分配!"); } } private IEnumerator DelayedStartIntroduction(AIController aiController) { yield return new WaitForSeconds(0.1f); // 短暂延迟 aiController.StartIntroduction(); } void Awake() { Ins = this; } void Start() { // 新增:初始化指引系统 InitializeGuideArrow(); } /// /// 创建门 /// public void CreateDoor() { UpdateConf(); if(!isServer) return; GameObject door = Instantiate(DoorPre); door.transform.position = GameLocal.Ins.doorPos.position.ReflectVectorXOZ(); NetworkServer.Spawn(door); CoroutineTaskManager.Instance.WaitSecondTodo(() => { StartGuide(); }, 0.5f); } /// /// 创建初回合 /// public void CreateFirstRound() { RpcShowHUD(); roundIndex = 0; PlaySound2DRPC("1.0"); // CoroutineTaskManager.Instance.WaitSecondTodo(() => // { // PlaySound2DRPC("1.1"); // }, 10f); CreatePlayerAi(); //SetGameState(GameState.Wave); RpcMessageRound(); } /// /// 创建下一回合 /// public void CreateNextRound() { roundIndex++; roundWaveTime = 3; curRoundWaveTime = 1; RpcMessageRound(); } [Server] public void GameStart() { gameState = GameState.Playing; gameMode=players.Count<=1 ? GameMode.OnePlayer : GameMode.TwoPlayers; vistEnd = (long)(DateTime.Now.Subtract(new DateTime(1970, 1, 1))).TotalSeconds + vistAllTime; isStart = true; ChangeBgmRpc(0); AstarPath.active.Scan(); CreateFirstRound(); // 延迟1s后给所有人发放武器 CoroutineTaskManager.Instance.WaitSecondTodo(() => { GivePistol(GunType.Pistol); }, 1f); CoroutineTaskManager.Instance.WaitSecondTodo(() => { //GameOver(GameState.Settle); StartSpecialEvent(GameEnemyEventType.CleanUp); }, vistAllTime); } [ClientRpc] public void GivePistol(GunType type) { Debug.Log("创建武器中..."); GameLocal.Ins.self.PickUpGun(type, -999); } [Server] public void SetGameState(GameState state) { gameState = state; } //1.游戏时间到 //2.所有怪物被消灭 //3.玩家被消灭 //以上都要跳出结算 [Server] public void GameOver() { RpcShow(); DamageAllEnemy(); isStart = false; PlaySound2DRPC("1.60"); Debug.Log("游戏结束,5秒后退出"); CoroutineTaskManager.Instance.WaitSecondTodo(() => { Debug.Log("正在退出游戏..."); Application.Quit(); }, 10f); } [Server] //public void PlayEndEvent() //{ // Debug.LogError("开启End事件"); // gameState = GameState.EndEvent; // GivePistol(GunType.MeleeWeapon); // foreach (var item in PlayerAiList.Values) // { // item.EndEvent(); // } // PlaySound2DRPC("1.61"); // OpenDoor(); // for (int i = 0; i < 7; i++) // { // Transform[] targetPos = GameLocal.Ins.enemyEndPos; // GameObject enemy = Instantiate(enemyEndAiPre); // NetworkServer.Spawn(enemy); // int posId =i/3; // enemy.transform.position = GameLocal.Ins.enemyStartPos[posId].position; // enemy.transform.eulerAngles = targetPos[posId].eulerAngles; // enemyIndex++; // Enemy enemyScript = enemy.GetComponent(); // enemyScript.OnSpawn(enemyIndex, 3, 1,GameLocal.Ins.pkPos.position); // enemyScript.ai.maxSpeed = 2; // EnemyList.Add(enemyIndex, enemyScript); // GameObject playerAi = Instantiate(playerEndAiPres); // NetworkServer.Spawn(playerAi); // playerAi.transform.position = GameLocal.Ins.endPlayerStartPos.position; // playerAi.transform.eulerAngles = AiInfos[1].EulerAngles; // PlayerAI playerAI = playerAi.GetComponent(); // playerAI.OnSpawn(i+2,PlayerAIType.EndPlayerAi, 1,enemy); // playerAI.ai.maxSpeed = 2; // PlayerAiList.Add(i+2, playerAI); // } // curRoundEnemyCount = EnemyList.Count; //} public void PlayEndEvent(bool isEnd) { Debug.LogError("开启End事件"); if (isEnd) { gameState = GameState.EndEvent; PlaySound2DRPC("1.61"); OpenDoor(); } else { // 如果是PK事件,设置游戏状态 gameState = GameState.PKEvent; isPkEvent = true; } GivePistol(GunType.MeleeWeapon); foreach (var item in PlayerAiList.Values) { item.EndEvent(); } // 检查 enemyStartPos 数组是否为空或长度不足 if (GameLocal.Ins.enemyStartPos == null || GameLocal.Ins.enemyStartPos.Length == 0) { Debug.LogError("enemyStartPos 数组为空或未初始化"); return; } // 定义3x3范围的中心点和大小 Vector3 centerPos = GameLocal.Ins.pkPos.position; float areaSize = 3f; // 3x3的范围 int createCount = isEnd ? 7 : 15; // 修复:计算安全的数组索引 int enemyStartPosCount = GameLocal.Ins.enemyStartPos.Length; int enemyEndPosCount = GameLocal.Ins.enemyEndPos?.Length ?? 0; for (int i = 0; i < createCount; i++) { Transform[] targetPos = GameLocal.Ins.enemyEndPos; // 检查 enemyEndPos 数组 if (targetPos == null || targetPos.Length == 0) { Debug.LogError("enemyEndPos 数组为空"); break; } GameObject enemy = Instantiate(enemyEndAiPre); NetworkServer.Spawn(enemy); // 检查是否为指定场地 if (GameLocal.Ins.place == Place.Jiangsu_Xvzhou_Guolou_Oulebao) { // 在3x3范围内随机生成位置 Vector3 randomOffset = new Vector3( Random.Range(-areaSize / 2f, areaSize / 2f), 0, Random.Range(-areaSize / 2f, areaSize / 2f) ); enemy.transform.position = centerPos + randomOffset; enemy.transform.eulerAngles = Vector3.zero; } else { // 修复:使用安全的数组索引 // 确保 posId 不超过数组边界 int posId = Mathf.Clamp(i / 3, 0, enemyStartPosCount - 1); enemy.transform.position = GameLocal.Ins.enemyStartPos[posId].position; // 同样确保 enemyEndPos 的索引安全 int targetPosId = Mathf.Clamp(i / 3, 0, enemyEndPosCount - 1); enemy.transform.eulerAngles = targetPos[targetPosId].eulerAngles; } enemyIndex++; Enemy enemyScript = enemy.GetComponent(); // 修复:检查 AiInfos 是否包含必要的键 Vector3 targetPosition = isEnd ? GameLocal.Ins.pkPos.position : GameLocal.Ins.self.transform.position; enemyScript.OnSpawn(enemyIndex, 3, 1, targetPosition); if (AiInfos.ContainsKey(3)) { enemyScript.ai.maxSpeed = 2; } else { Debug.LogWarning("AiInfos 中不存在键为 3 的条目,使用默认速度"); enemyScript.ai.maxSpeed = 2; // 使用默认值 } EnemyList.Add(enemyIndex, enemyScript); if (isEnd) { GameObject playerAi = Instantiate(playerEndAiPres); NetworkServer.Spawn(playerAi); // 检查是否为指定场地 if (GameLocal.Ins.place == Place.Jiangsu_Xvzhou_Guolou_Oulebao) { Vector3 randomOffset; bool positionValid; int attempts = 0; do { randomOffset = new Vector3( Random.Range(-areaSize / 2f, areaSize / 2f), 0, Random.Range(-areaSize / 2f, areaSize / 2f) ); positionValid = true; Vector3 proposedPos = centerPos + randomOffset; foreach (var existingEnemy in EnemyList.Values) { if (Vector3.Distance(proposedPos, existingEnemy.transform.position) < 0.8f) { positionValid = false; break; } } attempts++; if (attempts > 15) break; } while (!positionValid); playerAi.transform.position = centerPos + randomOffset; playerAi.transform.eulerAngles = Vector3.zero; } else { // 非指定场地也需要防重叠 int posId = Mathf.Clamp(i / 3, 0, enemyStartPosCount - 1); Vector3 basePosition = GameLocal.Ins.enemyStartPos[posId].position; // 在基础位置上添加随机偏移 Vector3 randomOffset = new Vector3( Random.Range(-1.5f, 1.5f), 0, Random.Range(-1.5f, 1.5f) ); Vector3 spawnPosition = basePosition + randomOffset; // 检查位置是否有效 bool positionValid = true; foreach (var existingEnemy in EnemyList.Values) { if (Vector3.Distance(spawnPosition, existingEnemy.transform.position) < 0.8f) { positionValid = false; break; } } // 如果位置无效,尝试另一个偏移 if (!positionValid) { randomOffset = new Vector3( Random.Range(-2.0f, 2.0f), 0, Random.Range(-2.0f, 2.0f) ); spawnPosition = basePosition + randomOffset; } enemy.transform.position = spawnPosition; int targetPosId = Mathf.Clamp(i / 3, 0, enemyEndPosCount - 1); enemy.transform.eulerAngles = targetPos[targetPosId].eulerAngles; } PlayerAI playerAI = playerAi.GetComponent(); playerAI.OnSpawn(i + 2, PlayerAIType.EndPlayerAi, 1, enemy); playerAI.ai.maxSpeed = 2; PlayerAiList.Add(i + 2, playerAI); } } // 对于现有的敌人,更新它们的目标 foreach (var enemy in EnemyList.Values) { if (enemy.behaviorTree != null) { // 如果isPkEvent为true,更新敌人的目标为玩家 if (isPkEvent) { GameObject player = GetPlayer(enemy.transform); if (player != null) { enemy.target = player; var sharedTarget = enemy.behaviorTree.GetVariable("target") as SharedGameObject; if (sharedTarget != null) { sharedTarget.Value = player; } } } } } curRoundEnemyCount = EnemyList.Count; // 可选:在指定场地时添加调试可视化 if (GameLocal.Ins.place == Place.Jiangsu_Xvzhou_Guolou_Oulebao) { Debug.Log($"在场地 {GameLocal.Ins.place} 中,所有单位已生成在3x3范围内,中心点: {centerPos}"); } } // 调试用:绘制3x3区域 private void DrawDebugArea(Vector3 center, float size) { Vector3 bottomLeft = center + new Vector3(-size / 2, 0, -size / 2); Vector3 bottomRight = center + new Vector3(size / 2, 0, -size / 2); Vector3 topLeft = center + new Vector3(-size / 2, 0, size / 2); Vector3 topRight = center + new Vector3(size / 2, 0, size / 2); Debug.DrawLine(bottomLeft, bottomRight, Color.red, 10f); Debug.DrawLine(bottomRight, topRight, Color.red, 10f); Debug.DrawLine(topRight, topLeft, Color.red, 10f); Debug.DrawLine(topLeft, bottomLeft, Color.red, 10f); } [ClientRpc] public void OpenDoor() { GameLocal.Ins.OpenDoor(); } [ClientRpc] public void RpcShowWin(bool isBlue) { } [ClientRpc] public void RpcShow() { SettlePanel.Show(); } /// /// 下一波进攻开始 /// [Server] public void RpcMessageRound() { if(!isStart) return; CoroutineTaskManager.Instance.WaitSecondTodo(() => { if(!isStart|| gameState== GameState.EndEvent) return; PlaySound2DRPC("1.4"); }, curRoundWaveTime); CoroutineTaskManager.Instance.WaitSecondTodo(() => { if(!isStart|| gameState== GameState.EndEvent) return; if(isPkEvent ==true ) { SetGameState(GameState.PKEvent ); } else { SetGameState(GameState.Playing ); } GetRoundEnemy(); }, roundWaveTime); } [ClientRpc] public void ShowHudRoundTime() { DragonLi.Core.EventDispatcher.TriggerEvent("NewWaveStart", roundIndex); } [ClientRpc] public void ChangeBgmRpc(int i) { GameLocal.Ins.BGMState.StateChange(i); } public int curRoundEnemyCount; /// /// 创建敌方单位 /// [Server] IEnumerator CreateEnemy(RoundInfo info) { curRoundEnemyCount = info.EnemyList.Count; for (int i = 0; i < info.EnemyList.Count; i++) { int enemyId = info.EnemyList[i]; bool isUerInfoAngles = (EnemyType)enemyId == EnemyType.Mortar; Transform[] targetPos = GameLocal.Ins.enemyEndPos; GameObject enemy = Instantiate(EnemyPres[enemyId-3]); NetworkServer.Spawn(enemy); int posId =i/3; int posX=i%3==0? 0: i%3==1 ? 1 : -1; if (isUerInfoAngles) posX = 0; switch ((EnemyType)enemyId) { case EnemyType.Ordinary: case EnemyType.Sniper: break; case EnemyType.Mortar: targetPos = GameLocal.Ins.mortarPos; break; } enemy.transform.position = GameLocal.Ins.enemyStartPos[posId].position; enemy.transform.eulerAngles =isUerInfoAngles? AiInfos[i+3].EulerAngles: targetPos[posId].eulerAngles; enemyIndex++; Enemy enemyScript = enemy.GetComponent(); Vector3 endPos = targetPos[posId].position + new Vector3(0, 0, posX); if (enemyScript.type == EnemyType.Riflemen) { endPos = GameLocal.Ins.riflemenPos.position; } enemyScript.OnSpawn(enemyIndex, info.EnemyList[i], 1,endPos); EnemyList.Add(enemyIndex, enemyScript); yield return new WaitForSeconds(0.5f); } } /// /// 创建辅助AI /// public void CreatePlayerAi() { for (int i = 0; i < 2; i++) { GameObject playerAi = Instantiate(playerAiPres[i]); NetworkServer.Spawn(playerAi); playerAi.transform.position = GameLocal.Ins.playerAiPos[i].position; playerAi.transform.eulerAngles = AiInfos[i+1].EulerAngles; PlayerAI enemyScript = playerAi.GetComponent(); enemyScript.OnSpawn(i,(PlayerAIType)i, 1,GameLocal.Ins.playerAiEndPos[i].gameObject); PlayerAiList.Add(i, enemyScript); } // else if(gameMode == GameMode.TwoPlayers) // { // GameObject playerAi = Instantiate(playerAiPres[0]); // NetworkServer.Spawn(playerAi); // playerAi.transform.position = GameLocal.Ins.playerAiPos[0].position; // playerAi.transform.eulerAngles = AiInfos[1].EulerAngles; // PlayerAI enemyScript = playerAi.GetComponent(); // enemyScript.OnSpawn(0,0, 1,GameLocal.Ins.playerAiEndPos[0].gameObject); // PlayerAiList.Add(0, enemyScript); // } } #region 创造敌人道具 [Server] public void CreateExplosion(Vector3 pos,EnemyType type) { if(!isServer) return; var ex= Instantiate(explosionPre, pos, Quaternion.identity); NetworkServer.Spawn(ex); ex.GetComponent().Init(type); } public void CreateGrenade(Vector3 startPos) { if(!isServer) return; var ex= Instantiate(enemyGrenadePre, startPos, Quaternion.identity); NetworkServer.Spawn(ex); float x=Random.Range(-3,3); float z=Random.Range(-3,3); ex.GetComponent().Init(startPos, GameLocal.Ins.endMortarPos.position+new Vector3(x,0,z)); } public void CreateGasBomb(Vector3 startPos) { if(!isServer) return; var ex= Instantiate(enemyGasBombPre, startPos, Quaternion.identity); NetworkServer.Spawn(ex); float x=Random.Range(-3,3); float z=Random.Range(-3,3); ex.GetComponent().Init(startPos,GameLocal.Ins.endMortarPos.position+new Vector3(x,0,z)); } public void CreateEnemyPlane(Transform startPos) { if(!isServer) return; var ex= Instantiate(enemyPlanePre, startPos.position, Quaternion.identity); NetworkServer.Spawn(ex); } public void CreateItemProp(Transform pos) { if(!isStart) return; var item= Instantiate(itemPropPre); NetworkServer.Spawn(item); item.transform.position=new Vector3(pos.position.x,0.5f,pos.position.z); item.GetComponent().SetItemProp(ItemPropType.Hp); } #endregion public List GetRoundEnemy() { List curEnemyList = new List(); int curRoundIndex=roundIndex>= RoundInfos.Count? Random.Range(1,RoundInfos.Count):roundIndex; var roundInfo=RoundInfos[curRoundIndex]; // curRoundIndex=5; // roundInfo= RoundInfos[curRoundIndex]; bool isPk; int pkIndex = Random.Range(0, 100); isPk = pkIndex <= 0; if (isPk) { isPkEvent = true; PlayEndEvent(false); } else { isPkEvent = false; GivePistol(GunType.Pistol); StartEvent(roundInfo); } if (roundIndex==5) { CoroutineTaskManager.Instance.WaitSecondTodo(() => { StartSpecialEvent(GameEnemyEventType.HelpNpc); }, 5); } return curEnemyList; } public GameEnemyEventType curEnemyEventType; public void StartEvent(RoundInfo info) { curEnemyEventType = (GameEnemyEventType)info.Belong; StartCoroutine(CreateEnemy(info)); switch (curEnemyEventType) { case GameEnemyEventType.SubmachineGun: case GameEnemyEventType.None: CoroutineTaskManager.Instance.WaitSecondTodo(() => { Debug.LogError("播放音效:1.10"); PlayerAiList[0].PlaySound3DRPC("1.10",true); EnemyShowOutline(EnemyType.Ordinary); EnemyShowOutline(EnemyType.Charge); }, 10); break; case GameEnemyEventType.Snipe: CoroutineTaskManager.Instance.WaitSecondTodo(() => { PlayerAiList[0].PlaySound3DRPC("1.14",true); }, 5); CoroutineTaskManager.Instance.WaitSecondTodo(() => { PlayerAiList[0].PlaySound3DRPC("1.15",true); EnemyShowOutline(EnemyType.Sniper); }, 7); break; case GameEnemyEventType.Mortar: CoroutineTaskManager.Instance.WaitSecondTodo(() => { PlayerAiList[0].PlaySound3DRPC("1.20",true); StartCoroutine(CreateMortarBullet()); }, 7); CoroutineTaskManager.Instance.WaitSecondTodo(() => { PlayerAiList[0].PlaySound3DRPC("1.21",true); EnemyShowOutline(EnemyType.Mortar); }, 10); break; case GameEnemyEventType.MachineGun: CoroutineTaskManager.Instance.WaitSecondTodo(() => { PlayerAiList[0].PlaySound3DRPC("1.24",true); StartCoroutine(CreateGQBullet()); }, 7); CoroutineTaskManager.Instance.WaitSecondTodo(() => { PlayerAiList[0].PlaySound3DRPC("1.25",true); EnemyShowOutline( EnemyType.Riflemen); }, 10); break; case GameEnemyEventType.Grenade: CoroutineTaskManager.Instance.WaitSecondTodo(() => { PlayerAiList[0].PlaySound3DRPC("1.35",true); }, 5); CoroutineTaskManager.Instance.WaitSecondTodo(() => { PlayerAiList[0].PlaySound3DRPC("1.38",true); EnemyShowOutline(EnemyType.Grenade); }, 7); break; case GameEnemyEventType.Biochemical: CoroutineTaskManager.Instance.WaitSecondTodo(() => { PlayerAiList[0].PlaySound3DRPC("1.40",true); }, 5); CoroutineTaskManager.Instance.WaitSecondTodo(() => { PlayerAiList[0].PlaySound3DRPC("1.42",true); EnemyShowOutline(EnemyType.Virus); }, 7); break; case GameEnemyEventType.Bomber: CoroutineTaskManager.Instance.WaitSecondTodo(() => { PlayerAiList[0].PlaySound3DRPC("1.56",true); EnemyShowOutline(EnemyType.Commander); EnemyShowOutline(EnemyType.Mortar); EnemyShowOutline(EnemyType.Grenade); EnemyShowOutline(EnemyType.Virus); EnemyShowOutline(EnemyType.Riflemen); StartCoroutine(CreateGQBullet()); StartCoroutine(CreateMortarBullet()); }, 10); CoroutineTaskManager.Instance.WaitSecondTodo(() => { PlayerAiList[0].PlaySound3DRPC("1.59",true); }, 20); break; } } public void StartSpecialEvent(GameEnemyEventType type) { switch (type) { case GameEnemyEventType.HelpNpc: CreateNpc(); CoroutineTaskManager.Instance.WaitSecondTodo(() => { PlayerAiList[0].PlaySound3DRPC("1.67",true); }, 5); break; case GameEnemyEventType.CleanUp: CreateEnemyEndBullet(true); break; } } private void Update() { // 更新指引箭头位置 UpdateGuideArrowPosition(); // if (Input.GetKeyDown(KeyCode.Space)) // { // StartSpecialEvent(GameEnemyEventType.HelpNpc); // CoroutineTaskManager.Instance.WaitSecondTodo(() => // { // StartSpecialEvent(GameEnemyEventType.CleanUp); // }, 5); // } } IEnumerator CreateMortarBullet() { Transform insPos=GameLocal.Ins.insMortarPos; Transform endPos=GameLocal.Ins.endMortarPos; bool isHaveMortar = IsHaveTypeEnemy(EnemyType.Mortar); while (isHaveMortar) { var item= Instantiate(mortarBulletPre); NetworkServer.Spawn(item); item.transform.position=insPos.position; int rangeIndexX = Random.Range(-5, 5); int rangeIndexZ = Random.Range(-2, 2); item.GetComponent().Init(insPos.position,endPos.position+new Vector3(rangeIndexX,0,rangeIndexZ)); yield return new WaitForSeconds(AiInfos[(int)EnemyType.Mortar].FiringRate); isHaveMortar = IsHaveTypeEnemy(EnemyType.Mortar); } } IEnumerator CreateGQBullet() { Transform insPos=GameLocal.Ins.gQPos; Vector3 endPos=GameLocal.Ins.riflemenEndPos.position; bool isHaveMortar = IsHaveTypeEnemy(EnemyType.Riflemen); while (isHaveMortar) { var item= Instantiate(gqBulletPre); NetworkServer.Spawn(item); item.transform.position=insPos.position; int rangeIndexZ = Random.Range(-2, 2); item.GetComponent().OnSpawn(endPos+new Vector3(rangeIndexZ,0,rangeIndexZ),20); var itemMuzzle= Instantiate(gqMuzzleBulletPre); NetworkServer.Spawn(itemMuzzle); itemMuzzle.transform.position=insPos.position; CoroutineTaskManager.Instance.WaitSecondTodo(() => { NetworkServer.Destroy(itemMuzzle.gameObject); }, 1); yield return new WaitForSeconds(AiInfos[(int)EnemyType.Riflemen].FiringRate); isHaveMortar = IsHaveTypeEnemy(EnemyType.Riflemen); } } [ClientRpc] public void OpenEndBullet() { GameLocal.Ins.OpenEndBullet(); } [ClientRpc] public void DownEndBullet() { GameLocal.Ins.DownEndBullet(); } public List NpcEnemyList = new List(); public void CreateNpc() { if (!isStart) return; for (int posId = 0; posId < 2; posId++) // npcPos[0] 和 npcPos[1] { List npcList = new List(); // === 生成 3 个 NPC === for (int i = 0; i < 3; i++) { int id = Random.Range(0, playerNpcPre.Length); // 在 npcPos 附近生成,带随机偏移 Vector3 randomOffset = new Vector3(Random.Range(-1f, 1f), 0, Random.Range(-1f, 1f)); Vector3 npcPos = GameLocal.Ins.npcPos[posId].position + randomOffset; var npc = Instantiate(playerNpcPre[id], npcPos, Quaternion.identity); NetworkServer.Spawn(npc); npc.GetComponent().OnSpawn(i, GameLocal.Ins.npcEndPos.gameObject, false); npcList.Add(npc); } // === 敌人生成逻辑 === // NPC 的平均位置(大致在3个 NPC 中心点) Vector3 centerPos = Vector3.zero; foreach (var npc in npcList) centerPos += npc.transform.position; centerPos /= npcList.Count; // 前进方向 Vector3 forward = GameLocal.Ins.npcPos[posId].forward; // 敌人在前进方向的反方向(后面) Vector3 backOffset = -forward * 3f; // 敌人与NPC中心点的间隔,数值可调 for (int j = 0; j < 2; j++) { // 敌人横向展开 Vector3 sideOffset = Vector3.Cross(Vector3.up, forward).normalized * (j == 0 ? -1.5f : 1.5f); Vector3 enemyPos = centerPos + backOffset + sideOffset; var enemy = Instantiate(EnemyPres[0], enemyPos, Quaternion.identity); NetworkServer.Spawn(enemy); enemy.GetComponent().OnSpawn(-j, 3, -1, GameLocal.Ins.npcPos[posId].position); enemy.GetComponent().isOutLine = true; NpcEnemyList.Add(enemy.GetComponent()); } } } public void CreateEnemyEndBullet(bool isEvent) { PlayerAiList[0].PlaySound3DRPC("1.65",true); OpenEndBullet(); CoroutineTaskManager.Instance.WaitSecondTodo(() => { foreach (var item in EnemyList.Values) { item.Die(null,transform); } foreach (var item in NpcEnemyList) { item.Die(null,transform); } }, 5); CoroutineTaskManager.Instance.WaitSecondTodo(() => { PlayEndEvent(isEvent); DownEndBullet(); }, 8); } public bool IsHaveTypeEnemy(EnemyType curType) { foreach (var enemy in EnemyList.Values) { if (enemy.type == curType) return true; } return false; } public void EnemyShowOutline(EnemyType type) { foreach (var enemy in EnemyList.Values) { if (enemy.type == type) { enemy.isOutLine = true; } } } /// /// 删除敌方单位 /// [Server] public void DeleteEnemy(int id,bool isNpcEnemy) { if (isNpcEnemy) { foreach (var item in NpcEnemyList) { if (item.id == id) { NpcEnemyList.Remove(item); NetworkServer.Destroy(item.gameObject); return; } } return; } GameObject enemy = EnemyList[id].gameObject; EnemyList.Remove(id); curRoundEnemyCount--; NetworkServer.Destroy(enemy); ChangePlayerAiCountdownTime(); if (curRoundEnemyCount<=0) { // 如果是PK事件结束,重置PK事件标志 if (gameState == GameState.PKEvent) { isPkEvent = false; gameState = GameState.Playing; // 创建下一波敌人 CreateNextRound(); } else if (gameState == GameState.EndEvent) { gameState = GameState.Victory; GameOver(); } else { CreateNextRound(); } } } /// /// 痛击场上所有敌方单位 /// [Command(requiresAuthority = false)] public void DamageAllEnemy() { foreach (Enemy enemy in EnemyList.Values) { enemy.ApplyDamage(999999, null, null); } } /// /// 更新配置表 /// 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); roundWaveTime = 15; curRoundWaveTime = 12; AiInfos.Clear(); RoundInfos.Clear(); TableReader infoReader = DB["AiInfo"].GetReader(); // EnemyInfos.Clear(); // while (infoReader.Read()) // { // EnemyInfo info = new EnemyInfo(infoReader); // EnemyInfos.Add((EnemyType)info.Type,info); // } // // infoReader = DB["TowersInfo"].GetReader(); // while (infoReader.Read()) // { // TowerInfo info = new TowerInfo(infoReader); // TowerInfos.TryGetValue((TowerType)info.Type, out Dictionary infoList); // if (infoList == null) // { // Dictionary list = new Dictionary(); // list.Add(info.Lvl, info); // TowerInfos.Add((TowerType)info.Type, list); // } // else // { // infoList.Add(info.Lvl, info); // } // } // infoReader = DB["GunsInfo"].GetReader(); // GunInfos.Clear(); // while (infoReader.Read()) // { // GunInfo info = new GunInfo(infoReader); // GunInfos.Add((GunType)info.Type,info); // } // // infoReader = DB["BulletsInfo"].GetReader(); // BulletInfos.Clear(); // while (infoReader.Read()) // { // BulletInfo info = new BulletInfo(infoReader); // BulletInfos.Add((BulletType)info.Type,info); // } infoReader = DB["AiInfo"].GetReader(); while (infoReader.Read()) { AiInfo info = new AiInfo(infoReader); AiInfos.Add(info.ID,info); } infoReader = DB["RoundInfo"].GetReader(); while (infoReader.Read()) { RoundInfo info = new RoundInfo(infoReader); RoundInfos.Add(info.ID,info); } // infoReader = DB["StoryProgressInfo"].GetReader(); // while (infoReader.Read()) // { // CombatUnitInfo info = new CombatUnitInfo(infoReader); // CombatUnitInfos.Add(info.Belong,info); // CombatUnitInfos.TryGetValue(info.Belong, out info); // } Debug.Log("游戏数值更新 -> complete"); } /// /// 获取范围内敌人 /// public int GetRangeEnemyId(Vector3 pos, float radius) { int res = -1; foreach (Enemy enemy in EnemyList.Values) { float dis = Vector3.Distance(enemy.transform.position, pos); if (dis <= radius) { radius = dis; res = enemy.id; break; } } return res; } /// /// 获取最近的Ai /// /// public GameObject GetPlayerAi(Transform curTransform) { if (PlayerAiList.Count==0 ) return null; GameObject nearestPlayer = null; float minDistance = float.MaxValue; foreach (var player in PlayerAiList) { if (player.Value == null|| player.Key<=1|| player.Value.isGet) continue; // 计算XZ平面距离(使用你的 ReflectVectorXOZ 方法) float dis = Vector3.Distance( player.Value.transform.position.ReflectVectorXOZ(), curTransform.position.ReflectVectorXOZ() ); if (dis < minDistance) { minDistance = dis; player.Value.isGet = true; nearestPlayer = player.Value.gameObject; } } return nearestPlayer; } public GameObject GetPlayer(Transform curTransform) { if (players == null || players.Count == 0) return null; GameObject nearestPlayer = null; float minDistance = float.MaxValue; foreach (var player in players.Values) { if (player == null) continue; // 计算XZ平面距离(使用你的 ReflectVectorXOZ 方法) float dis = Vector3.Distance( player.transform.position.ReflectVectorXOZ(), curTransform.position.ReflectVectorXOZ() ); if (dis < minDistance) { minDistance = dis; nearestPlayer = player.gameObject; } } return nearestPlayer; } /// /// 获取最近敌人 /// /// public GameObject GetEnemy(Transform playerAi) { GameObject nearestPlayer = null; float minDis = 999; foreach (var enemy in EnemyList.Values) { if (enemy == null) continue; // 计算XZ平面距离(使用你的 ReflectVectorXOZ 方法) float dis = Vector3.Distance( enemy.transform.position.ReflectVectorXOZ(), playerAi.position.ReflectVectorXOZ() ); if (dis < minDis) { nearestPlayer = enemy.gameObject; minDis = dis; } } return nearestPlayer; } public void ChangePlayerAiCountdownTime() { foreach (var ai in PlayerAiList.Values) { ai.ChangeKillEnemyTime(); } } public Transform GetPlayerPos() { int rand = Random.Range(0, 100); if (players.Count >= 2) // 双人模式 { if (rand < 45) // 0-44 return players[0].transform; // 玩家A else if (rand < 90) // 45-89 return players[1].transform; // 玩家B else // 90-99 return PlayerAiList[0].transform; // 队友A } else // 单人模式 { if (rand < 60) // 0-59 return players[0].transform; // 玩家 else if (rand < 80) // 60-79 return PlayerAiList[0].transform; // 队友A else // 80-99 return PlayerAiList[1].transform; // 队友B } } [Server] public void CreateEnemyUI(Enemy enemy) { GameObject enemyUI = Instantiate(EnemyUIPre); NetworkServer.Spawn(enemyUI); EnemyUI 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(); } #region 空投道具 /// /// 修复炮塔 /// public void RepairTower() { } /// /// 进入联控 /// [Server] public void JoinControlTower(int playerIndex) { PlayJointMusic(); // 修改所有炮塔模式 foreach (Tower tower in TowerList.Values) { if (tower.controllerId == -1) { tower.controllerId = playerIndex; tower.mode = TowerMode.Joint; } } TriggerEvent("JoinControlStart", playerIndex); CoroutineTaskManager.Instance.WaitSecondTodo(() => { ChangeBgmRpc(0); foreach (Tower tower in TowerList.Values) { if (tower.mode == TowerMode.Joint) { tower.controllerId = playerIndex; tower.mode = tower.originalMode; } TriggerEvent("JoinControlEnd", playerIndex); } }, 30f); } #endregion [ClientRpc] public void TriggerEvent(string key, int param) { DragonLi.Core.EventDispatcher.TriggerEvent(key, param); } public void AddScore(string index, int damage) { if (SettleInfos.ContainsKey(index)) { SettleInfos[index].Score += damage; } string jsonStr = JsonMapper.ToJson(SettleInfos); settleData = jsonStr; } public void SetTitle(string index, string title) { if (SettleInfos.ContainsKey(index)) { SettleInfos[index].Title = title; } Debug.Log(SettleInfos); } public void SetPlayerName(string index) { if (SettleInfos.ContainsKey(index)) { SettleInfos[index].playerName = int.Parse(index); } } [Server] public void AddData(int playerIndex) { SettleInfos.Add(playerIndex.ToString(), new SettleInfo()); } public string SerializeSettleInfos() { return JsonUtility.ToJson(SettleInfos); } public bool IsHaveEnemy() { return EnemyList.Count > 0; } #region 工具 public string GetLessTimeStr() { string res = ""; if (isStart) { res = FormatTime((int)(vistEnd - DateTime.Now.Subtract(new DateTime(1970, 1, 1)).TotalSeconds)); } return res; } public int GetLessTimeSeconds() { return (int)(vistEnd - DateTime.Now.Subtract(new DateTime(1970, 1, 1)).TotalSeconds); } // 时分秒 public string FormatTime(int 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 public void PlaySound2DRPC(string sound) { MasterAudio.PlaySound(sound); } [ClientRpc] public void PlaySound3DRPC(string sound,Transform tran,bool isStop) { if (isStop) MasterAudio.StopAllSoundsOfTransform(tran); MasterAudio.PlaySound3DAtTransform(sound, tran); } public void PlayJointMusic() { MasterAudio.PlaySound(JointIn); GameLocal.Ins.BGMState.StateChange(2); } #region 指引系统 /// /// 初始化指引箭头 /// private void InitializeGuideArrow() { // 检查预制体是否存在 if (GuideArrowPre == null) { Debug.LogError("GuideArrowPre 预制体未分配!"); return; } // 实例化指引箭头预制体 guideArrowInstance = Instantiate(GuideArrowPre); guideArrowInstance.name = "GuideArrow"; // 获取指引箭头组件 guideArrowComponent = guideArrowInstance.GetComponent(); if (guideArrowComponent == null) { Debug.LogError("GuideArrowPre 预制体上没有找到 GuideArrowPath 组件!"); Destroy(guideArrowInstance); guideArrowInstance = null; return; } // 设置指引箭头的层级和高度 guideArrowComponent.obstacleMask = LayerMask.GetMask("Default"); // 根据实际障碍物层级设置 guideArrowComponent.pathHeight = 0.5f; // 设置路径显示高度 // 初始隐藏指引 guideArrowComponent.ClosePath(); // 可选:设置父物体,保持场景整洁 guideArrowInstance.transform.SetParent(this.transform); // 初始化玩家位置 if (GameLocal.Ins.self != null) { lastPlayerPosition = GameLocal.Ins.self.transform.position; } } /// /// 更新指引箭头位置(实时跟随玩家移动) /// // 修改 UpdateGuideArrowPosition 方法 private void UpdateGuideArrowPosition() { if (!isGuideArrowActive || GameLocal.Ins .doorPos == null || GameLocal.Ins.self == null) return; // 获取玩家当前位置 Vector3 currentPlayerPosition = GameLocal.Ins.self.transform.position; // 检查冷却时间 if (Time.time - lastPathUpdateTime < updatePathCooldown) return; // 检查玩家是否移动了足够远的距离 float distanceMoved = Vector3.Distance(currentPlayerPosition, lastPlayerPosition); if (distanceMoved > updatePathThreshold) { // 获取门的位置 Vector3 doorPosition = GameLocal.Ins.doorPos.transform.position; // 使用曲线检测和路径平滑 UpdatePathWithCurveDetection(currentPlayerPosition, doorPosition); // 更新记录的位置和时间 lastPlayerPosition = currentPlayerPosition; lastPathUpdateTime = Time.time; } } // 新增:使用曲线检测和路径平滑的方法 private void UpdatePathWithCurveDetection(Vector3 start, Vector3 end) { if (guideArrowComponent == null) return; // 1. 检测是否为直接可见路径 if (IsDirectPathClear(start, end)) { // 直接路径,使用简单的贝塞尔曲线 smoothedPath = GenerateBezierCurve(start, end, 0.2f); guideArrowComponent.SetPath(smoothedPath); // 这里使用 List 参数 isPathSmoothed = true; return; } // 2. 间接路径,使用优化的绕路算法 List newPath = CalculateObstacleAvoidancePath(start, end); if (newPath != null && newPath.Count > 1) { // 应用路径平滑 smoothedPath = SmoothPath(newPath); guideArrowComponent.SetPath(smoothedPath); // 这里使用 List 参数 isPathSmoothed = true; lastPathPoints = smoothedPath.ToArray(); } } // 新增:检查直接路径是否畅通 private bool IsDirectPathClear(Vector3 start, Vector3 end) { Vector3 direction = (end - start).normalized; float distance = Vector3.Distance(start, end); // 使用射线检测,同时检查多个点 int checkPoints = Mathf.CeilToInt(distance / 0.5f); for (int i = 0; i <= checkPoints; i++) { float t = (float)i / checkPoints; Vector3 checkPoint = Vector3.Lerp(start, end, t); // 检查周围小范围的碰撞 if (Physics.CheckSphere(checkPoint, 0.3f, guideArrowComponent.obstacleMask)) { return false; } } return true; } // 新增:生成贝塞尔曲线路径 private List GenerateBezierCurve(Vector3 start, Vector3 end, float curveHeight) { List curvePoints = new List(); int segments = 20; // 曲线分段数 // 计算控制点(在中间稍微抬起形成曲线) Vector3 controlPoint = (start + end) * 0.5f + Vector3.up * curveHeight; for (int i = 0; i <= segments; i++) { float t = (float)i / segments; // 二次贝塞尔曲线公式 Vector3 point = (1 - t) * (1 - t) * start + 2 * (1 - t) * t * controlPoint + t * t * end; curvePoints.Add(point); } return curvePoints; } // 新增:优化后的绕障碍物路径计算 private List CalculateObstacleAvoidancePath(Vector3 start, Vector3 end) { List path = new List(); path.Add(start); // 尝试寻找最佳绕行点 Vector3 bypassPoint = FindOptimalBypassPoint(start, end); if (bypassPoint != start) { // 如果有绕行点,构建曲线路径 List curve1 = GenerateBezierCurve(start, bypassPoint, 0.3f); List curve2 = GenerateBezierCurve(bypassPoint, end, 0.3f); path.AddRange(curve1.Skip(1)); path.AddRange(curve2.Skip(1)); } else { // 没有找到绕行点,使用简单的曲线 path = GenerateBezierCurve(start, end, 0.2f); } return path; } // 新增:寻找最优绕行点 private Vector3 FindOptimalBypassPoint(Vector3 from, Vector3 to) { Vector3 direction = (to - from).normalized; float distance = Vector3.Distance(from, to); // 定义多个探测方向 Vector3[] probeDirections = new Vector3[] { Vector3.Cross(direction, Vector3.up).normalized, // 右侧 -Vector3.Cross(direction, Vector3.up).normalized, // 左侧 (Vector3.Cross(direction, Vector3.up).normalized + Vector3.up * 0.3f).normalized, // 右上 (-Vector3.Cross(direction, Vector3.up).normalized + Vector3.up * 0.3f).normalized // 左上 }; float[] probeDistances = new float[] { 2f, 3f, 4f, 5f }; Vector3 bestBypassPoint = from; float bestScore = float.MaxValue; foreach (Vector3 probeDir in probeDirections) { foreach (float probeDist in probeDistances) { Vector3 probePoint = from + probeDir * probeDist; // 检查探测点是否可行 if (!Physics.CheckSphere(probePoint, 0.5f, guideArrowComponent.obstacleMask)) { // 计算路径分数(距离 + 转向角度) float pathLength = Vector3.Distance(from, probePoint) + Vector3.Distance(probePoint, to); float angleCost = Vector3.Angle(probePoint - from, to - probePoint) * 0.1f; float score = pathLength + angleCost; if (score < bestScore) { bestScore = score; bestBypassPoint = probePoint; } } } } return bestBypassPoint; } // 新增:路径平滑算法 private List SmoothPath(List rawPath) { if (rawPath.Count < 3) return rawPath; List smoothed = new List(); smoothed.Add(rawPath[0]); // 使用简单的平均平滑 for (int i = 1; i < rawPath.Count - 1; i++) { Vector3 smoothedPoint = (rawPath[i - 1] + rawPath[i] + rawPath[i + 1]) / 3f; smoothed.Add(smoothedPoint); } smoothed.Add(rawPath[rawPath.Count - 1]); return smoothed; } /// /// 显示指引箭头 /// public void ShowGuideArrow() { if (guideArrowComponent != null && GameLocal.Ins.doorPos != null && GameLocal.Ins.self != null) { guideArrowComponent.ShowPath(); isGuideArrowActive = true; // 初始化路径 Vector3 playerPosition = GameLocal.Ins.self.transform.position; Vector3 doorPosition = GameLocal.Ins.doorPos.transform.position; UpdatePathWithCurveDetection(playerPosition, doorPosition); // 记录初始位置 lastPlayerPosition = playerPosition; } } /// /// 隐藏指引箭头 /// public void HideGuideArrow() { if (guideArrowComponent != null) { guideArrowComponent.ClosePath(); isGuideArrowActive = false; } } /// /// 开始指引(在创建门后调用) /// public void StartGuide() { if (GameLocal.Ins.doorPos != null && GameLocal.Ins.self != null) { ShowGuideArrow(); // 初始化路径 Vector3 playerPosition = GameLocal.Ins.self.transform.position; Vector3 doorPosition = GameLocal.Ins.doorPos.transform.position; // 使用新的路径计算方法 UpdatePathWithCurveDetection(playerPosition, doorPosition); // 记录初始位置 lastPlayerPosition = playerPosition; } } /// /// 停止指引(在游戏开始或删除门时调用) /// public void StopGuide() { HideGuideArrow(); } /// /// 清理指引箭头实例 /// public void CleanupGuideArrow() { if (guideArrowInstance != null) { Destroy(guideArrowInstance); guideArrowInstance = null; guideArrowComponent = null; isGuideArrowActive = false; } } #endregion }