Unity 状态转化机器
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/**
有限状态机系统基于Eric Dybsand的《游戏编程精粹》(Game Programming Gems)第3.1章
如何使用:
1. 在状态转化机中的相应枚举类中,声明 transitions 和 states 的label.
2. 写新的类集成 FSMState, 对于写的每一个类都要写上“转换条件” pairs (transition-state).
这些转换条件表明 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. 切记这是确定的FSM.
不能有 transition 导致两种 states.
“Reason” 方法用来决定transition 是否应该 fired.
如果你感觉爽,可以把这个方法留空,在别的地方写代码用来 fire transitions.
“Act” 用来写代码表现NPC在当前状态的 the actions.
如果你感觉爽,可以把这个方法留空,在别的地方写代码用来 code for the actions.
3. 创建 FSMSystem 类的实例,然后给他增加 states.
4. 在你的 UPdate或 FixedUpdate 方法中调用 Reason 和 Act (或者其他你用来 firing transitions 和 making the NPCs
behave 的方法).
Asynchronous transitions from Unity Engine, 例如 OnTriggerEnter, SendMessage, 依然可用,
只需要调用你的 PerformTransition 方法 from your FSMSystem 实例 with the correct Transition
when the event occurs.
*/
/// <summary>
/// 在这个枚举类中声明 Transitions 的labels.
/// 不要改变第一个 label ( NullTransition ) 因为 FSMSystem 类将会使用它.
/// </summary>
public enum Transition
{
NullTransition = 0, // 使用这个 transition 用来在你的系统中表示一个不存在的 transition
SawPlayer,
LostPlayer
}
/// <summary>
/// 在这个枚举类中声明 States 的labels.
/// 不要改变第一个 label( NullTransition ) 因为 FSMSystem 类将会使用它.
/// </summary>
public enum StateID
{
NullStateID = 0, // 使用这个 ID 用来在你的系统中表示一个不存在的 State
ChasingPlayer,
FollowingPath
}
/// <summary>
/// 这个类表示有限状态机中的 States.
/// 每个状态有一个 Dictionary with pairs (transition-state) showing
/// which state the FSM should be if a transition is fired while this state
/// is the current state.
/// Reason 方法用来判断 transition 是否可行.
/// 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)
{
// 检查trans和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;
}
// 因为这是一个确定的 FSM,
// 检查当前的 transition 是否已经在 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>
/// 这个方法用来用state's map中删除 transition-state对.
/// 如果 transition 不在 state's map但中,打印一个错误信息.
/// </summary>
public void DeleteTransition(Transition trans)
{
// 检查trans是否为空
if (trans == Transition.NullTransition)
{
Debug.LogError("FSMState ERROR: NullTransition is not allowed");
return;
}
//删除钱检查 pair i是否在map当中
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>
/// 这个方法用来返回 new state the FSM should be if
/// this state receives a transition and
/// </summary>
public StateID GetOutputState(Transition trans)
{
//检查 map 是否含有 transition
if (map.ContainsKey(trans))
{
return map[trans];
}
return StateID.NullStateID;
}
/// <summary>
/// 这个函数用来设置 the State condition before entering it.
/// 在进入这个状态之前 FSMSystem 自动调用这个方法.
/// </summary>
public virtual void DoBeforeEntering() { }
/// <summary>
/// 这个方法用来做必须的工作,在状态转化之前例如重新设置变量
/// 在进入到新状态之前 FSMSystem 会自动调用这个方法.
/// </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
/// </summary>
public abstract void Reason(GameObject player, GameObject npc);
/// <summary>
/// This method controls the behavior of the NPC in the game World.
/// Every action, movement or communication the NPC does should be placed here
/// NPC is a reference to the object that is controlled by this class
/// </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.
/// </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 simulation begins
if (states.Count == 0)
{
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
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
currentState.DoBeforeEntering();
break;
}
}
} // PerformTransition()
} //class FSMSystem
NPC 身上挂在的脚本
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();
}
// The NPC 有两个状态 FollowPath 和 ChasePlayer
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 void FixedUpdate()
{
fsm.CurrentState.Reason(player, gameObject);
fsm.CurrentState.Act(player, gameObject);
}
}
//跟随固定路径状态定义
public class FollowPathState : FSMState
{
private int currentWayPoint;
private Transform[] waypoints;
//初始化
public FollowPathState(Transform[] wp)
{
waypoints = wp;
currentWayPoint = 0;
stateID = StateID.FollowingPath;
}
public override void Reason(GameObject player, GameObject npc)
{
// 如果 Player 距离NPC小于2米
float distance = (player.transform.position-npc.transform.position).magnitude;
Debug.Log(distance);
if (distance<2)
{
//转换条件
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 < 1)
{
currentWayPoint++;
if (currentWayPoint >= waypoints.Length)
{
currentWayPoint = 0;
}
}
else
{
vel = moveDir.normalized * 3;
}
// 应用速度
npc.GetComponent<Rigidbody>().velocity = vel;
}
}
//追逐英雄状态
public class ChasePlayerState : FSMState
{
//初始化函数
public ChasePlayerState()
{
stateID = StateID.ChasingPlayer;
}
public override void Reason(GameObject player, GameObject npc)
{
// player已经距离 NPC超过30米, fire LostPlayer transition
if (Vector3.Distance(npc.transform.position, player.transform.position) >= 30)
npc.GetComponent<NPCControl>().SetTransition(Transition.LostPlayer);
}
public override void Act(GameObject player, GameObject npc)
{
// Follow the path of waypoints
//找到玩家 player 的方向
Vector3 vel = npc.GetComponent<Rigidbody>().velocity;
Vector3 moveDir = player.transform.position - npc.transform.position;
// Rotate towards the waypoint
vel = moveDir.normalized * 1;
// 使用速度
npc.GetComponent<Rigidbody>().velocity = vel;
}
}
Unity 状态转化机器的更多相关文章
- REST --- Representational State Transfer --- 表现层状态转化
引用:阮一峰的网络日志 如果一个架构符合REST原则,就称它为RESTful架构. 要理解RESTful架构,最好的方法就是去理解Representational State Transfer这个词组 ...
- REST 表现层状态转化
1.REST是什么? 1) REST:即 Representational State Transfer.(资源)表现层状态转化.是目前最流行的一种互联网软件架构.它结构清晰.符合标准.易于理解.扩展 ...
- TCP状态转化图 TIME_WAIT解析
先上转换图: 重点研究TIME_WAIT状态,根据UNIX网络编程中的思路,TIME_WAIT状态有两个存在的理由: 理由1. 客户端执行主动关闭,假设最终的ACK丢失,服务器将重新发送它的最后那个F ...
- 魔板 Magic Squares(广搜,状态转化)
题目背景 在成功地发明了魔方之后,鲁比克先生发明了它的二维版本,称作魔板.这是一张有8个大小相同的格子的魔板: 1 2 3 4 8 7 6 5 题目描述 我们知道魔板的每一个方格都有一种颜色.这8种颜 ...
- Java:线程的六种状态及转化
目录 Java:线程的六种状态及转化 一.新建状态(NEW) 二.运行状态(RUNNABLE) 就绪状态(READY) 运行状态(RUNNING) 三.阻塞状态(BLOCKED) 四.等待状态(WAI ...
- Java中线程的状态及其转化
线程状态转化图: 说明: 线程总共包括以下5种状态. 1.新状态New:该状态也叫新建状态,当线程对象被创建后,线程就进入了新建状态.例如:Thread thread = new Thread();. ...
- Asp.net 面向接口可扩展框架之类型转化基础服务
新框架正在逐步完善,可喜可贺的是基础服务部分初具模样了,给大家分享一下 由于基础服务涉及面太广,也没开发完,这篇只介绍其中的类型转化部分,命名为类型转化基础服务,其实就是基础服务模块的类型转化子模块 ...
- 【Hibernate框架】对象的三种持久化状态
一.综述 hibernate中的对象有三种状态,分别是TransientObjects(瞬时对象).PersistentObjects(持久化对象)和DetachedObjects(托管对象也叫做离线 ...
- TCP/IP详解--连接状态变迁图CLOSE_WAIT
终止一个连接要经过4次握手.这由TCP的半关闭(half-close)造成的.既然一个TCP连接是全双工(即数据在两个方向上能同时传递,可理解为两个方向相反的独立通道),因此每个方向必须单独地进行关闭 ...
随机推荐
- android SDK 更新问题完美解决 http://dl-ssl.google.com refused
现在由于GWF,google基本和咱们说咱见了,就给现在在做Android 或者想学习Android 的朋友带来了诸多的不便,最简单的就是Android SDK Manager 你无法更新了. 现在 ...
- request.getParameter 乱码问题
个简单的问题,我想追究一下深层次的原因: 前台的编码格式HTML里面的是utf-8的;; 但是后台使用request.getParameter("groupName");乱码; 我 ...
- 复制Informational constraints on LUW DB2 v105
An informational constraint is a constraint attribute that can be used by the SQL compiler to improv ...
- C# virtual override 和 new 的区别
一直以来我都对 virtual override 和 new 之间的区别感到疑惑不解. 特别笔试的时候特别容易考到,真的很容易弄错啊,畜生! 光看理论永远记不住,那不如写几行代码就懂了. 首先看看v ...
- GoLang搞一个基本的HTTP服务
慢慢和python的对应一下看看. package main import ( "fmt" "net/http" "strings" &qu ...
- ytu 1058: 三角形面积(带参的宏 练习)
1058: 三角形面积 Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 190 Solved: 128[Submit][Status][Web Boar ...
- poj 1007:DNA Sorting(水题,字符串逆序数排序)
DNA Sorting Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 80832 Accepted: 32533 Des ...
- log4j设置日志格式为UTF-8
想要log4j输出的日志文件的编码格式为UTF-8.正常情况下只需要添加下述的代码即可: log4j.appender.appender_name.Encoding=UTF-8 但是在spring与l ...
- android 入门-工序
页面: 1.启动页 2.引导页 3.主页面 自定义控件: 轮播控件 轮播列表控件 弹出控件 加载控件 引导页控件 下拉刷新 上拉加载控件
- MathType 常用快捷键
MathType 数学公式编辑器是广大理科生电脑上必不可少的软件!然而在大量公式时,不会巧妙使用快捷键真的是心累身累.巧妙使用MathType数学公式编辑器可加快数学符号的录入速度和效率,这将节约大量 ...