FSM,有限状态机,可以理解成是对行为逻辑的抽象,就好象人在生活中会做出各种行为,例如吃饭、睡觉等,这些所有我们都看作是“行为”的分支,由大脑决定每种行为具体是什么实施。
在整个FSM架构中,其实与上面解释一致,首先有一个状态基类stateObject,里面有三个方法,分别是状态前、状态中、状态后。所有具体行为都要继承这个基类,在这三个方法中具体实现各种方法的逻辑。然后,需要一个stateManager(大脑)状态管理类来管理这些状态,特别注意的是里面changeState方法,他是状态跳转的关键。至于如果存储各种状态,你可以用list类 ,字典类等等。
有限状态机是把一个对象的行为分解称为易于处理的“块”或状态。
例如,灯的开关,就是一个简单的有限状态机。它有两种状态:开和关。
*总而言之就是分离游戏逻辑代码的编码技巧,或者可以称之为模式,即经验和套路 不仅在游戏中有应用,在人工智能领域用途也很广泛
我们使用动画状态机时,有手动拖拽的方法,这时我们利用FSM的话就可以省略拖拽操作了。*
有限状态机的优点:
比如说我们游戏中需要些一个脚本来控制角色的一些逻辑。角色的移动,攻击等属性时,我们不可能把代码放在一个脚本中完成,我们肯定要通过一定的方法,把它们划分成多个脚本或者说多个部分,依次来实现。因为这样把复杂的代码划分成一个一个小部分去实现,可以提高我们代码的可维护性,以及重用性,这也就是为什么我们要使用状态机的原因!
在数据中的本质
FSM本质上是张连通图。
每个节点之间满足一定条件之后可以相互转化。
FSM生成的脚本和C#的模板不同,
我们首先要在Add Behaviour按键处生成,这类脚本继承于StateMachineBehavior。
经过以下的验证我们得出方法的运行是这样运行的,如果动画要执行首先执行OnStateEnter,然后一直执行OnStateUpdate,直到动画执行结束,执行OnStateExit。动画此时结束。
using UnityEngine;
using System.Collections;
public class Idle : StateMachineBehaviour {
// OnStateEnter is called before OnStateEnter is called on any state inside this state machine
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
Debug.Log("idle enter");
}
// OnStateUpdate is called before OnStateUpdate is called on any state inside this state machine
override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
Debug.Log("idle update");
}
// OnStateExit is called before OnStateExit is called on any state inside this state machine
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
Debug.Log("idle exit");
}
有限动态机的操作步骤:
using UnityEngine;
using System.Collections;
public class State {
public StateMachine machine;
//这里的方法名和StateMachineBehaviour里面的方法的一样
public virtual void EnterState() { }
//
public virtual void UpdateState() { }
//
public virtual void ExitState() { }
}
//注册的每一个状态知道自己被那个机器所管理
public class Sleep : State
{
public override void EnterState()
{
Debug.Log("睡前");
}
public override void UpdateState()
{
Debug.Log("睡中");
if (Input.GetKeyDown(KeyCode.A))
{
this.machine.ChangeState("Eat");
}
}
public override void ExitState()
{
Debug.Log("睡后,起床了");
}
}
public class Eat : State
{
public override void EnterState()
{
Debug.Log("饭前");
}
public override void UpdateState()
{
Debug.Log("饭中");
if (Input.GetKeyDown(KeyCode.B))
{
this.machine.ChangeState("Sleep");
}
}
public override void ExitState()
{
Debug.Log("饭后");
}
}
public class Work : State
{
public override void EnterState()
{
Debug.Log("打卡");
}
public override void UpdateState()
{
Debug.Log("敲代码");
}
public override void ExitState()
{
Debug.Log("准备下班");
}
}
public class OverTime : State
{
public override void EnterState()
{
Debug.Log("准备加班");
}
public override void UpdateState()
{
Debug.Log("加班中");
}
public override void ExitState()
{
Debug.Log("终于下班了");
}
}
public class Study : State
{
public override void EnterState()
{
Debug.Log("准备学习");
}
public override void UpdateState()
{
Debug.Log("学习中");
}
public override void ExitState()
{
Debug.Log("准备睡觉了");
}
}
using UnityEngine;
using System.Collections.Generic;
using System;
//状态机类 维护所有状态的注册,设置默认,切换,执行
public class StateMachine {
private Dictionary<string, State> _stateDic = new Dictionary<string, State>();
//注册
public void Register(string key, State value)
{
value.machine = this;
_stateDic.Add(key,value);
}
//设置默认
private State _currentState;
private State _lastState;
public void Setdefault(string key)
{
if (_stateDic.ContainsKey(key))
{
_currentState = _stateDic[key];
}
else
{
Debug.Log("设置默认失败");
}
}
public void ChangeState(string key)
{
if (_stateDic.ContainsKey(key))
{
_currentState = _stateDic[key];
}
else
{
Debug.Log("切换状态失败");
}
}
public void DoWork()
{
//执行的命令(重点)
if (_currentState!=_lastState)//_current=Sleep,_last=null
{
_currentState.EnterState();//sleeo.enter,_current=sleep,_last=sleep++++++_current=eat,_last=sleep
_lastState = _currentState;
}
_lastState.UpdateState();//sleep.update,_current=eat,_last=sleep++++++eat.enter
if (_lastState!=_currentState
)//+++++++++_current=rat,_last=eat
{
_lastState.ExitState();//sleep,exit
}
}
}
using UnityEngine;
using System.Collections;
public class TestMachine : MonoBehaviour {
//状态机
public StateMachine stateMachine =new StateMachine();
// Use this for initialization
void Start () {
//注册,
stateMachine.Register("Sleep", new Sleep());
stateMachine.Register("Eat", new Eat());
//默认动画
stateMachine.Setdefault("Sleep");
}
// Update is called once per frame
void Update () {
//持续更新
stateMachine.DoWork();
}
}
using UnityEngine;
using System.Collections;
public class State {
//被谁所管理
public StateMachine machine;
//继承变量,方便此类和派生类使用( protected受保护的)
protected float _time;
public virtual void EnterState() { }
public virtual void UpdateState() { }
public virtual void ExitState() { }
}
public class Idie : State
{
private float _toWalkTime;
public override void EnterState()
{
Debug.Log("进入站岗状态");
_toWalkTime = Random.Range(2.0f, 3.0f);
}
public override void UpdateState()
{
Debug.Log("持续站岗状态");
_time += Time.deltaTime;
if (_time>=_toWalkTime)
{
this.machine.ChangeState(StateType.Walk);
//此时我们不能使用协程,因为协程是继承于(MonoBehaviour)
}
if (Input.GetKeyDown(KeyCode.F))
{
this.machine.ChangeState(StateType.Fire);
}
}
public override void ExitState()
{
_time = 0;
}
}
public class Walk : State
{
private float _toIdieTime;
public override void EnterState()
{
Debug.Log("进入巡逻状态");
_toIdieTime = Random.Range(3.0f, 5.0f);
}
public override void UpdateState()
{
Debug.Log("持续巡逻状态");
_time += Time.deltaTime;
if (_time >= _toIdieTime)
{
this.machine.ChangeState(StateType.Idie);
}
if (Input.GetKeyDown(KeyCode.F))
{
this.machine.ChangeState(StateType.Fire);
}
if (this.machine.hp<=0)
{
this.machine.ChangeState(StateType.Die);
}
}
public override void ExitState()
{
_time = 0;
}
}
public class Fire : State
{
public override void EnterState()
{
Debug.Log("进入开火状态");
}
public override void UpdateState()
{
Debug.Log("持续开火状态");
if (Input.GetKeyDown(KeyCode.L))
{
this.machine.ChangeState(StateType.Walk);
}
if (this.machine.hp <= 0)
{
this.machine.ChangeState(StateType.Die);
}
}
public override void ExitState()
{
}
}
public class Die : State
{
public override void EnterState()
{
Debug.Log(“死了”);
}
public override void UpdateState()
{
}
public override void ExitState()
{
}
}
using UnityEngine;
using System.Collections.Generic;
public enum StateType
{
Idie,
Walk,
Fire,
Die
}
public class StateMachine
{
public Dictionary<StateType, State> _dic = new Dictionary<StateType, State>();
private State _currentState;
private State _lastState;
public int hp=5;
public void Register(StateType key,State valua)
{
valua.machine = this;
_dic.Add(key,valua);
}
public void ChangeState(StateType key)
{
if (_dic.ContainsKey(key))
{
Debug.Log("初始成功");
_currentState = _dic[key];
}
else
{
Debug.Log("初始失败");
}
}
public void DoWork()
{
if (_currentState!=_lastState)
{
_currentState.EnterState();
_lastState = _currentState;
}
_lastState.UpdateState();
if (_lastState!=_currentState)
{
_lastState.ExitState();
}
if (Input.GetKeyDown(KeyCode.S))
{
//手动减血
hp--;
}
}
}
using UnityEngine;
using System.Collections;
public class AI : MonoBehaviour {
private StateMachine _stateMachine=new StateMachine();
// Use this for initialization
void Start () {
_stateMachine.Register(StateType.Idie, new Idie());
_stateMachine.Register(StateType.Walk, new Walk());
_stateMachine.Register(StateType.Fire, new Fire());
_stateMachine.Register(StateType.Die, new Die());
//初始状态
_stateMachine.ChangeState(StateType.Idie);
}
// Update is called once per frame
void Update () {
_stateMachine.DoWork();
}
}