fix:补充ai行为流程

This commit is contained in:
bzx
2025-09-24 11:10:05 +08:00
parent d648a31930
commit d37bb0ae35
25 changed files with 742 additions and 310 deletions

View File

@@ -120,7 +120,7 @@ public class PlayerAI : Agent
}
[Server]
public virtual void OnSpawn(int id, PlayerAIType type, int lvl)
public virtual void OnSpawn(int id, PlayerAIType type, int lvl,GameObject target)
{
base.OnSpawn();
this.id = id;
@@ -142,7 +142,10 @@ public class PlayerAI : Agent
if (aiPath != null) aiPath.enabled = true;
if (rvoController != null) rvoController.enabled = true;
if (selfCollider != null) selfCollider.enabled = true;
if (behaviorTree != null) behaviorTree.enabled = true; // 仅 HostServer上运行 BT
if (behaviorTree != null) {
behaviorTree.enabled = true;
behaviorTree.SetVariable("target",(SharedGameObject)target);
}
// 初始血量已在 Agent.OnSpawn 中设置为 originHealth / health
}
}
@@ -217,7 +220,7 @@ public class PlayerAI : Agent
if (!isServer) return; // 攻击判定/伤害应由 Server 执行Host 权威)
AnimatorComponent.SetInteger("state",1);
//调用枪械开枪脚本
gun.Shoot();
gun.AiShoot();
}
@@ -231,4 +234,14 @@ public class PlayerAI : Agent
{
}
public void ChangeKillEnemyTime()
{
behaviorTree.SetVariable("CountdownTime",(SharedFloat)10f);
}
public void KillEnemyEvent()
{
//gun.Shoot()
}
}

View File

@@ -1,6 +1,7 @@
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;
using DragonLi.Core;
using UnityEngine;
public class Actions
@@ -78,15 +79,14 @@ public class Actions
public class EnemyMoveToward : Action
{
public SharedFloat dis;
public SharedVector3 targetPos;
public override TaskStatus OnUpdate()
{
Enemy enemy = transform.GetComponent<Enemy>();
if (enemy == null) return TaskStatus.Failure;
Vector3 targetPos = transform.position + new Vector3(0, 0, -1) * dis.Value;
enemy.ai.isStopped = false;
enemy.ai.destination = targetPos;
enemy.ai.destination = targetPos.Value;
return TaskStatus.Success;
}
}
@@ -127,21 +127,31 @@ public class Actions
public class PlayerAIAttack : Action
{
public SharedGameObject target;
public SharedVector3 targetPos;
public override TaskStatus OnUpdate()
{
PlayerAI playerAi = transform.GetComponent<PlayerAI>();
if (playerAi == null) return TaskStatus.Failure;
playerAi.StopAttack();
transform.LookAt(new Vector3(targetPos.Value.x, transform.position.y, targetPos.Value.z));
if (target.Value != null)
{
transform.LookAt(new Vector3(target.Value.transform.position.x, transform.position.y, target.Value.transform.position.z));
}
playerAi.DoAttack();
return TaskStatus.Success;
}
}
public class PlayerAIStopAttack: Action
{
public SharedGameObject target;
public SharedVector3 targetPos;
public override TaskStatus OnUpdate()
{
PlayerAI playerAi = transform.GetComponent<PlayerAI>();
if (playerAi == null) return TaskStatus.Failure;
target.Value = null;
playerAi.StopAttack();
return TaskStatus.Success;
}
@@ -187,4 +197,84 @@ public class Actions
return TaskStatus.Success;
}
}
/// <summary>
/// 正常射击寻找敌人
/// </summary>
public class TeammateFindTarget : Action
{
public SharedVector3 targetPos;
public float normalHitMin = 0.1f; // 10%
public float normalHitMax = 0.2f; // 20%
private float hitRate;
public override void OnStart()
{
// 每次开始时刷新命中率
hitRate = Random.Range(normalHitMin, normalHitMax);
}
public override TaskStatus OnUpdate()
{
var tran = GameManager.Ins.GetEnemy(transform);
if (tran == null) return TaskStatus.Failure;
bool isHit = Random.value < hitRate;
if (isHit)
{
targetPos.Value = tran.transform.position; // 命中敌人
}
else
{
// 偏移未命中
Vector2 randomCircle = Random.insideUnitCircle.normalized;
Vector3 offset = new Vector3(randomCircle.x, Random.Range(-0.5f, 0.5f), randomCircle.y) * 3f;
targetPos.Value = tran.transform.position + offset;
}
return TaskStatus.Success;
}
}
/// <summary>
/// 百分百杀死敌人
/// </summary>
public class TeammateSupportKill : Action
{
public SharedFloat CountdownTime; // 倒计时秒数
public SharedGameObject Target;
public override TaskStatus OnUpdate()
{
if (GameManager.Ins == null) return TaskStatus.Failure;
var enemy = GameManager.Ins.GetEnemy(transform);
if (enemy != null)
{
var enemyComponent = enemy.GetComponent<Enemy>();
CoroutineTaskManager.Instance.WaitSecondTodo(() =>
{
if (enemyComponent != null && enemyComponent.state != EnemyState.Die)
{
enemyComponent.Die(transform.position,null);
Debug.Log($"{gameObject.name} 倒计时结束,击杀一名敌人!");
CountdownTime.Value=10;
}
}, 3f);
Target = enemy;
}
return TaskStatus.Success;
}
}
public class CountdownTimeAction: Action
{
public SharedFloat CountdownTime; // 倒计时秒数
public override TaskStatus OnUpdate()
{
CountdownTime.Value-=Time.deltaTime;
return TaskStatus.Success;
}
}
}

