Unity——FSM有限状态机
FSM有限状态机
一、设计思路
1.共同的状态父类,提供可重写的进入,保持,退出该状态的生命周期方法;
2.状态机,管理所有状态(增删查改),状态机运行方法(Run);
3.在角色控制器中,实例化状态机,并初始化添加状态;
二、关键类
1.StateBase
给物体所有状态提供的基类,所有状态比较继承这个基类,并且重写生命周期的方法;
泛型T为角色控制类;
字段:
public int stateID; //状态ID,string也可
public T owner; //角色的实例
生命周期:
public abstract void OnEnter(params object[] args); //进入状态调用
public abstract void OnStay(params object[] args); //保持状态调用
public abstract void OnExit(params object[] args); //退出状态调用
完整代码:
public abstract class StateBase<T>
{
//给每个状态设置一个ID
public int stateID;
public T owner; //拥有者(范型)
public StateBase(int id,T o)
{
this.stateID = id;
owner = o;
}
//给子类提供方法
public abstract void OnEnter(params object[] args);
public abstract void OnStay(params object[] args);
public abstract void OnExit(params object[] args);
}
2.StateMachine
private Dictionary<int, StateBase<T>> FSMActDic; //存所有状态的字典
private int curState; //当前状态
public int nextState = 0; //下一个状态
关键方法:
切换状态时,调用当前方法的退出OnExit()后,继续调用下一个方法的进入OnEnter();
其他时候调用当前方法的OnStay();
public void FSMRun()
{
FSMActDic[curState].OnStay();
if (nextState != 0 && nextState != curState)
{
FSMActDic[curState].OnExit();
curState = nextState;
FSMActDic[curState].OnEnter();
nextState = 0;
}
}
完整代码:
public class FSM<T>
{
private Dictionary<int, StateBase<T>> FSMActDic;
private int curState;
public int nextState = 0;
private void SwitchState()
{
if (nextState != 0 && nextState != curState)
{
FSMActDic[curState].OnExit();
curState = nextState;
FSMActDic[curState].OnEnter();
nextState = 0;
}
}
public FSM(int id)
{
curState = id;
FSMActDic = new Dictionary<int, StateBase<T>>();
}
//增
public void AddState(int id, StateBase<T> state)
{
if(FSMActDic.ContainsKey(id))
return;
FSMActDic.Add(id, state);
}
//删
public void RemoveSatate(int id)
{
if (FSMActDic.ContainsKey(id))
FSMActDic.Remove(id);
}
//获取
public StateBase<T> GetState(int id)
{
if (!FSMActDic.ContainsKey(id))
return null;
return FSMActDic[id];
}
public void FSMRun()
{
FSMActDic[curState].OnStay();
SwitchState();
}
}
三、测试类
1.PlayerControl
实例状态机,添加几个状态,测试效果;这里我只做了简单的移动;
完整代码:
public class PlayerControl : MonoBehaviour
{
public enum PlayerState
{
none = 0,
idle,
move,
jump,
}
private FSM<PlayerControl> mPlayerFSM;
public PlayerState mState;
public Animator mAnimator;
public float mSpeed;
public Vector3 moveDir;
private void InitFSM()
{
mPlayerFSM.AddState((int) PlayerState.idle, new ActIdle((int) PlayerState.idle, this));
mPlayerFSM.AddState((int) PlayerState.move, new ActMove((int) PlayerState.move, this));
mPlayerFSM.AddState((int) PlayerState.jump, new ActScream((int) PlayerState.jump, this));
}
void Start()
{
mAnimator = GetComponentInChildren<Animator>();
mSpeed = 10;
mPlayerFSM = new FSM<PlayerControl>((int) PlayerState.idle);
InitFSM();
mState = PlayerState.idle;
}
void Update()
{
mPlayerFSM.FSMRun();
SwitchState();
mPlayerFSM.nextState = (int)mState;
}
private void SwitchState()
{
if (Input.GetAxis("Horizontal") > 0 && Input.GetAxis("Vertical") == 0)
{
moveDir = transform.right;
mState = PlayerState.move;
}
else if (Input.GetAxis("Horizontal") < 0 && Input.GetAxis("Vertical") == 0)
{
moveDir = -transform.right;
mState = PlayerState.move;
}
else if (Input.GetAxis("Horizontal") > 0 && Input.GetAxis("Vertical") < 0)
{
moveDir = transform.right - transform.forward;
mState = PlayerState.move;
}
else if (Input.GetAxis("Horizontal") < 0 && Input.GetAxis("Vertical") < 0)
{
moveDir = -transform.right - transform.forward;
mState = PlayerState.move;
}
else if (Input.GetAxis("Horizontal") > 0 && Input.GetAxis("Vertical") > 0)
{
moveDir = transform.right + transform.forward;
mState = PlayerState.move;
}
else if (Input.GetAxis("Horizontal") < 0 && Input.GetAxis("Vertical") > 0)
{
moveDir = -transform.right + transform.forward;
mState = PlayerState.move;
}
else if (Input.GetAxis("Horizontal") == 0 && Input.GetAxis("Vertical") < 0)
{
moveDir = -transform.forward;
mState = PlayerState.move;
}
else if (Input.GetAxis("Horizontal") == 0 && Input.GetAxis("Vertical") > 0)
{
moveDir = transform.forward;
mState = PlayerState.move;
}
if (Mathf.Abs(Input.GetAxis("Horizontal")) < 0.1f && Mathf.Abs(Input.GetAxis("Vertical")) < 0.1f)
mState = PlayerControl.PlayerState.idle;
if (Input.GetAxis("Jump") != 0)
{
mState = PlayerState.jump;
}
}
}
2.行为类
设置的移动,待机,吼叫三个行为类;
代码展示:
public class ActMove : StateBase<PlayerControl>
{
public ActMove(int id, PlayerControl t) : base(id, t)
{
}
//给子类提供方法
public override void OnEnter(params object[] args)
{
}
public override void OnStay(params object[] args)
{
owner.mAnimator.Play("Walk");
owner.transform.position += owner.moveDir * Time.deltaTime * owner.mSpeed;
}
public override void OnExit(params object[] args)
{
}
}
四、Demo展示

