FSM状态机改

一.前言

FSM状态机初版

之前写过一版有限状态机,后来发现很多问题;

前一个版本是记录了当前的状态,切换状态时,要等下一帧状态机Update的时候才会调动上个状态的退出,总会有一帧的延迟

除了导致动作延迟外,状态很多的情况报错也无法追述,断点只能回到状态机中;

因此做了如下修改;

1.状态机不再继承MonoBehaviour,只需要是单例,存储所有状态基类;

2.状态机提供切换状态的方法SwitchAction,传参下个状态ID;

3.切换状态时调用上一个状态的退出周期,再调用当前状态的开始周期;

4.同时将当前状态的引用重新赋值为传入的状态;

5.状态机提供Run方法给角色控制器调用,角色控制器Update只执行当前状态的Run;

效果展示:

二.修改

修改后FSM,除增删查外添加切换状态函数SwitchState;

提供FSM状态机的生命周期;FSMInit,FSMRun,FSMEnd;

  1. public class FSM<T>
  2. {
  3. private Dictionary<int, StateBase<T>> FSMActDic;
  4. private StateBase<T> curState;
  5. //切换状态时调用
  6. public void SwitchState(int nextID)
  7. {
  8. curState.OnExit();
  9. curState = FSMActDic[nextID];
  10. curState.OnEnter();
  11. }
  12. public int GetCurState()
  13. {
  14. foreach (var kv in FSMActDic)
  15. {
  16. if (kv.Value == curState)
  17. return kv.Key;
  18. }
  19. return -1;
  20. }
  21. public FSM()
  22. {
  23. FSMActDic = new Dictionary<int, StateBase<T>>();
  24. }
  25. //增
  26. public void AddState(int id, StateBase<T> state)
  27. {
  28. if(FSMActDic.ContainsKey(id))
  29. return;
  30. FSMActDic.Add(id, state);
  31. }
  32. //删
  33. public void RemoveSatate(int id)
  34. {
  35. if (FSMActDic.ContainsKey(id))
  36. FSMActDic.Remove(id);
  37. }
  38. //获取
  39. public StateBase<T> GetState(int id)
  40. {
  41. if (!FSMActDic.ContainsKey(id))
  42. return null;
  43. return FSMActDic[id];
  44. }
  45. //状态机初始化调用,给curState赋值并调用其OnStay
  46. public void FSMInit(int id)
  47. {
  48. curState = FSMActDic[id];
  49. curState.OnStay();
  50. }
  51. //每帧执行
  52. public void FSMRun()
  53. {
  54. curState.OnStay();
  55. }
  56. //退出状态机执行
  57. public void FSMEnd()
  58. {
  59. curState.OnExit();
  60. }
  61. }

三.测试代码

使用状态先初始化,同时设置初始状态;

角色控制类负责初始化和运行FSM状态机;

  1. public class PlayerControl : MonoBehaviour
  2. {
  3. public enum PlayerState
  4. {
  5. none = 0,
  6. idle,
  7. move,
  8. jump,
  9. }
  10. public FSM<PlayerControl> mPlayerFSM;
  11. public PlayerState mState;
  12. public Animator mAnimator;
  13. public float mSpeed;
  14. public Vector3 moveDir;
  15. private void InitFSM()
  16. {
  17. mPlayerFSM.AddState((int) PlayerState.idle, new ActIdle((int) PlayerState.idle, this));
  18. mPlayerFSM.AddState((int) PlayerState.move, new ActMove((int) PlayerState.move, this));
  19. mPlayerFSM.AddState((int) PlayerState.jump, new ActAttack((int) PlayerState.jump, this));
  20. mPlayerFSM.FSMInit((int)PlayerState.idle);
  21. }
  22. void Start()
  23. {
  24. mAnimator = GetComponentInChildren<Animator>();
  25. mSpeed = 10;
  26. mPlayerFSM = new FSM<PlayerControl>();
  27. InitFSM();
  28. mState = PlayerState.idle;
  29. }
  30. void Update()
  31. {
  32. //单纯为了在inspector面板中看到当前状态
  33. mState = (PlayerState)mPlayerFSM.GetCurState();
  34. mPlayerFSM.FSMRun();
  35. }
  36. }

