代码改变世界

具有静态行为的aI角色

2009-10-24 11:00  宝宝合凤凰  阅读(526)  评论(0)    收藏  举报

具有静态行为的AI角色

从第6章我们可以知道,在计算机游戏中,我们可能遇到的AI角色(以敌人或者敌对者的身份出现)的行为种类非常多。我们现在取其精华,将它们归结为相当简短,却可广泛应用的行为列表,如下所示:

  ● 静态行为(守卫,侦察者)

  ● 跟踪路径

  ● 自由漫游

  ● 追逐

  大多数AI角色的行为都是以上述一种或者几种行为为核心,再在其上加入一些修改或者突出某方面能力的行为而得出的行为。

软件开发网

 

  在本章中,我们会使用TorqueScript来实现上述的每一种行为,这需要非常大量的代码编写工作。

  在这里,我会对上述每一种行为进行独立地讲解。或许,您可以使用这些来加强您自己的解决方案中的功能;或许,您可以将这些做成例程,以函数库或者一组改进的玩家类的形式进行应用。如果您想这样做的话,则需要获得Torque游戏引擎的使用许可证,这样才可以直接修改AIPlayer 类或者从该类中派生出新类。

 

  7.1 具有静态行为的AI角色 http://www.mscto.com

  乍看起来,具有静态行为的AI角色似乎用处不大。但是,很多游戏中都有类似守卫灯塔、密室、碉堡炮塔,或者其他类似的场所。在游戏世界中,它们根本不用自己来规划自己的路径。它们只需要耐心地等待某一类型的对象进入它们视野中即可。它会攻击该对象直到该对象离开它的视野—— 至少它们会给这些侵入者一点小小的打击,让侵入者明白这里“禁止穿越”。

http://www.mscto.com

 

  现在,我们来尝试一下定义AI守卫的一些基本属性。在我看来,它们所具有的属性至少应该满足下列几个方面的能力:

软件开发网

 

  ● 潜在威胁的视觉检测(感观)

  ● 识别朋友和敌人(感观)

  ● 确认威胁的级别(感观)

  ● 扫视(设定的视野区域)(行动)

 

  ● 与敌对者(玩家)交战(反应)

 

  ● 对敌人进攻时间的检测(感观) http://www.mscto.com

  其中,每一种能力都需要相关脚本代码的支持。在某些情况下,根据游戏环境,还需要把它们的一些属性设置为AI角色的属性或者真实玩家化身对象的属性。

  在这里请大家注意,除了一个行动和一个反应能力之外,其余的能力都是感观上的。

 

  在这里,我要重点讲解一下AI角色利用视觉来探测潜在威胁的能力。当然,也许您会觉得术语“视觉”涉及的范围太广。其实,它只是包含了视觉探测的一种观念——仿真。计算机中的角色应该可以像实际中的人那样使用视觉对周围的事物进行探测。作为游戏设计者,有时我们会太容易将由软件构造的事物和现实中的事物混淆在一起。不过,当说起仿真或者对现实世界中的行为进行建模时,我们就必须把两者放在一起来考虑,也只有这样才可以获得成功。

 

  现在,首先我们需要确定究竟是什么构成了潜在的威胁。在这种情形下,我们通常会假设所有的角色(无论是玩家角色还是AI角色)都是潜在的威胁。在本书的演示中,我们只有一种角色类型,它既可以作为玩家出现,也可以作为AI角色来出现。因而,很容易就可以将所有的角色都看作是潜在的威胁。不过,同时我们也需要设定一个最大范围限度。如果超过这个范围限度,原来的威胁将不再成为一个威胁,也就是说不存在这样一个潜在的威胁。

 

  因此,这种感观能力需要做的只有一件事情:及时检测玩家化身是否已经进入潜在的威胁区域(最大感观范围)内。我们只需要简单地添加一个360度的扫视,查看玩家化身是否位于最大感观区域之内就可以实现这种能力。在具体实现上,我们可以通过对玩家列表来一次简单的遍历,检查他们的位置,并将他们的位置和AI角色的位置进行比较就可以了。

 

==========================================

