什么是FSM

FSM 即有限状态机,它是一个状态管理系统,表示一个对象的几种状态在指定条件下转移行为,即随着条件的不断改变内部状态不断地切换。

FSM用处或者使用背景

通常使用FSM去实现一些简单的AI逻辑,对于游戏中的每个对象都可以在其生命周期中分出一些状态,比如一个小兵,他可能在休息,或者巡逻,当敌人出现时,他的状态可能切换为追逐敌人或者攻击敌人,当某些条件成立时,状态机从当前状态转移到下一状态,在不同状态下有不同的任务,所以要使用有限状态机去实现。

FSM使用的必要性

当需要实现角色的状态时以及状态间的切换时,相信第一时间想到的应该是if-else,但是如果状态的切换条件表达式过于复杂,if-else就显得臃肿麻烦了。再加上所有条件的判断全在一起,状态一多也容易出现Bug,扩展也不好,使用if-else就会相当臃肿,所以在这种条件下,有限状态机的使用很有必要,当然如果相对简单的状态切换,使用有限状态机就没必要了,需要根据个人需求。

FSM使用注意点

FSM有两个重要的概念:状态和转移,转移是切换条件,必须有一个初始状态,并保存当前状态,以及注意每种状态的转移的必要条件。

FSM优点

使整个状态切换逻辑比较清晰,增强代码的可扩展性,也便于后期维护。

具体实现代码

下面就实现一下FSM,使用怪物AI的例子

1.创建状态基类FSMstate  ,此类负责处理一个状态的周期,状态的进入前,状态中,离开状态等。以及状态切换条件的增删。具体如下:

 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 状态ID
/// </summary>
public enum StateID
{
NoneStateID,
Parol,//巡逻状态
Chase,//追逐状态
}
/// <summary>
/// 状态切换条件
/// </summary>
public enum Transition
{
NoneTransition,
SeePlayer,//看到玩家
LosePlayer,//看不到玩家
} /// <summary>
/// FSM中状态基类(实现状态的基本方法)
/// </summary>
public abstract class FSMstate { protected StateID stateID;//状态对应的ID
public StateID ID { get { return stateID; } }
protected Dictionary<Transition, StateID> Transition_StateIDDic = new Dictionary<Transition, StateID>();//存储转换条件和状态的ID
protected FSMsystem fsmSystem;//管理状态对象(因为要状态更新需要通过FSMsystem去管理实现的,所以需要一个管理对象) public FSMstate(FSMsystem fsm)
{
this.fsmSystem = fsm;
}
/// <summary>
///增加转条件
/// </summary>
/// <param name="trans"></param>
/// <param name="id"></param>
public void AddTransition(Transition trans,StateID id)
{
if (trans==Transition.NoneTransition)
{
Debug.Log("添加的转换条件不能为null");
return;
}
if (id==StateID.NoneStateID)
{
Debug.Log("添加的状态ID不能为null");
return;
}
if (Transition_StateIDDic.ContainsKey(trans))
{
Debug.Log("添加转换条件的时候," + trans + "已经存在于Transition_StateIDDic中");
return;
}
Transition_StateIDDic.Add(trans,id);
}
/// <summary>
/// 删除转换条件
/// </summary>
/// <param name="trans"></param>
public void DeleteTransition(Transition trans)
{
if (trans == Transition.NoneTransition)
{
Debug.Log("删除的转换条件不能为null");
return;
}
if (!Transition_StateIDDic.ContainsKey(trans))
{
Debug.Log("删除转换条件的时候," + trans + "不存在于Transition_StateIDDic中");
return;
}
Transition_StateIDDic.Remove(trans);
}
/// <summary>
/// 根据转换条件获得状态ID
/// </summary>
/// <param name="trans"></param>
/// <returns></returns>
public StateID GetStateID(Transition trans)
{
if (Transition_StateIDDic.ContainsKey(trans))
{
return Transition_StateIDDic[trans];
}
return StateID.NoneStateID;
} /// <summary>
///转换到此状态前要执行的逻辑
/// </summary>
public virtual void DoBeforeEnterAcion() { }
/// <summary>
/// 离开此状态前要执行的逻辑
/// </summary>
public virtual void DoAfterLevAction(){ }
/// <summary>
/// 处在本状态时要执行的逻辑
/// </summary>
/// <param name="TargetObj"></param>
public abstract void CurrStateAction(GameObject TargetObj);
/// <summary>
/// 切换到下一状态需要执行的逻辑
/// </summary>
/// <param name="TargetObj"></param>
public abstract void NextStateAction(GameObject TargetObj); }

