Unity实现一个简单的状态机Demo

有时候在程序开发中,我们会经常碰到一些流程逻辑相关的问题,这个时候我们如果没有一个好的框架和方案的话,那么流程管理就很麻烦,这时候状态机的出现就使问题简单化啦,下面让我们来实现一个简单的状态机吧。

状态节点接口:

public interface IStateNode
{
    void OnCreate(StateMachine stateMachine);
    void OnEnter();
    void OnUpdate();
    void OnExit();  
}

接下来就是主角,我们的状态机管理类,也可以称之为操作手吧

using System;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEditor.Experimental.GraphView;

public class StateMachine
{
    private readonly Dictionary<string,IStateNode> _nodes=new Dictionary<string,IStateNode>(100);
    private IStateNode _curNode;
    private IStateNode _preNode;
    /// <summary>
    /// 状态机持有者
    /// </summary>
    public System.Object Owner { private set; get; }
    /// <summary>
    /// 当前节点的名称
    /// </summary>
    public string CurrentNode
    {
        get
        {
            return _curNode!=null?_curNode.GetType().FullName:string.Empty;
        }
    }
    /// <summary>
    /// 之前运行的节点名称
    /// </summary>
    public string PreviousNode
    {
        get
        {
            return _preNode != null ? _preNode.GetType().FullName : string.Empty;
        }
    }
    private StateMachine() { }
    public StateMachine(System.Object owner)
    {
        Owner = owner;
    }
    /// <summary>
    /// 更新状态机
    /// </summary>
    public void Update()
    {
        if (_curNode != null)
        {
            _curNode.OnUpdate();
        }
    }
    public void Run<TNode>() where TNode : IStateNode
    {
        var nodeType = typeof(TNode);
        var nodeName = nodeType.FullName;
        Run(nodeName);
    }
    public void Run(Type entryNode)
    {
        var nodeName = entryNode.FullName;
        Run(nodeName);
    }
    public void Run(string entryNode)
    {
        _curNode = TryGetNode(entryNode);
        _preNode = _curNode;
        if (_curNode == null)
            throw new Exception($"Not found entry node:{entryNode}");
        _curNode.OnEnter();
    }
    public void AddNode<TNode>() where TNode: IStateNode, new()
    {
        TNode stateNode = new TNode();
        AddNode(stateNode);
    }
    /// <summary>
    /// 加入一个节点
    /// </summary>
    /// <param name="stateNode"></param>
    /// <exception cref="ArgumentException"></exception>
    public void AddNode(IStateNode stateNode)
    {
        if (stateNode == null)
            throw new ArgumentException();
        var nodeType = stateNode.GetType();
        var nodeName = nodeType.FullName;
        if (_nodes.ContainsKey(nodeName) == false)
        {
            stateNode.OnCreate(this);
            _nodes.Add(nodeName, stateNode);
        }
        else
        {
            UnityEngine.Debug.LogError($"State node already existed: {nodeName}");
        }
    }
    public void ChangeState<TNode>() where TNode : IStateNode
    {
        var nodeType = typeof(TNode);
        var nodeName = nodeType.FullName;
        ChangeState(nodeName);
    }
    public void ChangeState(Type nodeType)
    {
        var nodeName = nodeType.FullName;
        ChangeState(nodeName);
    }
    public void ChangeState(string nodeName)
    {
        if (string.IsNullOrEmpty(nodeName))
            throw new ArgumentNullException();
        IStateNode node = TryGetNode(nodeName);
        if (node == null)
        {
            UnityEngine.Debug.LogError($"Can not found state node:{nodeName}");
            return;
        }
        UnityEngine.Debug.Log($"{_curNode.GetType().FullName}-->{node.GetType().FullName}");
        _preNode = _curNode;
        _curNode.OnExit();
        _curNode = node;
        _curNode.OnEnter();
    }
    private IStateNode TryGetNode(string nodeName)
    {
        _nodes.TryGetValue(nodeName, out IStateNode result);
        return result;
    }
}

因为每一个节点都要在Create的时候记录状态机对象,这时候我实现了一个简单的节点基类,在Create的时候实现统一记录自己的状态机对象,其他状态机节点都继承这个节点基类,简化了每一个状态节点都需要在创建的时候保存状态机管理类的问题。

public class StateNodeBase : IStateNode
{
    protected StateMachine _machine;
    public virtual void OnCreate(StateMachine stateMachine)
    {
        _machine = stateMachine;
    }

    public virtual void OnEnter()
    {
    }
    public virtual void OnUpdate()
    {

    }
    public virtual void OnExit()
    {
    }
}

最后让我们来看看几个简答的状态机节点吧,