为了支持这样的感观能力,我们需要设置每一个AI角色都具有下列属性:

 

  ● 最大视力范围(某些角色观察到的距离可以比其他的角色远)

  ● 最小视力范围(视力范围的最小值。若位于这个范围之内,发现率为100%)

 

  ● 警戒度(某些角色比另外一些角色更善于探测物体)

  ● 注意力级别(如果没有任何行动,注意力级别会随着时间的流逝而逐渐消弱)

http://www.mscto.com

 

  ● 侵略性(一些角色比其他的角色更具有攻击性)

 

  您可以逐个为角色设置这些属性,不过,如果某个AI角色的这些属性未被开发人员设置,引擎会自动将它们设置为默认值。

 

  7.1.1 准备工作

 

  当然,还需要指定AI守卫即将出现的位置。其中,最好的一种方式就是使用世界编辑创建器在游戏世界中放置一些标签,然后使用TorqueScript查找这些标签的位置,并在此处放置一个AI角色。这样做的好处有很多,我们只列出其中一小部分:

  ● 这样很容易就可以从视觉上判定标签放置的场所是否合适。

 

  ● 使用World Editor Creator,关卡或者地图设计者可以使用动态属性值来设置AI角色的属性值。

  ● 编写完成脚本代码,开始进行测试的时候,还可以根据需要及时调整标签放置的位置。 软件开发网

  使用这种可视化设计的方式,如果需要查看放置位置的实际坐标,我们还可以在保存或者新创建的任务文件中获得一个相关文本参考。

 

  (1) 先启动Torque演示程序,运行FPS演示,选中Create Server复选框。

  (2) 进入游戏世界之后,按下F11键打开Mission Editor,然后使用F4键切换到World Editor Creator界面。 http://www.mscto.com

  (3) 在右边较低的位置,点击小加号展开选择树形列表,拖动滚动条,找到Mission Objects, System条目。

  (4) 单击SimGroup条目。您将得到如图7-1所示的Building Object 对话框。在Object Name字段中键入AIDropPoints,然后单击OK按钮。

  现在使用F3键切换到World Editor Inspector,检查一下位于右上方的树形列表。您将看到一个名为AIDropPoints的条目。这就是新的SimGroup。

 

  接下来,我们需要做的就是将我们自动创建的 SpawnPoints插入到SimGroup中。所有新插入到游戏世界中的对象都将被插入到Instant Group中。Instant Group默认被指定为MissionGroup。我们也可以将任意一个SimGroup指定为Instant Group。现在,我们指定AIDropPoints为Instant Group: http://www.mscto.com

  (5) 按住Alt键的同时单击AIDropPoints条目,这将指定AIDropPoints为Instant Group,在列表中该条目将以轻灰色突出显示,如图7-2所示。

  现在,我们已经设定AIDropPoints为Instant Group,重复步骤(1)~步骤(5),创建另一个SimGroup作为AIDropPoints的一个子群。将其设定为Instant Group,并且命名为“Guard”。

  提示:

http://www.mscto.com

 

  通过使用“Alt 单击”的方式可以指定SimGroups为Instant Group,这是一个非常有用的任务组织方法。所以,您应该实践中多多地使用它。

  迄今为止,守卫是唯一一个我们在AIDropPoints内部创建的子SimGroup。如果您想创建其他的AI类型,您也可以使用AIDropPoints,在其内部创建您自己的SimGroups。

  接下来,我们需要在AI角色出现的地方设置标签。非常幸运的是Torque提供了一种形体——一个称之为SpawnSphere的八面体以供我们使用:

 

  (1) 使用F11键退出Mission Editor,然后使用F8键进入可移动摄像机模式。

 

  (2) 调整您的方向,在游戏中找到一个放置AI角色最合适的场所。确保您自己处于高于地形20“英尺”(大概相当于3个或者4个角色模型大小)的地方。从上往下观看您准备来插入AI角色的地点。

 

================================