在不同的行为类中,监听输入按键通过owner调用fsm的switch方法,切换状态;

举例移动行为类,监听两个轴的输入,切换idle,同时监听攻击按键切换攻击状态;

  1. public class ActMove : StateBase<PlayerControl>
  2. {
  3. public ActMove(int id, PlayerControl t) : base(id, t)
  4. {
  5. }
  6. //给子类提供方法
  7. public override void OnEnter(params object[] args)
  8. {
  9. owner.mAnimator.Play("Run");
  10. }
  11. public override void OnStay(params object[] args)
  12. {
  13. owner.transform.position += owner.moveDir * Time.deltaTime * owner.mSpeed;
  14. if (Input.GetAxis("Horizontal") > 0 && Input.GetAxis("Vertical") == 0)
  15. {
  16. owner.moveDir = owner.transform.right;
  17. }
  18. else if (Input.GetAxis("Horizontal") < 0 && Input.GetAxis("Vertical") == 0)
  19. {
  20. owner.moveDir = -owner.transform.right;
  21. }
  22. else if (Input.GetAxis("Horizontal") > 0 && Input.GetAxis("Vertical") < 0)
  23. {
  24. owner.moveDir = owner.transform.right - owner.transform.forward;
  25. }
  26. else if (Input.GetAxis("Horizontal") < 0 && Input.GetAxis("Vertical") < 0)
  27. {
  28. owner.moveDir = -owner.transform.right - owner.transform.forward;
  29. }
  30. else if (Input.GetAxis("Horizontal") > 0 && Input.GetAxis("Vertical") > 0)
  31. {
  32. owner.moveDir = owner.transform.right + owner.transform.forward;
  33. }
  34. else if (Input.GetAxis("Horizontal") < 0 && Input.GetAxis("Vertical") > 0)
  35. {
  36. owner.moveDir = -owner.transform.right + owner.transform.forward;
  37. }
  38. else if (Input.GetAxis("Horizontal") == 0 && Input.GetAxis("Vertical") < 0)
  39. {
  40. owner.moveDir = -owner.transform.forward;
  41. }
  42. else if (Input.GetAxis("Horizontal") == 0 && Input.GetAxis("Vertical") > 0)
  43. {
  44. owner.moveDir = owner.transform.forward;
  45. }
  46. if (Mathf.Abs(Input.GetAxis("Horizontal")) < 0.1f && Mathf.Abs(Input.GetAxis("Vertical")) < 0.1f)
  47. owner.mPlayerFSM.SwitchState((int)PlayerControl.PlayerState.idle);
  48. if (Input.GetAxis("Jump") != 0)
  49. owner.mPlayerFSM.SwitchState((int)PlayerControl.PlayerState.jump);
  50. }
  51. public override void OnExit(params object[] args)
  52. {
  53. }
  54. }

自从出了行为树之后,有限状态机就没太大的用武之地了,后面有机会介绍官方的BehaviourTree插件吧;

