有限状态机实现简单的AI状态(巡逻,追逐玩家,攻击)
2018-02-15 21:34
423 查看
点击打开有限状态机的实现基类
去Wiki.Unity3D就可以搜索到有关基类,下面是我自己修改了一小部分的基类public class FSMSystem
{
private Dictionary<StateID, FSMState> states;
private StateID currentStateID;
public StateID CurrentStateID { get { return currentStateID; } }
private FSMState currentState;
public FSMState CurrentState { get { return currentState; } }
public FSMSystem()
{
states = new Dictionary<StateID, FSMState>();
}
public void AddState(FSMState s)
{
if (s == null)
{
Debug.LogError("FSMState不能为空");
}
if (states.ContainsKey(s.ID))
{
Debug.LogError("状态" + s.ID + "已存在,不能再添加");
return;
}
//第一个设置的状态就是当前状态
if (currentState == null)
{
currentState = s;
currentStateID = s.ID;
}
states.Add(s.ID, s);
}
public void DeleteState(StateID id)
{
if (id == StateID.NullStateID)
{
Debug.LogError("无法删除空状态");
return;
}
if (states.ContainsKey(id)==false)
{
Debug.LogError("无法删除不存在的状态:"+id);
return;
}
states.Remove(id);
}
public void PerformTransition(Transition trans)
{
if (trans == Transition.NullTransition)
{
Debug.LogError("无法执行空条件");
return;
}
StateID id = currentState.GetOutputState(trans);
if (id == StateID.NullStateID)
{
Debug.LogWarning("当前状态" + currentStateID + "无法根据转换条件" + trans+"发生转换");
return;
}
if (states.ContainsKey(id) == false)
{
Debug.LogError("在状态机中不存在该状态,无法发生转换");
return;
}
FSMState nextState = states[id];
currentState.DoBeforeLeaving();
currentState = nextState;
currentStateID = id;
currentState.DoBeforeEntering();
}
}
private FSMSystem fsm;
private GameObject player;
private Animation anim;
void Awake()
{
player = GameObject.Find("Player");
anim = GetComponentInChildren<Animation>();
InitFSM();
}
// Update is called once per frame
void Update()
{
fsm.CurrentState.Act(player,gameObject,anim);
fsm.CurrentState.Reason(player,gameObject);
}
void InitFSM()
{
fsm = new FSMSystem();
IdleState idleState = new IdleState(fsm);
idleState.AddTransition(Transition.RestOver, StateID.Patrol);
idleState.AddTransition(Transition.SeePlayer, StateID.Chase);
PatrolState patrolState = new PatrolState(fsm);
patrolState.AddTransition(Transition.PatrolOver, StateID.Idle);
patrolState.AddTransition(Transition.SeePlayer, StateID.Chase);
ChaseState chaseState = new ChaseState(fsm);
chaseState.AddTransition(Transition.LostPlayer, StateID.Idle);
chaseState.AddTransition(Transition.ClosePlayer, StateID.Attack);
AttackState attackState = new AttackState(fsm);
attackState.AddTransition(Transition.OutAttackRange, StateID.Chase);
fsm.AddState(idleState);
fsm.AddState(patrolState);
fsm.AddState(chaseState);
fsm.AddState(attackState);
}
}最后是各个相关状态的脚本public class IdleState : FSMState
{
private float restTime = 2.0f;
private float restTimer = 0.0f;
private float viewDistance = 3.0f;
public IdleState(FSMSystem fsm) : base(fsm)
{
stateID = StateID.Idle;
}
public override void Act(GameObject player, GameObject npc,Animation anim)
{
anim.CrossFade("idle");
}
public override void Reason(GameObject player, GameObject npc)
{
restTimer += Time.deltaTime;
if (restTimer > restTime)
{
restTimer = 0;
fsm.PerformTransition(Transition.RestOver);
}
Vector3 vec = player.transform.position - npc.transform.position;
if (vec.magnitude < viewDistance)
{
fsm.PerformTransition(Transition.SeePlayer);
}
}
}
public class ChaseState : FSMState
{
private float chaseSpeed = 3.0f;
private float lostViewDistance = 5.0f;
private float attackRange = 1.0f;
public ChaseState(FSMSystem fsm) : base(fsm)
{
stateID = StateID.Chase;
}
public override void Act(GameObject player, GameObject npc, Animation anim)
{
npc.transform.LookAt(player.transform);
npc.transform.Translate(Vector3.forward * Time.deltaTime * chaseSpeed);
anim.CrossFade("run");
}
public override void Reason(GameObject player, GameObject npc)
{
Vector3 vec = player.transform.position - npc.transform.position;
if (vec.magnitude> lostViewDistance)
{
fsm.PerformTransition(Transition.LostPlayer);
}
if(vec.magnitude < attackRange)
{
fsm.PerformTransition(Transition.ClosePlayer);
}
}
}public class AttackState : FSMState
{
private float waitTime = 2.0f;
private float waitTimer = 2.0f;
private float attackRange = 1.0f;
private float attackAngle = 60.0f;
public AttackState(FSMSystem fsm) : base(fsm)
{
stateID = StateID.Attack;
}
public override void Act(GameObject player, GameObject npc, Animation anim)
{
if (waitTimer < waitTime)
{
waitTimer += Time.deltaTime;
anim.CrossFadeQueued("idle");
}
else
{
npc.transform.LookAt(player.transform);
anim.Play("attack");
waitTimer = 0;
}
}
public override void Reason(GameObject player, GameObject npc)
{
Vector3 vec = player.transform.position - npc.transform.position;
if (vec.magnitude > attackRange)
{
fsm.PerformTransition(Transition.OutAttackRange);
}
}
public override void DoBeforeEntering()
{
base.DoBeforeEntering();
waitTimer = 2.0f;
}
}当前状态涉及到的只有GameObject,Animation,如果涉及到更复杂的情况就要传入许多参数,后面可以尝试新建一个EnemyData类,挂载在需要的哥布林身上,让Enemy类去获取哥布林身上的EnemyData组件,再直接传入给State类,就不需要每次Update都要传入给Act方法大量参数。
去Wiki.Unity3D就可以搜索到有关基类,下面是我自己修改了一小部分的基类public class FSMSystem
{
private Dictionary<StateID, FSMState> states;
private StateID currentStateID;
public StateID CurrentStateID { get { return currentStateID; } }
private FSMState currentState;
public FSMState CurrentState { get { return currentState; } }
public FSMSystem()
{
states = new Dictionary<StateID, FSMState>();
}
public void AddState(FSMState s)
{
if (s == null)
{
Debug.LogError("FSMState不能为空");
}
if (states.ContainsKey(s.ID))
{
Debug.LogError("状态" + s.ID + "已存在,不能再添加");
return;
}
//第一个设置的状态就是当前状态
if (currentState == null)
{
currentState = s;
currentStateID = s.ID;
}
states.Add(s.ID, s);
}
public void DeleteState(StateID id)
{
if (id == StateID.NullStateID)
{
Debug.LogError("无法删除空状态");
return;
}
if (states.ContainsKey(id)==false)
{
Debug.LogError("无法删除不存在的状态:"+id);
return;
}
states.Remove(id);
}
public void PerformTransition(Transition trans)
{
if (trans == Transition.NullTransition)
{
Debug.LogError("无法执行空条件");
return;
}
StateID id = currentState.GetOutputState(trans);
if (id == StateID.NullStateID)
{
Debug.LogWarning("当前状态" + currentStateID + "无法根据转换条件" + trans+"发生转换");
return;
}
if (states.ContainsKey(id) == false)
{
Debug.LogError("在状态机中不存在该状态,无法发生转换");
return;
}
FSMState nextState = states[id];
currentState.DoBeforeLeaving();
currentState = nextState;
currentStateID = id;
currentState.DoBeforeEntering();
}
}
public enum Transition { NullTransition = 0, RestOver, PatrolOver, OutAttackRange, SeePlayer, LostPlayer, ClosePlayer } public enum StateID { NullStateID = 0, Idle, Patrol, Chase, Attack, Dead } public abstract class FSMState { protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>(); protected StateID stateID; public StateID ID { get { return stateID; } } protected FSMSystem fsm; public FSMState(FSMSystem fsm) { this.fsm = fsm; } public void 4000 AddTransition(Transition trans, StateID id) { if (trans == Transition.NullTransition) { Debug.LogError("不允许添加NullTransition"); return; } if (id == StateID.NullStateID) { Debug.LogError("不允许添加NullState"); return; } if (map.ContainsKey(trans)) { Debug.LogError("该条件"+trans+"已经存在"); return; } map.Add(trans, id); } public void DeleteTransition(Transition trans) { if (trans == Transition.NullTransition) { Debug.LogError("不允许删除NullTransition"); return; } if (map.ContainsKey(trans)) { map.Remove(trans); return; } Debug.LogError("转换条件 " + trans + " 不存在于字典中 "); } public StateID GetOutputState(Transition trans) { if (map.ContainsKey(trans)) { return map[trans]; } return StateID.NullStateID; } public virtual void DoBeforeEntering() { } public virtual void DoBeforeLeaving() { }
//在状态中判断是否转换至下一状态 public abstract void Reason(GameObject player, GameObject npc);
//该状态用来调用动画播放 public abstract void Act(GameObject player, GameObject npc,Animation anim); }所有的动画状态继承FSMState,新建一个Enemy脚本继承Monobehavior用来调用生命周期函数,在其中初始化一个FSM状态机,并赋值给相关State类,让其他State类获得该状态机的引用,Enemy脚本将挂载在一只哥布林并在场景中启用。public class Enemy : MonoBehaviour {
private FSMSystem fsm;
private GameObject player;
private Animation anim;
void Awake()
{
player = GameObject.Find("Player");
anim = GetComponentInChildren<Animation>();
InitFSM();
}
// Update is called once per frame
void Update()
{
fsm.CurrentState.Act(player,gameObject,anim);
fsm.CurrentState.Reason(player,gameObject);
}
void InitFSM()
{
fsm = new FSMSystem();
IdleState idleState = new IdleState(fsm);
idleState.AddTransition(Transition.RestOver, StateID.Patrol);
idleState.AddTransition(Transition.SeePlayer, StateID.Chase);
PatrolState patrolState = new PatrolState(fsm);
patrolState.AddTransition(Transition.PatrolOver, StateID.Idle);
patrolState.AddTransition(Transition.SeePlayer, StateID.Chase);
ChaseState chaseState = new ChaseState(fsm);
chaseState.AddTransition(Transition.LostPlayer, StateID.Idle);
chaseState.AddTransition(Transition.ClosePlayer, StateID.Attack);
AttackState attackState = new AttackState(fsm);
attackState.AddTransition(Transition.OutAttackRange, StateID.Chase);
fsm.AddState(idleState);
fsm.AddState(patrolState);
fsm.AddState(chaseState);
fsm.AddState(attackState);
}
}最后是各个相关状态的脚本public class IdleState : FSMState
{
private float restTime = 2.0f;
private float restTimer = 0.0f;
private float viewDistance = 3.0f;
public IdleState(FSMSystem fsm) : base(fsm)
{
stateID = StateID.Idle;
}
public override void Act(GameObject player, GameObject npc,Animation anim)
{
anim.CrossFade("idle");
}
public override void Reason(GameObject player, GameObject npc)
{
restTimer += Time.deltaTime;
if (restTimer > restTime)
{
restTimer = 0;
fsm.PerformTransition(Transition.RestOver);
}
Vector3 vec = player.transform.position - npc.transform.position;
if (vec.magnitude < viewDistance)
{
fsm.PerformTransition(Transition.SeePlayer);
}
}
}
public class PatrolState : FSMState { private float patrolTime = 2.0f; private float patrolTimer = 0.0f; private float patrolSpeed = 2.0f; private Vector3 patrolDir=Vector3.zero; private float viewDistance = 3.0f; private bool turnOver = false; public PatrolState(FSMSystem fsm) : base(fsm) { stateID = StateID.Patrol; } public override void Act(GameObject player, GameObject npc,Animation anim) { anim.CrossFade("run"); if (!turnOver) { npc.transform.Rotate(patrolDir); turnOver = true; } npc.transform.Translate(Vector3.forward * Time.deltaTime * patrolSpeed); } public override void Reason(GameObject player, GameObject npc) { patrolTimer += Time.deltaTime; if (patrolTimer > patrolTime) { patrolTimer = 0; turnOver = false; fsm.PerformTransition(Transition.PatrolOver); } Vector3 vec = player.transform.position - npc.transform.position; if (vec.magnitude < viewDistance) { fsm.PerformTransition(Transition.SeePlayer); } } public override void DoBeforeEntering() { patrolDir = new Vector3(0, Random.Range(-180.0f, 180.0f), 0); } }实际开发中可以把Idle和Patrol状态改为同一个状态例如NonIntruder,这里我是一开始为了试验状态机的基类有没有bug用的,所以现在的状态如下
public class ChaseState : FSMState
{
private float chaseSpeed = 3.0f;
private float lostViewDistance = 5.0f;
private float attackRange = 1.0f;
public ChaseState(FSMSystem fsm) : base(fsm)
{
stateID = StateID.Chase;
}
public override void Act(GameObject player, GameObject npc, Animation anim)
{
npc.transform.LookAt(player.transform);
npc.transform.Translate(Vector3.forward * Time.deltaTime * chaseSpeed);
anim.CrossFade("run");
}
public override void Reason(GameObject player, GameObject npc)
{
Vector3 vec = player.transform.position - npc.transform.position;
if (vec.magnitude> lostViewDistance)
{
fsm.PerformTransition(Transition.LostPlayer);
}
if(vec.magnitude < attackRange)
{
fsm.PerformTransition(Transition.ClosePlayer);
}
}
}public class AttackState : FSMState
{
private float waitTime = 2.0f;
private float waitTimer = 2.0f;
private float attackRange = 1.0f;
private float attackAngle = 60.0f;
public AttackState(FSMSystem fsm) : base(fsm)
{
stateID = StateID.Attack;
}
public override void Act(GameObject player, GameObject npc, Animation anim)
{
if (waitTimer < waitTime)
{
waitTimer += Time.deltaTime;
anim.CrossFadeQueued("idle");
}
else
{
npc.transform.LookAt(player.transform);
anim.Play("attack");
waitTimer = 0;
}
}
public override void Reason(GameObject player, GameObject npc)
{
Vector3 vec = player.transform.position - npc.transform.position;
if (vec.magnitude > attackRange)
{
fsm.PerformTransition(Transition.OutAttackRange);
}
}
public override void DoBeforeEntering()
{
base.DoBeforeEntering();
waitTimer = 2.0f;
}
}当前状态涉及到的只有GameObject,Animation,如果涉及到更复杂的情况就要传入许多参数,后面可以尝试新建一个EnemyData类,挂载在需要的哥布林身上,让Enemy类去获取哥布林身上的EnemyData组件,再直接传入给State类,就不需要每次Update都要传入给Act方法大量参数。
相关文章推荐
- 实现敌人(怪物)的简单AI(自动巡逻、看到玩家攻击玩家、玩家离开恢复自动巡逻)
- 行为树-学习笔记(5)-AI寻找玩家目标并攻击的实现
- Unity 有限状态机(Finite State Machine)的理解 与 实现简单的可插拔(Pluggable)AI脚本对象。
- 一个简单的敌人自动寻找玩家进行攻击及受到伤害死亡效果实现
- Unity3D 巡逻敌兵探测玩家逻辑实现
- AFN框架实现简单的网络状态监测以及支持https安全证书
- Android简单实现实时监听网络状态
- NAPTHA攻击方式在2K下的简单实现
- 实现简易而强大的游戏AI——FSM,有限状态机
- [python]有限状态机(FSM)简单实现
- 游戏开发设计模式之状态模式 & 有限状态机 & c#委托事件(unity3d 示例实现)
- 贪吃蛇的实现及其简单AI设计
- Cocos3.4 横版游戏制作-《KillBear》-添加敌人+简单AI实现
- ai引擎中最简单的状态机
- Android 视频播放状态链控制简单实现
- J2ME(追逐)AI的实现
- Unity3DAI行为------避开障碍物简单实现
- 基于RFC6265 (HTTP状态管理协议)实现简单的登录系统
- 代码实现Thompson构造:由简单到复杂的构建NFA状态机
- 游戏开发设计模式之状态模式 & 有限状态机 & c#委托事件(unity3d 示例实现)