翻译:http://wiki.unity3d.com/index.php/Finite_State_Machine

有限状态机

表示有限个状态以及在这些状态之间的转换和动作等行为的数学模型。

有限状态机框架:

  • Transition enum:此枚举包含可由系统触发的转换标签。
  • StateID枚举:这是游戏具有的状态ID。
  • FSMState类:有限状态机系统中的状态。每个状态都有一个字典,字典中有(转换-状态)键值对,保存Transition转换枚举类型的Key时,把枚举类型转换为int类型。(Enum没有实现IEquatable接口。因此,当我们使用Enum类型作为key值时,Dictionary的内部操作就需要将Enum类型转换为System.Object,这就导致了Boxing的产生。)
  • FSMSystem:这是有限状态机类,游戏中的每个NPC或GameObject必须具有这些类才能使用该框架。它将NPC的状态存储在List中,具有添加和删除状态的方法以及基于传递给它的转换来更改当前状态的方法PerformTransition()。您可以在代码中的任何位置调用此方法,如在碰撞测试中,或在Update()或FixedUpdate()中。

FSMSystem.cs

using System;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 转换状态
/// </summary>
public enum Transition
{NullTransition = 0,LostPlayer,SawPlayer,
}/// <summary>
/// 状态ID
/// </summary>
public enum StateID
{NullStateID = 0,FollowingPath,ChasingPlayer,
}/// <summary>
/// 有限状态机系统中的状态
/// 每个状态都有一个字典,字典中有键值对(转换-状态),保存转换Key时,把枚举转换为int,作为key
/// 表示如果在当前状态下触发转换,那么FSM应该处于对应的状态。
/// </summary>
public abstract class FSMState
{protected Dictionary<int, StateID> m_Map = new Dictionary<int, StateID>();protected StateID stateID;public StateID ID { get { return stateID; } }/// <summary>/// 添加转换/// </summary>public void AddTransition(Transition trans, StateID id){if (trans == Transition.NullTransition){Debug.LogError("FSMState ERROR: NullTransition is not allowed for a real transition");return;}if (id == StateID.NullStateID){Debug.LogError("FSMState ERROR: NullStateID is not allowed for a real ID");return;}int transition = (int)trans;if (m_Map.ContainsKey(transition)){Debug.LogError("FSMState ERROR: State " + stateID.ToString() + " already has transition " + trans.ToString() +"Impossible to assign to another state");return;}m_Map.Add(transition, id);}/// <summary>/// 删除转换/// </summary>public void DeleteTransition(Transition trans){if (trans == Transition.NullTransition){Debug.LogError("FSMState ERROR: NullTransition is not allowed");return;}int transition = (int)trans;if (m_Map.ContainsKey(transition)){m_Map.Remove(transition);return;}Debug.LogError("FSMState ERROR: Transition " + trans.ToString() + " passed to " + stateID.ToString() +" was not on the state's transition list");}/// <summary>/// 根据转换返回状态ID/// </summary>public StateID GetOutputState(Transition trans){int transition = (int)trans;if (m_Map.ContainsKey(transition)){return m_Map[transition];}return StateID.NullStateID;}/// <summary>/// 用于进入状态前,设置进入的状态条件/// 在进入当前状态之前,FSM系统会自动调用/// </summary>public virtual void DoBeforeEntering() { }/// <summary>/// 用于离开状态时的变量重置/// 在更改为新状态之前,FSM系统会自动调用/// </summary>public virtual void DoBeforeLeaving() { }/// <summary>/// 用于判断是否可以转换到另一个状态,每帧都会执行/// </summary>public abstract void CheckTransition(GameObject player, GameObject npc);/// <summary>/// 控制NPC行为,每帧都会执行/// </summary>public abstract void Act(GameObject player, GameObject npc);}/// <summary>
/// FSMSystem类
/// 持有一个状态集合
/// </summary>
public class FSMSystem
{private List<FSMState> m_States;// 改变FSM状态的唯一方式是触发转换// 不要直接改变状态private StateID currentStateID;public StateID CurrentStateID { get { return currentStateID; } }private FSMState currentState;public FSMState CurrentState { get { return currentState; } }public FSMSystem(){m_States = new List<FSMState>();}/// <summary>/// 添加新的状态/// </summary>public void AddState(FSMState state){if (state == null){Debug.LogError("FSM ERROR: Null reference is not allowed");}if (m_States.Count == 0){m_States.Add(state);currentState = state;currentStateID = state.ID;return;}foreach (FSMState s in m_States){if (s.ID == state.ID){Debug.LogError("FSM ERROR: Impossible to add state " + state.ID.ToString() +" because state has already been added");return;}}m_States.Add(state);}/// <summary>/// 删除状态/// </summary>public void DeleteState(StateID id){if (id == StateID.NullStateID){Debug.LogError("FSM ERROR: NullStateID is not allowed for a real state");return;}foreach (FSMState state in m_States){if (state.ID == id){m_States.Remove(state);return;}}Debug.LogError("FSM ERROR: Impossible to delete state " + id.ToString() +". It was not on the list of states");}/// <summary>/// 通过转换,改变FSM的状态/// </summary>public void PerformTransition(Transition trans){if (trans == Transition.NullTransition){Debug.LogError("FSM ERROR: NullTransition is not allowed for a real transition");return;}//获取转换对应的状态IDStateID id = currentState.GetOutputState(trans);if (id == StateID.NullStateID){Debug.LogError("FSM ERROR: State " + currentStateID.ToString() + " does not have a target state " +" for transition " + trans.ToString());return;}// 更新当前状态ID,currentStateID      currentStateID = id;foreach (FSMState state in m_States){if (state.ID == currentStateID){// 离开状态时的变量重置currentState.DoBeforeLeaving();// 更新当前状态currentStatecurrentState = state;// 进入状态前,设置进入的状态条件currentState.DoBeforeEntering();break;}}}
}

应用实例

在Unity下开发,如果目标距离带有此脚本的GameObject小于一定的距离,GameObject将开始追踪目标,否则,带有此脚本的GameObject将按照路径点巡逻。

using UnityEngine;[RequireComponent(typeof(Rigidbody))]
public class NPCControl : MonoBehaviour
{public GameObject m_Player;public Transform[] m_Path;private FSMSystem m_FSM;public void SetTransition(Transition t) { m_FSM.PerformTransition(t); }public void Start(){MakeFSM();}public void FixedUpdate(){m_FSM.CurrentState.CheckTransition(m_Player, gameObject);m_FSM.CurrentState.Act(m_Player, gameObject);}// NPC有两个状态: FollowPath(沿着路径巡逻) 和 ChasePlayer(追寻玩家)// 当在FollowPath状态时,SawPlayer转换被触发时,将变为ChasingPlayer状态// 当在ChasePlayer状态时,LostPlayer转换被触发,将变为FollowPath状态private void MakeFSM(){FollowPathState follow = new FollowPathState(m_Path);follow.AddTransition(Transition.SawPlayer, StateID.ChasingPlayer);ChasePlayerState chase = new ChasePlayerState();chase.AddTransition(Transition.LostPlayer, StateID.FollowingPath);m_FSM = new FSMSystem();m_FSM.AddState(follow);m_FSM.AddState(chase);}
}/// <summary>
/// FollowPath(沿着路径巡逻)
/// </summary>
public class FollowPathState : FSMState
{private int currentWayPoint;private Transform[] waypoints;public FollowPathState(Transform[] wp){waypoints = wp;currentWayPoint = 0;stateID = StateID.FollowingPath;}public override void CheckTransition(GameObject player, GameObject npc){//RaycastHit hit;//if (Physics.Raycast(npc.transform.position, npc.transform.forward, out hit, 15f))//{//    if (hit.transform.gameObject.CompareTag("Player"))//        npc.GetComponent<NPCControl>().SetTransition(Transition.SawPlayer);//}// 当Player距离NPC小于15米时,触发SawPlayer状态Collider[] colliders = Physics.OverlapSphere(npc.transform.position, 5f);if(colliders.Length <= 0){return;}// 需要设置场景中Player的Tag为Playerif (colliders[0].transform.gameObject.CompareTag("Player"))npc.GetComponent<NPCControl>().SetTransition(Transition.SawPlayer);}public override void Act(GameObject player, GameObject npc){// 沿着路径点巡逻Rigidbody rigidbody = npc.GetComponent<Rigidbody>();Vector3 vel = rigidbody.velocity;// 计算移动方向Vector3 moveDir = waypoints[currentWayPoint].position - npc.transform.position;// 如果距离小于1,前往下一个路径点if (moveDir.magnitude < 1){currentWayPoint++;if (currentWayPoint >= waypoints.Length){currentWayPoint = 0;}}else{vel = moveDir.normalized * 10;// 面向路径点npc.transform.rotation = Quaternion.Slerp(npc.transform.rotation,Quaternion.LookRotation(moveDir),5 * Time.deltaTime);npc.transform.eulerAngles = new Vector3(0, npc.transform.eulerAngles.y, 0);}rigidbody.velocity = vel;}}/// <summary>
/// ChasePlayer(追寻玩家)
/// </summary>
public class ChasePlayerState : FSMState
{public ChasePlayerState(){stateID = StateID.ChasingPlayer;}public override void CheckTransition(GameObject player, GameObject npc){// 如果玩家距离NPC超出30米的距离,触发LostPlayer转换if (Vector3.Distance(npc.transform.position, player.transform.position) >= 30)npc.GetComponent<NPCControl>().SetTransition(Transition.LostPlayer);}public override void Act(GameObject player, GameObject npc){Rigidbody rigidbody = npc.GetComponent<Rigidbody>();Vector3 vel = rigidbody.velocity;// 找到玩家的方向Vector3 moveDir = player.transform.position - npc.transform.position;// 面向路径点npc.transform.rotation = Quaternion.Slerp(npc.transform.rotation,Quaternion.LookRotation(moveDir),5 * Time.deltaTime);npc.transform.eulerAngles = new Vector3(0, npc.transform.eulerAngles.y, 0);vel = moveDir.normalized * 10;rigidbody.velocity = vel;}}

工程地址:

链接:https://pan.baidu.com/s/1H07NQYw-gqDOXWaWh-oFPw 
提取码:dqse

【最通俗易懂】C#有限状态机相关推荐

  1. 我彻底服了,大牛讲解信号与系统(通俗易懂)

    我彻底服了,大牛讲解信号与系统(通俗易懂) (2015-10-13 21:22:36) 转载▼   分类: 电力电子技术 第一课什么是卷积卷积有什么用什么是傅利叶变换什么是拉普拉斯变换 引子 很多朋友 ...

  2. 【游戏设计模式】之三 状态模式 有限状态机 Unity版本实现

     本系列文章由@浅墨_毛星云 出品,转载请注明出处.    文章链接: http://blog.csdn.net/poem_qianmo/article/details/52824776  作者:毛星 ...

  3. Java注解---通俗易懂

    本文转载于Java注解-最通俗易懂的注解 Annotation 中文译过来就是注解.标释的意思,在 Java 中注解是一个很重要的知识点,但经常还是有点让新手不容易理解. 我个人认为,比较糟糕的技术文 ...

  4. JavaScript 中的有限状态机

    http://www.ibm.com/developerworks/cn/web/wa-finitemach/ JavaScript 中的有限状态机 Page navigation 系列文章 有限状态 ...

  5. 通俗易懂,到底什么是区块链?

    链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载. 2017年9月4日,中国政府正式明令禁止ICO和数字货币交易行为,随即关闭了多个数字货币交易所.同时政府也多次声明,不会 ...

  6. 通俗易懂!使用Excel和TF实现Transformer

    作者 | 石晓文 转载自小小挖掘机(ID:wAIsjwj) 本文旨在通过最通俗易懂的过程来详解Transformer的每个步骤! 假设我们在做一个从中文翻译到英文的过程,我们的词表很简单如下: 中文词 ...

  7. 通俗易懂讲解梯度下降法!

    Datawhale干货 作者:知乎King James,伦敦国王大学 知乎 | https://zhuanlan.zhihu.com/p/335191534 前言:入门机器学习必须了解梯度下降法,虽然 ...

  8. Codeforces Round #700 (Div. 2) D2 Painting the Array II(最通俗易懂的贪心策略讲解)看不懂来打我 ~

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 整场比赛的A ~ E 6题全,全部题目超高质量题解链接: Codeforces Round #700 ...

  9. 通俗易懂的ReentrantLock,不懂你来砍我

    前言 自己开的坑,跪着也要填完,欢迎来到Java并发编程系列第五篇ReentrantLock,文章风格依然是图文并茂,通俗易懂,本文带读者们深入理解ReentrantLock设计思想. 认识下Reen ...

最新文章

  1. usaco Score Inflation
  2. 开发效率不高?墙裂推荐这十款精选 IntelliJ IDEA 插件
  3. 【五线谱】拍号与音符时值 ( 全音符 | 二分音符 | 四分音符 | 八分音符 | 十六分音符 | 三十二分音符 )
  4. QT写入cmd命令并且调用,以及指定路径新建文件夹
  5. 图解防雷技术基础知识
  6. ECCV 2012 KCF/DCF:《High-speed tracking with kernelized correlation filters》论文笔记
  7. url 转换中文_数字快速转换成中文大写,我有妙招
  8. Most Powerful
  9. php+jquery实现图片上传预览_和拖动位置值,PHP教程:thinkphp jquery实现图片上传和预览效果...
  10. python qt gui快速编程_《PYTHON QT GUI快速编程 PYQT编程指南》源码
  11. Linux内核源代码分析——中断(一鞭一条痕)(上)
  12. iOS最新验证电话号码与手机号码的正则方法
  13. Spring Cloud Ribbon 的请求分发与原理
  14. 敏感词在线检测-敏感词在线检测工具
  15. MongoDB学习笔记之索引(一)
  16. C语言--逻辑判断题(2)
  17. MySQL存储过程、函数、视图、触发器、索引和锁的基本知识
  18. Unity 动画模拟果冻抖动效果
  19. Web前端:什么是Web开发?
  20. 联想拯救者系统重装?不求人教程

热门文章

  1. spool for oracle
  2. 生活随机 - 2020和CSDN
  3. boost::asio::ssl 漏洞扫描应对
  4. solidity数据类型(四)storage memory calldata modifier前置条件 继承 接口合约 导入库 using...for solc编译
  5. 病狗问题和舞会戴帽子问题
  6. saiku java_Saiku源码完整搭建及问题解决方案
  7. 基于python+django学生信息管理系统设计与实现(含程序源码和毕业设计)
  8. java 数组字符串拼接字符串_java数组、字符串拼接
  9. 监督性机器学习算法笔记
  10. 2018春运火车票务系统:每天1500亿浏览量,1秒卖票700张