转载请标明出处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. 解决MVC模式文件下载附件中文名称乱码

    解决如下: 进行url编码:Server.UrlPathEncode(file.AttachmentName) return File(file.TempWorkPath, CommonTools.G ...

  2. 第1阶段——uboot分析之通过nand命令读内核(8)

    本节主要学习: 详细分析UBOOT中"bootcmd=nand read.jffs2 0x30007FC0 kernel;bootm 0x30007FC0" 怎么实现nand命令读 ...

  3. [置顶] Chat Room:基于JAVA Socket的聊天室设计

    d0304 更新功能实现 d0312 更新部分图片&UI设计部分 d0318 更新功能实现 d1222 实现添加好友功能.实现注册功能.修改大量BUG github:https://githu ...

  4. Java课设--俄罗斯方块Tetris

    Java程序设计课程作业报告 作业:俄罗斯方块游戏 姓名 赵璐媛 学号 程序得分 90% 作业报告 得分10% 实验总分 100% 作业目的: 掌握基本的图形程序设计方法 掌握Java事件处理程序编写 ...

  5. js Web存储方式

    JSON是数据交互中最常用的一种数据格式. 由于各种语言的语法都不同,在传递数据时,可以将自己语言中的数组.对象等转换为JSON字符串> 传递之后,可以讲JSON字符串,在解析为JSON对象. ...

  6. Java-反射机制学习

    反射机制是Java的一个重要性,它使得Java语言具有了动态特性.比如说,可以在代码中动态地获取某个类的信息,生成它的实例.获取其成员变量.调用它的方法.下面通过几个示例来演示反射机制的作用与用法. ...

  7. 201521123017 《Java程序设计》第5周学习总结

    1. 本周学习总结 2. 书面作业 1.代码阅读:Child压缩包内源代码 1.1 com.parent包中Child.java文件能否编译通过?哪句会出现错误?试改正该错误.并分析输出结果. 1.2 ...

  8. Java课程设计 201521123078

    计时器 掌握java图形界面操作以及多线程技术. 1.Mythread1 写一个类Mythread1实现Runnable,当需要开一个线程时就是用这个类.其中的run()通过标记flag和循环实现时间 ...

  9. java课程设计-猜数游戏

    1. 团队名称.团队成员介绍 团队名称:breeze 团队成员 组长:网络1514张朝玮 201521123106 组员:网络1513侯帅军 201521123092 2. 项目git地址 https ...

  10. 关于APP在小米5s第一次安装启动后,点击home返回桌面,再次进入重进闪屏页问题

    现象 今天工作中,在对公司产品进行测试的时候,程序员小哥点出了一个问题.问题点出的步骤是这样的: 1.安装APP 2.点击打开 3.经过闪屏页,进入主页后,点击HOME键 4.再次进入程序会重新进入闪 ...