View File

@@ -196,4 +196,16 @@ public class Conditionals
return GameManager.Ins.IsHaveEnemy()? TaskStatus.Success : TaskStatus.Failure;
}
}
/// <summary>
/// 击杀敌人倒计时
/// </summary>
public class CheckKillEnemyCountdownTime: Conditional
{
public SharedFloat CountdownTime;
public override TaskStatus OnUpdate()
{
return CountdownTime.Value<=0? TaskStatus.Success : TaskStatus.Failure;
}
}
}

View File

@@ -123,9 +123,12 @@ public class Bullet : NetworkBehaviour
#endregion
[Server]
public virtual void OnSpawn(int ownerIndex, Vector3 recoil, float recoilCount)
public virtual void OnSpawn(int curOwnerIndex, Vector3 recoil, float recoilCount)
{
this.ownerIndex = ownerIndex;
if (curOwnerIndex != -1)
ownerIndex = curOwnerIndex;
rigidbodyComponent.velocity = recoil.normalized * recoilCount;
rigidbodyComponent.rotation = Quaternion.LookRotation(recoil.normalized);
}

View File

@@ -3,6 +3,11 @@ using System.Collections.Generic;
using Mirror;
using UnityEngine;
public enum DefendEventType
{
}
//事件基类
public class DefendEvent : NetworkBehaviour
{
@@ -16,6 +21,19 @@ public class DefendEvent : NetworkBehaviour
public bool isOver;
public bool isStart;
public virtual void Init(List<Enemy> enemies, List<NPC> curNpcList)
{
enemyList = enemies;
npcList = curNpcList;
isOver = false;
isStart = false;
}
public virtual void StartEvent()
{
isStart = true;
}
public virtual void EndEvent(){}

View File

@@ -2,7 +2,7 @@ using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class StartEvent : DefendEvent
public class NormalEvent : DefendEvent
{
}

View File

@@ -138,7 +138,7 @@ public class Enemy : Agent
}
[Server]
public virtual void OnSpawn(int id, EnemyType type, int lvl)
public virtual void OnSpawn(int id, EnemyType type, int lvl,Vector3 targetPos)
{
base.OnSpawn();
this.id = id;
@@ -161,7 +161,11 @@ public class Enemy : Agent
if (aiPath != null) aiPath.enabled = true;
if (rvoController != null) rvoController.enabled = true;
if (selfCollider != null) selfCollider.enabled = true;
if (behaviorTree != null) behaviorTree.enabled = true; // 仅 HostServer上运行 BT
if (behaviorTree != null)
{
behaviorTree.enabled = true;
behaviorTree.SetVariable("targetPos",(SharedVector3)targetPos);
} // 仅 HostServer上运行 BT
// 初始血量已在 Agent.OnSpawn 中设置为 originHealth / health
}
}

