转载请标明出处http://www.cnblogs.com/zblade/

  在设计一款游戏的时候,如果我们是玩家,是希望自己能够操作角色畅玩游戏的。在一款MMORPG游戏中,大部分的实际游戏角色,是需要玩家来操作的,通过在游戏大世界相互完成游戏中的任务等等来体验游戏。在大世界交互场景中,不可避免的会有怪物的存在,也会有NPC,某些策划布置的场景角色怪物等等。同时,在常见的MMORPG游戏中,自动战斗是不可避免的一个功能。这些表面的角色或者功能的背后,其实就是游戏的AI机制。

  说到游戏的AI,和现在比较流行的人工智能有一定的区别。现在的人工智能是全面的AI,包含数据收集,数据分析,行为支配等等操作。而游戏中的AI,属于比较特定的一类,主要是为战斗系统提供的一种机制。现在主流的机制主要分为两类:状态机和行为树。这两类机制,各有其优势,在实际的应用中需要结合实际的需求来采用。我分别在两款游戏中采用了这两种机制,下面也算一个个人的总结吧。

一、状态机

  状态机这种机制,主要是针对战斗状态不太复杂的应用场景。如果我们的角色,在整个游戏过程中,只有少数几个可以列举的状态,比如:站立,寻敌,攻击,行走,受击这样可以枚举出来的几个状态,那么,我们可以考虑用状态机的方式实现这几种状态的相互切换。具体的关于状态机的实现,我这儿就不再赘述了,网上有很多详细的讲解,也有比较多开源的代码,大家可以参考github上的代码类似的实现自己的状态机。我就写几点个人的一些总结吧。

    1、从基础搭建开始

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

2、保持节点的简洁性和去耦性

  通常,在最初的节点搭建完后,是不可能一劳永逸的,后续反复的修改,都会对这些节点进行特定的修改和某些特定规则的设计。

  举个列子,射击这样一个状态节点,A英雄的设计是单发,B英雄是连续10发,那么如何保证对这两个英雄的同一个射击状态的兼容?一种设计方式,是将当前的英雄作为一个参数进行传递,那么在执行的时候,就读取当前传入参数的英雄的具体配置,进行相关的射击动作。把射击相关的逻辑封装在该英雄对应的攻击执行器中,那么其具体执行的射击单发还是射击10发,就可以作为一个循环执行,单发循环一次,10发循环10次。

  以前我的设计实现思路,是针对单一的英雄,特定重载实现其对应的射击节点,这样也算一种解决方法,只是这样的设计有一个弊端,就是随着英雄类型的增多,其对应的特定实现会不断的扩大,相应的重载实现的版本会增多。如果设计思路和文本清晰,那还可以维护,如果设计思路不清晰,那么就会带来维护的消耗。

3、做好节点的剥离,避免节点耦合太多

  通常状态机是用在比较简易的游戏类型中,角色本身的状态类型不会太多,那么对应的状态节点应该避免相互之间的耦合和功能交叉,状态的切换可以走相同的属性设置来实现交互。如果随着设计的变化,状态实现越来越多,可以考虑用行为树来实现,避免相互之间的耦合太多。

二、行为树

  在RPG游戏中,行为树是用的比较多的一种AI机制。就其本质而言,行为树是状态机的一种更高封装的实现。我个人的理解,行为树就是将特定的行为节点进行封装,做成叶子节点,这样可以实现任意节点的拼接。想象一下,如果我们把每个特定的状态机进一步的封装,做成一片片叶子,这样我们在搭建树的时候,就可以收集特定的叶子,来搭建特定的树,最后得到特定功能的行为树,这就是我对行为树的一个简易理解:D 如果用更为规范的说法,那么这些叶子节点,就是行为树中的行为节点,行为树的行为节点的执行结果,可以用枚举的方式列出:

