赞
踩
构建易扩展的AI基类和数据结构
有限状态机FSM控制小兵AI行为状态
NavMesh自动寻路系统控制小兵移动
缓存池CatchTool 控制小兵动态创建和销毁
……
直接上代码…………………………………………
- public class AI : MonoBehaviour
- {
- /// <summary>
- /// AI自身对象
- /// </summary>
- public GameObject obj;
- /// <summary>
- /// 自身ID
- /// </summary>
- public int AIID;
- /// <summary>
- /// 名字
- /// </summary>
- public string Name;
- /// <summary>
- /// 唯一识别ID
- /// </summary>
- public int UniqueID;
- /// <summary>
- /// AI类型
- /// </summary>
- public AIType AIType;
- /// <summary>
- /// 阵营
- /// </summary>
- public CampEnum CampEnum;
- /// <summary>
- /// 是否存活
- /// </summary>
- public bool isAlive;
- /// <summary>
- /// AI皮肤
- /// </summary>
- public AISkin skin = new AISkin();
- /// <summary>
- /// AI属性
- /// </summary>
- public AIAttribute attribute = new AIAttribute();
- /// <summary>
- /// AI属性变化事件
- /// </summary>
- public AttributeEvent attributeEvent = new AttributeEvent();
- /// <summary>
- /// AI成长属性
- /// </summary>
- public AIGrowUp growUp = new AIGrowUp();
- /// <summary>
- /// 播放动画
- /// </summary>
- public AIAnimation aiAnimation = new AIAnimation();

AI小兵派生类:
- /// <summary>
- /// 小兵AI派生类
- /// </summary>
- public class SoldierAI : AI
- {
- /// <summary>
- /// 自动寻路组件
- /// </summary>
- public AINaveMesh aINaveMesh;
- /// <summary>
- /// 小兵状态机组件
- /// </summary>
- public SoldierBehaviour soldierBehaviour;
- /// <summary>
- /// 小兵侦察距离检测类
- /// </summary>
- public SoldierDetectionCollider soldierDetectionCollider;
- /// <summary>
- /// 小兵战斗距离检测类
- /// </summary>
- public SoldierAttackCollider soldierAttackCollider;
- /// <summary>
- /// 小兵技能
- /// </summary>
- public SoldierSkill soldierSkill;
- /// <summary>
- /// 小兵UI画布
- /// </summary>
- public SoldierCanvas soldierCanvas;
- /// <summary>
- /// 小兵类型
- /// </summary>
- public string SoldierType;
- /// <summary>
- /// 是否死亡
- /// </summary>
- private bool isDeath = false;

定义小兵AI管理器类,用于管理小兵AI的创建、销毁、属性成长等等
- /// <summary>
- /// 小兵管理器类
- /// </summary>
- public class SoldierManager : MonoSingle<SoldierManager>
- {
-
- /// <summary>
- /// 小兵加载波次
- /// </summary>
- private int Frequency = 0;
- /// <summary>
- /// 加载小兵间隔
- /// </summary>
- private float LoadInterval = 0;
- /// <summary>
- /// 倒计时
- /// </summary>
- private float Countdown = 1f;
- /// <summary>
- /// 间隔时间
- /// </summary>
- private float IntervalTime = 30f;
- /// <summary>
- /// 判定是否开始小兵系统
- /// </summary>
- private bool isStart = false;
- /// <summary>
- ///
- /// </summary>
- private TerrainEnum GameTerrain;
- /// <summary>
- /// 对象池小兵预制体
- /// </summary>
- public Dictionary<string, SoldierAI> SoldierPrefabs = new Dictionary<string, SoldierAI>();
-
- //小兵临时对象
-
- public Queue<SoldierAI> Temp_SoldierMelee_Blue = new Queue<SoldierAI>();
- public Queue<SoldierAI> Temp_SoldierRemote_Blue = new Queue<SoldierAI>();
- public Queue<SoldierAI> Temp_GunTruck_Blue = new Queue<SoldierAI>();
- public Queue<SoldierAI> Temp_SoldierMelee_Red = new Queue<SoldierAI>();
- public Queue<SoldierAI> Temp_SoldierRemote_Red = new Queue<SoldierAI>();
- public Queue<SoldierAI> Temp_GunTruck_Red = new Queue<SoldierAI>();
-
- /// <summary>
- /// 所有小兵容器
- /// Key:小兵唯一识别符
- /// Value:小兵AI
- /// </summary>
- public Dictionary<int, SoldierAI> Soldiers_Dic = new Dictionary<int, SoldierAI>();
- /// <summary>
- /// 是否刷新小兵
- /// </summary>
- private bool isRefresh = false;
- /// <summary>
- /// 当前刷新帧
- /// </summary>
- private float refreshCDNow = 0;
- /// <summary>
- /// 每个小兵刷新间隔
- /// </summary>
- private float refreshCD = 0.7f;
- /// <summary>
- ///
- /// </summary>
- private int refreshNumber = 0;
- /// <summary>
- /// 小兵类型列表
- /// </summary>
- private string[] refreshList = new string[7];

定义小兵缓存池,并创建小兵缓存
- //根据游戏模式加载小兵缓存池
- int SoldierRemote = 0;
- int SoldierMelee = 0;
- int GunTruck = 0;
-
- switch (GameTerrain)
- {
- case TerrainEnum.Gorge:
- SoldierRemote = 27;
- SoldierMelee = 27;
- GunTruck = 9;
- break;
- case TerrainEnum.Bridge:
- SoldierRemote = 9;
- SoldierMelee = 9;
- GunTruck = 3;
- break;
- case TerrainEnum.Jungle:
- SoldierRemote = 27;
- SoldierMelee = 27;
- GunTruck = 9;
- break;
- default:
- break;
- }
- //创建小兵缓存池
- ToolManager.objectPool_C.Init_Public("SoldierRemote_Blue", PoolParentType.Model);
- ToolManager.objectPool_C.SetPool_Public("SoldierRemote_Blue", SoldierPrefabs["SoldierRemote_Blue"].obj, SoldierRemote);
- ToolManager.objectPool_C.Init_Public("SoldierMelee_Blue", PoolParentType.Model);
- ToolManager.objectPool_C.SetPool_Public("SoldierMelee_Blue", SoldierPrefabs["SoldierMelee_Blue"].obj, SoldierMelee);
- ToolManager.objectPool_C.Init_Public("GunTruck_Blue", PoolParentType.Model);
- ToolManager.objectPool_C.SetPool_Public("GunTruck_Blue", SoldierPrefabs["GunTruck_Blue"].obj, GunTruck);
-
- ToolManager.objectPool_C.Init_Public("SoldierRemote_Red", PoolParentType.Model);
- ToolManager.objectPool_C.SetPool_Public("SoldierRemote_Red", SoldierPrefabs["SoldierRemote_Red"].obj, SoldierRemote);
- ToolManager.objectPool_C.Init_Public("SoldierMelee_Red", PoolParentType.Model);
- ToolManager.objectPool_C.SetPool_Public("SoldierMelee_Red", SoldierPrefabs["SoldierMelee_Red"].obj, SoldierMelee);
- ToolManager.objectPool_C.Init_Public("GunTruck_Red", PoolParentType.Model);
- ToolManager.objectPool_C.SetPool_Public("GunTruck_Red", SoldierPrefabs["GunTruck_Red"].obj, GunTruck);

打包ab包方法:
- /// <summary>
- /// 编译资源
- /// </summary>
- /// <param name="targetPath">目标位置</param>
- /// <param name="prefabs">预制体</param>
- /// <param name="scenes">场景</param>
- /// <param name="buildTarget">目标平台</param>
- [MenuItem("开始打包/打包Obj")]
- public static void BuildingObj()
- {
- AssetBundleManifest prs = null;
- try
- {
- UnityEngine.Object[] selects = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.DeepAssets);
- //添加资源
- foreach (UnityEngine.Object obj in selects)
- {
- List<AssetBundleBuild> prefabBuilds = new List<AssetBundleBuild>(); //预制体资源
- AssetBundleBuild build = new AssetBundleBuild();
- build.assetBundleName = obj.name + ".assetBundle";
- string assetPath = AssetDatabase.GetAssetPath(obj);
- build.assetNames = new string[] { assetPath };
- prefabBuilds.Add(build);
-
- string prefabPath = Environment.CurrentDirectory + "/DownLoad/Assetbundle/" + obj.name;
- if (!Directory.Exists(prefabPath)) Directory.CreateDirectory(prefabPath);
- try
- {
- prs = BuildPipeline.BuildAssetBundles(prefabPath, prefabBuilds.ToArray(), BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);
- }
- catch (System.Exception ex)
- {
- Debug.Log("预制体 打包 失败:" + ex.ToString());
- }
- }
- }
- catch (UnityException e)
- {
- Debug.Log("预制体 打包 失败:" + e.ToString());
- prs = null;
- }
- if (prs != null)
- {
- Debug.Log("预制体 打包 成功");
- }
- else
- {
- Debug.Log("预制体 打包 失败");
- }
- //刷新编辑器(不写的话要手动刷新,否则打包的资源不能及时在Project视图内显示)
- AssetDatabase.Refresh();
- }

加载ab包方法:
- UnityWebRequest www = UnityWebRequestAssetBundle.GetAssetBundle(URL);
- www.SendWebRequest();
- while (!www.isDone)
- {
- yield return null;
- }
- yield return www;
- assetBundle = DownloadHandlerAssetBundle.GetContent(www);
- if (assetBundle != null)
- {
- GameObject[] objects = assetBundle.LoadAllAssets<GameObject>();
- yield return new WaitForEndOfFrame();
- GameObject model;
- if (objects[0])
- {
- try
- {
- model = Instantiate(objects[0]);
- action(model);
- }
- catch (System.Exception EX)
- {
- iDebug.YiYan("克隆模型物体失败!" + EX, DebugColor.red);
- model = GameObject.CreatePrimitive(PrimitiveType.Cube);
- action(model);
- }
- }
- else
- {
- iDebug.YiYan("AB包为空!", DebugColor.red);
- model = GameObject.CreatePrimitive(PrimitiveType.Cube);
- action(model);
- }
- }
- else
- {
- iDebug.YiYan("AB包为空", DebugColor.red);
- }
- if (assetBundle != null)
- {
- assetBundle.Unload(false);
- }
- www.Dispose();

这个是AI系统中比较重要的环节,双方小兵按照规定移动轨迹走到线上,然后开始自动战斗,其中AI状态大概分为:
自动寻路状态 对线状态 攻击状态 防守状态
死亡状态 停止状态 追击状态 强制攻击状态
小兵的行为应该在遇到对应变化时适时切换这几种状态,而切换的方法则要用到设计模式中常用的模式:状态模式,也就是FSM有限状态机,代码如下:
状态机基类:
- /// <summary>
- /// 状态机基类
- /// </summary>
- public class BaseState
- {
- /// <summary>
- /// 状态名称
- /// </summary>
- private string m_StateName = "BaseState";
- public string StateName
- {
- get { return m_StateName; }
- set { m_StateName = value; }
- }
- /// <summary>
- /// 控制器
- /// </summary>
- protected BaseStateController m_Controller = null;
- /// <summary>
- /// AI行为逻辑
- /// </summary>
- public StateBehaviour m_Behaviour = null;
- /// <summary>
- /// 构造函数——建造者
- /// </summary>
- /// <param name="Controller"></param>
- public BaseState(BaseStateController Controller, StateBehaviour behaviour = null)
- {
- m_Controller = Controller;
- if (behaviour != null)
- m_Behaviour = behaviour;
- }
- /// <summary>
- /// 状态开始
- /// </summary>
- public virtual void StateBegin() { }
- /// <summary>
- /// 状态更新
- /// </summary>
- public virtual void StateUpdate() { }
- /// <summary>
- /// 状态结束
- /// </summary>
- public virtual void StateEnd() { }
-
-
- /// <summary>
- /// 输出当前状态
- /// </summary>
- /// <returns></returns>
- public override string ToString()
- {
- return string.Format("[StateName = {0}]", StateName);
- }
-
-
- }

状态机控制器类:
- /// <summary>
- /// 状态机控制器类
- /// </summary>
- public class BaseStateController
- {
- /// <summary>
- /// 切换的当前状态
- /// </summary>
- private BaseState m_State;
- /// <summary>
- /// 是否已经开始执行当前状态
- /// </summary>
- private bool m_bRunBegin = false;
- /// <summary>
- /// 构造
- /// </summary>
- public BaseStateController() { }
-
- /// <summary>
- /// 状态机操作方法——执行设置状态方法,执行结束状态方法
- /// 1、执行上一个状态的结束方法
- /// 2、设置新的状态
- /// </summary>
- /// <param name="State"></param>
- /// <param name="Value"></param>
- public void SetState(BaseState State)
- {
- m_bRunBegin = false;
- //通知前一个状态结束
- if (m_State != null)
- {
- m_State.StateEnd();
- }
- m_State = State;
- }
- /// <summary>
- /// 状态机操作方法——执行开始状态方法
- /// </summary>
- public void StateBegin()
- {
- if (m_State != null && m_bRunBegin == false)
- {
- m_State.StateBegin();
- m_bRunBegin = true;
- }
- }
- /// <summary>
- /// 状态机操作方法——执行更新状态方法
- /// </summary>
- public void StateUpdate()
- {
- if (m_State != null && m_bRunBegin == true)
- {
- m_State.StateUpdate();
- }
- }
- }

状态机控制器:
- public class StateBehaviour : MonoBehaviour
- {
- /// <summary>
- /// ID
- /// </summary>
- public int ID;
- /// <summary>
- /// 名字
- /// </summary>
- public string Name;
- /// <summary>
- /// 状态集合
- /// </summary>
- public Dictionary<string, BaseState> StateDic;
- /// <summary>
- /// 状态机控制器
- /// </summary>
- public BaseStateController StateController;
-
- public virtual void Awake()
- {
- StateDic = new Dictionary<string, BaseState>();
- StateController = new BaseStateController();
- }
-
- public virtual void Start()
- {
-
- }
-
- public virtual void Update()
- {
- StateController.StateUpdate();
- }
-
- /// <summary>
- /// 初始化状态机
- /// </summary>
- public virtual void Init(int id, string name)
- {
- ID = id;
- Name = name;
-
- }
-
- /// <summary>
- /// 设置状态
- /// </summary>
- /// <param name="state"></param>
- public virtual void SetState(string state)
- {
- StateController.SetState(StateDic[state]);
- StateController.StateBegin();
- }
-
-
-
-
-
-
- }

小兵AI状态机管理器
- public class SoldierBehaviour : StateBehaviour
- {
- /// <summary>
- /// 本身物体
- /// </summary>
- public GameObject Local;
- /// <summary>
- /// 目标物体
- /// </summary>
- public GameObject Target;
- /// <summary>
- /// 自身AI对象
- /// </summary>
- public SoldierAI AI;
-
- public override void Awake()
- {
- base.Awake();
- StateDic.Add("SoldierState_Alignment", new SoldierState_Alignment(StateController, this));
- StateDic.Add("SoldierState_Attack", new SoldierState_Attack(StateController, this));
- StateDic.Add("SoldierState_CrazyAttack", new SoldierState_CrazyAttack(StateController, this));
- StateDic.Add("SoldierState_Defense", new SoldierState_Defense(StateController, this));
- StateDic.Add("SoldierState_Death", new SoldierState_Death(StateController, this));
- StateDic.Add("SoldierState_Pathfinding", new SoldierState_Pathfinding(StateController, this));
- StateDic.Add("SoldierState_Pursuit", new SoldierState_Pursuit(StateController, this));
- StateDic.Add("SoldierState_Stop", new SoldierState_Stop(StateController, this));
- }
-
- public override void Start()
- {
- base.Start();
-
- }
-
- public override void Update()
- {
- base.Update();
-
- }
-
- /// <summary>
- /// 初始化状态机
- /// </summary>
- public void Init(int id, string name, SoldierAI ai)
- {
- base.Init(id, name);
- AI = ai;
- // iDebug.YiYan("小兵状态机初始化");
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="name"></param>
- public void SetGameState(string name)
- {
- SetState(name);
- }
-
- /// <summary>
- /// 结束系统
- /// </summary>
- public void End()
- {
- Target = null;
-
- }
- }

然后,就是在小兵进行途中,在小兵不同的状态下写好当前状态应该做的行为,和判定切换状态的条件行为,举个例子:
小兵在自动寻路状态下在兵线上走,突然攻击预警范围内有敌人出现,则自动切换到追击状态。
等走到攻击范围内,则切换到攻击状态,开始攻击敌人。
当敌人离开攻击范围但尚未离开预警范围,则继续切换到追击状态。
当敌人离开预警范围,则切换到自动寻路状态。
自动寻路状态代码:
- /// <summary>
- /// 状态开始
- /// </summary>
- public override void StateBegin()
- {
- // iDebug.YiYan("开始State:SoldierState_Pathfinding" + soldierBehaviour.AI.obj.GetInstanceID());
-
- //播放行走动画
-
- //获取寻路目标
- soldierBehaviour.AI.aINaveMesh.SetState(true);
- soldierBehaviour.AI.aINaveMesh.SetFinalTarget();
-
- }
-
- /// <summary>
- /// 状态更新
- /// </summary>
- public override void StateUpdate()
- {
- //判断自身区域触发器是否有敌人
- if (soldierBehaviour.AI.soldierDetectionCollider.EnemyList.Count > 0)
- {
- soldierBehaviour.AI.aINaveMesh.Target = soldierBehaviour.AI.soldierDetectionCollider.EnemyList[0];
- soldierBehaviour.SetState("SoldierState_Pursuit");
- }
-
-
- }

追击状态代码:
- /// <summary>
- /// 状态开始
- /// </summary>
- public override void StateBegin()
- {
- // iDebug.YiYan("开始State:SoldierState_Pursuit" + soldierBehaviour.AI.obj.GetInstanceID());
- //播放行走动画
-
- if (soldierBehaviour.AI.aINaveMesh.Target != null && soldierBehaviour.AI.soldierDetectionCollider.EnemyList.Contains(soldierBehaviour.AI.aINaveMesh.Target))
- {
- soldierBehaviour.AI.aINaveMesh.SetDestinationTarget(soldierBehaviour.AI.aINaveMesh.Target.obj);
- }
- }
-
- /// <summary>
- /// 状态更新
- /// </summary>
- public override void StateUpdate()
- {
- //判断自身区域触发器是否有敌人
- if (soldierBehaviour.AI.soldierAttackCollider.EnemyList.Contains(soldierBehaviour.AI.aINaveMesh.Target))
- {
- soldierBehaviour.AI.aINaveMesh.SetState(false);
- soldierBehaviour.SetState("SoldierState_Attack");
- }
- if (!soldierBehaviour.AI.soldierDetectionCollider.EnemyList.Contains(soldierBehaviour.AI.aINaveMesh.Target))
- {
- soldierBehaviour.SetState("SoldierState_Pathfinding");
- }
- }

攻击状态代码:
- /// <summary>
- /// 状态开始
- /// </summary>
- public override void StateBegin()
- {
- // iDebug.YiYan("开始State:SoldierState_Attack" + soldierBehaviour.AI.obj.GetInstanceID());
- if (soldierBehaviour.AI.aINaveMesh.Target != null && soldierBehaviour.AI.soldierAttackCollider.EnemyList.Contains(soldierBehaviour.AI.aINaveMesh.Target))
- {
- soldierBehaviour.AI.soldierSkill.SetTarget(soldierBehaviour.AI, soldierBehaviour.AI.aINaveMesh.Target);
- soldierBehaviour.AI.soldierSkill.AutoAttack(true);
- }
- }
-
- /// <summary>
- /// 状态更新
- /// </summary>
- public override void StateUpdate()
- {
- //持续对目标释放技能
-
- //播放攻击动画
-
- if (!soldierBehaviour.AI.soldierAttackCollider.EnemyList.Contains(soldierBehaviour.AI.aINaveMesh.Target) && soldierBehaviour.AI.soldierDetectionCollider.EnemyList.Contains(soldierBehaviour.AI.aINaveMesh.Target))
- {
- soldierBehaviour.AI.aINaveMesh.SetState(true);
- soldierBehaviour.SetState("SoldierState_Pursuit");
- }
- else if (!soldierBehaviour.AI.soldierDetectionCollider.EnemyList.Contains(soldierBehaviour.AI.aINaveMesh.Target)|| soldierBehaviour.AI.aINaveMesh.Target.attribute.attributeValueNow.HP <= 0)
- {
- soldierBehaviour.AI.aINaveMesh.SetFinalTarget();
- soldierBehaviour.AI.aINaveMesh.SetState(true);
- soldierBehaviour.SetState("SoldierState_Pathfinding");
- // iDebug.YiYan("切换目标");
- }
- else
- {
- // iDebug.YiYan("不断攻击");
- }
-
- }

是不是非常简单易懂(笑脸+手动狗头~~)
(为了连贯性,所以把UI界面和资源导入的效果都录入了)
这里有的小伙伴会有疑问了,小兵的战斗数值和防御塔的属性数值是多少,如何设置呢?
哈~ 当然是在数据库中,由服务器统一分发了
数值就动态读取,然后赋予到AI基类里即可,后面的篇章会介绍技能系统和战斗计算,那时候再详细介绍数值调用的详细逻辑吧
↓↓↓↓↓↓
文末福利:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。