Unity学习之FSM有限状态机

前言:一个游戏里的一个人物会存在多种状态,那么就需要有一个专门管理这些状态的类。不然会显得杂乱无章,不易于后面状态的增加或者减少。
思路:既然要方便管理,那么首先肯定得有个系统类(专门用来存放所有的状态、状态的增删等功能);然后就是需要把所有的状态都单独写一个类(已达到修改某个状态的时候,其他状态不会受到影响)。

状态管理类:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FSMSystem
{
    private Dictionary<StateID, FSMState> states = new Dictionary<StateID, FSMState>();

    private StateID currentStateID;
    private FSMState currentFSMState;

    public void Update(GameObject npc)
    {
        currentFSMState.Act(npc);
        currentFSMState.Reason(npc);
    }

    /// <summary>
    /// 添加状态
    /// </summary>
    /// <param name="fSMState"></param>
    public void AddState(FSMState fSMState)
    {
        if (fSMState == null) return;

        //if (currentFSMState == null)
        //{
            currentStateID = fSMState.ID;
            currentFSMState = fSMState;
        //}
        if (states.ContainsKey(currentStateID)) return;
        states.Add(currentStateID, currentFSMState);
    }
    /// <summary>
    /// 删除状态
    /// </summary>
    /// <param name="stateID"></param>
    public void DeleteState(StateID stateID)
    {
        if (stateID == StateID.Null) return;
        if (!states.ContainsKey(stateID)) return;

        states.Remove(stateID);
    }

    /// <summary>
    /// 执行状态条件转换
    /// </summary>
    /// <param name="transition"></param>
    public void PerformTransition(Transition transition)
    {
        if (transition == Transition.NullTransition) return;

        StateID stateID = currentFSMState.GetStateID(transition);
        if (stateID == StateID.Null) return;
        if (!states.ContainsKey(stateID)) return;

        FSMState fSMState = states[stateID];
        currentFSMState.StateExit();
        currentFSMState = fSMState;
        currentStateID = fSMState.ID;
        currentFSMState.StateEnter();
    }
}

状态基类:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum Transition
{
    NullTransition,
    SeePlayer,//发现玩家
    LostPlayer,//玩家脱离视野范围
    AttackPlayer,//攻击玩家
}
public enum StateID
{
    Null,
    Chase,//追逐
    Patrol,//巡逻
    Attack,//攻击
}
public abstract class FSMState
{
    protected Transition transition;

    protected StateID stateID;
    protected FSMSystem fSM;
    public StateID ID
    {
        get { return stateID; }
    }

    protected Dictionary<Transition, StateID> dic = new Dictionary<Transition, StateID>();

    public FSMState(FSMSystem fSM)
    {
        this.fSM = fSM;
    }

    /// <summary>
    /// 增加状态
    /// </summary>
    /// <param name="transition"></param>
    /// <param name="stateID"></param>
    public void AddTransition(Transition transition, StateID stateID)
    {
        if (transition == Transition.NullTransition) return;
        if (stateID == StateID.Null) return;
        if (dic.ContainsKey(transition)) return;

        dic.Add(transition, stateID);
    }
    /// <summary>
    /// 删除状态
    /// </summary>
    /// <param name="transition"></param>
    public void DeleteTransition(Transition transition)
    {
        if (transition == Transition.NullTransition) return;
        if (!dic.ContainsKey(transition)) return;

        dic.Remove(transition);
    }
    /// <summary>
    /// 获取状态
    /// </summary>
    /// <param name="transition"></param>
    /// <returns></returns>
    public StateID GetStateID(Transition transition)
    {
        if (transition == Transition.NullTransition) return StateID.Null;
        if (!dic.ContainsKey(transition)) return StateID.Null;
        return dic[transition];
    }

    /// <summary>
    /// 进入状态
    /// </summary>
    public virtual void StateEnter() { }
    /// <summary>
    /// 退出状态
    /// </summary>
    public virtual void StateExit() { }

    /// <summary>
    /// 状态持续中,,,
    /// </summary>
    /// <param name="npc"></param>
    public abstract void Act(GameObject npc);
    /// <summary>
    /// 状态退出前,,,
    /// </summary>
    /// <param name="npc"></param>
    public abstract void Reason(GameObject npc);
}

