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
}