(3) 使用F11键重新切换回到Mission Editor。

 

  提示:

 

  其实,您并不需要在 Mission Editor和可移动摄像机模式之间进行不断地切换。您可以在Editor中使用可移动摄像机模式。当您移动摄像机修改视图的时候,只需一直按下鼠标右键 (按压鼠标按钮的右侧)即可(记住一点,当您不在Editor中的时候,则不能使用鼠标右键来修改摄像机视图)。可移动摄像机的快捷键不变。

 

  (4) 按下F4键进入World Editor Creator。

  (5) 在底部右方的树形列表中,找到Shapes,Misc条目。

 

  (6) 再次确认您现在在游戏世界中看到的地点就是您准备放置插入标签的位置,单击SpawnSphereMaker条目,就会在游戏世界中出现插入标签。并且会出现一大块红色的线框球体将其包围,如图7-4所示。在图7-4中,请注意看一下插入的标签,它是出现在屏幕中央上面标有其ID “2301(null)”的一个小项。图中标签的ID数字也许和您制作的ID数字不同。 软件开发网

  当然,您也可以将这些标签放置在游戏世界中的任意位置。当您放置这些标签之后,您可以通过下列方式将每一个标签周围的球体缩小。首先,全部选中它们,然后:

 

  (1) 按下F3键切换到World Editor Inspector。

 

  (2) 按下Ctrl键,同时在位于右上方的树形列表中点击它们的树形视图条目,选定所有的球体。图7-5向我们展示了选定两个球体之后的场景。

 

  (3) 选中所有的球体之后,单击位于右下方面板的顶部的Expand All按钮。

 

  (4) 将图7-6作为一个向导,在一个选中的插入标签群中,找到半径字段,删除其中的数字,填入数值10,然后单击Apply按钮。这样,所有已经选中的球体的半径条目都将使用这个新值。

 

  注意:

http://www.mscto.com

 

  在由插入球体的半径所制造出区域的内部,游戏引擎会自动选择一组X、Y和Z的坐标值,用于放置最近新添加的玩家(或者在本文情况下,添加一个AI角色)。球体越大,将会出现越多可能的放置地点,每一个单独的角色最终选择的插入地点也更加随机。

  如果您想更加精确地知道玩家被插入的位置,那么您可以将半径设置为1。如果您想确认玩家将出现的位置,但又不希望出现的位置一成不变,那么我建议您设置半径为数值10。

  在这里,插入球体的底部一定要高于被插入角色正中央的高度。否则,插入的角色就有可能被插入到地下。为了得到最好的结果,我们应该更加细心,一定要让插入球体的底部高于被插入角色的顶部位置。

 

  接下来,需要添加一些动态变量或者属性。请确定此时仍然选中了所有的插入球体。如果还没有选中,请先回到世界编辑查看器(F3)模式下再一次将它们全部选定。然后就是按下Add按钮,在属性列表的底部,添加如表7-1所示的动态域及其数值。按下Add按钮,将会出现动态域添加对话框,请填入相应内容,当然这必须在您已经创建了3个插入标签之后。

 

  表7-1 AI守卫动态域数值

 

  在Mission Editor的菜单中,选择File,Save Mission选项,保存您所做的更改。然后,使用F11键退出Editor,使用Escape键退出当前任务。退出所有和演示有关的界面,回到您进行这一切之前的桌面状态。

======================================

