从基础搭建开始。
网上比较多的状态机都是结合其实际的应用场景包含一些特定的实现设计在里面我们在搭建自己的状态机的时候可以从一个细小的状态开始搭建先搭建一个节点然后依据同样的设计搭建类似的多个节点2、保持节点的简洁性和去耦性。通常在最初的节点搭建完后是不可能一劳永逸的后续反复的修改都会对这些节点进行特定的修改和某些特定规则的设计。举个列子射击这样一个状态节点A英雄的设计是单发B英雄是连续10发那么如何保证对这两个英雄的同一个射击状态的兼容一种设计方式是将当前的英雄作为一个参数进行传递那么在执行的时候就读取当前传入参数的英雄的具体配置进行相关的射击动作。把射击相关的逻辑封装在该英雄对应的攻击执行器中那么其具体执行的射击单发还是射击10发就可以作为一个循环执行单发循环一次10发循环10次。以前我的设计实现思路是针对单一的英雄特定重载实现其对应的射击节点这样也算一种解决方法只是这样的设计有一个弊端就是随着英雄类型的增多其对应的特定实现会不断的扩大相应的重载实现的版本会增多。如果设计思路和文本清晰那还可以维护如果设计思路不清晰那么就会带来维护的消耗。3、做好节点的剥离避免节点耦合太多。通常状态机是用在比较简易的游戏类型中角色本身的状态类型不会太多那么对应的状态节点应该避免相互之间的耦合和功能交叉状态的切换可以走相同的属性设置来实现交互。如果随着设计的变化状态实现越来越多可以考虑用行为树来实现避免相互之间的耦合太多。二、行为树在RPG游戏中行为树是用的比较多的一种AI机制。就其本质而言行为树是状态机的一种更高封装的实现。我个人的理解行为树就是将特定的行为节点进行封装做成叶子节点这样可以实现任意节点的拼接。想象一下如果我们把每个特定的状态机进一步的封装做成一片片叶子这样我们在搭建树的时候就可以收集特定的叶子来搭建特定的树最后得到特定功能的行为树这就是我对行为树的一个简易理解:D 如果用更为规范的说法那么这些叶子节点就是行为树中的行为节点行为树的行为节点的执行结果可以用枚举的方式列出复制代码RunningStatus {INVALID 0,SUCCESS 1, --执行成功FAILURE 2, --执行失败RUNNING 3, --执行中}复制代码一棵树的搭建不能只有叶子还需要有枝干这就需要行为树中的一些特定的节点来搭建这棵树这就是行为树中的控制节点Control Node的作用。注意一点行为节点是和游戏实际关联的在行为节点中我们会去具体的定义如何攻击(shoot/attack)寻路(patrol)闲置(idle)等但是在控制节点中其具体运行的逻辑是和实际游戏数据没有关联的其只需要负责基本的控制逻辑即可 。通过控制节点我们可以清晰的知道整个行为树的执行逻辑这就是行为树的一大优势执行逻辑可见。 行为树主要有以下几种控制节点举例说一下4种Composite节点1、Sequence节点 顺序执行控制节点其执行的基本逻辑是对其下面的所有叶子节点均顺序执行直到遇到返回为FAILURE的节点。我对这种节点的理解就是串联电路的思维电流顺序走过每个电阻叶子如果遇到第一个无法流过的电阻则返回否则会一直流过去直到所有电阻都流过2、Selector节点选择执行控制节点其执行的基本逻辑是对其下面的所有叶子节点顺序执行直到遇到第一个返回为SUCCESS/RUNNING执行结果的节点。观察上面的Sequence节点和Selector节点的区别仅仅在于选中的节点的返回结果的不同处理。在其他地方好像对选择节点进行了分类可以分为带优先级的选择节点不带优先级的选择节点带权值的选择节点。带优先级是指在选择那个节点更新的时候会根据优先级进行比较来选择。不带优先级的时候一般会设置为每次更新的时候会沿用上一次的更新节点因为一般更新的时候会有一段时间持续处于某个节点的状态目前我用的就是这样的选择节点而对于带权重的选择节点一般用来做多样的随机性比如游戏中的宠物需要表现一个交互动作那么可以做多个交互动作在每次做选择节点的时候根据权重随机一个节点用来表示交互动作。 3、Parallel节点并行执行控制节点其执行的基本逻辑是对其下面的所有叶子节点各自执行一次如果返回结果不为RUNNING则分别统计SUCCESS和FAILURE的结果一般结果会采用“与”和“或”的操作。比如当前Parallel节点的返回条件可以分为 全SUCCESS或者任意一个SUCCESS全FAILURE或者任意一个FAILURE。在执行完所有叶子节点后可以对比其执行的SUCCESS和FAILURE节点的个数和其设置的返回结果对比如果满足则返回SUCCESSSUCCESS条件或者FAIULUREFAILURE条件或者返回RUNNING。 4、Detector节点检测执行控制节点其执行的基本逻辑是对其下面的所有叶子节点逐个执行如果返回的不为RUNNING则检测如果为SUCCESS则接着执行为FAILURE则执行结束。即遇到第一个返回为FAILURE的节点就结束执行。 6种Decorator节点 1、Invert节点反转装饰节点其执行的基本逻辑是只有一个叶子节点如果返回为SUCCESS则返回FAILURE如果返回为FAILURE则返回SUCCESS否则返回RUNNING 2、Sucees节点Success装饰节点其执行的基本逻辑是只有一个叶子节点执行后直接返回SUCCESS 3、SuccessToRunning节点根据名字就可以知道其叶子节点在执行完后如果返回为SUCCESS则修改其结果为RUNNING其他的状态不改变返回最终执行状态给上一层。 4、FailureTORunning节点类似于上一个节点其叶子节点在执行完后如果返回为FAILURE则修改其结果为RUNNING其他的状态不改变返回最终执行状态给上一层。 5、SuppressSuccess节点suppress的意思是抑制所以这个节点的功能就是不准返回SUCCESS的执行结果如果叶子节点返回为RUNNING则返回RUNNING其他都返回为FAILURE给上一层 6、SuppressFailure节点类似于上一个节点该节点只会返回RUNNING或者SUCCESS这两种执行结果 除了常见的Compositor节点和Decorator节点还有Condition节点依据前面两种类型节点不难推出后面的Condition节点的功能就是在某些条件下才触发某些特定返回结果的一些节点。 简而言之行为树就是在三个大类的控制节点Compositor节点、Decorator节点、Condition节点的搭建下结合各个行为叶子节点拼接出一个基本的AI执行机制举个例子bt_atk这是一个简单的站在原地攻击的行为树设计首先执行Selector节点然后顺序执行其下面的所有叶子节点首先会执行攻击的叶子节点如果返回为Success则继续执行Homing节点。所以其基本的设计思想就是触发一次攻击检测如果有攻击对象则执行攻击返回SUCCESS同时结束本次tick否则返回FAILUER那么就会执行下一个节点Homing。通过构建一颗基本的行为树我们可以组件一颗更大的树比如我们再构建一颗巡逻的行为树bt_patrol通过这两颗基本的行为树我们可以组建一个更大的行为树bt_monster复制代码Sequence Attack Homing /Sequence /Selector复制代码 当然我只是一个引申更加具体的设计可以根据具体的设计来实现有时候不只是叶子节点可以复用某些树也可以整体作为一个叶子复用这样可以实现设计的复用。在行为树中一般的设计思路是会构建一个行为树的模版比如上面的bt_atk在每个使用该模版的角色进行初始化行为树的时候是从该模版缓存中取出一份然后初始化相关的信息得到一份实例在进行行为树更新的时候是更新其对应的实例。而且为了通用各个树干和叶子节点都是采用数据封包传递在数据包中封闭当前角色相关的信息或者黑板信息这样在每个节点更新的时候都是从数据封包中获取当前角色相关的信息从而进行对应的逻辑更新。这样就避免了一些设计上的将数据封存在action行为节点上的问题通过封包的传递可以降低耦合提高复用性。对于行为树的优化我提一个优化点吧。大部分的行为树在具体的项目中应用都是结合具体的设计来实现的所以我采用的优化未必适合于其他游戏的优化但是可以采用一个更新频率的设置来降低行为树的更新频率这个是可以通用的。我在测试服务器的代码性能的时候发现当场景中的角色数量比较少的时候大部分的游戏性能都被场景中的怪占用了而怪物的更新中对于AI的更新又是一个很大的占用。我采用一种距离配置的方法进行优化当怪物周边没有玩家的时候降低怪物的更新频率或者就不执行怪物的AI更新当怪物进入玩家的视野的时候采用较高的更新频率当怪物进入玩家的攻击范围的时候采用正常的更新频率。通过不同距离检测设置不同的更新频率可以较好的优化在玩家个数较少时的怪物AI性能。总结游戏中两种常见的状态机和行为树都做了一个简单的讲解当然现在网上比较多相关的资料如果想深入的学习可以搜集相关的资料研究有较多的开源代码也可以参考研究一下。当然我说的都是较为浅显的设计具体的状态机和行为树节点的设计其中具体逻辑的编写是需要结合实际的游戏设计来实现的这就需要程序和策划具体的商量和实现了。好了今天AI的简介就说到这儿下一篇再说说一些优化的总结吧D

