制作游戏,需要带给玩家一种体验和挑战,那必不可少的就是电脑AI喽,
电脑AI也就是我们要书写的程序代码,用来告诉电脑,应该执行什么动作!
今天我给大家分享一下,我在这几天制作的一个简单的状态机,希望给大家一些启发! 希望大家可以写出更好的有限状态机!
感谢 softimagewht 的文章帮助 http://www.cnblogs.com/softimagewht/
以下代码为讲解!打包了Unity3D资源包,在我的资源里 15M,如有需要请自行下载!
有限状态机的执行顺序是
当前状态 -> 规则(用于判断到另一个状态的限制) -> 事件(另一个状态的需要干什么) -> 另一个状态激活
如此循环运作,由当前状态到另一个状态的控制,并且不会冲突
一,最简单的状态机就是通过if和switch...case判断,写入条件,到达某种状态,你需要各种各样的bool类型的开关来控制怪物的攻击,移动,巡逻,追逐,死亡...... 想必大家都有过这样的体验,最后,弄的26个字母全部用在写bool类型的开关上了,好不容易编辑完逻辑和代码,还要判断某个状态是否会和其他状态同时执行,还需要添加新的bool类型的开关,烦都烦死了.
二,今天给大家介绍的有限制状态机,应该是最常用到的一种写电脑AI的方法,因为,有限状态机的调理很清晰,可以很好的控制各个状态之间的转换,而且不用写那些繁琐的bool类型的开关字段,相信大家如果理解了我有限状态机,也一定会想我一样爱上他的,哈哈哈! 废话不多说了,还是让我们进入,有限状态机的世界吧!
①首先我们书写一个所有电脑AI怪物的有限状态机的根基脚本,用于其他状态的继承使用,在这里添加可以转换的规则和执行的事件(这里不是Unity的event哦,不要理解错了)
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// 功能:所有电脑AI怪物的有限状态机根级
/// 创建者: VioletTD
/// 修改时间: 2015年11月13日 9:37
/// 感谢: Me 加油!
/// </summary>
public enum AIMonsterRule//*************************** 用于记录规则(用于转换)的状态(需要什么条件来触发怪物行动(下面的事件Event)) 枚举 ***************************
{
NullAIMonsterRule = 0,//*************************** 没有规则,表示不执行任何规则 ***************************
AIMonsterFindTargetRule,//*************************** 电脑AI怪物发现了目标的规则 ***************************
AIMonsterLostTargetRule,//*************************** 电脑AI怪物失去了目标的规则 ***************************
AIMonsterATKRangeRule,//*************************** 电脑AI怪物进入了攻击范围的规则 ***************************
AIMonsterMPEnoughRule,//*************************** 电脑AI怪物魔法充足释放技能的规则 ***************************
AIMonsterMPLackRule,//*************************** 电脑AI怪物魔法不足变回攻击状态的规则 ***************************
AIMonsterXPEnoughRule,//*************************** 电脑AI怪物特殊技能值充足时释放特殊技能的规则 ***************************
AIMonsterXPLackRule,//*************************** 电脑AI怪物特殊技能值不足变回攻击状态的规则 ***************************
AIMonsterBeatBackRule,//*************************** 电脑AI怪物被攻击后找到玩家的规则 ***************************
AIMonsterHPLackRule,//*************************** 电脑AI怪物血量不足 逃跑的规则 ***************************
AIMonsterHPEnoughRule,//*************************** 电脑AI怪物血量充足的规则 ***************************
AIMonsterHPZeroRule//*************************** 电脑AI怪物血量为零的规则 ***************************
}
public enum AIMonsterEvent//*************************** 用于记录达到某规则的状态下,怪物执行对应的事件(用于执行)Event ***************************
{
NullAIMonsterEvent = 0,//*************************** 没有事件,表示怪物不执行任何事件 ***************************
AIMonsterChaseEvent,//*************************** 电脑AI怪物追逐玩家怪物事件 ***************************
AIMonsterPatrolEvent,//*************************** 电脑AI怪物巡逻的事件 ***************************
AIMonsterATKEvent,//*************************** 电脑AI怪物攻击的事件 ***************************
AIMonsterSkillEvent,//*************************** 电脑AI怪物释放技能的事件 ***************************
AIMonsterXPSkillEvent,//*************************** 电脑AI怪物释放特殊技能的事件 ***************************
AIMonsterEscapeEvent,//*************************** 电脑AI怪物逃跑的事件 ***************************
AIMonsterDeadEvent//*************************** 电脑AI怪物死亡的事件 ***************************
}
public abstract class AIMonsterFSM {//*************************** 定义抽象类,其他脚本必须继承他才能实现里面的方法 ***************************
protected Dictionary<AIMonsterRule, AIMonsterEvent> AIMonster_RE_FSM = new Dictionary<AIMonsterRule, AIMonsterEvent>();//*************************** 定义保护性的字典用于存储电脑AI怪物的规则与事件的键值对 ***************************
protected AIMonsterEvent aiMonsterEvent;//*************************** 定义保护类型的电脑AI怪物事件的枚举 ***************************
public AIMonsterEvent MonsterEvent { get { return aiMonsterEvent; } }//*************************** 定义公有只读的电脑AI怪物事件(Event)的枚举 ***************************
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 电脑AI怪物的有限状态机模块 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//-------------------------------------------------------------------------------------------- 添加键值对规则(用于转换),事件(用于执行)方法作用 --------------------------------------------------------------------------------------------
#region 添加键值对规则(用于转换),事件(用于执行)方法作用
/// <summary>
/// 添加键值对规则(用于转换),事件(用于执行)方法作用
/// </summary>
/// <param name="AMR">AM.</param>
/// <param name="AME">AM.</param>
public void AddAIMonsterRuleAndEvent(AIMonsterRule AMR, AIMonsterEvent AME)*************************** 添加键值对规则(用于转换),事件(用于执行)方法作用 ***************************
{
if (AMR == AIMonsterRule.NullAIMonsterRule)//@@@@@@@@@ 如果增加的转换规则是个NullAIMonsterRule(空转换规则),直接Debug.LogError,然后返回 @@@@@@@@@
{
Debug.LogError("AIMonsterFSM ERROR: NullAIMonsterRule is not allowed for a real AIMonsterRule");//*************************** 警告传入的转换规则为空转换规则 ***************************
return;//######### 跳过继续执行下面代码 #########
}
if (AME == AIMonsterEvent.NullAIMonsterEvent)//@@@@@@@@@ 如果AIMonsterEvent执行事件是NullAIMonsterEvent(空执行事件),Debug.LoError,然后返回 @@@@@@@@@
{
Debug.LogError("AIMonsterFSM ERROR: NullAIMonsterEvent is not allowed for a real AIMonsterEvent");//*************************** 警告传入的执行事件为空执行事件 ***************************
return;//######### 跳过继续执行下面代码 #########
}
if (AIMonster_RE_FSM.ContainsKey(AMR))//@@@@@@@@@ 如果将要增加的关联对是之前就存在与关联容器中,也照样Debug.LogError,之后返回被调用处 @@@@@@@@@
{
Debug.LogError("AIMonsterFSM ERROR: States " + aiMonsterEvent.ToString() + " already has AIMonsterRule " + AMR.ToString() +
"Impossible to assign to another state");//*************************** 当前的状态机已经分配了另一个状态 ***************************
return;//######### 跳过继续执行下面代码 #########
}
AIMonster_RE_FSM.Add(AMR, AME);//冲破了这些阻碍的话,终归可以添加此关联对了,下面的DeleteTransition函数就不用我写注释了吧!
}
#endregion
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 电脑AI怪物的有限状态机模块 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//-------------------------------------------------------------------------------------------- 删除键值对函数方法作用 --------------------------------------------------------------------------------------------
#region 删除键值对函数,前提是里面要有这个键值对啊
/// <summary>
/// 删除键值对函数,前提是里面要有这个键值对啊
/// </summary>
/// <param name="AMR">AM.</param>
public void DeleteTransition(AIMonsterRule AMR)//@@@@@@@@@ 删除键值对函数,前提是里面要有这个键值对啊 @@@@@@@@@
{
if (AMR == AIMonsterRule.NullAIMonsterRule)//@@@@@@@@@ 如果要删除的转换规则是空 @@@@@@@@@
{
Debug.LogError("AIMonsterFSM ERROR: NullAIMonsterRule is not allowed");//*************************** 该转换规则不允许被删除 ***************************
return;//######### 跳过继续执行下面代码 #########
}
if (AIMonster_RE_FSM.ContainsKey(AMR))//@@@@@@@@@ 如果键值对中包含要删除的键 @@@@@@@@@
{
AIMonster_RE_FSM.Remove(AMR);//*************************** 移除键值对 ***************************
return;//######### 跳过继续执行下面代码 #########
}
Debug.LogError("AIMonsterFSM ERROR: AIMonsterRule " + AMR.ToString() + " passed to " + aiMonsterEvent.ToString() +
" was not on the state's AIMonsterRule list");//*************************** 提示该转换规则已经在枚举列表中被移除 ***************************
}
#endregion
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 电脑AI怪物的有限状态机模块 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//-------------------------------------------------------------------------------------------- 此函数由下面这个脚本AIMonsterFSM.cs中的PerformTransition函数调用。是用来检索状态的方法作用 --------------------------------------------------------------------------------------------
#region 此函数由下面这个脚本AIMonsterFSM.cs中的PerformTransition函数调用。是用来检索状态的
/// <summary>
/// 此函数由下面这个脚本AIMonsterFSM.cs中的PerformTransition函数调用。是用来检索状态的
/// </summary>
/// <returns>The output state.</returns>
/// <param name="AMR">AM.</param>
public AIMonsterEvent GetOutputState(AIMonsterRule AMR)//*************************** 此函数由下面这个脚本AIMonsterFSM.cs中的PerformTransition函数调用。是用来检索状态的 ***************************
{
if (AIMonster_RE_FSM.ContainsKey(AMR))//@@@@@@@@@ 如果键值对中包含该转换规则@@@@@@@@@
{
return AIMonster_RE_FSM[AMR];//*************************** 就返回该转换规则的值(执行事件) ***************************
}
else//@@@@@@@@@ 不包含该转换规则 @@@@@@@@@
{
return AIMonsterEvent.NullAIMonsterEvent;//*************************** 返回执行事件为空 ***************************
}
}
#endregion
public virtual void DoBeforeEntering() { }//*************************** 在转换状态之前 ***************************
public virtual void DoBeforeLeaving() { }//*************************** 在转换状态之后 ***************************
public abstract void Change_Rule(GameObject player, GameObject AIMonster);//*************************** 该方法用于设置规则的转换,可传入多个参数 ***************************
public abstract void Execute_Event(GameObject player, GameObject AIMonster);//*************************** 该方法用于在不同的规则下对应执行的事件 ***************************
}
②再写一个电脑AI怪物的管理状态机的系统脚本
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// 功能:电脑AI怪物的有限状态机的管理系统
/// 创建者: VioletTD
/// 修改时间: 2015年11月13日 14:19
/// 感谢: Me 加油!
/// </summary>
public class AIMonsterFSMSystem {//*************************** 定义一个系统控制类 ***************************
private List<AIMonsterFSM> states;//*************************** 定义一个电脑AI怪物的状态机的数组 ***************************
private AIMonsterEvent currentAIMonsterEvent ;//*************************** 定义一个私有的用于记录电脑AI怪物的当前的执行事件,不要直接修改电脑AI怪物的执行事件 ***************************
public AIMonsterEvent CurrentAIMonsterEvent { get { return currentAIMonsterEvent;} }//*************************** 该属性记录当前怪物的执行事件,用于其他脚本的调用 ***************************
private AIMonsterFSM currentAIMonsterFSM;//*************************** 定义一个私有的用于记录电脑AI怪物的当前的转换规则,不要直接修改电脑AI怪物的转换规则 ***************************
public AIMonsterFSM CurrentAIMonsterFSM{ get { return currentAIMonsterFSM;} }//同上
public AIMonsterFSMSystem()//*************************** 因为该类不继承MonoBehaviour,所以该类只能用构造方法来初始化 ***************************
{
states = new List<AIMonsterFSM>();//*************************** 实例化电脑AI怪物的状态机数组 ***************************
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 电脑AI怪物的有限状态机管理系统模块 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//-------------------------------------------------------------------------------------------- 增加状态转换电脑AI怪物的有限状态机方法作用 --------------------------------------------------------------------------------------------
#region 增加状态转换电脑AI怪物的有限状态机方法作用
/// <summary>
/// 增加状态转换电脑AI怪物的有限状态机方法作用
/// </summary>
/// <param name="AMF">AM.</param>
public void AddState(AIMonsterFSM AMF)//增加状态转换电脑AI怪物的有限状态机
{
if (AMF == null)//@@@@@@@@@ 如果电脑AI怪物的状态机为空 @@@@@@@@@
{
Debug.LogError("AIMonsterFSMSystem ERROR: Null reference is not allowed");//*************************** 输出警告电脑AI怪物的状态为空 ***************************
}
if (states.Count == 0)//@@@@@@@@@ 如果电脑AI怪物的状态机数组的个数为零 @@@@@@@@@
/*第一次添加时必定执行这块代码,因为一开始states是空的,并且这块代码设置了第一次添加的状态是默认的当前状态。这一点读者一定要理解,不然对于后面的东西读者会非常困惑的,因为其他地方没有地方设置运行后默认的当前状态。*/
{
states.Add(AMF);//*************************** 添加当前的电脑AI怪物的状态为当前传入的状态 ***************************
currentAIMonsterFSM = AMF;//*************************** 电脑AI怪物的当前状态为传入的状态 ***************************
currentAIMonsterEvent = AMF.MonsterEvent;//*************************** 实例化当前怪物执行的事件 ***************************
return;//######### 跳出当前的执行,继续运行下面的代码 #########
}
foreach (AIMonsterFSM state in states)//*************************** 遍历排除相同的状态 ***************************
{
if (state.MonsterEvent == AMF.MonsterEvent)//@@@@@@@@@ 如果参数的Foreach执行事件等于传入的电脑AI怪物的执行事件 @@@@@@@@@
{
Debug.LogError("AIMonsterFSMSystem ERROR: Impossible to add state " + AMF.MonsterEvent.ToString() +
" because state has already been added");//*************************** 输出没有添加当前的状态,因为该状态已经被添加过 ***************************
return;//######### 跳出当前的执行,继续运行下面的代码 #########
}
}
states.Add(AMF);//*************************** 这一句代码第一次不执行,因为第一次states是空的,执行到上面的if里面后立即返回了 ***************************
}
#endregion
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 电脑AI怪物有限状态机管理系统模块 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//-------------------------------------------------------------------------------------------- 跟据怪物执行事件MonsterEvent来从容器states中定向移除电脑AI怪物的有限状态机AIMonsterFSM实例方法作用 --------------------------------------------------------------------------------------------
#region 跟据怪物执行事件MonsterEvent来从容器states中定向移除电脑AI怪物的有限状态机AIMonsterFSM实例方法作用
/// <summary>
/// 跟据怪物执行事件MonsterEvent来从容器states中定向移除电脑AI怪物的有限状态机AIMonsterFSM实例方法作用
/// </summary>
/// <param name="AME">AM.</param>
public void DeleteState(AIMonsterEvent AME)//*************************** 跟据怪物执行事件MonsterEvent来从容器states中定向移除电脑AI怪物的有限状态机AIMonsterFSM实例方法作用 ***************************
{
if (AME == AIMonsterEvent.NullAIMonsterEvent)//@@@@@@@@@ 如果传入的当期怪物的执行事件等空 @@@@@@@@@
{
Debug.LogError("AIMonsterFSMSystem ERROR: NullAIMonsterEvent is not allowed for a real state");//*************************** 输出空的执行事件是不被允许的 ***************************
return;//######### 跳出当前的执行,继续运行下面的代码 #########
}
foreach (AIMonsterFSM state in states)//*************************** 遍历当前的电脑AI怪物的有限状态机数组 ***************************
{
if (state.MonsterEvent == AME)//@@@@@@@@@ 如果电脑AI怪物的有限状态机数组中的某一执行事件与当前传入的当期事件相同 @@@@@@@@@
{
states.Remove(state);//*************************** 电脑AI怪物的有限状态机数组移除当前的有限状态机 **********