public class FirstStep : StateNodeBase
{
    public override void OnCreate(StateMachine stateMachine)
    {
        base.OnCreate(stateMachine);
    }
    public override void OnExit()
    {
        base.OnExit();
        _machine.ChangeState<SecondStep>();
    }
}
public class SecondStep : StateNodeBase
{
    public override void OnCreate(StateMachine stateMachine)
    {
        base.OnCreate(stateMachine);
    }
    public override void OnExit()
    {
        base.OnExit();
        _machine.ChangeState<ThirdStep>();
    }
}
public class ThirdStep : StateNodeBase
{
    public override void OnCreate(StateMachine stateMachine)
    {
        base.OnCreate(stateMachine);
    }
    public override void OnExit()
    {
        base.OnExit();
        _machine.ChangeState<FourthStep>();
    }
}
public class FourthStep : StateNodeBase
{
    public override void OnCreate(StateMachine stateMachine)
    {
        base.OnCreate(stateMachine);
    }
    public override void OnExit()
    {
        base.OnExit();
        _machine.ChangeState<FirstStep>();
    }
}

好了,最基本的状态机框架我们已经搭建完成了,这时候就是最关键的如何去开启这个状态机和使用他了,因为我们是在Unity中实现,所以需要一个MonoBehaviour来启动他。


public class GameProcessCtrl:MonoBehaviour
{
        private StateMachine _machine;
        private void Start()
        {
            InitStateMachine();
        }

        private void InitStateMachine()
        {
            _machine = new StateMachine(this);
            _machine.AddNode<FirstStep>();
            _machine.AddNode<SecondStep>();
            _machine.AddNode<ThirdStep>();
            _machine.AddNode<FourthStep>();
            _machine.Run<FirstStep>();
        }
        private void Update()
        {
            if (_machine != null)
            {
                _machine.Update();
            }
        }
}

好了,到这里一个简单的状态机就实现完成啦,在这个实例中我们默认启动了第一阶段的阶段,之后在退出的时候分别切换到下一节点,当然我们也可以在每一个节点运行的时候进行判断,满足某种条件的时候直接跳转其他节点,这些都是要根据具体内容自己来设计规划啦。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/573868.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JavaWeb--04YApi,Vue-cli脚手架Node.js环境搭建,创建第一个Vue项目

04 1 Yapi2 Vue-cli脚手架Node.js环境搭建配置npm的全局安装路径 3 创建项目&#xff08;这个看下一篇文章吧&#xff09; 1 Yapi 前后端分离中的重要枢纽"接口文档",以下一款为Yapi的接口文档 介绍&#xff1a;YApi 是高效、易用、功能强大的 api 管理平台&#…

Hive主要介绍

Hive介绍 hive是基于 Hadoop平台操作 HDFS 文件的插件工具 可以将结构化的数据文件映射为一张数据库表 可以将 HQL 语句转换为 MapReduce 程序 1.hive 是由驱动器组成&#xff0c;驱动器主要由4个组件组成&#xff08;解析器、编译器、优化器、执行器&#xff09; 2.hive本身不…

递归排列枚举(c++)

全部排列问题 输入 n 输出 1…n 个数的全部排列。全部排列中&#xff0c;数字可以重复 。 例如 输入 3 输出全部排列的结果如下&#xff1a;111、112、113、121、122、123、131、132、133、211、212、213、221、222、223、231、232、233、311、312、313、321、322、323、33…

4.18.2 EfficientViT:具有级联组注意力的内存高效Vision Transformer

现有Transformer模型的速度通常受到内存低效操作的限制&#xff0c;尤其是MHSA&#xff08;多头自注意力&#xff09;中的张量整形和逐元素函数。 设计了一种具有三明治布局的新构建块&#xff0c;即在高效FFN&#xff08;前馈&#xff09;层之间使用单个内存绑定的MHSA&#x…

浅谈数据模型

1&#xff1a;事实表和维表的概述 前言&#xff1a;数据仓库是一种用于存储和管理大量数据的技术。其中&#xff0c;事实表和维表是数据仓库中的两个重要概念&#xff0c;首先了解一下事实表和维度表 1.事实表&#xff1a;是指用于存储测量“事实数据”的表&#xff0c;事实数…

Unity 异常 bug

OverlapBoxNonAlloc 使用bug 环境&#xff1a; Unity2021.3.15 在测试场景中使用 OverlapBoxNonAlloc 测试检测没有问题 但是到了真实应用场景&#xff0c;使用 OverlapBoxNonAlloc 检测移动中的小怪 小怪碰撞体为&#xff1a;带有 Rigidbody 的Circle Collider 2D 就会出现异…

Java虚拟机(jvm)常见问题总结

1.电脑怎样认识我们编写的Java代码 首先先了解电脑是二进制的系统&#xff0c;他只认识 01010101比如我们经常要编写 HelloWord.java 电脑是怎么认识运行的HelloWord.java是我们程序员编写的&#xff0c;我们人可以认识&#xff0c;但是电脑不认识 Java文件编译的过程 1. 程…

代码随想录(番外)图论3|1020. 飞地的数量|130. 被围绕的区域

