什么是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. 关于ios 的耳机线线控

    如题 线控主要就是指的用ios耳机的  中间那个键来自定义一些程序自己的事件(比如玩吃鸡的时候,作为开枪扳机) 而想要控制耳机的音量加减的事件, 有一种方法,就是监听 系统音量的变化,根据音量变化来触 ...

  2. project的操作说明

    project 1 操作的步骤 设定一个起始时间:7月1号 安排好摘要(任务)的先后顺序 一个一个任务的来:A任务,下面有几个分布实现的部门: 设计部门 3个工作日 程序部门 1个工作日.然后配置相互 ...

  3. vue2.0使用基础

    开发情况下需要引入vue.js和vue-resource.js,el:dom生效范围,data,dom静态数据,mounted:初始化调用方法,注意,官方文档需要添加this.$nextTict(fu ...

  4. 2019杭电多校第三场hdu6609 Find the answer(线段树)

    Find the answer 题目传送门 解题思路 要想变0的个数最少,显然是优先把大的变成0.所以离散化,建立一颗权值线段树,维护区间和与区间元素数量,假设至少减去k才能满足条件,查询大于等于k的 ...

  5. Java中的注解是如何工作的?

    自Java5.0版本引入注解之后,它就成为了Java平台中非常重要的一部分.开发过程中,我们也时常在应用代码中会看到诸如@Override,@Deprecated这样的注解.这篇文章中,我将向大家讲述 ...

  6. 快速高效学习Java编程在线资源Top 20(转载)

    想要加强你的编程能力吗?想要提升你的 Java 编程技巧和效率吗? 不用担心.本文将会提供快速高效学习 Java 编程的 50 多个网站资源: 开始探索吧: 1.MKyong:许多开发者在这里可以找到 ...

  7. 209. Minimum Size Subarray Sum【滑动窗口】

    Given an array of n positive integers and a positive integer s, find the minimal length of a contigu ...

  8. Ansible角色

    Ansible角色介绍 官方地址: https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html 角色目录 ...

  9. day01 mysql认识 安装 配置 起服务 密码 字符集 用户授权

    day01 mysql      一.认识mysql     关系型数据库:         最流行的关系型数据库管理系统,支持大型数据库,处理上千万条记录         关系型: oracle,  ...

  10. Mysql流程解析

    Mysql流程解析 流程图 流程图解析 客户端发送一条sql语句. 1.此时,mysql会检查sql语句,查看是否命中缓存,如果命中缓存,直接返回结果,不继续执行.没有命中则进入解析器. 2.解析器会 ...