Files
MRCS/Assets/_MrCs/Scripts/Manager/GameManager.cs

1284 lines
36 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
/// <summary>
/// 游戏状态
/// </summary>
public enum GameState
{
None = 0,
Playing = 1,
/// <summary>
/// 胜利
/// </summary>
Victory = 2,
/// <summary>
/// 失败
/// </summary>
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<EnemyType, Dictionary<int, EnemyInfo>> EnemyInfos = new Dictionary<EnemyType, Dictionary<int, EnemyInfo>>();
// 枪械信息集合
public Dictionary<GunType, Dictionary<int, GunInfo>> GunInfos = new Dictionary<GunType, Dictionary<int, GunInfo>>();
/// <summary>
/// 所有敌人UI
/// </summary>
public Dictionary<int, PlayerUI> EnemyUIList = new Dictionary<int, PlayerUI>();
public Dictionary<string, SettleInfo> SettleInfos = new Dictionary<string, SettleInfo>();
public List<Player> allPlayers = new List<Player>();
// 游玩结束时间
[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;
}
}
/// <summary>
/// 创建门
/// </summary>
[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);
}
/// <summary>
/// 同步门的位置给所有客户端
/// </summary>
[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<DamageNumberNetwork>();
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<CoroutineTaskManager>.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<Player>();
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<AirdropPlane>();
airdropPlaneScript.OnSpawn(endPos);
}
[Server]
public void CreateAirDropItem(int id, Vector3 startPos)
{
GameObject airdropItem = Instantiate(airdropItems[id], startPos, quaternion.identity);
NetworkServer.Spawn(airdropItem);
}
/// <summary>
/// 痛击场上所有敌方单位
/// </summary>
[Command(requiresAuthority = false)]
public void DamageAllEnemy()
{
}
/// <summary>
/// 更新配置表
/// </summary>
public void UpdateConf()
{
string text = Resources.Load<TextAsset>("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<int, GunInfo> infoList);
if (infoList == null)
{
Dictionary<int, GunInfo> list = new Dictionary<int, GunInfo>();
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<PlayerUI>();
//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<CoroutineTaskManager>.Instance.WaitSecondTodo(() =>
{
foreach (var item in allPlayers)
{
RpcPlayerRoundEnd(item.connectionToClient);
}
}, 1f, this);
CreateRoundPlaceDoorPre();
MonoSingleton<CoroutineTaskManager>.Instance.WaitSecondTodo(() =>
{
if (gameState == GameState.Settle)
return;
BlueRoundScore = 0;
RedRoundScore = 0;
foreach (var player in allPlayers)
{
RpcPlayerWaitRoundEnd(player.connectionToClient);
}
}, 10f, this);
MonoSingleton<CoroutineTaskManager>.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<CoroutineTaskManager>.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<Transform>())
{
if (child == curPlace.transform) continue; // 跳过根节点
Collider childCollider = child.GetComponent<Collider>();
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
/// <summary>
/// 红色方加分
/// </summary>
/// <param name="score"></param>
public void AddRedScore(int score)
{
RedRoundScore += score;
}
/// <summary>
/// 蓝色方加分
/// </summary>
/// <param name="score"></param>
public void AddBlueScore(int score)
{
BlueRoundScore += score;
}
#endregion
#region - 线使Vector3.Lerp
/// <summary>
/// 初始化指引箭头(每个客户端独立)
/// </summary>
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<GuideArrowPath>();
guideArrowComponentBlue = guideArrowInstanceBlue.GetComponent<GuideArrowPath>();
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);
}
}
/// <summary>
/// 尝试查找门并开始指引(客户端使用)
/// </summary>
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("客户端:未找到两个门,等待门位置同步...");
}
}
}
/// <summary>
/// 查找门的位置(客户端使用)- 优化版,减少查找频率
/// </summary>
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;
}
/// <summary>
/// 开始指引(在创建门后调用)
/// </summary>
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();
}
/// <summary>
/// 路径更新协程 - 优化性能,减少更新频率
/// </summary>
private IEnumerator UpdatePathsCoroutine()
{
while (isGuideArrowActive && isGuideInitialized)
{
// 检查时间间隔
if (Time.time - lastPathUpdateTime >= updatePathInterval)
{
UpdateGuidePaths();
lastPathUpdateTime = Time.time;
}
// 等待下一帧
yield return null;
}
}
/// <summary>
/// 更新指引路径 - 使用Vector3.Lerp优化计算
/// </summary>
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<Vector3> pathToRed = CalculateSimplePath(currentPlayerPosition, guideTargetPositionRed);
List<Vector3> pathToBlue = CalculateSimplePath(currentPlayerPosition, guideTargetPositionBlue);
// 设置路径
if (pathToRed != null && pathToRed.Count > 1)
{
guideArrowComponentRed.SetPath(pathToRed);
}
if (pathToBlue != null && pathToBlue.Count > 1)
{
guideArrowComponentBlue.SetPath(pathToBlue);
}
}
/// <summary>
/// 计算简单路径 - 使用Vector3.Lerp插值
/// </summary>
private List<Vector3> CalculateSimplePath(Vector3 start, Vector3 end)
{
if (start == Vector3.zero || end == Vector3.zero)
return null;
List<Vector3> path = new List<Vector3>();
// 添加起点
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;
}
/// <summary>
/// 显示指引箭头(本地调用)
/// </summary>
public void ShowGuideArrow()
{
if (guideArrowComponentRed != null && guideArrowComponentBlue != null &&
GameLocal.Ins.self != null && isGuideInitialized)
{
guideArrowComponentRed.ShowPath();
guideArrowComponentBlue.ShowPath();
isGuideArrowActive = true;
Debug.Log("指引箭头已显示2条线");
}
}
/// <summary>
/// 隐藏指引箭头(本地调用)
/// </summary>
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条线");
}
}
/// <summary>
/// 清理指引箭头实例
/// </summary>
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;
}
/// <summary>
/// RPC调用停止所有客户端的指引
/// </summary>
[ClientRpc]
public void RpcStopAllGuide()
{
StopGuide();
}
/// <summary>
/// 停止指引(在游戏开始或删除门时调用)
/// </summary>
public void StopGuide()
{
HideGuideArrow();
}
#endregion
}