View File

@@ -9,9 +9,9 @@ public class SDRRMI : Enemy
public SDRRMIHand rightHand;
[Server]
public override void OnSpawn(int id, EnemyType type, int lvl)
public override void OnSpawn(int id, EnemyType type, int lvl,Vector3 targetPos)
{
base.OnSpawn(id, type, lvl);
base.OnSpawn(id, type, lvl,targetPos);
leftHand.atk = atk;
leftHand.gameObject.SetActive(true);
rightHand.atk = atk;

View File

@@ -62,11 +62,10 @@ public class GameLocal : MonoBehaviour
[NonSerialized]
public GameObject DieUI;
public Transform[] enemyStartPos;
public Transform[] enemyEndPos;
public Transform[] playerAiPos;
public Transform[] playerAiEndPos;
public Transform[] gunPropPos;
public Transform doorPos;

View File

@@ -115,6 +115,7 @@ public class Launcher : NetworkBehaviour
Shoot(curOwnerIndex);
}
[Server]
public bool Shoot(int ownerIndex)
{

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4001bf13659a2af438166907115a6968
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,13 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DefendEventManager : MonoBehaviour
{
public Dictionary<int,DefendEvent> defendEventDic = new Dictionary<int,DefendEvent>();
public void StartEvent(int id)
{
defendEventDic[id].StartEvent();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a2809f452877aef4d8b8ff4e955a57f9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -39,6 +39,12 @@ public enum GameState
Wave=5,
}
public enum GameMode
{
OnePlayer=0,
TwoPlayers=1,
}
public class GameManager : NetworkBehaviour
{
@@ -84,6 +90,8 @@ public class GameManager : NetworkBehaviour
public Dictionary<int, Player> players = new Dictionary<int, Player>();
public GameMode gameMode = GameMode.OnePlayer;
/// <summary>
/// 敌人自增
/// </summary>
@@ -175,6 +183,7 @@ public class GameManager : NetworkBehaviour
{
PlaySound2DRPC("1.1");
}, 10f);
CreatePlayerAi();
SetGameState(GameState.Wave);
RpcMessageRound();
}
@@ -186,8 +195,8 @@ public class GameManager : NetworkBehaviour
{
roundIndex++;
SetGameState(GameState.Wave);
roundWaveTime = 15;
curRoundWaveTime = 12;
roundWaveTime = 5;
curRoundWaveTime = 2;
RpcMessageRound();
}
@@ -195,6 +204,7 @@ public class GameManager : NetworkBehaviour
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(1);
@@ -328,35 +338,45 @@ public class GameManager : NetworkBehaviour
{
GameObject enemy = Instantiate(EnemyPres[info.EnemyList[i]]);
NetworkServer.Spawn(enemy);
int posId = Random.Range(0, 4);
int posX=Random.Range(-1, 1);
int posZ=Random.Range(-1, 1);
enemy.transform.position = GameLocal.Ins.enemyStartPos[posId].position+new Vector3(posX,0,posZ);
int posId =i/3;
int posX=i%3==0? 0: i%3==1 ? 1 : -1;
enemy.transform.position = GameLocal.Ins.enemyStartPos[posId].position;
enemy.transform.eulerAngles = new Vector3(0, 0, 0);
enemyIndex++;
Enemy enemyScript = enemy.GetComponent<Enemy>();
enemyScript.OnSpawn(enemyIndex, (EnemyType)info.EnemyList[i], 1);
enemyScript.OnSpawn(enemyIndex, (EnemyType)info.EnemyList[i], 1,GameLocal.Ins.enemyStartPos[posId].position+new Vector3(posX,0,0));
EnemyList.Add(enemyIndex, enemyScript);
yield return new WaitForSeconds(0.5f);
}
}
/// <summary>
/// 创建场景
/// 创建辅助AI
/// </summary>
public void CreatePlayerAi()
{
for (int i = 0; i < players.Count; i++)
if (gameMode == GameMode.OnePlayer)
{
GameObject playerAi = Instantiate(playerAiPres[i]);
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 = new Vector3(0, 0, 0);
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[i].position;
playerAi.transform.position = GameLocal.Ins.playerAiPos[0].position;
playerAi.transform.eulerAngles = new Vector3(0, 0, 0);
enemyIndex++;
PlayerAI enemyScript = playerAi.GetComponent<PlayerAI>();
enemyScript.OnSpawn(enemyIndex,(PlayerAIType)i, 1);
PlayerAiList.Add(enemyIndex, enemyScript);
curRoundEnemyCount++;
enemyScript.OnSpawn(0,0, 1,GameLocal.Ins.playerAiEndPos[0].gameObject);
PlayerAiList.Add(0, enemyScript);
}
}
@@ -365,18 +385,18 @@ public class GameManager : NetworkBehaviour
/// </summary>
public void CreateBoss()
{
GameObject enemy = Instantiate(EnemyPres[(int)EnemyType.ZombieBoss]);
NetworkServer.Spawn(enemy);
int posId = Random.Range(0, 4);
int posX=Random.Range(-1, 1);
int posZ=Random.Range(-1, 1);
enemy.transform.position = GameLocal.Ins.enemyStartPos[posId].position+new Vector3(posX,0,posZ);
enemy.transform.eulerAngles = new Vector3(0, 0, 0);
enemyIndex++;
Enemy enemyScript = enemy.GetComponent<Enemy>();
enemyScript.OnSpawn(enemyIndex, EnemyType.ZombieBoss, 1);
EnemyList.Add(enemyIndex, enemyScript);
curRoundEnemyCount++;
// GameObject enemy = Instantiate(EnemyPres[(int)EnemyType.ZombieBoss]);
// NetworkServer.Spawn(enemy);
// int posId = Random.Range(0, 4);
// int posX=Random.Range(-1, 1);
// int posZ=Random.Range(-1, 1);
// enemy.transform.position = GameLocal.Ins.enemyStartPos[posId].position+new Vector3(posX,0,posZ);
// enemy.transform.eulerAngles = new Vector3(0, 0, 0);
// enemyIndex++;
// Enemy enemyScript = enemy.GetComponent<Enemy>();
// enemyScript.OnSpawn(enemyIndex, EnemyType.ZombieBoss, 1);
// EnemyList.Add(enemyIndex, enemyScript);
// curRoundEnemyCount++;
}
public void CreateExplosion(Transform pos)
@@ -548,7 +568,7 @@ public class GameManager : NetworkBehaviour
if (curRoundEnemyCount<=0)
{
CreateGunProp();
//CreateGunProp();
CreateNextRound();
}
}
@@ -733,6 +753,14 @@ public class GameManager : NetworkBehaviour
return nearestPlayer;
}
public void ChangePlayerAiCountdownTime()
{
foreach (var ai in PlayerAiList.Values)
{
ai.ChangeKillEnemyTime();
}
}
public Transform GetPlayerPos()
{
int rand = Random.Range(0, 100);
@@ -773,6 +801,7 @@ public class GameManager : NetworkBehaviour
GameObject enemyUI = EnemyUIList[id].gameObject;
EnemyUIList.Remove(id);
NetworkServer.Destroy(enemyUI);
ChangePlayerAiCountdownTime();
}
[ClientRpc]