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连接是全双工(即数据在两个方向上能同时传递,可理解为两个方向相反的独立通道),因此每个方向必须单独地进行关闭 ...
随机推荐
- volatile和const
volatile可理解为“编译器警告指示字” volatile用于告诉编译器必须每次去内存中取变量值 volatile主要修饰可能被多个线程访问的变量 volatile也可以修饰可能被未知因数更改的变 ...
- 三、jQuery--jQuery基础--jQuery基础课程--第3章 jQuery过滤性选择器
1.:first过滤选择器 本章我们介绍过滤选择器,该类型的选择器是根据某过滤规则进行元素的匹配,书写时以“:”号开头,通常用于查找集合元素中的某一位置的单个元素. 在jQuery中,如果想得到一组相 ...
- Java中读取xml方法
package zaxiang; import java.io.*; import javax.xml.parsers.DocumentBuilder; import javax.xml.parser ...
- Android Programming: Pushing the Limits -- Chapter 7:Android IPC -- ApiWrapper
前面两片文章讲解了通过AIDL和Messenger两种方式实现Android IPC.而本文所讲的并不是第三种IPC方式,而是对前面两种方式进行封装,这样我们就不用直接把Aidl文件,java文件拷贝 ...
- Java集合源码学习(四)HashMap分析
ArrayList.LinkedList和HashMap的源码是一起看的,横向对比吧,感觉对这三种数据结构的理解加深了很多. >>数组.链表和哈希表结构 数据结构中有数组和链表来实现对数据 ...
- 什么是DMI,SMBIOS,符合SMBIOS规范的计算机的系统信息获取方法
转自:http://www.cnblogs.com/gunl/archive/2011/08/08/2130719.html DMI是英文单词Desktop Management Interface的 ...
- 【PHP&&MySQL详解】
PHP和MySQL是一对好搭档,PHP中有一个很大的扩展库专门针对对MySQL的操作.当然,作为一个PHP程序员,首先对MySQL的增删查改要非常熟悉才行. MySQL数据库的连接数大概在6w个左右, ...
- ytu 2558: 游起来吧!超妹!(水题,趣味数学题)
2558: 游起来吧!超妹! Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 7 Solved: 3[Submit][Status][Web Board ...
- PHPCMS V9 点击量排行调用方法
首先调用的标签代码如下: {pc:content action=”sitehits” siteid=”4″ num=”10″ order=”views DESC” cache=”3600″} {loo ...
- 阿里云(ECS)Centos服务器LNMP环境搭建
阿里云( ECS ) Centos7 服务器 LNMP 环境搭建 前言 第一次接触阿里云是大四的时候,当时在校外公司做兼职,关于智能家居项目的,话说当时俺就只有一个月左右的 php 后台开发经验(还是 ...