RunningStatus =
{
INVALID = ,
SUCCESS = , --执行成功
FAILURE = , --执行失败
RUNNING = , --执行中
}

  一棵树的搭建,不能只有叶子,还需要有枝干,这就需要行为树中的一些特定的节点来搭建这棵树,这就是行为树中的控制节点(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的简介就说到这儿,下一篇再说说一些优化的总结吧:D

MMORPG战斗系统随笔(三)、AI系统简介的更多相关文章

  1. MMORPG战斗系统随笔(一)、战斗系统流程简介

    前言 转载请标明出处http://www.cnblogs.com/zblade/ 很久没有更新博客,中间迁移过一次博客,后来一直忙于项目的开发,忙的晚上回去没时间写博客,周日又要自我调整一下,所以空闲 ...

  2. MMORPG战斗系统随笔(一)

    前言 很久没有更新博客,中间迁移过一次博客,后来一直忙于项目的开发,忙的晚上回去没时间写博客,周日又要自我调整一下,所以空闲了很久没有继续写博客.最近终于慢慢放慢节奏,项目也快上线了,可以有空写一些个 ...

  3. MMORPG战斗系统随笔(二)、浅谈场寻路Flow Field PathFinding算法

    转载请标明出处http://www.cnblogs.com/zblade/ 今天给大家带来一篇游戏中寻路算法的博客.去年,我加入一款RTS的游戏项目,负责开发其中的战斗系统,战斗系统的相关知识,属于游 ...

  4. MMORPG战斗系统随笔(四)、优化客户端游戏性能

    转载请标明出处http://www.cnblogs.com/zblade/ 说到游戏性能,这是一个永恒的话题.在游戏开发的过程中,性能问题一直是我们研发需要关注的一个节点.当然,说句客观话,很多程序员 ...

  5. Silverlight 2.5D RPG游戏技巧与特效处理:(十一)AI系统

    Silverlight 2.5D RPG游戏技巧与特效处理:(十一)AI系统 作者: 深蓝色右手  来源: 博客园  发布时间: 2011-04-19 11:18  阅读: 1282 次  推荐: 0 ...

  6. Android系统简介(中):系统架构

    Android的系统架构栈分为4层,从上往下分别是Applications.Application framework.Libraries  & Android Runtime.Linux  ...

  7. Qt 学习之路 2(24):Qt 绘制系统简介

    Qt 学习之路 2(24):Qt 绘制系统简介 豆子 2012年10月30日 Qt 学习之路 2 77条评论 Qt 的绘图系统允许使用相同的 API 在屏幕和其它打印设备上进行绘制.整个绘图系统基于Q ...

  8. CMS系统简介(从简介到使用)

    CMS系统简介 1.简介 CMS是Content Management System的缩写,意为"内容管理系统". 在中国互联网的发展历程中,一直以来默默地为中国站长提供动力的CM ...

  9. dell笔记本三个系统,ubuntu16.04更新,boot分区容量不足解决办法

    本人自己dell物理机上安装windows 7 .centos 1704 和ubuntu1604 三个系统的,分区当时没有使用lVM,boot单独挂/dev/sda7 分区,只有200M,随着2次li ...

随机推荐

  1. eval浅解

    关于eval,你了解多少呢?来看看 eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码. 需要一个参数(string),切必需.要计算的字符串,其中含有要计算的 JavaS ...

  2. Topshelf便捷创建Windows服务

    结合Quartz.net学习,前提已经创建了一个定时任务,可见 <定时调度框架:Quartz.net> (基于配置文件形式) 首先引用Topshelf.dll 自定义服务TestServi ...

  3. Calico 的网络结构是什么?- 每天5分钟玩转 Docker 容器技术(68)

    上一节我们部署了 Calico 网络,今天将运行容器并分析 Calico 的网络结构. 在 host1 中运行容器 bbox1 并连接到 cal_net1: docker container run ...

  4. my new start

    my new start in blog csdn : today i formally migrate my personal technical blog from sina to here in ...

  5. 团队作业4----第一次项目冲刺(Alpha版本)4.25

    a.提供当天站立式会议照片 会议内容: ①:对有的接口编写遇到的困难,由于基础问题,建议百度,谷歌现成的接口 ②:课程较多,时间不够,任务的调整以及进度的调整 b. 每个人的工作 每个人在尽量完成自己 ...

  6. 团队作业9——测试与发布(Beta版本)

    Beta版本测试报告 一bug汇总 计时没有显示即倒计时,难度不同的功能没有实现(已修复) 没有导入试卷和错题功能(不打算修复) 前台管理功能(部分修复) 界面美观问题(没有修复也不打算修复) 二.场 ...

  7. 201521123105《jave程序》第二周学习总结

    1. 本周学习总结 学习了各种java数据类型以及各种运算符的使用 学习了一维,二维数组的用法 学习了String类对象使用 2. 书面作业 使用Eclipse关联jdk源代码,并查看String对象 ...

  8. 201521123079 《Java程序设计》第1周学习总结

    1. 本周学习总结 了解学习了JAVA的开发环境的基础内容以及JDK,JRE等,学会用eclipse编写简单的代码 2. 书面作业 Q1.为什么java程序可以跨平台运行?执行java程序的步骤是什么 ...

  9. #黑客攻防实战详解#[Chapter 1]

    1.1 ①配置模拟入侵环境 ②配置网络拓扑   

  10. 201521123071 《JAVA程序设计》第十四周学习总结

    第14周作业-数据库 1. 本周学习总结 1.1 以你喜欢的方式(思维导图.Onenote或其他)归纳总结多数据库相关内容. 1.使用JDBC将Java程序与数据库连接 1.1注册驱动 Class.f ...