news 2026/7/2 4:31:52

、从基础搭建开始。

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
、从基础搭建开始。

网上比较多的状态机,都是结合其实际的应用场景,包含一些特定的实现设计在里面,我们在搭建自己的状态机的时候,可以从一个细小的状态开始搭建,先搭建一个节点,然后依据同样的设计,搭建类似的多个节点;

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节点的个数,和其设置的返回结果对比,如果满足则返回SUCCESS(SUCCESS条件)或者FAIULURE(FAILURE条件),或者返回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

<Root> <Selector> <Attack> <Homing> </Selector> </Root>

这是一个简单的站在原地攻击的行为树设计,首先执行Selector节点,然后顺序执行其下面的所有叶子节点,首先会执行攻击的叶子节点,如果返回为Success,则继续执行Homing节点。所以其基本的设计思想就是:触发一次攻击检测,如果有攻击对象,则执行攻击,返回SUCCESS,同时结束本次tick;否则返回FAILUER,那么就会执行下一个节点Homing。

通过构建一颗基本的行为树,我们可以组件一颗更大的树,比如我们再构建一颗巡逻的行为树:

bt_patrol

<Root> <Selector> <Patrol> <Homing> </Selector> </Root>

通过这两颗基本的行为树,我们可以组建一个更大的行为树:

bt_monster

<Root> <Selector> <Sequence> <Patrol> <Homing> </Sequence> <Sequence> <Attack> <Homing> </Sequence> </Selector> </Root>

当然我只是一个引申,更加具体的设计可以根据具体的设计来实现,有时候不只是叶子节点可以复用,某些树也可以整体作为一个叶子复用,这样可以实现设计的复用。

在行为树中,一般的设计思路,是会构建一个行为树的模版,比如上面的bt_atk,在每个使用该模版的角色进行初始化行为树的时候,是从该模版缓存中取出一份,然后初始化相关的信息得到一份实例,在进行行为树更新的时候,是更新其对应的实例。

而且为了通用各个树干和叶子节点,都是采用数据封包传递,在数据包中封闭当前角色相关的信息或者黑板信息,这样在每个节点更新的时候,都是从数据封包中获取当前角色相关的信息,从而进行对应的逻辑更新。这样,就避免了一些设计上的将数据封存在action行为节点上的问题,通过封包的传递,可以降低耦合,提高复用性。

对于行为树的优化,我提一个优化点吧。大部分的行为树在具体的项目中应用都是结合具体的设计来实现的,所以我采用的优化未必适合于其他游戏的优化,但是可以采用一个更新频率的设置来降低行为树的更新频率,这个是可以通用的。我在测试服务器的代码性能的时候发现,当场景中的角色数量比较少的时候,大部分的游戏性能都被场景中的怪占用了,而怪物的更新中对于AI的更新又是一个很大的占用。我采用一种距离配置的方法进行优化,当怪物周边没有玩家的时候,降低怪物的更新频率或者就不执行怪物的AI更新,当怪物进入玩家的视野的时候,采用较高的更新频率,当怪物进入玩家的攻击范围的时候,采用正常的更新频率。通过不同距离检测设置不同的更新频率,可以较好的优化在玩家个数较少时的怪物AI性能。

总结:游戏中两种常见的状态机和行为树都做了一个简单的讲解,当然现在网上比较多相关的资料,如果想深入的学习,可以搜集相关的资料研究,有较多的开源代码也可以参考研究一下。当然我说的都是较为浅显的设计,具体的状态机和行为树节点的设计,其中具体逻辑的编写,是需要结合实际的游戏设计来实现的,这就需要程序和策划具体的商量和实现了。好了,今天AI的简介就说到这儿,下一篇再说说一些优化的总结吧

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/2 4:30:33

【计算机毕业设计案例】基于 SpringBoot 的用户云端日记创作管理系统的设计与实现 基于 SpringBoot 的网络个人日志云存储系统(程序+文档+讲解+定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/7/2 4:30:25

2026核心网盘多线程提速实测:官方通道优化VS直链并发谁更强?

作为一个从2017年就开始泡在各大技术论坛、天天跟各种网盘客户端死磕的资深玩家&#xff0c;我这些年折腾过的传输工具少说也有几十款了。讲真&#xff0c;很多人一看到几百KB/s的进度条就头大&#xff0c;第一反应就是到处找所谓的黑科技。呃&#xff0c;说实话&#xff0c;现…

作者头像 李华
网站建设 2026/7/2 4:28:26

Three.js 自定义遮罩通道教程

自定义遮罩通道 Custom Mask ▶ 在线运行案例 案例合集&#xff1a; 三维可视化功能案例&#xff08;threehub.cn&#xff09;开源仓库github地址&#xff1a; https://github.com/z2586300277/three-cesium-examples400个案例代码: 网盘链接 你将学到什么 EffectComposer…

作者头像 李华
网站建设 2026/7/2 4:28:02

如何用pan-baidu-download免费破解百度网盘限速:终极完整指南

如何用pan-baidu-download免费破解百度网盘限速&#xff1a;终极完整指南 【免费下载链接】pan-baidu-download 百度网盘下载脚本 项目地址: https://gitcode.com/gh_mirrors/pa/pan-baidu-download 还在为百度网盘下载速度只有几十KB而烦恼吗&#xff1f;每次下载大文件…

作者头像 李华
网站建设 2026/7/2 4:26:27

3步永久保存B站视频:m4s-converter格式转换工具终极指南

3步永久保存B站视频&#xff1a;m4s-converter格式转换工具终极指南 【免费下载链接】m4s-converter 一个跨平台小工具&#xff0c;将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 你是否曾经遇到过这样的情况…

作者头像 李华
网站建设 2026/7/2 4:26:07

AI视频去水印技术:oiioii工具实战解析

1. 项目概述在短视频创作和影视剪辑领域&#xff0c;水印问题一直困扰着内容创作者。无论是从平台下载的素材&#xff0c;还是需要二次加工的成品视频&#xff0c;那些顽固的水印总会在关键时刻破坏画面美感。传统的去水印方法要么效果粗糙&#xff0c;要么操作复杂&#xff0c…

作者头像 李华