以上是我对FSM的总结,如果有更好的意见,欢迎给作者评论留言;
Unity——FSM有限状态机的更多相关文章
- Unity FSM 有限状态机
翻译了一下unity wiki上对于有限状态机的案例,等有空时在详细写一下.在场景中添加两个游戏物体,一个为玩家并修改其Tag为Player,另一个为NPC为其添加NPCControl脚本,并为其将玩 ...
- Unity中FSM有限状态机
什么是FSM FSM 即有限状态机,它是一个状态管理系统,表示一个对象的几种状态在指定条件下转移行为,即随着条件的不断改变内部状态不断地切换. FSM用处或者使用背景 通常使用FSM去实现一些简单的A ...
- FSM有限状态机
1.什么是有限状态机 有限状态机(Finite State Machine),简称FSM,它由一组有限个状态.输入和根据输入及现有状态转换为下一个状态的转换函数组成,当然,通常每个状态机都必须有一个初 ...
- Unity 使用有限状态机 完美还原 王者荣耀 虚拟摇杆
Unity 使用有限状态机 完美还原 王者荣耀 虚拟摇杆 效果如图所示 摇杆的UI组成 如图所示 简单的可以认为摇杆由1.2.3贴图组成 为摇杆的底座 为摇杆的杆 为摇杆的指向 可以理解这就是街机上的 ...
- FSM有限状态机 ---C#、Unity
抽象类State public interface State//定义状态接口 { void Init();//初始化 int GetCurrentStateId();//返回当前状态Id void ...
- Unity中有限状态机的用法教程
Unity开发VR之Vuforia 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- ...
- 使用 Unity 3D 开发游戏的架构设计难点
Unity 3D 引擎对于开发者来说,入手非常快,因为它采用的是 C# 作为开发语言,这也大大降低了开发者的门槛.但凡只要懂一门编程语言的人都能使用 Unity 3D 引擎开发,另外 Unity 3D ...
- Lua中使用状态机FSM简单例子
FSM 有限状态机: 一个有限状态机是一个设备,或者是一个设备模型,具有有限数量的状态,它可以在任何给定的时间根据输入进行操作,使得一个状态变换到另一个状态,或者是使一个输入或者一种行为的发生.一个有 ...
- 新FSM的一些思路
好久之前写过一篇关于状态机的小例子,可以看这里http://www.cnblogs.com/mawanli/p/5966080.html,这篇博客首先感谢需要感谢当时看到凉鞋的笔记博客, 凉鞋的博客地 ...
随机推荐
- SpringMVC之@ControllerAdvice
@ControllerAdvice ,很多初学者可能都没有听说过这个注解,实际上,这是一个非常有用的注解,顾名思义,这是一个增强的 Controller.使用这个 Controller ,可以实现三个 ...
- 使用javascript纯前端导出excel
前言(感谢技术的分享者) 参考博客地址 github地址 由SheetJS出品的js-xlsx是一款非常方便的只需要纯JS即可读取和导出excel的工具库,功能强大,支持格式众多,支持xls.xlsx ...
- MFC中L, _T(),TEXT,_TEXT区别以及含义
字符串前面加L表示该字符串是Unicode字符串. _T是一个宏,如果项目使用了Unicode字符集(定义了UNICODE宏),则自动在字符串前面加上L,否则字符串不变.因此,Visual C++里边 ...
- Redis cluster的部署
Redis 集群是一个提供在多个Redis间节点间共享数据的程序集. Redis集群并不支持处理多个keys的命令,因为这需要在不同的节点间移动数据,从而达不到像Redis那样的性能,在高负载的情况下 ...
- Kubernetes的安装部署
前言:简述kubernetes(k8s)集群 k8s集群基本功能组件由master和node组成. master节点上主要有kube-apiserver.kube-scheduler.kube-con ...
- Codeforces 1365D Solve The Maze
### 题目大意: 在一个 $n * m$ 的矩阵中,有空地.坏人.好人和墙.你可以将空地变成墙来堵住坏人.$(n, m)$为出口,是否存在一个方案使得矩阵中所有好人能够走到出口,而所有坏人不能通过出 ...
- AQS快速入门
一.模板方法模式 父子类多态,父类中用一个方法调用执行所有所需要的方法: 父类: 子类: 主线程执行时候调用父类的模板方法: 二.AQS思想 sync都是独占锁,lock显示锁也是,只有读写锁是共享锁 ...
- 详解C3P0(数据库连接池)
详解C3P0(数据库连接池) 快速索引 一.基本定义 二.使用C3P0(数据库连接池)的必要性 1.JDBC传统模式开发存在的主要问题 三.数据库连接池的详细说明 四.使用连接池的明显优势 1.资源的 ...
- iNeuLink硬件网关与iNeuOS工业互联网操作系统互联互通应用案例
目 录 1. 应用概述... 2 2. 模拟硬件设备配置... 2 3. iNeuLink硬件网关配置... 4 3.1 硬件介绍... ...
- springboot整合jsp报错
今天在学springboot整合jsp时遇到了一个问题,虽然jsp不被spring官方推荐使用,但抱着学习的心态还是想解决一下这个问题.在写好了需要pom文件之后,访问网站得到了500的错误提示,后台 ...