相关新闻

国产大模型技术选型与工程实践指南:从SOTA评估到生产部署

国产大模型技术选型与工程实践指南:从SOTA评估到生产部署

国产大模型技术迭代速度惊人,几乎每周都有新的模型发布或性能突破,SOTA(State Of The Art)榜单的领先位置频繁更替。这种快速演进的态势既体现了国内AI技术的活力,也给开发者、研究者和企业技术选型带来了实实在在的挑…

2026/11/14 4:49:45 阅读更多 →
C++ 无锁并发队列:高性能设计与实现

C++ 无锁并发队列:高性能设计与实现

1. 为什么需要无锁并发队列在多线程编程中,队列是最常用的共享数据结构之一。传统的基于互斥锁(std::mutex)的队列在高并发场景下容易成为瓶颈,因为锁的争用会导致频繁的上下文切换和缓存行失效。无锁(lock-free&#…

2026/11/14 4:49:46 阅读更多 →
C++面向对象项目实战:从零构建控制台ATM模拟器

C++面向对象项目实战:从零构建控制台ATM模拟器

1. 项目概述:从零构建一个控制台ATM模拟器最近在带新人,发现很多刚学完C基础语法的朋友,对于如何将零散的知识点(类、文件操作、流程控制)串联成一个完整的、有逻辑的项目感到无从下手。正好,之前用C写过一…

2026/11/10 21:51:35 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/13 5:13:25 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/13 5:13:25 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/13 5:13:25 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/13 22:08:41 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/14 2:41:37 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/13 5:13:25 阅读更多 →

日新闻

周新闻

月新闻