1415 lines
41 KiB
C#
1415 lines
41 KiB
C#
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;
|
||
|
||
|
||
[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;
|
||
|
||
[NonSerialized]
|
||
[SyncVar]
|
||
public int JoinPlayerCount;
|
||
|
||
[Header("指引系统")]
|
||
public GameObject GuideArrowPre; // 指引箭头预制体
|
||
private GameObject guideArrowInstanceRed; // 指向红门的指引箭头实例
|
||
private GameObject guideArrowInstanceBlue; // 指向蓝门的指引箭头实例
|
||
private GuideArrowPath guideArrowComponentRed; // 红门指引箭头组件
|
||
private GuideArrowPath guideArrowComponentBlue; // 蓝门指引箭头组件
|
||
private bool isGuideArrowActive = false;
|
||
private Vector3 lastPlayerPosition; // 记录玩家上一次的位置
|
||
private float updatePathThreshold = 0.5f; // 更新路径的阈值(玩家移动超过这个距离才更新路径)
|
||
private float updatePathCooldown = 0.3f; // 更新路径的冷却时间
|
||
private float lastPathUpdateTime = 0f;
|
||
private Vector3[] lastPathPointsRed; // 上一次的红门路径点
|
||
private Vector3[] lastPathPointsBlue; // 上一次的蓝门路径点
|
||
private bool isPathSmoothed = false;
|
||
private List<Vector3> smoothedPathRed = new List<Vector3>();
|
||
private List<Vector3> smoothedPathBlue = new List<Vector3>();
|
||
|
||
// 指引系统目标位置
|
||
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 =GameLocal.Ins.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 + GameLocal.Ins.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 playerIndex)
|
||
{
|
||
var isStartGame = playerIndex == GameLocal.Ins.addPlayerIndex;
|
||
if (isStartGame)
|
||
{
|
||
|
||
BlueRoundScore = 0;
|
||
RedRoundScore = 0;
|
||
|
||
// 停止所有客户端的指引
|
||
RpcStopAllGuide();
|
||
|
||
NetworkServer.Destroy(redDoor);
|
||
NetworkServer.Destroy(blueDoor);
|
||
|
||
RpcShowHUD();
|
||
MonoSingleton<CoroutineTaskManager>.Instance.WaitSecondTodo(() =>
|
||
{
|
||
GameStart();
|
||
if (gameState == GameState.Playing)
|
||
return;
|
||
AutoAssignTeams();
|
||
gameState = GameState.Playing;
|
||
}, 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 (gameState == GameState.Playing)
|
||
{
|
||
GameLocal.Ins.curGameTime+= Time.deltaTime;
|
||
}
|
||
|
||
if (isServer)
|
||
{
|
||
if (Input.GetKeyDown(KeyCode.Space))
|
||
{
|
||
StopRound();
|
||
}
|
||
}
|
||
|
||
// 调试:强制显示指引(测试用)
|
||
if (forceShowGuide && Input.GetKeyDown(KeyCode.G) && isGuideInitialized && !isGuideArrowActive)
|
||
{
|
||
Debug.Log("强制显示指引");
|
||
StartGuide();
|
||
}
|
||
|
||
// 更新指引箭头位置
|
||
UpdateGuideArrowPosition();
|
||
}
|
||
|
||
[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;
|
||
GameLocal.Ins.ClearObject();
|
||
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 指引系统(联机版本)- 双线版本
|
||
|
||
/// <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 void UpdateGuideArrowPosition()
|
||
{
|
||
if (!isGuideArrowActive || GameLocal.Ins.self == null || !isGuideInitialized)
|
||
return;
|
||
|
||
// 获取玩家当前位置
|
||
Vector3 currentPlayerPosition = GameLocal.Ins.self.transform.position;
|
||
|
||
// 检查冷却时间
|
||
if (Time.time - lastPathUpdateTime < updatePathCooldown)
|
||
return;
|
||
|
||
// 检查玩家是否移动了足够远的距离
|
||
float distanceMoved = Vector3.Distance(currentPlayerPosition, lastPlayerPosition);
|
||
|
||
if (distanceMoved > updatePathThreshold)
|
||
{
|
||
// 使用门位置(客户端本地存储的)
|
||
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)
|
||
{
|
||
return; // 没有找到门,不更新路径
|
||
}
|
||
else
|
||
{
|
||
guideTargetPositionRed = doorPositions[0];
|
||
guideTargetPositionBlue = doorPositions[1];
|
||
}
|
||
}
|
||
|
||
// 使用曲线检测和路径平滑,分别计算两条路径
|
||
UpdatePathWithCurveDetection(currentPlayerPosition, doorPositionRed, doorPositionBlue);
|
||
|
||
// 更新记录的位置和时间
|
||
lastPlayerPosition = currentPlayerPosition;
|
||
lastPathUpdateTime = Time.time;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查找门的位置(客户端使用)
|
||
/// </summary>
|
||
private Vector3[] FindDoorPositions()
|
||
{
|
||
Vector3[] doorPositions = new Vector3[2] { Vector3.zero, Vector3.zero };
|
||
|
||
// 查找所有门对象
|
||
GameObject[] redDoors = GameObject.FindGameObjectsWithTag("RedDoor");
|
||
GameObject[] blueDoors = GameObject.FindGameObjectsWithTag("BlueDoor");
|
||
|
||
// 如果没有标签,尝试通过名称查找
|
||
if (redDoors.Length == 0)
|
||
{
|
||
redDoors = GameObject.FindGameObjectsWithTag("RedDoor");
|
||
}
|
||
|
||
if (blueDoors.Length == 0)
|
||
{
|
||
blueDoors = GameObject.FindGameObjectsWithTag("BlueDoor");
|
||
}
|
||
|
||
// 如果还是没有找到,尝试通过名称查找
|
||
if (redDoors.Length == 0)
|
||
{
|
||
GameObject redDoorObj = GameObject.Find("RedDoor(Clone)");
|
||
if (redDoorObj != null) redDoors = new GameObject[] { redDoorObj };
|
||
}
|
||
|
||
if (blueDoors.Length == 0)
|
||
{
|
||
GameObject blueDoorObj = GameObject.Find("BlueDoor(Clone)");
|
||
if (blueDoorObj != null) blueDoors = new GameObject[] { blueDoorObj };
|
||
}
|
||
|
||
// 查找所有网络对象
|
||
NetworkIdentity[] allNetObjs = FindObjectsOfType<NetworkIdentity>();
|
||
if (redDoors.Length == 0)
|
||
{
|
||
foreach (var netObj in allNetObjs)
|
||
{
|
||
if (netObj.gameObject.name.Contains("RedDoor") || netObj.gameObject.name.Contains("redDoor"))
|
||
{
|
||
doorPositions[0] = netObj.transform.position;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
doorPositions[0] = redDoors[0].transform.position;
|
||
}
|
||
|
||
if (blueDoors.Length == 0)
|
||
{
|
||
foreach (var netObj in allNetObjs)
|
||
{
|
||
if (netObj.gameObject.name.Contains("BlueDoor") || netObj.gameObject.name.Contains("blueDoor"))
|
||
{
|
||
doorPositions[1] = netObj.transform.position;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
doorPositions[1] = blueDoors[0].transform.position;
|
||
}
|
||
|
||
if (doorPositions[0] != Vector3.zero && doorPositions[1] != Vector3.zero)
|
||
{
|
||
Debug.Log("客户端:找到门位置,红门: " + doorPositions[0] + ",蓝门: " + doorPositions[1]);
|
||
}
|
||
|
||
return doorPositions;
|
||
}
|
||
|
||
// 新增:使用曲线检测和路径平滑的方法,计算两条路径
|
||
private void UpdatePathWithCurveDetection(Vector3 start, Vector3 endRed, Vector3 endBlue)
|
||
{
|
||
if (guideArrowComponentRed == null || guideArrowComponentBlue == null)
|
||
return;
|
||
|
||
// 1. 检测是否为直接可见路径
|
||
bool isPathClearRed = IsDirectPathClear(start, endRed);
|
||
bool isPathClearBlue = IsDirectPathClear(start, endBlue);
|
||
|
||
// 2. 计算红门路径
|
||
if (isPathClearRed)
|
||
{
|
||
smoothedPathRed = GenerateBezierCurve(start, endRed, 0.2f);
|
||
}
|
||
else
|
||
{
|
||
smoothedPathRed = CalculateObstacleAvoidancePath(start, endRed);
|
||
}
|
||
|
||
// 3. 计算蓝门路径
|
||
if (isPathClearBlue)
|
||
{
|
||
smoothedPathBlue = GenerateBezierCurve(start, endBlue, 0.2f);
|
||
}
|
||
else
|
||
{
|
||
smoothedPathBlue = CalculateObstacleAvoidancePath(start, endBlue);
|
||
}
|
||
|
||
// 4. 设置路径
|
||
if (smoothedPathRed != null && smoothedPathRed.Count > 1)
|
||
{
|
||
guideArrowComponentRed.SetPath(smoothedPathRed);
|
||
}
|
||
|
||
if (smoothedPathBlue != null && smoothedPathBlue.Count > 1)
|
||
{
|
||
guideArrowComponentBlue.SetPath(smoothedPathBlue);
|
||
}
|
||
|
||
isPathSmoothed = true;
|
||
}
|
||
|
||
// 新增:检查直接路径是否畅通
|
||
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, guideArrowComponentRed.obstacleMask))
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
// 新增:生成贝塞尔曲线路径
|
||
private List<Vector3> GenerateBezierCurve(Vector3 start, Vector3 end, float curveHeight)
|
||
{
|
||
List<Vector3> curvePoints = new List<Vector3>();
|
||
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<Vector3> CalculateObstacleAvoidancePath(Vector3 start, Vector3 end)
|
||
{
|
||
List<Vector3> path = new List<Vector3>();
|
||
path.Add(start);
|
||
|
||
// 尝试寻找最佳绕行点
|
||
Vector3 bypassPoint = FindOptimalBypassPoint(start, end);
|
||
|
||
if (bypassPoint != start)
|
||
{
|
||
// 如果有绕行点,构建曲线路径
|
||
List<Vector3> curve1 = GenerateBezierCurve(start, bypassPoint, 0.3f);
|
||
List<Vector3> 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, guideArrowComponentRed.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;
|
||
}
|
||
|
||
/// <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 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);
|
||
|
||
// 初始化路径
|
||
Vector3 playerPosition = GameLocal.Ins.self.transform.position;
|
||
UpdatePathWithCurveDetection(playerPosition, guideTargetPositionRed, guideTargetPositionBlue);
|
||
|
||
// 记录初始位置
|
||
lastPlayerPosition = playerPosition;
|
||
|
||
// 显示指引
|
||
ShowGuideArrow();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 隐藏指引箭头(本地调用)
|
||
/// </summary>
|
||
public void HideGuideArrow()
|
||
{
|
||
if (guideArrowComponentRed != null && guideArrowComponentBlue != null && isGuideInitialized)
|
||
{
|
||
guideArrowComponentRed.ClosePath();
|
||
guideArrowComponentBlue.ClosePath();
|
||
isGuideArrowActive = false;
|
||
Debug.Log("指引箭头已隐藏(2条线)");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清理指引箭头实例
|
||
/// </summary>
|
||
public void CleanupGuideArrow()
|
||
{
|
||
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
|
||
} |