2. 创建状态管理类FSMSystem的创建。用来管理所有的状态(状态的添加,删除,切换,更新等)。

 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 状态处理类(添加,删除,切换,更新等管理所有状态)
/// </summary>
public class FSMsystem { private Dictionary<StateID, FSMstate> StateDic = new Dictionary<StateID, FSMstate>();//保存状态ID以及ID对应的状态
private StateID _CurrentStateID;//当前处于的状态ID
private FSMstate _CurrentState;//当前处于的状态 /// <summary>
/// 添加状态
/// </summary>
/// <param name="state">需管理的状态</param>
public void AddState(FSMstate state) {
if (state==null)
{
Debug.Log("添加的状态"+state+"不能为null");
return;
}
if (_CurrentState==null)
{
_CurrentState = state;
_CurrentStateID = state.ID;
}
if (StateDic.ContainsKey(state.ID))
{
Debug.Log("状态机 "+state.ID+"已经存在,无法添加");
return;
}
StateDic.Add(state.ID,state);
}
/// <summary>
/// 删除状态
/// </summary>
/// <param name="stateID">删除要管理状态的ID</param>
public void DeleteState(StateID stateID)
{
if (stateID==StateID.NoneStateID)
{
Debug.Log("无法删除Null的状态");
return;
}
if (!StateDic.ContainsKey(stateID) )
{
Debug.Log("无法删除不存在的状态:" + stateID);
return;
}
StateDic.Remove(stateID);
}
/// <summary>
/// 状态转换(状态的切换是根据转换条件的变化)
/// </summary>
/// <param name="trans">转换条件</param>
public void PerformTranstion(Transition trans)
{
if (trans == Transition.NoneTransition)
{
Debug.Log("无法执行NULL的转换条件");
return;
}
StateID stateId = _CurrentState.GetStateID(trans);
if (stateId==StateID.NoneStateID)
{
Debug.Log("要转换的状态ID为null");
return;
}
if (!StateDic.ContainsKey(stateId))
{
Debug.Log("状态机中没找到状态ID "+stateId+" 无法转换状态");
return;
}
FSMstate state = StateDic[stateId];//根据状态ID获取要转换的状态
_CurrentState.DoAfterLevAction();//执行离开上一状态逻辑
_CurrentState = state;//更新当前状态
_CurrentStateID = stateId;//更新当前状态ID
_CurrentState.DoBeforeEnterAcion();//执行进入当前状态前要执行的逻辑
} /// <summary>
/// 更新当前状态行为
/// </summary>
/// <param name="TargetObj"></param>
public void UpdateState(GameObject TargetObj)
{
_CurrentState.CurrStateAction(TargetObj);
_CurrentState.NextStateAction(TargetObj);
} }

3.那我们用怪物巡逻,追逐玩家的例子来实现状态机。

3.1. 增加一个PartalState类,用怪物的巡逻。

 using System.Collections;
using System.Collections.Generic;
using UnityEngine; /// <summary>
/// 怪物巡逻状态类
/// </summary>
public class PatrolState : FSMstate {
private List<Transform> path = new List<Transform>();//巡逻点
private int index = ;
private Transform PlayerTrasform; /// <summary>
/// 初始化巡逻状态数据
/// </summary>
/// <param name="fsm"></param>
public PatrolState(FSMsystem fsm):base(fsm)
{
stateID = StateID.Parol;
//路点
Transform pathTransform = GameObject.Find("Path").transform;
Transform[] children = pathTransform.GetComponentsInChildren<Transform>();
foreach (Transform child in children)
{
if (child != pathTransform)
{
path.Add(child);
}
}
PlayerTrasform = GameObject.Find("Player").transform;
} /// <summary>
/// 当前状态(巡逻状态)执行的逻辑
/// </summary>
/// <param name="TargetObj"></param>
public override void CurrStateAction(GameObject TargetObj)
{
TargetObj.transform.LookAt(path[index].position);
TargetObj.transform.Translate(Vector3.forward * Time.deltaTime * );
if (Vector3.Distance(TargetObj.transform.position, path[index].position) < )
{
index++;
index %= path.Count;
}
}
/// <summary>
/// 切换到追逐状态(下一状态)执行的的逻辑
/// </summary>
/// <param name="TargetObj"></param>
public override void NextStateAction(GameObject TargetObj)
{
if (Vector3.Distance(PlayerTrasform.position, TargetObj.transform.position) < )
{
fsmSystem.PerformTranstion(Transition.SeePlayer);
}
} }

