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,这篇博客首先感谢需要感谢当时看到凉鞋的笔记博客, 凉鞋的博客地 ... 
随机推荐
- Maven创建项目一些常见的问题
			1 .创建的项目中没有src/main/java.没有src/test/java 主要原因在于在创建项目的时候,使用的是系统自带的jdk,修改方法: 右键项目——Properties——javaBui ... 
- JNI由浅入深_2_C语言基础
			*含义 1.乘法 3*5 2.定义指针变量 int * p://定义了一个名字叫p的变量,能够存放int数据类型的地址 3.指针运算符, //如果p是一个已经定义好的指针变量则*p表示以p的内容为地址 ... 
- 怎样卸载wineQQ?
			好久没实用ubuntu系统的wineqq了.今天用的时候,提示无法使用,要求官网又一次下载新版本号, 感觉挺麻烦的,准备卸载,半天卸载不了. 经过努力,终于还是卸载了. 卸载命令: sudo dpk ... 
- Linux内核中的jiffies及其作用介绍及jiffies等相关函数详解
			在LINUX的时钟中断中涉及至二个全局变量一个是xtime,它是timeval数据结构变量,另一个则是jiffies,首先看timeval结构struct timeval{time_t tv_sec; ... 
- 我告诉你 ,一个 window免费系统下载的网站!
			一个 window免费系统下载的网站! https://msdn.itellyou.cn/ 
- DBCP数据库连接池的简单使用
			0.DBCP简介 DBCP(DataBase connection pool)数据库连接池是 apache 上的一个Java连接池项目.DBCP通过连接池预先同数据库建立一些连接放在内存中( ... 
- thinkphp3.2  上传图片兼容小程序
			第一步在配置文件中设置图片的大小和路径 return array( //'配置项'=>'配置值' 'img_save'=>[ 'size' =>[ 'app_gszc_Card'=& ... 
- lrzsz Linux服务器Windows互传文件工具
			lrzsz是一款在linux里可代替ftp上传和下载的程序,但只限于较小的文件,如果是目录需要打包成单个文件在实现下载. 条件:需要使用SecureCRT或者Xshell等客户端工具连接Linux 下 ... 
- 偏前端 + rsa加解密 +  jsencrypt.min.js--(新增超长字符分段加解密)
			<html> <head> <title>JavaScript RSA Encryption</title> <meta charset=&quo ... 
- 调试日志——基于stm32的智能声光报警器(二)
			今天调试了音频部分的播放功能,这里我采用的是输出pwm来播放声音的方式. 声音的源文件是一个wav文件,然后用一个软件将wav文件转化为一个数组的文件. 要播放的文件就是这个数组文件里面的数据部分,前 ... 