代码随想录&#xff08;番外&#xff09;图论3|1020. 飞地的数量|130. 被围绕的区域 1020. 飞地的数量 class Solution { public:int dir[4][2]{0,1,1,0,0,-1,-1,0};int count;void dfs(vector<vector<int>>& grid,int x,int y){grid[x][y]0;count;for(int i…

大数据开发详解

点击下载《大数据开发详解》 1. 前言 随着信息化时代的快速发展&#xff0c;大数据已经成为了企业和组织不可或缺的重要资源。大数据开发则是指通过一系列技术手段&#xff0c;对海量数据进行收集、存储、处理、分析和挖掘&#xff0c;以实现数据的价值化利用。大数据开发涉及…

哈希表练习题

前言 本次博客将要写一写&#xff0c;哈希表的一些使用 哈希表主要是一个映射&#xff0c;比如数组就是一个哈希表 是一个整型对应另一个整型&#xff0c;介绍的哈希表还是要以写题目为例 第一题 242. 有效的字母异位词 - 力扣&#xff08;LeetCode&#xff09; 直接来看…

C# 给图片添加文字水印

目录 应用场景 开发运行环境 方法说明 方法代码 调用示例 小结 应用场景 在某些应用项目&#xff08;如电子档案信息管理&#xff09;中&#xff0c;查看电子图片信息是经常使用到的功能&#xff0c;此时我们就需要给显示在浏览器中的图片添加文字水印版权或提示信息。…

Java面试八股之Java中==和equals()的区别

Java中和equals()的区别 操作符&#xff1a; 对于基本数据类型&#xff08;如int、char、boolean等&#xff09;&#xff0c;比较的是它们的值是否相等。 对于对象引用类型&#xff0c;比较的是两个对象的内存地址&#xff08;即是否指向同一个对象实例&#xff09;。也就是…

Jetbrains Fleet这十个快捷键,效率提高50倍

当我们无法解决一段感情中的问题 就会选择解决这段感情 如果真诚不得到回应 那么再热情的人 也会沉默 很多人对你感兴趣 却没有人执着于你 我们知道任何一款牛批的IDE 都是有很多快捷键的,但是我们没有superpower ,不能记住所有的快捷键。 所以下面就总结了使用fleet 过…

电磁兼容(EMC):静电放电(ESD)抗扰度试验深度解读(七)

目录 1. 第一步 确定电磁环境 2. 第二步 确认设备工作状态 3. 第三步 制定试验计划 4. 间接施加的放电 4.1 水平耦合板 4.2 垂直耦合板 静电抗扰度的试验测试细节对测试结果影响比较大&#xff0c;本文详细介绍静电抗扰度试验的测试程序和注意事项。 1. 第一步 确定电磁…

PostgreSQL的学习心得和知识总结(一百三十九)|深入理解PostgreSQL数据库GUC参数 allow_alter_system 的使用和原理

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

【学习】​CSMM和CMMI的关系你了解吗

CMMI和CSMM都是评估和提升软件组织能力成熟度的模型&#xff0c;但它们在起源、应用范围、模型结构和实施目的等方面存在一些区别。在当今竞争激烈的软件市场中&#xff0c;提升软件能力成为了多数组织追求成功的关键因素。而选择适合的体系标准能够助力企业发展得更加迅速。作…

企业实施定制鞋厂ERP软件需要注意哪些问题?

企业实施定制鞋厂ERP软件是个复杂的管理系统工程&#xff0c;为了成功地为企业定制实施ERP软件&#xff0c;需要注意和解决几个关键的问题&#xff1a; . 确立ERP系统实施和定制的决策者&#xff1b;. 做好前期咨询与调研工作&#xff1b;. 做好系统产品或项目迭代规划&#x…

【MySQL 数据宝典】【内存结构】- 003 Change Buffer 详解

一、 Change Buffer基本概念 Change Buffer&#xff1a;写缓冲区,是针对二级索引(辅助索引) 页的更新优化措施。 作用: 在进行DML操作时&#xff0c;如果请求的是 辅助索引&#xff08;非唯一键索引&#xff09;没有在缓冲池 中时&#xff0c;并不会立刻将磁盘页加载到缓冲池…

【Qt】设置QT标准对话框为中文字体

设置QT标准对话框为中文字体 一、问题二、解决方法1、找到Qt内置的翻译文件 qt_zh_CN.qm2、在代码中加载该文件 一、问题 在Qt中我们使用的标准对话框都是英文&#xff0c;例如下面的 字体选择对话框&#xff0c;但是实际中我们需要构建的是中文对话框。 所以我们需要使用Qt官…

T1级,生产环境事故—Shell脚本一键备份K8s的YAML文件

大家好&#xff0c;我叫秋意零。 最近对公司进行日常运维工作时&#xff0c;出现了一个 T1 级别事故。导致公司的“酒云网”APP的无法使用。我和我领导一起搞了一个多小时&#xff0c;业务也停了一个多小时。 起因是&#xff1a;我的部门直系领导&#xff0c;叫我**删除一个 …
最新文章