3.2.增加一个ChaseState类,用怪物的追逐。

 using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine; /// <summary>
/// 追逐状态类
/// </summary>
public class ChaseState : FSMstate { private Transform PlayerTransForm;//玩家位置信息 public ChaseState(FSMsystem fsm):base(fsm)
{
stateID = StateID.Chase;
PlayerTransForm = GameObject.Find("Player").transform;
}
/// <summary>
/// 追逐状态下执行的逻辑
/// </summary>
/// <param name="targrtObj"></param>
public override void CurrStateAction(GameObject targrtObj)
{
targrtObj.transform.LookAt(PlayerTransForm.transform.position);
targrtObj.transform.Translate(Vector3.forward**Time.deltaTime);
}
/// <summary>
/// 切换到下一状态(巡逻)前要执行的逻辑
/// </summary>
/// <param name="targrtObj"></param>
public override void NextStateAction(GameObject targrtObj)
{
if (Vector3.Distance(PlayerTransForm.position,targrtObj.transform.position)>)
{
fsmSystem.PerformTranstion(Transition.LosePlayer);
}
}
}

3.3 创建一个控制怪物的脚本Enemy

 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 怪物类
/// </summary>
public class Enemy : MonoBehaviour { private FSMsystem fsmsystem; //在Enemy类中,实例化FSMSystem对象,添加巡逻和追逐状态,还有之间的转换条件
void Start () {
InitFsm();//创建状态机
} void Update () {
fsmsystem.UpdateState(this.gameObject);//检查更新状态
}
/// <summary>
/// 创建状态机
/// 怪物有两种状态分别是巡逻和追逐玩家
/// 如果怪物初始状态(设置为Parol状态)一旦SeePlayer 切换状态被激活后,就切换到Chase状态
/// 如果他在Chase状态一旦LosePlayer状态被激活了,它就转变到Parol状态
/// </summary>
void InitFsm()
{
fsmsystem = new FSMsystem();
FSMstate PatrolState = new PatrolState(fsmsystem);
PatrolState.AddTransition(Transition.SeePlayer, StateID.Chase);
FSMstate ChaseState = new ChaseState(fsmsystem);
ChaseState.AddTransition(Transition.LosePlayer, StateID.Parol);
fsmsystem.AddState(PatrolState);//初始状态
fsmsystem.AddState(ChaseState);
}
}

以上就用FSM有限状态机实现了怪物AI,具体代码中都有注释,有理解不到位的地方,望请指正。

