我所遭遇过的游戏中间件--Kynapse
我所遭遇过的游戏中间件--Kynapse
Autodesk Kynapse游戏中间件是一款面向游戏开发、非玩家控制角色实时模拟的领先的人工智能解决方案。Kynapse具有先进的路径查找功能,比如三维路径查找、动态环境评估,并支持复杂地形中的群体路径查找。高效的制作工具包以及专为简便集成和自定义而设计的结构可帮助游戏开发人员简化创建顶级控制台游戏和 PC游戏的过程。这项高性能的游戏人工智能技术能够帮助游戏开发者在复杂的地形中,模拟先进的动态三维自动寻路、环境感知与大规模群体移动。其主要功能有:
(1)实时三维自动寻路—使用面向游戏内角色的经过验证的算法,能够面向复杂地形的动态3D路径查找,找到他们以智能方式围绕大型三维动态环境的路径。
(2)路径数据生成—自动化工具可以帮助团队评估其结果,生成同等级别的AI数据,无需手动添加标签。
(3)三维空间感知—游戏内角色可以在运行中,在动态环境中智能地定位自身。如果在三维空间路径查找告诉角色怎样从A到B,那么动态3D感知就是要告诉角色B为什么是一个值得去的地方。因此,3D感知是创建复杂的、有吸引力角色的关键。
(4)团队协作—多角色游戏中的角色能够相互交流并进行小组讨论。多个NPC可以智能地分工完成一件任务,如足球游戏,CS射击游戏等。
以上文字是从Kynapse的官网和广告手册上扒下来的,不过现在找它的官网的话需要费点工夫,因为Autodesk已经不再对Kynapse进行升级和维护了.他们目前主推的两个与人工智能相关的中间件是navigation(导航)和cognition(认知).我个人的感觉是Autodesk将Kynapse拆成两部分,一个做寻路(navigation),另一个做认知与决策(cognition).有点跑题了,这篇文章还是要讲Kynapse,文章的中心思想与之前那个Scaleform的差不多,"吐嘈".个人感觉Kynapse这个中间件最大的特点是不够成熟完善,使用起来有太多的问题太多的坑需要绕过去.下面将一一吐嘈:
(1)Demo
Kynapse的Demo是我碰到过的中间件中最麻烦的.它使用了一款游戏引擎好像是Version.要想完全熟悉它的DEMO非得将这个游戏引擎弄清楚.记得当时我费了很大劲才将它的Demo编译出来,可没跑几天Version的版本过期了.而且要弄清楚Version引擎也不容易,所以看它的Demo代码很麻烦.好在项目中对AI的需求只是寻路,所以它的DEMO我也不需要太细看.
(2)地形数据
Kynapse的地形数据(.PathData文件)的生成不直观,不是所见即所得,并且每次生成的时间较长。没有一种工具直接修改PathData文件,为其添加删除路点,修改地形属性。美术人员通过需要叠代多次才能生成一个满意的.PathData文件,效率低下。这一点向他们官方提过很多次,始终没有解决.那怕在他们脱胎换骨的新版本Navigation中也没有得到解决.还有,Kynapse无法支持美术生成一个只允许半径在0.5米以下的角色才能通过的通路.
寻路使用的地形数据为路点和关联组成,Kynapse生成的地形数据中,会出现若干个路点挤在一起的情况.这种情况确实不该出现,而且出现了还没办法修改,只能将就.这个问题貌似在navigation有所改善.
(3)智能体的种种BUG
AI是个有极大不确定性的问题.很难保证每次寻路的正确性,总会出现一些难以重现的问题.我之所以觉得Kynapse不够成熟,原因就在这里.
Kynapse中整体流程是:在场景中创建智能体Agent,设置Agent的目标位置,然后在每一帧中调用其接口函数think(),这时其状态中会有该智能体当前的朝向,速度等信息,然后按照这些信息移动智能体.直到到达为止.
实际应用时种种问题纷纷而至,问其官方总是得到种种扯皮的答复,于是我的代码里充斥着大量特殊处理的代码:
a.
智能体的到达不是真正的到达,这一距离可以在其属性中设置,这个不算是它的Bug,应该算潜规则.
if (m_agentBehaviour == AB_GOTO && m_pathFinder->HasArrived()) { /* Kyanpse的智能体并不会在绝对到达目标点时,认为Arrived。 实际上当它的HasArrived()返回true时,智能体和目标点会 有一小段距离。此时需要调用ExecuteOnDirectlyGo() */ m_bDirectlyGo = true; ExecuteOnDirectlyGo(deltaTime); return; }
b.
当智能体的目标位置设置到障碍物的OBB内部时,智能体会来回行走,而永远到不了目标位置。应该是当设置目标位置在障碍物内部时,直接返回寻路失败。这个问题给官方提出后,给解决了,但与之相似的下一个问题没有解决.
c.
#ifdef BUG_HACK // 当智能体在一个障碍物旁边,而目标位置在该障碍物内部,并且其距离小于智能体的 // 到达距离(m_pathFinder->SetDistanceToTarget设置的)时,kynapse判断为寻路到达, // 智能体会走进障碍物内,为解决这一BUG,添加如下代码: if (s_pAISystem->IsPositionInObstacle(m_vTarget)) { m_agentState = AS_ARRIVED; m_bFailedToFindPath = true; return; } #endif
d.
#ifdef BUG_HACK /* 有时智能体会变得无旋转无平移,虽然它的寻路状态为:PATH_FOUND 但它却静止在一个位置,这时需要给它一个最小的移动速度 */ else { // 只有移动 m_fLinearSpeed = max(fTargetSpeed, m_fRotatingSpeed); } #endif
e.
#ifdef BUG_HACK /* 此时按照kynapse给的路线走,竟然走进了不可行走区域 */ else { position = m_body.GetPosition() + oriVector*0.005f; if ( position是可行走位置 ) { // 向前移动一小步距离; } else { // 此时为死路,进不可进,退无可退,只得强行移动 // 当出现死路时,由直接走向最终目标点改为走向当前目标点. } } #endif
f.
这是最常碰到的问题:智能体会在障碍物的一个边或者寻路地形的边界处来回走动,卡死在那里,而找不到路。当时的情况是,智能体得到当前的think结果是旋转180,然后当旋转完成后得到的think结果是再转回去.此时就看到我们的角色在那个点上一直不停地旋转.
#ifdef BUG_HACK /* 用于解决智能体出现两个目标位置不停切换的BUG. 当发生BLOCK时,强制使智能体移动到上一帧所得到的目标位置 */ if (m_isBlock) { ExecuteOnBlock(deltaTime); return; } #endif
总之,以上每一个BUG_HACK都记录着我的血泪史,我不知道浪费了多少时间在这些BUG_HACK上.
(4)寻路性能问题
在一个较大的场景中,智能体从一点到另一点进行寻路。如果两点间无路径可达时,寻路算法将会遍历几乎整个场景。这时的计算量很大,如果在一帧之中进行计算,将很是耗时;通过设置寻路算法的每帧处理的路点数目可以解决单帧耗时过大的问题,但是这样设置后,寻路算法需要更多的计算时间(比在单帧中计算要多花数倍的时间),通常需要很多帧才能得到寻路结果。
(5)寻路同步问题
这是个纠结了很久都没有结果的问题.网络游戏的角色同步是个大问题.之前还一直在讨论这个模块放在客户端合适,还是放在服务器端合适.对于这种难解的问题,我是不想细说了.
(6)Kynapse异步处理
Kynapse提供了异步处理功能,但是我依照DEMO写了函数SetupAsync(),调用后却发现无法寻路了,设置的自由漫步智能体不动了。由于对这个功能没有太急迫的需求,所以我就没再细查.