Unity FSM 有限状态机
翻译了一下unity wiki上对于有限状态机的案例,等有空时在详细写一下。在场景中添加两个游戏物体,一个为玩家并修改其Tag为Player,另一个为NPC为其添加NPCControl脚本,并为其将玩家角色和路径添加上去。(该案例利用状态机简单的实现了一个NPC的简单AI---巡逻---看到玩家----追逐玩家----丢失玩家----巡逻)
效果:
状态机:
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- /**
- A Finite State Machine System based on Chapter 3.1 of Game Programming Gems 1 by Eric Dybsand
- Written by Roberto Cezar Bianchini, July 2010
- How to use:
- 1. Place the labels for the transitions and the states of the Finite State System
- in the corresponding enums.
- 2. Write new class(es) inheriting from FSMState and fill each one with pairs (transition-state).
- These pairs represent the state S2 the FSMSystem should be if while being on state S1, a
- transition T is fired and state S1 has a transition from it to S2. Remember this is a Deterministic(确定的) FSM.
- You can't have one transition leading to two different states.
- Method Reason is used to determine which transition should be fired.
- You can write the code to fire transitions in another place, and leave this method empty if you
- feel it's more appropriate 合适 to your project.
- Method Act has the code to perform the actions the NPC is supposed do if it's on this state.
- You can write the code for the actions in another place, and leave this method empty if you
- feel it's more appropriate to your project.
- 3. Create an instance of FSMSystem class and add the states to it.
- 4. Call Reason and Act (or whichever methods you have for firing transitions and making the NPCs
- behave in your game) from your Update or FixedUpdate methods.
- Asynchronous transitions from Unity Engine, like OnTriggerEnter, SendMessage, can also be used,
- just call the Method PerformTransition from your FSMSystem instance with the correct Transition
- when the event occurs 重现.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
- AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- /// <summary>
- /// Place the labels for the Transitions in this enum.
- /// Don't change the first label, NullTransition as FSMSystem class uses it.
- /// 为过渡加入枚举标签
- /// 不要修改第一个标签,NullTransition会在FSMSytem类中使用
- /// </summary>
- public enum Transition
- {
- NullTransition = , // Use this transition to represent a non-existing transition in your system
- //用这个过度来代表你的系统中不存在的状态
- SawPlayer,//这里配合NPCControl添加两个NPC的过渡
- LostPlayer,
- }
- /// <summary>
- /// Place the labels for the States in this enum.
- /// Don't change the first label, NullStateID as FSMSystem class uses it.
- /// 为状态加入枚举标签
- /// 不要修改第一个标签,NullStateID会在FSMSytem中使用
- /// </summary>
- public enum StateID
- {
- NullStateID = , // Use this ID to represent a non-existing State in your syste
- //使用这个ID来代表你系统中不存在的状态ID
- ChasingPlayer,//这里配合NPCControl添加两个状态
- FollowingPath,
- }
- /// <summary>
- /// This class represents the States in the Finite State System.
- /// Each state has a Dictionary with pairs (transition-state) showing
- /// which state the FSM should be if a transition is fired while this state
- /// is the current state.
- /// Method Reason is used to determine which transition should be fired .
- /// Method Act has the code to perform the actions the NPC is supposed do if it's on this state.
- /// 这个类代表状态在有限状态机系统中
- /// 每个状态都有一个由一对搭档(过渡-状态)组成的字典来表示当前状态下如果一个过渡被触发状态机会进入那个状态
- /// Reason方法被用来决定那个过渡会被触发
- /// Act方法来表现NPC出在当前状态的行为
- /// </summary>
- public abstract class FSMState
- {
- protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();
- protected StateID stateID;
- public StateID ID { get { return stateID; } }
- public void AddTransition(Transition trans, StateID id)
- {
- // Check if anyone of the args is invalid
- //验证每个参数是否合法
- 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;
- }
- // Since this is a Deterministic FSM,
- // check if the current transition was already inside the map
- //要知道这是一个确定的有限状态机(每个状态后金对应一种状态,而不能产生分支)
- //检查当前的过渡是否已经在地图字典中了
- if (map.ContainsKey(trans))
- {
- Debug.LogError("FSMState ERROR: State " + stateID.ToString() + " already has transition " + trans.ToString() +
- "Impossible to assign to another state");
- return;
- }
- map.Add(trans, id);
- }
- /// <summary>
- /// This method deletes a pair transition-state from this state's map.
- /// If the transition was not inside the state's map, an ERROR message is printed.
- /// 这个方法用来在状态地图中删除transition-state对儿
- /// 如果过渡并不存在于状态地图中,那么将会打印出一个错误
- /// </summary>
- public void DeleteTransition(Transition trans)
- {
- // Check for NullTransition
- if (trans == Transition.NullTransition)
- {
- Debug.LogError("FSMState ERROR: NullTransition is not allowed");
- return;
- }
- // Check if the pair is inside the map before deleting
- //再删除之前确认该键值对是否存在于状态地图中(键值对集合)
- if (map.ContainsKey(trans))
- {
- map.Remove(trans);
- return;
- }
- Debug.LogError("FSMState ERROR: Transition " + trans.ToString() + " passed to " + stateID.ToString() +
- " was not on the state's transition list");
- }
- /// <summary>
- /// This method returns the new state the FSM should be if
- /// this state receives a transition and
- /// 该方法在该状态接收到一个过渡时返回状态机需要成为的新状态
- /// </summary>
- public StateID GetOutputState(Transition trans)
- {
- // Check if the map has this transition
- if (map.ContainsKey(trans))
- {
- return map[trans];
- }
- return StateID.NullStateID;
- }
- /// <summary>
- /// This method is used to set up the State condition before entering it.
- /// It is called automatically by the FSMSystem class before assigning it
- /// to the current state.
- /// 这个方法用来设立进入状态前的条件
- /// 在状态机分配它到当前状态之前他会被自动调用
- /// </summary>
- public virtual void DoBeforeEntering() { }
- /// <summary>
- /// This method is used to make anything necessary, as reseting variables
- /// before the FSMSystem changes to another one. It is called automatically
- /// by the FSMSystem before changing to a new state.
- /// 这个方法用来让一切都是必要的,例如在有限状态机变化的另一个时重置变量。
- /// 在状态机切换到新的状态之前它会被自动调用。
- /// </summary>
- public virtual void DoBeforeLeaving() { }
- /// <summary>
- /// This method decides if the state should transition to another on its list
- /// 动机-->这个方法用来决定当前状态是否需要过渡到列表中的其他状态
- /// NPC is a reference to the object that is controlled by this class
- /// NPC是被该类约束下对象的一个引用
- /// </summary>
- public abstract void Reason(GameObject player, GameObject npc);
- /// <summary>
- /// This method controls the behavior of the NPC in the game World.
- /// 表现-->该方法用来控制NPC在游戏世界中的行为
- /// Every action, movement or communication the NPC does should be placed here
- /// NPC的任何动作,移动或者交流都需要防止在这儿
- /// NPC is a reference to the object that is controlled by this class
- /// NPC是被该类约束下对象的一个引用
- /// </summary>
- public abstract void Act(GameObject player, GameObject npc);
- } // class FSMState
- /// <summary>
- /// FSMSystem class represents the Finite State Machine class.
- /// It has a List with the States the NPC has and methods to add,
- /// delete a state, and to change the current state the Machine is on.
- /// 该类便是有限状态机类
- /// 它持有者NPC的状态集合并且有添加,删除状态的方法,以及改变当前正在执行的状态
- /// </summary>
- public class FSMSystem
- {
- private List<FSMState> states;
- // The only way one can change the state of the FSM is by performing a transition
- // Don't change the CurrentState directly
- //通过预装一个过渡的唯一方式来盖面状态机的状态
- //不要直接改变当前的状态
- private StateID currentStateID;
- public StateID CurrentStateID { get { return currentStateID; } }
- private FSMState currentState;
- public FSMState CurrentState { get { return currentState; } }
- public FSMSystem()
- {
- states = new List<FSMState>();
- }
- /// <summary>
- /// This method places new states inside the FSM,
- /// or prints an ERROR message if the state was already inside the List.
- /// First state added is also the initial state.
- /// 这个方法为有限状态机置入新的状态
- /// 或者在该状态已经存在于列表中时打印错误信息
- /// 第一个添加的状态也是最初的状态!
- /// </summary>
- public void AddState(FSMState s)
- {
- // Check for Null reference before deleting
- //在添加前检测空引用
- if (s == null)
- {
- Debug.LogError("FSM ERROR: Null reference is not allowed");
- }
- // First State inserted is also the Initial state,
- // the state the machine is in when the begins
- //被装在的第一个状态也是初始状态
- //这个状态便是状态机开始时的状态
- if (states.Count == )
- {
- states.Add(s);
- currentState = s;
- currentStateID = s.ID;
- return;
- }
- // Add the state to the List if it's not inside it
- //如果该状态未被添加过,则加入集合
- foreach (FSMState state in states)
- {
- if (state.ID == s.ID)
- {
- Debug.LogError("FSM ERROR: Impossible to add state " + s.ID.ToString() +
- " because state has already been added");
- return;
- }
- }
- states.Add(s);
- }
- /// <summary>
- /// This method delete a state from the FSM List if it exists,
- /// or prints an ERROR message if the state was not on the List.
- /// 该方法删除一个已存在以状态几个中的状态
- /// 在它不存在时打印错误信息
- /// </summary>
- public void DeleteState(StateID id)
- {
- // Check for NullState before deleting
- //在删除前检查其是否为空状态
- if (id == StateID.NullStateID)
- {
- Debug.LogError("FSM ERROR: NullStateID is not allowed for a real state");
- return;
- }
- // Search the List and delete the state if it's inside it
- //遍历集合如果存在该状态则删除它
- foreach (FSMState state in states)
- {
- if (state.ID == id)
- {
- states.Remove(state);
- return;
- }
- }
- Debug.LogError("FSM ERROR: Impossible to delete state " + id.ToString() +
- ". It was not on the list of states");
- }
- /// <summary>
- /// This method tries to change the state the FSM is in based on
- /// the current state and the transition passed. If current state
- /// doesn't have a target state for the transition passed,
- /// an ERROR message is printed.
- /// 该方法基于当前状态和过渡是否通过来尝试改变状态机的状态,当当前的状态没有目标状态用来过渡(叫通道应该更合适吧)时通过时则打印错误消息
- /// </summary>
- public void PerformTransition(Transition trans)
- {
- // Check for NullTransition before changing the current state
- //在改变当前状态前检测NullTransition
- if (trans == Transition.NullTransition)
- {
- Debug.LogError("FSM ERROR: NullTransition is not allowed for a real transition");
- return;
- }
- // Check if the currentState has the transition passed as argument
- //在改变当前状态前检测当前状态是否可作为过渡的参数
- StateID 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;
- }
- // Update the currentStateID and currentState
- //更新当前的状态个和状态编号
- currentStateID = id;
- foreach (FSMState state in states)
- {
- if (state.ID == currentStateID)
- {
- // Do the post processing of the state before setting the new one
- //在状态变为新状态前执行后处理
- currentState.DoBeforeLeaving();
- currentState = state;
- // Reset the state to its desired condition before it can reason or act
- //在状态可以使用Reason(动机)或者Act(行为)之前为它的的决定条件重置它自己
- currentState.DoBeforeEntering();
- break;
- }
- }
- } // PerformTransition()
- } //class FSMSystem
NPCControl:
- using System;
- using System.Collections.Generic;
- using System.Text;
- using UnityEngine;
- [RequireComponent(typeof(Rigidbody))]
- public class NPCControl : MonoBehaviour
- {
- public GameObject player;
- public Transform[] path;
- private FSMSystem fsm;
- public void SetTransition(Transition t)
- {
- //该方法用来改变有限状态机的状体,有限状态机基于当前的状态和通过的过渡状态。
- //如果当前的状态没有用来通过的过度状态,则会抛出错误
- fsm.PerformTransition(t);
- }
- public void Start()
- {
- MakeFSM();
- }
- public void FixedUpdate()
- {
- fsm.CurrentState.Reason(player, gameObject);
- fsm.CurrentState.Act(player, gameObject);
- }
- //NPC有两个状态分别是在路径中巡逻和追逐玩家
- //如果他在第一个状态并且SawPlayer 过度状态被出发了,它就转变到ChasePlayer状态
- //如果他在ChasePlayer状态并且LostPlayer状态被触发了,它就转变到FollowPath状态
- private void MakeFSM()//建造状态机
- {
- FollowPathState follow = new FollowPathState(path);
- follow.AddTransition(Transition.SawPlayer, StateID.ChasingPlayer);
- ChasePlayerState chase = new ChasePlayerState();
- chase.AddTransition(Transition.LostPlayer, StateID.FollowingPath);
- fsm = new FSMSystem();
- fsm.AddState(follow);//添加状态到状态机,第一个添加的状态将作为初始状态
- fsm.AddState(chase);
- }
- }
- public class FollowPathState : FSMState
- {
- private int currentWayPoint;
- private Transform[] waypoints;
- //构造函数装填自己
- public FollowPathState(Transform[] wp)
- {
- waypoints = wp;
- currentWayPoint = ;
- stateID = StateID.FollowingPath;//别忘设置自己的StateID
- }
- public override void DoBeforeEntering()
- {
- Debug.Log("FollowingPath BeforeEntering--------");
- }
- public override void DoBeforeLeaving()
- {
- Debug.Log("FollowingPath BeforeLeaving---------");
- }
- //重写动机方法
- public override void Reason(GameObject player, GameObject npc)
- {
- // If the Player passes less than 15 meters away in front of the NPC
- RaycastHit hit;
- if (Physics.Raycast(npc.transform.position, npc.transform.forward, out hit, 15F))
- {
- if (hit.transform.gameObject.tag == "Player")
- npc.GetComponent<NPCControl>().SetTransition(Transition.SawPlayer);
- }
- }
- //重写表现方法
- public override void Act(GameObject player, GameObject npc)
- {
- // Follow the path of waypoints
- // Find the direction of the current way point
- Vector3 vel = npc.GetComponent<Rigidbody>().velocity;
- Vector3 moveDir = waypoints[currentWayPoint].position - npc.transform.position;
- if (moveDir.magnitude < )
- {
- currentWayPoint++;
- if (currentWayPoint >= waypoints.Length)
- {
- currentWayPoint = ;
- }
- }
- else
- {
- vel = moveDir.normalized * ;
- // Rotate towards the waypoint
- npc.transform.rotation = Quaternion.Slerp(npc.transform.rotation,
- Quaternion.LookRotation(moveDir),
- * Time.deltaTime);
- npc.transform.eulerAngles = new Vector3(, npc.transform.eulerAngles.y, );
- }
- // Apply the Velocity
- npc.GetComponent<Rigidbody>().velocity = vel;
- }
- } // FollowPathState
- public class ChasePlayerState : FSMState
- {
- //构造函数装填自己
- public ChasePlayerState()
- {
- stateID = StateID.ChasingPlayer;
- }
- public override void DoBeforeEntering()
- {
- Debug.Log("ChasingPlayer BeforeEntering--------");
- }
- public override void DoBeforeLeaving()
- {
- Debug.Log("ChasingPlayer BeforeLeaving---------");
- }
- public override void Reason(GameObject player, GameObject npc)
- {
- // If the player has gone 30 meters away from the NPC, fire LostPlayer transition
- if (Vector3.Distance(npc.transform.position, player.transform.position) >= )
- npc.GetComponent<NPCControl>().SetTransition(Transition.LostPlayer);
- }
- public override void Act(GameObject player, GameObject npc)
- {
- // Follow the path of waypoints
- // Find the direction of the player
- Vector3 vel = npc.GetComponent<Rigidbody>().velocity;
- Vector3 moveDir = player.transform.position - npc.transform.position;
- // Rotate towards the waypoint
- npc.transform.rotation = Quaternion.Slerp(npc.transform.rotation,
- Quaternion.LookRotation(moveDir),
- * Time.deltaTime);
- npc.transform.eulerAngles = new Vector3(, npc.transform.eulerAngles.y, );
- vel = moveDir.normalized * ;
- // Apply the new Velocity
- npc.GetComponent<Rigidbody>().velocity = vel;
- }
- } // ChasePlayerState
Unity最受欢迎的插件,可以让您的游戏如虎添翼,为您节省大量时间可以投入在游戏的创意和细节上
Unity FSM 有限状态机的更多相关文章
- Unity——FSM有限状态机
FSM有限状态机 一.设计思路 1.共同的状态父类,提供可重写的进入,保持,退出该状态的生命周期方法: 2.状态机,管理所有状态(增删查改),状态机运行方法(Run): 3.在角色控制器中,实例化状态 ...
- 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,这篇博客首先感谢需要感谢当时看到凉鞋的笔记博客, 凉鞋的博客地 ...
随机推荐
- inux下使用自带mail发送邮件告警
安装mailx工具,mailx是一个小型的邮件发送程序. 具体步骤如下: 1.安装 [root@localhost ~]# yum -y install mailx 2.编辑配置文件 [root@lo ...
- Java 获取指定包下的所有类
package com.s.rest.util; import java.io.File; import java.io.FileFilter; import java.io.IOException; ...
- 算法学习记录-查找——平衡二叉树(AVL)
排序二叉树对于我们寻找无序序列中的元素的效率有了大大的提高.查找的最差情况是树的高度.这里就有问题了,将无序数列转化为 二叉排序树的时候,树的结构是非常依赖无序序列的顺序,这样会出现极端的情况. [如 ...
- Python 学习笔记(十二)Python文件和迭代(二)
迭代 基本含义 迭代是重复反馈过程的活动,其目的通常是为了接近并到达所需的目标或结果.每一次对过程的重复被称为一次“迭代”,而每一次迭代得到的结果会被用来作为下一次迭代的初始值. 在计算科学中,迭代 ...
- 嵌入式STM32开发环境之Keil5的安装(附资源)--
全文copy,原文见https://blog.csdn.net/weixin_42602730/article/details/81007685 --------------------------- ...
- [USACO15DEC]最大流Max Flow(树链剖分,线段树)
FJ给他的牛棚的N(2≤N≤50,000)个隔间之间安装了N-1根管道,隔间编号从1到N.所有隔间都被管道连通了. FJ有K(1≤K≤100,000)条运输牛奶的路线,第i条路线从隔间si运输到隔间t ...
- Delphi XE7调用Java Class,JAR
Delphi XE5,XE6需要用户手工编译并将Classes.Dex加入到包中,不过Delphi XE7可以省掉这些工作了. 如何在XE7中调用Java,具体步骤如下: 1.将jar文件添加到XE7 ...
- python学习笔记(一)学习资料记录
相关资料网站 1. python3简明教程 适合新学者,因为可以在线操作,并且校验结果,同时还有考试系统.比较基础 2. python数据分析数据科学中文英文工具书籍下载 免费的中英文数据的PDF下载 ...
- 【Spark】编程实战之模拟SparkRPC原理实现自定义RPC
1. 什么是RPC RPC(Remote Procedure Call)远程过程调用.在Hadoop和Spark中都使用了PRC,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的 ...
- 通过SSH服务登陆linux服务器(版本RHEL7)
通过SSH服务登陆linux服务器(版本RHEL7) SSH服务概述:是一种能够以安全的方式提供远程登陆的协议,也是目前远程管理linux系统的首选方式.在此之前,我们一般使用FTP或者telnet来 ...