Unity中FSM有限状态机的更多相关文章

  1. Unity——FSM有限状态机

    FSM有限状态机 一.设计思路 1.共同的状态父类,提供可重写的进入,保持,退出该状态的生命周期方法: 2.状态机,管理所有状态(增删查改),状态机运行方法(Run): 3.在角色控制器中,实例化状态 ...

  2. FSM有限状态机

    1.什么是有限状态机 有限状态机(Finite State Machine),简称FSM,它由一组有限个状态.输入和根据输入及现有状态转换为下一个状态的转换函数组成,当然,通常每个状态机都必须有一个初 ...

  3. Unity中使用Attribute

    Attribute是c#的语言特性 msdn说明如下: The Attribute class associates predefined system information or user-def ...

  4. 在Unity中使用UGUI修改Mesh绘制几何图形

    在商店看到这样一个例子,表示很有兴趣,他们说是用UGUI做的.我想,像这种可以随便变形的图形,我第一个就想到了网格变形. 做法1: 细心的朋友应该会发现,每个UGUI可见元素,都有一个‘Canvas ...

  5. Unity中使用WebView

    Unity中使用WebView @(设计) 需求,最近游戏中需要引入H5直播页面和更新比较频繁的赛事页面,需求包括:加密传参数.和Unity交互,在Unity框架下其实有几种方案: 内置函数Appli ...

  6. Unity中创建二维码

    在网络上发现了一个可以把字符串转换成二维码的dll,但是我们要怎么使用他呢.不废话,直接进入主题. 用到的引用 using UnityEngine;using ZXing;using ZXing.Qr ...

  7. 在Unity中创建可远程加载的.unity3d包

    在一个Unity项目中,发布包本身不一定要包括所有的Asset(译为资产或组件),其它的部分可以单独发布为.unity3d,再由程序从本地/远程加载执行,这部分不在本文讨论范围.虽然Unity并没有直 ...

  8. 【原创翻译】初识Unity中的Compute Shader

    一直以来都想试着自己翻译一些东西,现在发现翻译真的很不容易,如果你直接把作者的原文按照英文的思维翻译过来,你会发现中国人读起来很是别扭,但是如果你想完全利用中国人的语言方式来翻译,又怕自己理解的不到位 ...

  9. 【unity shaders】:Unity中的Shader及其基本框架

    shader和Material的基本关系 Shader(着色器)实际上就是一小段程序,它负责将输入的Mesh(网格)以指定的方式和输入的贴图或者颜色等组合作用,然后输出.绘图单元可以依据这个输出来将图 ...

随机推荐

  1. 关于iframe定位碰到的问题。

    这几天在做自动化测试的时候发现一个问题,用JQUERY定位时,总是报错,MES:JQUERY IS NOT DEFINED. 检查自己定位没有问题,后来想是不是语法出了问题. 切换成XPATH来定位, ...

  2. CSS:CSS 图像拼合技术

    ylbtech-CSS:CSS 图像拼合技术 1.返回顶部 1. CSS 图像拼合技术 图像拼合 图像拼合就是单个图像的集合. 有许多图像的网页可能需要很长的时间来加载和生成多个服务器的请求. 使用图 ...

  3. linux进阶之路(三):vi/vim编辑器

    所有Linux都会内置vi,vim是vi的增强版本,被誉为"编辑之神",玩转vim可以让你完全脱离鼠标. vim可以分为两种模式: 普通模式:使用vim 文件名,进入普通模式.普通 ...

  4. QinQ 技术解析

    目录 1. 概述 2. QinQ 的产生背景 3. QinQ的作用及原理 4. QinQ 技术的优点 5. QinQ 的报文结构 6. QinQ的实现方式 (1) 基本QinQ (2) 灵活QinQ ...

  5. Bochs调试加载符号文件的问题

    1. Bochs中的调试命令ldsym没有触发的情况. 参考:http://www.ibm.com/developerworks/cn/linux/sdk/lex/ Lex 代表 Lexical An ...

  6. java-day18

    函数式接口在java中指:有且仅有一个抽象方法的接口 @FunctionalInterface注解:可以检测接口是否是一个函数式接口 函数式接口的使用:一般可以作为方法的参数和返回值类型 自定义函数式 ...

  7. pytest----fixture(1)--使用fixture执行配置及销毁逻辑

    1使用fixture执行配 置及销毁;非常灵活 使用. 2数据共享:在 conftest.py配置里写方 法可以实现数据共享, 不需要import导入.可 以跨文件共享 3scope的层次及神 奇的y ...

  8. pytest-文件名类名方法名执行部分用例

    pytest test_class_01.py 执行文件名 pytest -v -s test_class_01.py 执行文件名 pytest -v test_class_01.py::TestCl ...

  9. Git部署项目

    前言 学习Git的目标是要学会如何部署项目.Git是一种版本控制系统,与之对应的还有CVS和SVN,其中CVS和SVN都是集中式的版本控制,而Git是一种分布式的,在现代项目开发中,大多使用Git来管 ...

  10. Android开发 MediaPlayer播放本地视频完善的demo(只是代码记录)

    xml <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.w ...