巡逻状态:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 巡逻状态
/// </summary>
public class PatrolState : FSMState
{
    /// <summary>
    /// 巡逻路径点集合
    /// </summary>
    private Transform[] paths;
    /// <summary>
    /// 当前巡逻路径点索引
    /// </summary>
    private int index = 0;
    /// <summary>
    /// 移动速度
    /// </summary>
    private float moveSpeed = 0.5f;
    /// <summary>
    /// 玩家
    /// </summary>
    private Transform player;
    public PatrolState(FSMSystem fSM, Transform player) : base(fSM)
    {
        this.player = player;
        paths = GameObject.Find("Path").GetComponentsInChildren<Transform>();
        stateID = StateID.Patrol;
    }

    public override void Act(GameObject npc)
    {
        npc.transform.LookAt(paths[index].position);
        npc.transform.Translate(Vector3.forward * Time.deltaTime * moveSpeed);
        if (Vector3.Distance(npc.transform.position, paths[index].position) < 1)
        {
            index++;
            index %= paths.Length;
        }
    }

    public override void Reason(GameObject npc)
    {
        npc.GetComponent<Animator>().SetFloat("Speed", moveSpeed );
        if (Vector3.Distance(player.position, npc.transform.position) < 10)
        {
            fSM.PerformTransition(Transition.SeePlayer);
        }
    }
}

追逐状态:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 追逐状态
/// </summary>
public class ChaseState : FSMState
{
    /// <summary>
    /// 移动速度
    /// </summary>
    private float moveSpeed = 2f;
    /// <summary>
    /// 玩家
    /// </summary>
    private Transform player;
    public ChaseState(FSMSystem fSM, Transform player) : base(fSM)
    {
        stateID = StateID.Chase;
        this. player = player;
    }

    public override void Act(GameObject npc)
    {
        npc.transform.LookAt(player.position);
        npc.transform.Translate(Vector3.forward * Time.deltaTime * moveSpeed);
    }

    public override void Reason(GameObject npc)
    {
        npc.GetComponent<Animator>().SetFloat("Speed", moveSpeed / 2);
        if (Vector3.Distance(player.position, npc.transform.position) >= 10)
        {
            fSM.PerformTransition(Transition.LostPlayer);
        }
        else if (Vector3.Distance(player.position, npc.transform.position) <= 1f )
        {
            fSM.PerformTransition(Transition.AttackPlayer);
        }
    }
}

攻击状态:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 攻击状态
/// </summary>
public class AttackState : FSMState
{
    /// <summary>
    /// 玩家
    /// </summary>
    private Transform player;
    public AttackState(FSMSystem fSM, Transform player) : base(fSM)
    {
        stateID = StateID.Attack;
        this.player = player;
    }
    public override void Act(GameObject npc)
    {

    }

    public override void Reason(GameObject npc)
    {
        if (Vector3.Distance(player.position, npc.transform.position) > 1f)
        {
            if (Vector3.Distance(player.position, npc.transform.position) >= 10)
            {
                fSM.PerformTransition(Transition.LostPlayer);
            }
            else if (Vector3.Distance(player.position, npc.transform.position) < 10)
            {
                fSM.PerformTransition(Transition.SeePlayer);
            }
            return;
        }
        npc.GetComponent<Animator>().SetTrigger("Attack01");
    }
}

状态持有者实现类:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Enemy : MonoBehaviour
{
    private FSMSystem fSM;
    private Transform player;
    private void Start()
    {
        fSM = new FSMSystem();
        FSMState patrolState = new PatrolState(fSM, player);
        patrolState.AddTransition(Transition.SeePlayer, StateID.Chase);
        patrolState.AddTransition(Transition.AttackPlayer, StateID.Attack);
        //patrolState.AddTransition(Transition.LostPlayer, StateID.Patrol);

        FSMState chaseState = new ChaseState(fSM, player);
        chaseState.AddTransition(Transition.LostPlayer, StateID.Patrol);
        chaseState.AddTransition(Transition.AttackPlayer, StateID.Attack);
        //chaseState.AddTransition(Transition.SeePlayer, StateID.Chase);

        FSMState attackState = new AttackState(fSM, player);
        attackState.AddTransition(Transition.SeePlayer, StateID.Chase);
        attackState.AddTransition(Transition.LostPlayer, StateID.Patrol);
        //attackState.AddTransition(Transition.AttackPlayer, StateID.Attack);

        fSM.AddState(patrolState);
        fSM.AddState(chaseState);
        fSM.AddState(attackState);
    }

    private void Update()
    {
        fSM.Update(gameObject);
    }
}

