Files
DefendNJ/Assets/_DefendNJ/Scripts/Manager/GameManager.cs
2025-12-25 18:17:45 +08:00

1956 lines
60 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 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;
/// <summary>
/// 游戏状态
/// </summary>
public enum GameState
{
None = 0,
Playing = 1,
/// <summary>
/// 胜利
/// </summary>
Victory = 2,
/// <summary>
/// 失败
/// </summary>
Failur = 3,
Settle=4,
/// <summary>
/// 准备阶段
/// </summary>
Wave=5,
/// <summary>
/// 冲锋阶段
/// </summary>
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<EnemyType, EnemyInfo> EnemyInfos = new Dictionary<EnemyType, EnemyInfo>();
public Dictionary<int,AiInfo> PlayerAiInfos = new Dictionary<int,AiInfo>();
// 炮塔信息集合
public Dictionary<TowerType, Dictionary<int, TowerInfo>> TowerInfos = new Dictionary<TowerType, Dictionary<int, TowerInfo>>();
// 枪械信息集合
//public Dictionary<GunType, GunInfo> GunInfos = new Dictionary<GunType, GunInfo>();
// 子弹信息集合
public Dictionary<BulletType, BulletInfo> BulletInfos = new Dictionary<BulletType,BulletInfo>();
public Dictionary<int,AiInfo> AiInfos = new Dictionary<int,AiInfo>();
public Dictionary<int,RoundInfo> RoundInfos = new Dictionary<int,RoundInfo>();
// 剧情信息
public Dictionary<int, CombatUnitInfo> CombatUnitInfos = new Dictionary<int, CombatUnitInfo>();
private List<GameObject> curGunPropList = new List<GameObject>();
public Dictionary<int, Player> players = new Dictionary<int, Player>();
public GameMode gameMode = GameMode.OnePlayer;
/// <summary>
/// 敌人自增
/// </summary>
private int enemyIndex = 0;
/// <summary>
/// 所有敌人
/// </summary>
public Dictionary<int, Enemy> EnemyList = new Dictionary<int, Enemy>();
public Dictionary<int,PlayerAI> PlayerAiList = new Dictionary<int,PlayerAI>();
/// <summary>
/// 所有敌人UI
/// </summary>
public Dictionary<int, EnemyUI> EnemyUIList = new Dictionary<int, EnemyUI>();
/// <summary>
/// 所有炮塔
/// </summary>
public Dictionary<int, Tower> TowerList = new Dictionary<int, Tower>();
public Dictionary<TowerType, List<Tower>> TowerList2 = new Dictionary<TowerType, List<Tower>>();
public Dictionary<string, SettleInfo> SettleInfos = new Dictionary<string, SettleInfo>();
//public List<Enemy> curEnemyList = new List<Enemy>();
[SyncVar]
public bool isStart;
/// <summary>
/// 波次自增
/// </summary>
[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<Vector3> smoothedPath = new List<Vector3>();
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<AIController>();
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();
}
/// <summary>
/// 创建门
/// </summary>
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);
}
/// <summary>
/// 创建初回合
/// </summary>
public void CreateFirstRound()
{
RpcShowHUD();
roundIndex = 0;
PlaySound2DRPC("1.0");
// CoroutineTaskManager.Instance.WaitSecondTodo(() =>
// {
// PlaySound2DRPC("1.1");
// }, 10f);
CreatePlayerAi();
//SetGameState(GameState.Wave);
RpcMessageRound();
}
/// <summary>
/// 创建下一回合
/// </summary>
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<Enemy>();
// 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>();
// 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<Enemy>();
// 修复:检查 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>();
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();
}
/// <summary>
/// 下一波进攻开始
/// </summary>
[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;
/// <summary>
/// 创建敌方单位
/// </summary>
[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<Enemy>();
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);
}
}
/// <summary>
/// 创建辅助AI
/// </summary>
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<PlayerAI>();
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<PlayerAI>();
// 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<Explosion>().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<Grenade>().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<Grenade>().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<ItemProp>().SetItemProp(ItemPropType.Hp);
}
#endregion
public List<Enemy> GetRoundEnemy()
{
List<Enemy> curEnemyList = new List<Enemy>();
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<EnemyMortarBullet>().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<Bullet>().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<Enemy> NpcEnemyList = new List<Enemy>();
public void CreateNpc()
{
if (!isStart)
return;
for (int posId = 0; posId < 2; posId++) // npcPos[0] 和 npcPos[1]
{
List<GameObject> npcList = new List<GameObject>();
// === 生成 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<NPC>().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<Enemy>().OnSpawn(-j, 3, -1, GameLocal.Ins.npcPos[posId].position);
enemy.GetComponent<Enemy>().isOutLine = true;
NpcEnemyList.Add(enemy.GetComponent<Enemy>());
}
}
}
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;
}
}
}
/// <summary>
/// 删除敌方单位
/// </summary>
[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();
}
}
}
/// <summary>
/// 痛击场上所有敌方单位
/// </summary>
[Command(requiresAuthority = false)]
public void DamageAllEnemy()
{
foreach (Enemy enemy in EnemyList.Values)
{
enemy.ApplyDamage(999999, null, null);
}
}
/// <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);
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<int, TowerInfo> infoList);
// if (infoList == null)
// {
// Dictionary<int, TowerInfo> list = new Dictionary<int, TowerInfo>();
// 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");
}
/// <summary>
/// 获取范围内敌人
/// </summary>
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;
}
/// <summary>
/// 获取最近的Ai
/// </summary>
/// <returns></returns>
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;
}
/// <summary>
/// 获取最近敌人
/// </summary>
/// <returns></returns>
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<EnemyUI>();
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
/// <summary>
/// 修复炮塔
/// </summary>
public void RepairTower()
{
}
/// <summary>
/// 进入联控
/// </summary>
[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
/// <summary>
/// 初始化指引箭头
/// </summary>
private void InitializeGuideArrow()
{
// 检查预制体是否存在
if (GuideArrowPre == null)
{
Debug.LogError("GuideArrowPre 预制体未分配!");
return;
}
// 实例化指引箭头预制体
guideArrowInstance = Instantiate(GuideArrowPre);
guideArrowInstance.name = "GuideArrow";
// 获取指引箭头组件
guideArrowComponent = guideArrowInstance.GetComponent<GuideArrowPath>();
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;
}
}
/// <summary>
/// 更新指引箭头位置(实时跟随玩家移动)
/// </summary>
// 修改 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<Vector3> 参数
isPathSmoothed = true;
return;
}
// 2. 间接路径,使用优化的绕路算法
List<Vector3> newPath = CalculateObstacleAvoidancePath(start, end);
if (newPath != null && newPath.Count > 1)
{
// 应用路径平滑
smoothedPath = SmoothPath(newPath);
guideArrowComponent.SetPath(smoothedPath); // 这里使用 List<Vector3> 参数
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<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, 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<Vector3> SmoothPath(List<Vector3> rawPath)
{
if (rawPath.Count < 3)
return rawPath;
List<Vector3> smoothed = new List<Vector3>();
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;
}
/// <summary>
/// 显示指引箭头
/// </summary>
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;
}
}
/// <summary>
/// 隐藏指引箭头
/// </summary>
public void HideGuideArrow()
{
if (guideArrowComponent != null)
{
guideArrowComponent.ClosePath();
isGuideArrowActive = false;
}
}
/// <summary>
/// 开始指引(在创建门后调用)
/// </summary>
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;
}
}
/// <summary>
/// 停止指引(在游戏开始或删除门时调用)
/// </summary>
public void StopGuide()
{
HideGuideArrow();
}
/// <summary>
/// 清理指引箭头实例
/// </summary>
public void CleanupGuideArrow()
{
if (guideArrowInstance != null)
{
Destroy(guideArrowInstance);
guideArrowInstance = null;
guideArrowComponent = null;
isGuideArrowActive = false;
}
}
#endregion
}