Unity——有限状态机FSM修改的更多相关文章

  1. cocos2d-x 游戏开发之有限状态机(FSM) (一)

    cocos2d-x 游戏开发之有限状态机(FSM) (一) 参考:http://blog.csdn.net/mgphuang/article/details/5845252<Cocos2d-x游 ...

  2. cocos2d-x 游戏开发之有限状态机(FSM) (二)

    cocos2d-x 游戏开发之有限状态机(FSM)  (二) 1 状态模式

  3. 有限状态机FSM(自动售报机Verilog实现)

    有限状态机FSM(自动售报机Verilog实现) FSM 状态机就是一种能够描述具有逻辑顺序和时序顺序事件的方法. 状态机有两大类:Mealy型和Moore型. Moore型状态机的输出只与当前状态有 ...

  4. Unity有限状态机编写

    有限状态机FSM 是对行为逻辑的抽象. 在整个FSM架构中 首先有一个状态基类stateObject 里面有三个方法,分别是状态前.状态中.状态后. 所有具体行为类都要继承这个基类,在这三个方法中具体 ...

  5. cocos2d-x 游戏开发之有限状态机(FSM) (四)

    cocos2d-x 游戏开发之有限状态机(FSM) (四) 虽然我们了解了FSM,并且可以写自己的FSM,但是有更好的工具帮我们完成这个繁琐的工作.SMC(http://smc.sourceforge ...

  6. cocos2d-x 游戏开发之有限状态机(FSM) (三)

    cocos2d-x 游戏开发之有限状态机(FSM) (三) 有限状态机简称FSM,现在我们创建一个专门的FSM类,负责管理对象(Monkey)的状态.然后Monkey类就实现了行为与状态分离.Monk ...

  7. 有限状态机FSM

    有限状态机(Finite-state machine)又称有限状态自动机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型.常用与:正则表达式引擎,编译器的词法和语法分析,游戏设计,网络 ...

  8. Atitit. 有限状态机 fsm 状态模式

    Atitit. 有限状态机 fsm 状态模式 1. 有限状态机 1 2. "状态表"和"状态轮换表" 1 3. 有限状态机概念(状态(State)事件(Even ...

  9. Unity编辑器 - 资源修改立即写入磁盘AssetDataBase.SaveAssets()

    Unity编辑器 - 资源修改立即写入磁盘AssetDataBase.SaveAssets() 在编写编辑器时,如果需要修改Unity序列化资源(如Prefab,美术资源,ScriptableObje ...

随机推荐

  1. Python日常Bug集

    1.TypeError: 'int' object is not iterable: 场景示例: data = 7 for i in data: print(i) # 原因:直接对int数据进行迭代造 ...

  2. div标签的理解

    在HTML里面,div标签是一个块状元素,不会和其他元素排列在同一行,会默认和下面的元素换行,但是如果我们需要把几个div标签排在同一行,需要怎么做? 第一种:修改块状元素 源码: <div i ...

  3. JVM学习笔记——GC垃圾收集器

    GC 垃圾收集器 Java 堆内存采用分代回收算法,因此 JVM 针对新生代和老年代提供了多种垃圾收集器. 1. Serial 收集器 Serial 收集器是单线程收集器,采用复制算法. 是最基本的垃 ...

  4. 创业公司用 Serverless,到底香不香?

    作者 | Mike Butusov 来源 | Serverless 公众号 在过去的 5 年里,使用云厂商处理应用后台的流行程度大幅飙升.其一,初创企业主采用 Serverless 方式,以节省基础设 ...

  5. 学习使用SignalR

    1.创建空白的控制台程序 2.添加两个NuGet包(Microsoft.AspNet.SignalR.SelfHost.Microsoft.Owin.Cors.Topshelf)Topshelf用于快 ...

  6. 题解 AVL 树

    link Description 给出一个 \(n\) 个点的 AVL 树,求保留 \(k\) 个点使得字典序最小. \(n\le 5\times 10^5\) Solution 因为我很 sb ,所 ...

  7. 洛谷1501 Tree II(LCT,路径修改,路经询问)

    这个题是一个经典的维护路径信息的题,对于路径上的修改,我们只需要把对应的链\(split\)上来,然后修改最上面的点就好,注意pushdown的时候的顺序是先乘后加 然后下传乘法标记的时候,记得把对应 ...

  8. poj1248 (线性筛欧拉函数)(原根)

    强烈鸣谢wddwjlss 题目大意:给出一个奇素数,求出他的原根的个数,多组数据. 这里先介绍一些基本性质 阶 设\((a,m)=1\),满足\(a^r \equiv 1 \pmod m\)的最小正整 ...

  9. CTF入门记录(1

    (https://ctf-wiki.org) 00 基础了解 CTF简介 (wolai.com) 00-1 CTF题目类型 Web 大部分情况下和网.Web.HTTP等相关技能有关. Web攻防的一些 ...

  10. 关于C、Java、Python程序运行耗时及内存用量

    最近没有刷题,而是在PTA找几个题目寻找有关程序输入流问题以及各种语言在运行时对计算机消耗内存的问题, 以免很多同学解题的时候发现自己做的对但是出现运行超时的问题:针对运行内存,肯定用C/C++的同学 ...