到此这篇关于Unity学习之FSM有限状态机的文章就介绍到这了,更多相关UnityFSM有限状态机内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Unity Shader实现线框效果的制作步骤

    先上图看看效果: 下面详细分享一下制作步骤吧: 一.首先模型本身需要特殊处理 二.编写Shader shader "Giraffe/Wireframe" { properties{ _Color("Color",Color) = (1.0,1.0,1.0,1.0) _EdgeColor("Edge Color",Color) = (1.0,1.0,1.0,1.0) _EdgeColor2("Edge Color",Color)

  • 浅谈Unity脚本生命周期与执行顺序

    一.脚本生命周期 Unity脚本中的常见必然事件如下表所示 名称 触发时机 用途 Awake 脚本实例被创建时调用 用于游戏对象的初始化,注意Awake的执行早于所有脚本的Start函数 OnEnable 当对象变为可用或激活状态时被调用 用途 Start Update函数第一次运行之前调用 用于游戏对象的初始化 Update 每帧调用一次 用于更新游戏场景和状态 FixedUpdate 每个固定物理时间间隔调用一次 用于物理状态的更新 LateUpdate 每帧调用一次(在update之后调用

  • 打开一个Unity工程步骤

    首先,2018以上的支持UnityHub的,要先安装UnityHub,unity.cn去下载. 然后打开unity工程目录 在ProjectSettings下找到ProjectVersion.txt 打开该txt文件可以知道工程的unity版本 去unity.cn下载对应的unity版本,如果没有对应的版本可以下载比较接近的版本,例如2019.2.7f1的工程,可以用2019.2系列其他版本尝试打开,一般建议高本版unity打开低版本工程,不建议低版本unity打开高版本unity工程. 打开u

  • Unity连接MySQL并读取表格数据的实现代码

    表格如下: 在Unity读取并调用时的代码: 而如果想要查看该数据库中的另一个表,不是直接使用Table[1],而是需要更改SELECT * from <?>的表名 代码: using System.Collections; using System.Collections.Generic; using UnityEngine; using MySql.Data.MySqlClient; using System.Data; using System; public class getGame

  • Android实现Unity3D下RTMP推送的示例

    目录 数据采集推送 简单调用流程 完成接口初始化后,调用Push()接口 调用OpenPusher() InitAndSetConfig() ClosePusher() 相关Event处理 总结 关于屏幕采集,有两种方案: 1. 直接封装Android原生的屏幕采集工程,在unity提供接口,拿到屏幕权限后,获取屏幕数据并推送: 2. 如果只需要拿到Unity的窗体或摄像机数据推出去,可在Unity下获取到需要推送的原始数据,然后封装原生的RTMP推流接口,调用原生SDK实现数据推送,这种做法的

  • Unity学习之FSM有限状态机

    前言:一个游戏里的一个人物会存在多种状态,那么就需要有一个专门管理这些状态的类.不然会显得杂乱无章,不易于后面状态的增加或者减少. 思路:既然要方便管理,那么首先肯定得有个系统类(专门用来存放所有的状态.状态的增删等功能):然后就是需要把所有的状态都单独写一个类(已达到修改某个状态的时候,其他状态不会受到影响). 状态管理类: using System.Collections; using System.Collections.Generic; using UnityEngine; public

  • JS前端实现fsm有限状态机实例详解

    目录 引言 举个栗子 从零开始 获取状态 状态改变 transition 实现fsm状态机 实现钩子函数 完整代码 总结 引言 我们平时开发时本质上就时对应用程序的各种状态进行切换并作出相应处理,最直接的方法就是添加标志位然后考虑所有可能出现的边界问题,通过if...else if...else 来对当前状态进行判断从而达成页面的交互效果, 但随着业务需求的增加各种状态也会随之增多,我们就不得不再次修改if...else代码或者增加对应的判断,最终使得程序的可读性.扩展性.维护性变得很麻烦 有限

  • unity学习教程之定制脚本模板示例代码

    1.unity的脚本模板 新版本unity中的C#脚本有三类,第一类是我们平时开发用的C# Script:第二类是Testing,用来做单元测试:第三类是Playables,用作TimeLine中管理时间线上每一帧的动画.声音等.我们点击创建脚本时,会自动生成unity内置的一套模板: using System.Collections; using System.Collections.Generic; using UnityEngine; public class NewBehaviourSc

  • Unity 按钮事件封装操作(EventTriggerListener)

    我就废话不多说了,大家还是直接看代码吧~ using UnityEngine; using UnityEngine.EventSystems; namespace Mx.UI { public class EventTriggerListener :UnityEngine.EventSystems.EventTrigger { public delegate void VoidDelegate(GameObject go); public VoidDelegate onClick; public

  • Linux有限状态机FSM的理解与实现

    有限状态机(finite state machine)简称FSM,表示有限个状态及在这些状态之间的转移和动作等行为的数学模型,在计算机领域有着广泛的应用.FSM是一种逻辑单元内部的一种高效编程方法,在服务器编程中,服务器可以根据不同状态或者消息类型进行相应的处理逻辑,使得程序逻辑清晰易懂. 那有限状态机通常在什么地方被用到? 处理程序语言或者自然语言的 tokenizer,自底向上解析语法的parser, 各种通信协议发送方和接受方传递数据对消息处理,游戏AI等都有应用场景. 状态机有以下几种实

  • 学习JavaScript设计模式之状态模式

    状态模式的关键是区分事物内部的状态,事物内部状态的改变往往会带来事物的行为改变. 当电灯开着,此时按下开关,电灯会切换到关闭状态:再按一次开关,电灯又将被打开.同一个开关在不同的状态下,表现出来的行为是不一样的. 一.有限状态机 状态总数(state)是有限的. 任一时刻,只处在一种状态之中. 某种条件下,会从一种状态转变(transition)到另一种状态. 允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类. 解释: (1)将状态封装成独立的类,并将请求委托给当前的状态对

  • C#中Socket与Unity相结合示例代码

    前言 初步接触了Socket,现使其与Unity相结合,做成一个简单的客户端之间可以互相发送消息的一个Test.下面话不多说了,来一起看看详细的介绍吧. 方法如下: 首先,是服务端的代码. 创建一个连接池,用于存储客户端的数量. using System; using System.Net; using System.Net.Sockets; using System.Collections; using System.Collections.Generic; namespace Server

  • C++有限状态机实现计算器小程序

    本文介绍利用有限状态机原理开发计算器小程序的过程. 实现的功能 支持整数.小数输入 支持+ - * / 四则运算 CE 清除当前操作数 C 清除所有.回到初始状态 回显操作数和结果 HSM状态图 计算器可以分为七种状态:Start.Operand_1.Negate_1.Operator.Operand_2.Negate_2.Error.其中Start.Operand_1.Operand_1状态又分了几种子状态. 下面简要的介绍下状态状态转换的过程: 启动软件,进入Start状态 当用户点击1-9

  • unity实现摄像头跟随

    代码很简单,这里就不多废话了,直接奉上代码 using UnityEngine; using System.Collections; public class FllowTarget : MonoBehaviour { public Transform character; //摄像机要跟随的人物 public float smoothTime = 0.01f; //摄像机平滑移动的时间 private Vector3 cameraVelocity = Vector3.zero; private

  • javascript与有限状态机详解

    简单说,它有三个特征: 复制代码 代码如下: * 状态总数(state)是有限的.* 任一时刻,只处在一种状态之中.* 某种条件下,会从一种状态转变(transition)到另一种状态. 它对JavaScript的意义在于,很多对象可以写成有限状态机. 举例来说,网页上有一个菜单元素.鼠标悬停的时候,菜单显示:鼠标移开的时候,菜单隐藏.如果使用有限状态机描述,就是这个菜单只有两种状态(显示和隐藏),鼠标会引发状态转变. 代码可以写成下面这样: 复制代码 代码如下: var menu = { //

随机推荐