KBEngine服务器demo中的AI(人工智能)

来源:互联网 发布:手机怎么开淘宝网店 编辑:程序博客网 时间:2024/05/22 16:38

简介:

最近公司在使用KBEngine服务器来开发网游,从接触python到开始搞KBEngine时间比较短,我负责写AI这块,以前也没有什么经验,项目又催的急,各种翻资料,逛论坛,自己挖了很多的坑,刚开始接触AI,整体的对写AI概念都不是很清晰,网上关于这块也不多,后来慢慢摸索,使用了有限状态机的模式,总算实现了大部分的需求,今天来拿官方的demo给大家一起分享一下AI。

整体概述

在官方给出的demo中,作为NPC的Monster继承了一系列的接口, 每种接口对应于不同的功能,我们说的AI就是来控制这些NPC的行为逻辑。

class Monster(KBEngine.Entity, NPCObject,// NPC父类

Flags,// 一个管理标记信息的模块,标记如: 正在交易中、正在xx。 State,// 状态模块, 主状态例如:死亡、活着。子状态例如:闲置状态、战斗状态 Motion,// 关于移动的封装 Combat,// 关于战斗公式、战斗属性等等的封装 Spell,// 技能释放、buff/debuff维护等 AI):// 智能思考模块

实现原理

1.给Monster加入心跳

 服务端上怪物成千上万, 而AI是比较耗的,如果只有一个玩家在线, 显然大量的怪物是不需要开启AI思考来白白耗掉CPU,于是kbe引入了AOI的概念,即规定一段范围,服务端将客户端感兴趣的区域中发生的所有事件同步给客户端。事件包括: 实体移动、客户端广播类型的属性改变、死亡销毁等等。进入AOI会触发onWitnessed()回调,demo中使用该回调让monster实体enable(),此时在enable()函数中,开启了计时器,每秒钟执行一次,类似于人的心跳,此时的Monster已经有了心跳。

2.给Monster加入思维

在Monster有了心跳之后,心跳计时器内,每次心跳都会去执行think(),在该函数中,Monster会根据自身的状态,去思考自己的行为,官方demo中有两个状态,Free和Fight,当自身或者外界的因素改变的时候,自己的状态的迁移,也会根据状态执行相应的逻辑。

3.给Monster加入触觉

在Monster有了心跳和思维之后,需要具有感知世界环境的能力,感受到食物或者危险,在官方的demo中,空闲状态下,即在Free状态下,会在think中进行执行onThinkFree()函数,此时使用KBE提供的addProximity()给Monster加入了一个范围触发器,当有其它实体进入或离开这个触发器区域的时候会通知这个Entity。这个区域是一个方形(为了效率)。如果其它实体在x轴和z轴上均在给定的距离里面,则实体被视为在这个范围里面。这个Entity通过onEnterTrap()和onLeaveTrap()函数被通知,我们在写Monster感受食物或者敌人的时候,都是使用这两个回调进行筛选操作,如果有敌人进入到自己的触发器范围内,onEnterTrap()执行,视为自己可以进行攻击,从自由状态进入到战斗状态,因为是指在xz轴提供了触发,如果是有需要在y轴实现三维的触发器,可以在kbengine_defs.xml文件中开放y轴管理器即可。但是该操作谨慎进行,开放y轴管理器会增加消耗。<rangemgr_y>true</rangemgr_y>

4.给Monster加入动作

在Monster加入了思维和触觉,已经可以使用触觉感应到敌人,由思维判断进入到战斗状态,执行onThinkFight(),在战斗状态,就开始进行检查自己的敌人列表,如果找到敌人,进行移动追击,此时会调用Motion类中的gotoPosition()移动到目标点,此外Motion类中还提供了gotoEntity()方法,移动到某个实体的位置,这两个函数都是对KBE引擎提供的底层API函数的实现,即moveToPoint()函数。此时已经实现了Monster的移动。

5.说明:

官方的Demo中,AI模块做的比较简单,只是添加了一个定时器以一定频率执行一些流程, 这些流程根据状态区分,我们在做项目的时候,简单的状态的迁移也可以使用这种方式。

关于移动和朝向

在项目中,移动函数moveToPoint是使用最频繁的, 先看一下API的说明。
moveToPoint( self, destination, velocity, distance, userData, faceMovement, moveVertically )

功能说明:

直线移动实体到给定的坐标点,成功或失败会调用回调函数。
任何实体,在任意时刻只能有一个移动控制器,重复调用任何移动函数将终止之前的移动控制器。
返回一个可以用于取消这次移动的控制器ID。

参数:

destination

Vector3,Entity要移动到的目标位置点

velocity

float,Entity的移动速度,单位m/s

distance

float,距离目标小于该值停止移动,如果该值为0则移动到目标位置。

userData

object,传给通知函数的数据

faceMovement

bool,如果实体面向移动方向则为true。如果是其它机制则为false。

moveVertically

bool,设为true指移动为直线移动,设为false指贴着地面移动。

返回:

int,新创建的控制器ID。


1.控制器

在使用该函数的过程中,需注意,实体一次只能有一个移动控制器,在多个移动控制器同时使用,而未使用entity.cancelController( "Movement" )取消控制器的控制的时候,会依次执行控制器的控制逻辑,所以如果是状态切换时候,移动的逻辑改变,可以先使用entity.cancelController( "Movement" )取消当前控制器,再开始新的移动控制器。

2.朝向

关于参数faceMovement,是否朝向移动的方向,在项目中这个基本都是要朝向的,也就是设置为True,但是有些人可能会发现此时的朝向在xz轴是朝向移动方向,如果是在三维空间内,y轴并未正常朝向移动的方向,是因为KBE在底层中是默认注释了y轴。

1. 修改底层代码

可以在底层代码中进行修改,moveto_point_handler.cpp中,打开y轴注释。

if(KBEVec3Length(&movement) < velocity_ + distance_)
{
float y = currpos.y;
currpos = dstPos;
if(distance_ > 0.0f)
{
// 单位化向量
KBEVec3Normalize(&movement, &movement); 
movement *= distance_;
currpos -= movement;
}
if (!moveVertically_)
currpos.y = y;
ret = false;
}

2. 修改def文件

在Monster的def文件中加入

<Volatile>
<position/>
<yaw/>
<roll/>
<pitch/>
</Volatile>

即可实现,绕xyz轴的旋转。

结束语

这是我对官方demo中的Monster的AI的一些见解,在实际项目中,如果写一些简单的AI可以按照该框架进行实现,希望对大家有帮助。

0 0
原创粉丝点击