具有静态行为的AI角色(2

紧接着主方向设置之后,代码模块中是AI守卫类型的数据块定义。在这里请大家注意,该数据块名称是AIGurdDB,它是由LightMaleHumanArmor数据块派生而来,因而它自动继承了LightMaleHumanArmor数据块的所有属性。

 

  提示:

  如果您想访问一个父数据块的方法,则可以在调用的方法名称前使用Parent::前缀。比如说,如果父数据块中存在一个名为Foobar的方法,那么可以使用下面的语句来调用该方法:Parent::Foobar();通过这种方式,即使您已经在派生而来的数据块中重新定义了父数据块中的该方法,同样也可以调用父数据块中的该方法。

  在这个新数据块中,为了更加符合我们的要求,我们重新给其中的两个属性进行赋值。

软件开发网

 

  对于需要进行适当反应的威胁, AI守卫可以使用AIGuardDB::checkThreat方法来感知(通过传递参数%obj)。getClosetEnemy方法将由特定的AI守卫实例进行调用。我需要提醒您一下,在这里我们所实现的模块中,玩家就是敌人!

  我们必须知道,AI守卫的每一个实例都可以拥有一个最靠近它的敌人(这些敌人并不是同一个),这是很重要的一点。因而,一个数据块方法并不适合于找到位置上最近的敌人。这也就是为什么我们需要为每一个 AIPlayer对象实例定义getClosetEnemy方法,而不是在AIGurdDB数据块中定义的原因。

 

如果发现了一个潜在的敌人,那么我们首先会检查敌人是否位于该AI对象的视力范围之内(和视场相似)。当然,视力范围是基于某一点某一时刻AI所观望的方向而建立。在某种程度上来说,范围的大小是由伪常量 $ARC_DF_SIGHT来定义,在模块的起始部分它已经被设置为60。如果这个数值并不适合您的游戏,即使是在游戏世界的运行时间,您也可以很容易地使用控制台来更改它——这也正是它是一个伪常量而不是一个真正意义上的常量的方便之处!
软件开发网

 

  然后,我们使用attentionLever属性来测试是否需要一直保持对周围环境进行的观察。在这里,使用它的方式非常简洁:当为非0值的时候,保持观察;否则,停止观察。attentionLever 属性值的慢慢降低也可以用于模拟角色已经逐渐开始厌倦。警惕性高的守卫会一直以最大视场范围进行观察,而已经开始厌倦的AI角色则只是例行公事,不会去尝试任何稍远距离的观察。当然,这是比较简单化的描述。您可以使用这种机制在AI的注意力级别上实现一个更加真实的、成熟的扫视算法。

  假定AI守卫正在关注近距离的事物,它的最大兴趣范围由标准的设置范围(%obj.range)和该守卫当前的侵略性的别(%obj.aggression)来共同决定。守卫越具有侵略性,它进行威胁查找的区域范围就会越大。

  AI总是先处理最近的威胁——也就是紧接着注意力层级代码处理之后的代码部分。

 

  遍览整个模块,您将发现如下的一些语句:

  if ( isObject(%obj)) 软件开发网

  随着游戏情节的展开,威胁对象可能已经被另外一个玩家或者AI角色杀死,从而移出了游戏世界,或者玩家已经退出这个游戏。使用isObject函数,可以让我们在试图对付敌人之前先确定该敌人是否依然存在。 http://www.mscto.com

  接下来的方法AIGuardDB::DoScan可以让您环顾四周。不过,更为重要的是,从这里开始AI角色就开始模拟真实的扫视。在我们这个版本中,我们所做的只是旋转角色面向某一个主方向。(还记得模块起始部分的数组吗?)

  DoScan方法其实是一个调用计划。所以,我们处理DoScan方法的第一个步骤就是取消计划调用,这样我们就不会在结束之前却又重新回到这里。

http://www.mscto.com

 

  在这之后,我们将AI角色注意力的级别降低一点点,最终让它重新回到厌烦状态。如果它一直保持沿着它观看的方向观看,并且不扫视,这就是模拟“注视”的一种方法。

 

  如果AI没有处于“注视”状态,那么它会在每次进行扫视时使用此方法来选择一个随机方向。这里有一个改进,您可以在对水平面进行有规律的扫视时,添加一些随机的方向更改。可以增加一个“妄想狂”属性,当它的值越高就会引起越随机的扫视方向的更改。当然,这里只是我个人的想法。

  AIPlayer方法setAimLocation实现让我们的化身转向某一个方向的动作,它将从我们的主方向查看表中得出方向向量。

  一旦AI改变了它的姿势,我们就使用checkForThreat方法来寻找其他的新威胁、最近的威胁或者检测旧的威胁是否依然存在。如果旧的威胁依然存在,我们就使用AIPlayer方法setAimObject针对于构成威胁的对象采取必要的措施。

  现在,我们已经在代码上把新威胁和已知威胁区别开来,其实我们对它们所做的处理并没有什么太大的不同,不过在实际开发中,对新的和旧的威胁进行不同的处理,可以在调试中可以避免很多麻烦,这是一个很好的办法。

 

  事实上,跟踪新威胁和旧威胁的主要原因是如果威胁已经发生改变,我们就需要改变我们所瞄准的位置。并且如果旧威胁已经不再成为一个威胁,而又没有新威胁出现的时候,我们需要使用AIPlayer方法clearAim来“忘记”正在瞄准的旧威胁。

 

  我们在DoScan中做的最后一件事情就是为以后的应用调度一个扫视过程。具体的指标由以下几部分决定。$MIN_SCAN_GAP定义了最小扫视时间。我们可以根据AI角色的警戒度给它添加一个随机的时间范围。AI警戒度越高,添加的时间间隔也就越短;扫视之间的时间间隔越短,扫视也就越经常发生。

那么,我们该如何让AI按照您的意愿开始向目标射击呢?好,您问出这个问题我很高兴。Torque提供了一个数据块回调函数可供我们使用。当出现下列两种情况的时候,引擎就会调用onTArgetEnterLOS函数:

 

  (1) 已经设置了一个瞄准的目标对象(使用setAimObject函数)

 

  并且

  (2) AI已经发现该目标。

软件开发网

 

  发现目标意味着在AI和指向目标之间没有任何阻挡视线(LOS)的地形或者建筑物。

  通常,在确认了对象存在之后,开始使用onTargetEnterLOS之前,我们所需要做的第一件事情就是设置AI的注意力级别为初始数值。嘿,我们已经看到敌人!当然,我们现在应该集中注意力!我们可以使用在末尾出现的语句,以尽可能快的速度向敌人射击:

 

  %obj.setImageTrigger(0.true);

 

  这将把武器扳机的图像设置在0刻度上。开始疯狂进攻的具体时间由对pauseFire方法的计划调用所决定。开火持续的时间则是由我们“不甘平静的手指”所决定,对手指所设的最小值和最大值决定了最小计划时间和最大可能(随机选定)计划时间。当时间到来的时候,对pauseFire方法的定期调用,AI角色的手指也暂时性从扳机位置上离开。如果不这样做的话,AI就不能再次开火,因为下一次开火之前,“手指”需要离开扳机的位置处。 软件开发网

  在这里,退出该方法之前,我们制定了对方法doScan的另一个调用。注意一下,这两个预定的调用在代码中位置比开始射击的代码位置靠前。

 

  如果一个AI正在射击的目标对象离开了AI的视线,那么AI将停止射击,以便节省弹药。这种情形由数据块回调函数onTargetExitLOS来获取。在该方法中,射击被暂停,瞄准也被取消,目标对象也被“遗忘”。然后,另一个对doScan的调用将在极短的的间隔内开始实行。 http://www.mscto.com

  提示:

  如果您正在设计您的 AI角色,尝试确定其扫视间隔的时间,那么您一定要注意准备在游戏世界中放置AI角色的数目。您放置的AI角色越多,就需要处理越多的扫视过程,这样会耗费宝贵的CPU资源。因此,您放置的AI角色数量越多,扫视之间的最小时间间隔应该越长,这样才可以保证每一个扫视都获得及时的处理而不会影响服务器整体性能。

  现在,我们已经知道pauseFire方法的主要功能——暂停射击(释放扣动扳机的手指),以便我们可以立即再次开火。同样,基于允许射击的最大时间,它也调用了ceaseFire方法。 ceaseFire方法将停止所有的开火行动,清除所有的瞄准,释放扣动扳机的手指,“遗忘”目标对象。 软件开发网

  这些就是我们AI的行动、反应和感觉中最基本的部分。模块中剩余的部分提供在游戏世界中放置AI的方法同时还提供必要的数学函数以及其他的一些工具函数。

 

  下面我将不再一个函数接着一个函数的进行讲解,而是按顺序将模块中其余的部分大致过一遍。第一个AI函数是CreatBots(在demo/server/scripts/game.cs文件中)。在该函数中,我们对所有已经插入到游戏世界中的插入标签进行循环检查。注意一下MissionGroup/AIDropPoints/Guard;字符串,它是我们所放置的插入标签的子SimGroup,因而,它们被称之为“角色”。当AI中的其他角色也被定义了之后,就可以为它们创建新的子SimGroup,比如,“袭击”、“闲逛”或者“巡逻”。然后我们可以使用同样的方式来处理其余每一个角色的插入标签。

软件开发网

 

  对于一个给定角色的每一个插入标签,我们调用AIPlayer方法spawnbot, 将插入标签的索引以及角色名称进行传递。 http://www.mscto.com

  而对于spawnbot方法,要实现的东西就比较多。首先,我们使用AIGuardDB数据块创建一个AIPlayer的新实例。如果我们使用的是另外一个数据块,比如说是AIPatrolDB,那么就需要确定AIGuardDB不是硬编码,而只是作为一个变量来出现。

软件开发网

 

  我们为MissionCleanup对象添加了一个AI对象引用,因此,在任务即将结束的时候,该对象会被移出内存。我们还要将它添加到AIGroup SimGroup中。

 

接着,我们需要对一系列属性实行初始化操作,这是为了防止在任务文件中,任务设计者或者构建者没有对相应的动态域进行设置。然后,使用插入标签的索引和角色的名称选取一个合适的插入标签。当我们获取该插入标签的句柄之后,就可以使用它来访问插入标签的动态域以及它所在的位置。

 

  有一点是要注意的,注意力、侵略性、警戒度和射程设置都来自于插入标签的动态域。不过,这里并不包括对插入标签值的合法性检查(依据我们的伪常量)。在这里,我们还调整了一些属性,这些都只是为了让它们处于一个更加有感觉的范围内。

  接下来,还有非常重要的一步就是对Equip方法的调用。Equip方法旨在武装AI并且向它提供给弹药。Equip方法也是一个非常好的能给AI角色提供一些小工具的地方,比如一个幸运兔足或者其他什么。嘿,到底有没有兔足可不关我的事情!

http://www.mscto.com

 

  最终,我们会在控制台中输出一些信息以方便对程序进行控制。将AI设置为在某一个方向上观望,然后计划设定其第一次扫视的时间。然后,让它自由活动! http://www.mscto.com

  现在,您应该已经透彻理解了Equip方法中一切相关的内容——其实,它里面并没有什么新奇的东西。

 

  在我已经讨论过的方法中,有一个方法值得大家注意,那就是AIPlayer的gerClosestEnemy方法(当然,还有其他方式也可以完成类似gerClosestEnemy方法的功能。)只是它和我所掌握的技术没有一点共通之处,我在这里就不再进行讲解。当AI敌人全部属于几个截然不同的群体的时候,我所使用的方法是非常有效的。基本上,该方法就是对可能敌人列表中所有成员进行循环操作,找出一个和AI位置距离最近的敌人,然后,返回该成员的索引。这种方式可以让我们总是知道所有可能的敌人中,哪一个距离我们最近。

  还有一种方式,您也可以使用 initContainerRadiusSearch或者一个相似函数来搜索某一指定区域。它可以剔除那些不存在威胁的对象类型(比如,树木和岩石),找到存在威胁的最近对象——一个玩家。每一种方式都有自己的独到之处和不足之处,不过,我相信一定还有其他非常实用的方法存在。

 

  数学函数GetBear值得大家注意一下,通过它我可以使用一种新的方法来获得两个向量之间的夹角。在第3章早先的部分,我们已经学习了向量和矩阵的数学知识。GetBearing使用的方法非常简单:找到两点之间的斜率,然后使用反正切(mAtan)获得弧度所表示的夹角,将其转换为度数表示,接着根据它所在的象限调整其值。实际上这些只是中学的基础数学知识。这里还有一个GetRelatveBearing函数,它获得一个对象的方向和另一对象的方向之间的差。该差总是在–180度到 180度之间的范围之内,相对负的方向就是向左,相对正方向是向右。

 

  最后,就是一组调试程序。如果您喜欢,您可以使用它们;如果不喜欢,您也可以不使用。当处理AI行为的时候,您掌握的信息越多,解决问题起来就越容易。所以,您也可以创建一些新的调试程序。

  哦,我的意思是创建新的功能。好,也许我第一次的说法是正确的!

  7.1.4 继续研究守卫

  现在,马上让我们来看一下这些是由什么组成吧。您的任务(也就是您选择接受的工作)就是测试aiGuard的防卫能力。 http://www.mscto.com

  当您更改代码,并且创建aiGuard.cs模块之后,请运行演示程序。当您进入游戏之后,在您放置守卫插入标签附近的区域内小跑,注意不要向右跑。保持在50英尺(目测)之内,然后沿着之字行前进。沿着一个方向之字行,加点小跑动作,然后停止。然后转换为另一个方向,再停止。始终保持观察守卫的动静。看一下,在他们向您发起攻击之前,您和它们之间的距离到底有多近。 软件开发网

  您不仅需要注意在您被发现之前,您达到的最近距离,您还要注意到它们改变其扫视范围的速度。同时,还要注意当它们发现您之后多久才会对您发起攻击。然后(假设您还没有被干掉)您继续移动,留意一下,它们多久之后会失去对您的兴趣,不再攻击您。

以一个弧度前进,不是直线方向,逃出它们的视线领域。看一下您可以维持这种情况多久。

  祝您好运喽

 

===============================================

此文章摘自《3D游戏开发大全》定价:78元 特价:58.5元 购买>>

  7.2 跟踪路径

  最简单地指导AI行为的方式就是路径跟踪(path -following)。路径跟踪并没有像早先提到的路径查找(path-finding)那样精确定义。在路径跟踪中,路径是已知的(虽然它可能是通过使用一个路径或者路线查找方法获得的)。通常情况下,在游戏世界或者地图创建的时候,路径已经被设计者预先定义好了。这就相当于将路径跟踪放到直升梯中。 AI并不固定出现在某一个地方,但是它行走的路线却被路径所严格限制。

http://www.mscto.com

 

  当知道希望AI角色跟踪的行走路线或者路径之后,就可以使用路径跟踪方法。这并不意味着角色必须一直沿着同一个行走路线移动。我们可以预先设定几条路线,让AI角色随机地选取或者基于某种因素进行计算从中挑选出一条路线。

  Torque演示中自带有一个路径跟踪的脚本,它保存在\A3D\demo\server\scripts\ aiPlayer.cs文件中。然而,在Torque游戏引擎1.3版本的演示程序中,实际上并不会调用这段脚本代码。因此在这一部分,我们来使用它。 http://www.mscto.com

  首先,我们需要创建被跟踪的路径。然后,我们需要在游戏世界中放置一个AI角色,编程告诉它跟踪我们所创建的这个路径。为了更简洁明了,我并没有将路径跟踪功能和aiGuard方法(我在本章早些部分提到的)连接在一起的代码包括进来。这段代码的难度不是很大,在这里,我将它留作读者自己的一个练习。

 

  7.2.1 制定一条路径

  打开Torque演示程序,按下F8键进入可移动摄像机模式,然后进入Mission Editor中的World Editor Creator中。

  我们的第一件工作就是创建一个名为Paths的新 SimGroup。创建的方法和您在“具有静态行为的AI角色”一节中的“准备工作”小节中创建AIDropPoints的SimGroup的方法一样。您只需给您的SimGroup起名为Paths,将这个新的SimGroup放置在MissionGroup SimGroup目录下即可,就像AIDropPoints那样。

 

  在完成这些之后,需要在您的Paths SimGroup中添加一些路径。在面板右部较下的地方,展开Mission Objects群,然后在Mission Object群中展开Mission条目。

 

  您会看到两个条目:Path 和PathMarker。单击Path条目,在随后出现的对话框中输入PathA,单击OK按钮。PathA将出现在树形列表的右上端。设置PathA为一个Instant Group(Alt 单击),然后在地面上移动您的摄像机,将它面对着您准备放置一个路径导航点的地点。只要是水平和整洁的地点,都可以放置导航点。单击 Mission,右下方的PathMarker 条目。在随后出现的对话框中输入wp1,单击OK按钮。重复这个PathMarker位移例程,获得至少两个导航点,将其分别命名为“wp2”和 “wp3”。如图7-7所示,您会看到两个导航点之间连接的点线。

 

  您会发现曲线在导航点与导航点之间的转换非常平滑。这是因为所有导航点的smoothingType都设置为“spline”。您也可以将smoothingType设置为“linear”。

 

  当然,您可以在不同的导航点之间使用不同的平滑类型,以便满足您的需要。当您已经设置好您的路径和导航点的位置,请保存好您的任务文件。最好退出演示程序回到桌面,因为我们需要给脚本文件添加更多的代码。

  7.2.2 应用路径

  接下来,打开\A3D\demo\server\scripts\aiPlayer.cs文件,在文件的底部添加如下所示代码段。

  function InsertPathedAI()

  { http://www.mscto.com

  %player = AIPlayer::spawnOnPath("Follower","MissionGroup/Paths/PathA");

 

  %player.mountImage(CrossbowImage,0);

 

%player.setInventory(CrossbowAmmo,1000);

  %player.followPath("MissionGroup/Paths/PathA",-1);

 

  }

http://www.mscto.com

 

  将文件重新保存。现在运行演示程序,当您进入游戏的时候,进入可移动摄像机模式,将摄像机移动到您设置的路径某个位置的上方,俯瞰这片区域。打开控制台窗口(使用~键),输入:

 

  InsertPathedAI();

 

  然后按下Enter键。一个AI角色就会出现在游戏中,并且沿着您设置的路径奔跑。这里关键的方法是调用:

 

  %player.followPath(“MissionGroup/Paths/PathA”,-1);

 

其中第二个参数用于指示路径上将要进行的下一个节点。一个节点就是我们早期放置的路径标记的路径版本。每一个路径标记在路径中都是一个节点。–1表示AI角色需要去的最近节点处(这将是路径上的第一个节点,很巧合的是,这也将是它插入到游戏世界中的位置),然后就是依照次序沿着插入标签在路径上奔跑。

  AI跑,继续跑!

  为了让跑步显得更加生动、灵活,我们需要给AI角色添加一点主动性和活跃性。在\A3D\demo\server\scripts\aiPlayer.cs的末尾部分添加如下代码段:

  function InsertPathedAIShooter() 软件开发网

  {

  %player = AIPlayer::spawnOnPath("Shooter","MissionGroup/Paths/PathA");

  %player.mountImage(CrossbowImage,0);

 

  %player.setInventory(CrossbowAmmo,1000);

  %player.pushTask("playThread(0,\"celwave\")");

  %player.pushTask("followPath(\"MissionGroup/Paths/PathA\",2)");

  %player.pushTask("aimAt(\"MissionGroup/target\")");

 

  %player.pushTask("wait(5)");

  %player.pushTask("fire(true)");

  %player.pushTask("wait(1)");

  %player.pushTask("fire(false)");

 

  %player.pushTask("wait(1)");

软件开发网

 

  %player.pushTask("followPath(\"MissionGroup/Paths/PathA\",1)");

软件开发网

 

  %player.pushTask("aimAt(\"MissionGroup/target\")"); 软件开发网

  %player.pushTask("wait(1)");

 

  %player.pushTask("fire(true)");

软件开发网

 

  %player.pushTask("wait(1)");

 

  %player.pushTask("fire(false)"); 软件开发网

  %player.pushTask("playThread(0,\"celwave\")");

  %player.pushTask("done()"); http://www.mscto.com

  }

  遗憾的是,这里的AI脚本代码(由GargeGames支持)中存在着一个错误。还好,我们可以对此进行修正!找到AIPlayer::aimAt方法,它大概在aiPlayer.cs文件中位于208行左右。在该函数中找到下面一行语句:

 

  %this.setAimObject(%object); 软件开发网

  在它之后,添加这样一行语句:

 

  %this.setAimLocation(%object.getPosition());

  添加的这行语句将使AI角色直接面向目标对象的坐标位置。

  好了,在进行测试之前,请先确保您已经完全退出演示程序。随后,重新运行演示程序,进入游戏世界中。

  进行测试之前,我们需要为之添加一个目标对象。使用 Mission Editor Creator,在场景中添加一棵树。您可以在Creator中的Static Shapes、demo、data、Shapes和trees group中很容易地找到树。不过在这里,这棵树属于mission SimGroup而不是其他的SimGroup。将树命名为“target”(记住一点,在树对象的Mission Editor Inspector视图中的Apply按钮的下方区域处设置名称)。退出Mission Editor。当您将自己放置在观察您先前创建的路径区域上的位置之后,打开控制台,输入下面的命令:

 

InsertPathedAIShooter();

  看,一个好玩的AI怪物!

 

  现在,查看一下aiPlayer.cs模块。您会发现其中有一些函数和我在aiGuard.cs中提供的函数非常相似。很明显,这里有太多的方法可以完成相似的任务,正如这两个模块的不同之处所强调的那样。

  您还应该注意到,在aiPlayer.cs文件中,为了管理AI的任务而新添加的工具函数。在InsertPathedAI函数中,玩家只是被简单地告知去跟踪路径。但是在 InsertPathedAIShooter函数中,任务则被排成队列。队列中的第一个任务也就是时机到来时,第一个被执行的任务。这样,您可以一次积累一组需要被完成的任务,就像一个初始序列,只需要使用nextTask和wait之类的函数,在合适的调度时间之内将任务完成即可。