即时战略游戏中如何协调对象移动
2010-04-05 01:00
197 查看
作者:Dave C. Pottinger 翻译改写:lzc 在图论中人们研究了通过怎样的计算才能找到一条从A点到B点的通路,以图论本身来说这已经解决了从A到B的问题,剩下的只是从A沿着找到的路线移动到B就可以了。这样的认识基于一个默认的假设--道路中的一切障碍物都是固定的,但是在现在已经广泛流行的即时战略类游戏中问题却远远不止这些。举个例子说上下班高峰期的时候,路上的每一个人都清楚地知道自己的目的地和所要走的路径,但是由于某些个人不遵守规则或其他人为原因还是会造成堵车现象。而如果这样的事情发生在一个即时战略的游戏中,那么带给玩家的沮丧感和愤怒将远超过现实中的堵车现象。当游戏中的一个士兵接到玩家的命令要从基地的一侧移动到另一侧以帮助抵抗敌人的进攻时,它需要一个计划(更明确的说是一条寻找出来的路线)使它能够到达目的地,但很可能在它移动的过程中预定的路线上出现了变化(例如玩家让工人们在士兵的必经之路上修建一个建筑或另外一批士兵出现在路上,从而堵塞了道路),这时如果没有一个优秀的移动系统,那么之前的寻道工作等于是白费工夫。 本文将介绍一种相当有效的个体移动系统,以从另一个角度探讨游戏中的自动移动问题。虽然本文主要是针对即时战略类型,但所介绍的方法可以很容易的扩展到其它的类型中使用。部分基本定义: 移动(Movement): 本文主要指对于一条已知路径的执行。简单的移动算法使个体沿给定的路线运动,复杂的移动算法在移动的同时进行碰撞判断,调整各个体的运动以避免碰撞并允许个体组成特殊的队形共同运动。 寻道(Pathfinding): 找出所需路径的工作。所使用的算法可以是简单的遍历也可以是经过高度优化的A*算法。 中继点(Waypoint): 当个体要前往目的地是所必须经过的路径上的点。每一条路径拥有至少2个中继点:起点和终点。 个体(Unit): 游戏中可以在整个游戏地图中移动的实体。
首先让我们对比一下同时移动单个对象与同时移动数十、数百个对象的不同。一次移动一个对象是非常简单的,但是一个可以相当完美的移动单个对象的算法并不一定能很好地解决数百个对象的同时移动,这其中最大的问题就是CPU时间的消耗。请一定要切记如果你要制作一个需要同时移动大量个体的游戏程序,那么在CPU的使用上一定要非常的保守。 某些移动算法是很依赖CPU速度的,这就是那些要同时移动大量个体的游戏中只有很少的一部分支持高级的移动方式(例如个体的加速和减速)的原因。玩家们总是认为游戏中的大船和被重装备武装起来的战士们应该具有能够加速和减速的能力,这样才能体现出真实性,但是这小小的真实性将会增加超乎想象的额外的CPU计算工作。这种情况下事实上用来处理个体移动的时间增加了,因为你不得不花费更多的时间来计算加速度,从而获得新的速度值。在后面我们扩展例子程序到处理移动的预操作部分时,你将清楚地看到这样的工作所增加的计算复杂度有多大。另一个将会大大增加CPU计算量的问题是个体的转动半径。大多数寻道算法都不考虑个体的转动半径(转动半径是指一个个体原地旋转一周所需的最小圆的半径,因为我们不可能让一个士兵倒退着走上半张地图去袭击敌人的基地,所以经常需要个体进行旋转)对道路选择的影响。于是就会出现一种情况,虽然我们的一头大象已经找到了一条通往目的地的通路,但是它却不能沿着这条路线移动到目的地,因为路线中的一个拐角面积要比大象的转动半径小一些。大多数移动系统通过减缓个体的速度,再作出一个缓慢而更节省空间的转身动作(相信很多人都看到过即时战略游戏中士兵在拐角处被堵住时所作的动作吧)来解决这一问题,但这种方法会极大的增加CPU的计算量。 正如大家所想象的那样,即使是即时战略游戏也并不是即时的,而是不断的进行循环,在每次循环中处理游戏的全部数据和玩家的指令(我们可以称它为UL-Update Loop)。为了增加游戏的性能,一般采用的方法是记录上一次UL的所消耗的时间,以预测下一次UL大约将要花费的时间(为了能尽可能地逼近即时处理,这样做是很有必要的),但是这就给个体的移动带来了大问题--每次UL中个体的移动距离很可能是完全不同的,下面的图1就是这种情况的一个例子。负责个体移动的算法显然在面对每次移动相同距离时比每次都要为所有移动个体计算不同的时间下移动出的不同距离并将其显示出来要轻松得多。当然,如果游戏的UL系统制作的非常优秀,那将略微改善一点这样的窘境。
注意考虑地图的复杂性。在复杂的地图上实现良好的移动的算法比简单地图上做到同样效果的算法要复杂得多,由于现在地图越来越倾向于复杂和真实,对于移动算法的要求也进一步提高了。 随机生成的地图可能造成意想不到的问题。由于不能通过给固定道路编写预定路线来减小寻道的难度,因此随机生成的地图在复杂性上要更高于预设地图,尤其对于寻道而言。当寻道变得使CPU负担过重时,唯一的解决方法是降低寻道的精度和质量,这时就要求提高移动算法的质量以弥补寻道上的不足造成的程序反应迟钝。 一定要认真处理各类个体的体积和由此产生的空间问题,这个问题最能说明你所需要的移动算法的精度。如果你的程序中需要移动的物体很少,以至于几乎不会出现彼此互相碰撞的情况(例如大部分的第一人称射击游戏),那你可以放心地使用一些简单的算法。如果你的程序所要处理的移动物体非常多,并且还要处理彼此的碰撞和诸如检测两个个体之间的缝隙是否足够更小的个体从中间穿过等等操作,这时对你的移动算法在精确度上和质量上的要求将会使计算量大幅度的增加。
移动循环 { if "增加中继点(Waypoint)": 增加中继点。 if 正在巡逻: 沿指向目的地的方向获取下一个中继点 设置状态为"等待寻道" else: if 已经不能再得到下一个中继点: 设置状态为"已抵达目的地" else: 设置设置状态为"等待寻道" if "已抵达目的地": 作出适当的通知(如果存在通知方式的话) 移动完成,停止播放移动动画,退出函数 if "等待寻道": 找出一条通路并将之保存 if 无法找到通路 失败,函数退出 计算出向中继点移动所需的方向 通过旋转改变个体的方向指向中继点 使用新的方向,计算一次UL时间将会移动到的位置 if 新位置将会导致碰撞 设置状态为"等待寻道" 返回循环头 判断现在和移动后的位置: if 移动之前距离中继点更近: 设置状态为"增加中继点" 返回循环头 if 移动过程中将会经过中继点: 设置状态为"增加中继点" 跳出移动循环 } 设置加速度 进行移动 设置并更新所需要使用的动画 刷新预期位置 我们假定被给定了一条通向目的地的路径,并且这条路径在提供给我们时是精确的,并且是可行的(也就是不会发生碰撞)。由于大多数即时战略游戏拥有巨大的地图,以至于一个个体可能要花费几分钟的时间来走完整个路程,而在这几分钟里地图上可能发生使当前路径不在可用的变化(例如在路径上新建了建筑)。为了解决这个问题,现在我们加入少许碰撞检测,一旦遇到碰撞,就进行重新寻道。在后面你将看到,我们可以采取几种方法来避免重新寻道,以减少CPU消耗。
大部分即时战略游戏中使用的简单的碰撞判断实际上是将每一个个体看作一个球体(2D中是圆),再进行简单的球体碰撞检测,而不在意这样简单的判断是否能够满足游戏对于碰撞的要求。这样做确实有利于提升性能,即使一个游戏要进行非常复杂的碰撞判断--例如判断击出的拳头是否击中敌人,或者甚至是低精度的多边形(polygon to polygon)交叉判断,这时为可能出现的碰撞保有一个球体区域往往也能够提升程序的性能。 当我们设计一个碰撞检测系统时要注意面对3种截然不同的实体:单个的个体、一群个体的集合以及经过队形编制的个体群(就像《帝国时代2》中的阵形一样,见图2)。事实上对所有这3种类型使用简单的球形判断都能工作得很好,单个个体可以简单的使用单个球体进行它的全部碰撞判断,而对于其它两种情况只需要再稍微增加一点工作量。
对于编队来说,不仅仅要进行如上面的组群那样简单的个体碰撞检测,还要进行大量更加复杂的检测操作。首先要保证编队中的个体之间不能互相碰撞,同时如果编队中的各个个体之间有一定的缝隙,还要保证任何一个不属于该编队的个体不能占用这一空间。另外,一个编队应该不能改变队形或重组,但是游戏的规则又可能规定当没有足够大的通路提供给整个编队保持队形穿越某一障碍物时,编队可以先散开,待各个体越过障碍物之后再重新组成编队,这样的设计更加体贴玩家。 我们可以尝试使用基于时间的碰撞描述机制。立即碰撞用来描述当前正发生在两个个体之间的碰撞;未来碰撞用来记录预计在程序运行的后续时间中将会发生在预定地点的碰撞(当然,前提是将要碰撞的对象都不改变各自的移动路线)。在任何情况下立即碰撞的情况都应该比未来碰撞的情况更优先被处理。同时我们也应该定义碰撞的3种状态:未处理的、正在处理和已处理完毕的。
另一种方法是创建一种称之为移动线路(Move Line)的对象。我们可以使用这条移动线路来描述个体的移动,个体的原始位置作为线段的起点,目的地作为线段的终点,就好像《红色警报2》里所表现出来的那样。这种方法并不用添加新的数据,但是会加大碰撞检测部分的复杂性--我们必须把简单的球形碰撞检测修改为对一个点到一条线段的距离的检测,而这样做将会增加计算的难度,也会消耗更多的时间。大多数3D游戏都已经实现了一种可以快速挑选出可被游戏者观察到的物体的分级系统(也就是能够迅速地判断出游戏中的哪些物体处于玩家角色的视野之内的系统),我们可以对该类系统进行修改,使它们可以用来快速挑选出那些在我们的游戏中可能发生碰撞的个体。这样做的好处是大幅度地减少了需要进行碰撞判断的个体的个数,于是所需要的计算量常常就能够降低到所能允许的范围之内了。
预测的位置只不过是一个位置列表(至少包含个体的运动方向和时间标记,有时也需要记录加速度等信息)以指出未来某时刻个体所在的位置,参考图4。一个移动系统可以将用来实现个体移动的算法拿来负责计算个体的位置预测,这些预测越准确其可用性也就越大。当然预测计算也会增大计算量,为了不降低游戏的效率,下面我们就来讨论一下如何减少多余的CPU消耗。
在实际的应用中,你可能每次只能进行一个UL时间的计算来进行位置预测,这时要注意未来每次UL的时间很可能并不等长。如果只是简单的按照预测位置移动个体而不考虑每次UL的长度,这将有可能造成一些问题,当然某些游戏(或者游戏中的某些类个体)可以很好的适应这样的操作。一般的游戏都通过每次对列表中的数据进行一些修正来改善预测的准确性,而这样做的同时也应注意何时应该完全抛掉原来计算的已与现在情况有较大误差的预测而重新计算整个列表。 实际对位置预测的应用中主要的难题是由于我们在碰撞检测中将这些预测的位置做为个体的当前位置来使用所造成的。你将很容易的看到对某给定的区域内个体预测位置的比较所需要消耗的计算量,但是为了很好的实现个体间的协作我们必须知道未来一小段时间内每个个体的目的地以及它们可能会与哪些其它个体相碰撞,这都需要一个优秀而且快速的碰撞检测系统。此时最佳的优化措施就是如同前面所述使用3D引擎中的相关部分舍去那些不大可能碰撞的个体组合,这将允许你使用更多的CPU时间来处理最可能发生的那些碰撞。
如果我们的工作做得很好,那么我们所要处理的绝大部分碰撞将是未来的碰撞(因为我们已经使用位置预测尽量地避免了立即碰撞)。由于处理未来碰撞最后的方法将是停止移动并重新寻道,因此为了不使寻道过于频繁,尽量使用其他方法解决碰撞就变得很重要。 下面就详细的介绍对于这种个体与个体碰撞的方法。 未处理的碰撞: CASE 1:if 个体已经全部停止移动: 1. if 是低优先级的个体,什么也不做 2. if 是高优先级的个体,找出哪一个个体将要移动(如果存在),告知该个体进行尽可能最短的移动来解决碰撞,改变状态为正在处理的碰撞 CASE 2:if 个体没有移动,是另一个个体将要移动,什么也不做 CASE 3:if 当前个体正要移动,其它个体已经停止 1.if 是高优先级个体,其它停滞个体为低优先级并且能够从通路上移开,计算出下一步的位置并通知低优先级的个体从通路上移开(见图7),改变状态为正在处理的碰撞
3.Else,if 是高优先级个体并且能够沿移动路线推动低优先级个体,推动它,改变状态为正在处理的碰撞 4.Else,if 停下,重新寻道 CASE 4:if 当前个体正在移动,另一个个体也在移动: 1.if 当前个体是低优先级,什么也不做 2.if 碰撞不可避免,并且当前个体是高优先级,通知另一个体停止移动,转状态为CASE 3.1 3.Else,if 当前个体是高优先级的,计算出下步移动位置,通知另一个体减速到足以避免碰撞。 正在处理的碰撞: 1.if 是一个移动的个体要处理CASE 1的碰撞,并已经移动到了目的地,碰撞解决 2.if 是CASE 3.1中低优先级个体,并且高优先级个体已经抵达预定位置,开始返回原位置,碰撞解决 3.if 是CASE 3.1中高优先级个体,等待(减速或停止)直到低优先级个体从通路上离开,之后继续移动 4.if 是CASE 3.3中高优先级个体并且现在低优先级个体已可以从通路中离开,转状态为CASE 3.1 5.if 是CASE 4.3中低优先级个体并且高优先级个体已经抵达预计地点,恢复移动速度,碰撞解决 解决碰撞的关键之一是排定个体优先级的顺序,如果没有一套强壮的完好定义的优先级体系,你将看到碰撞在一起的个体有如旋转木马一般运动,因为每个个体都要求对方让出道路,而同时又没有一个个体能拒绝这个要求。我们也应该为碰撞进行分级,在处理时应该优先处理那些有最高优先级的碰撞,当有足够富裕的时间时再去处理那些优先级低一些的碰撞。在游戏中碰撞处理也需要注意碰撞个体的密度。如果一场大型的战斗使得许多的战士在狭小的空间中碰撞在一起,那你就应该花费更多的CPU时间来处理这些碰撞而不是地图上远处两个矿工间的碰撞。对这类较容易发生碰撞的区域的关注的另一好处是你将能够在其它个体进行寻道时使它们避过这类区域。
组队(Group)移动 首先要弄清楚何谓组队(Group):由用户(玩家)为方便操作而选取的简单的个体集合(一般会对其成员发布相同的命令),除了在移动时要保持成员一同移动之外组队并没有其他对移动系统的限制。组队的使用使我们必须记录许多信息,例如组队成员的列表以及当整个组队还在一起时所能移动的最大速度。也许我们还应该保存整个组队的中心,以作为一个可以很容易得到的操作参考点。同时还应该选定一个组队的指挥者,大多数游戏中怎样选出这个个体并不重要,重要的是一定要有一个这样的个体。 在我们开始工作之前有一个问题需要回答:当组队在地图上移动时我们有必要保持所有个体在一起吗?如果不,组队将只是为使用户方便操作而存在的,每一个个体都会独自寻道和移动就如同用户对每个个体分别下达指示一样。当我们关注如何加强组队的管理时,我们可以发现组队的凝聚力可以分为多个等级。 组队中的个体都以相同的速度移动。一般地这将使用组队中速度最低的个体的最大速度,不过有时让那些速度较慢的个体在组对中移动的稍快一些会更好(见图8)。然而一般游戏的设计人员给一类个体较低的速度总是有原因的,例如如果允许强力的个体能够非常高速的在地图上移动将会极大的破坏游戏的平衡性。
怎样才能实现最后的要求?这要使用一种分级的移动系统,这样我们就能在处理每个个体的移动时兼顾那些同属于某个组队的个体了。如果我们对组队的个体创建一个组队对象,我们就能够记录所有必需的数据,为整个组队计算最大速度,以及判断何时需要前面的个体等待后面的个体。下面就是一个组队类的简单定义: Listing 2. BUnitGroup. //***************************************************************************** // BUnitGroup //***************************************************************************** class BUnitGroup { public: BUnitGroup( void ); ~BUnitGroup( void ); //Returns the ID for this group instance. int getID( void ) const { return(mID); } //Various get and set functions. Type designates the type of the group //(and is thus game specific). Centroid, maxSpeed, and commander are //obvious. FormationID is the id lookup for any formation attached to //the group (will be some sentinel value if not set). int getType( void ) const { return(mType); } void setType( int v ) { mType=v; } BVector& getCentroid( void ) const { return(mCentroid); } float getMaxSpeed( void ) const { return(mMaxSpeed); } int getCommanderID( void ) const { return(mCommanderID); } BOOL getFormationID( void ) const { return(mFormationID); } BOOL setFormationID( int fID ); //Standard update and render functions. Update generates all of the //decision making within the group. Render is here for graphical //debugging. BOOL update( void ); BOOL render( BMatrix& viewMatrix ); //Basic unit addition and removal functions. BOOL addUnit( int unitID ); BOOL removeUnit( int unitID ); int getNumberUnits( void ) const { return(mNumberUnits); } int getUnit( int index ); protected: int mID; int mType; BVector mCentroid; float mMaxSpeed; int mCommanderID; int mFormationID; int mNumberUnits; BVector* mUnitPositions; BVector* mDesiredPositions; }; BGroup类在其内部管理整个组队中个体之间的交互操作。在任何时间点,它都应该有一个时间表以来处理组队内的个体之间的碰撞,它也应该有能力通过参数和优先级管理来控制或修正个体移动。如果你的游戏只支持一种移动优先级,那么你就应该为你在组队中的个体们添加第二种优先级。虽然一个组队对外的表现似乎只有一种优先级,但在其内部还是应该分为不同的移动优先级。基本上来说,BGroup类是另一个完善的封闭的移动系统。 组队的指挥者将负责整个组队的寻道工作,它将决定整个组队的移动路线,在简单的组队移动系统中所需的工作只是由这个个体本身来寻道即可。然而在下面的部分中我们将看到指挥者所能够作的其它事情。 编队控制基础 首先应该给出编队的定义:编队(Fomation)是一种更复杂的组队,编队有自己的方向(前方、后方、左翼和右翼)。编队中的每一个个体都试图保持自己在编队中的位置,而这个位置是唯一固定的也是相互关联的。更加复杂的模型使得编队中各个个体的朝向需要单独处理,而同时也要求在移动中提供整体旋转的方法。 编队是建立在组队系统之上的,它是一种限制更加严格的组队,因为我们必须非常详尽的规定编队中每个个体的位置。所有的个体在移动中必须保持一起行动,并要求在速度、路径上一致以及相互之间的位置和距离保持不变--如果在移动中编队出现了大间距的缝隙,那么它也就与组队没有什么不同了。 下面给出的这个BFomation类能够清晰的管理一个编队的预定位置(我们要求编队中的每个个体所处的位置以及它的方向)、编队方向和编队的状态。大多数游戏中所使用的编队都是预先定义的,显然,在开发过程中进行这项工作是很简单的(通过使用一些非专业人员也能熟练操作的文本编辑器就可以很好的完成这项工作)。我们当然希望能在游戏过程中实时的定义编队,但这样做就需要更多的内存以保证每一个由玩家定义的编队都能在内存中保留一份自身定义的副本。 Listing 3. The BFormation Class //********************************************************* // BFormation Class //********************************************************* class BFormation { public: //The three formation states. enum { cStateBroken=0, cStateForming, cStateFormed }; BFormation( void ); ~BFormation( void ); //Accessors for the formation’s orientation and state. The expectation //is that BFormation is really a data storage class; BGroup drives the //state by calling the set method as needed. BVector& getOrientation( void ) { return(mOrientation); } void setOrientation( BVector& v ) { mOrientation=v; } int getState( void ) const { return(mState); } void setState( int v ) { mState=v; } //The unit management functions. These all return information for the //canonical definition of the formation. It would probably be a good //idea to package the unit information into a class itself. BOOL setUnits( int num, BVector* pos, BVector* ori, int* types ); int getNumberUnits( void ) const { return(mNumberUnits); } BVector& getUnitPosition( int index ); BVector& getUnitOrientation( int index ); int getUnitType( int index ); protected: BVector mOrientation; int mState; int mNumberUnits; BVector* mPositions; BVector* mOrientations; int* mTypes; }; 使用这个模型,我们必须时刻关注编队的状态。cStateBroken表示编队并没有被创建也没有创建的企图;cStateForming表明我们的编队正在建立但还没有达到cStateFormed状态;一旦所有的个体都已位于它们的预定位置,我们就可以将状态改变为cStateFormed。为了使编队的移动简单化,我们可以使一个编队在完成组建之前(达到cStateFormed状态之前)不可移动。 当我们准备使用一个编队时,第一件工作就是组建这个编队。当给定一个编队时,BFormation(译者注:原文这里是BGroup,但该类并没有编队管理功能,经过反复推敲认定为编写错误)控制每个个体移动到编队中的预定位置,该位置的计算是与当前编队方向相关的,如果这个方向发生了变化,那么预定位置将自动被重新计算并修正为正确的位置。 为了组建一个编队,我们可以使用预定安置--每一个预定位置拥有一个预设值(由定义规定或由算法确定)来指明个体组建编队时应该按照那种顺序进驻那些预定位置,这样才能使整个组建过程从里到外进行得相当有条理(见图10)。下面的算法列表说明如何实现这样的组建方式。 Listing 4. 设置组队中的所有个体移动优先级到一个相同的低优先级 设置状态为cStateForming While 状态为cStateForming { 找出离编队中心最近的未有个体占据的位置 If 没有个体再可用 设置状态为cStateFormed,跳出组建循环 选定一个个体前往所找出的位置,要求满足如下条件: 使个体移动距离最短 与其它编队成员碰撞的几率最小 移动时间最短 设置个体的移动优先级到中等值 等待直到个体就位(可能要经过多个UL时间) 设置个体移动优先级为最大值,这样做可以保证以后进行组建工作的个体不会使该个体离开其位置 }
现在我们已经可以将编队在整个地图中移动了,但是由于游戏的地图是动态而且复杂的,所以很可能出现选出的移动路径不可用的情况。如果这样的事情发生,就需要我们对编队进行操作,一般的操作方式有3种,下面就逐一介绍。 缩放个体间距(Scaling unit positions).由于编队中的预定位置都是由矢量进行定义的,因此我们可以很方便的对整个编队的间距进行放缩以使它变得更小,这就使得编队能够通过城墙或树林中更小的缝隙(见图14)。这种方法对于那些排列得较为分散的编队很有效,但对于那些排列紧凑的编队就没有什么用处了。
路径栈就是一种简单的用来记录个体移动路由信息的栈操作(后进先出,见图17)。一个路径栈记录的信息一般包括个体当前所采用的路线,现在个体正在向哪个中继点移动以及个体是否正处于巡逻中。一个路径栈对我们的目的有两大作用。
这样创建保存路径信息的结构还能提供给我们一些其它好处。通过将一条高级寻道路径拆分成多个低级寻道路径,我们可以在执行具体路径之前充分的对这些低级路径进行更精确的计算。而且如果我们确定高级寻道的结果是可用的话,也可以将低级寻道的工作略微推后再做。如果我们正在进行高协调度的个体移动,路径栈将允许我们向栈顶添加一条临时的用来避免碰撞的路径,并能够很好的使用这一路径修正个体移动(见图19)。
我们定义混合碰撞为同时发生在两个以上个体之间的碰撞。大多数游戏都对于可以解决的混合碰撞中的个体个数有限制,超过这个数目就只能分几次解决了。下面我们将探讨如何使用已有的移动系统对这类情况进行简单的处理。 如果我们遇到了一个由三个个体造成的混合碰撞(见图20),首要的工作是找出其中优先权最高的个体。一旦找到它,我们就要立即找到与之碰撞的另一个个体并确定优先权最高的个体所遇到的最主要的碰撞(该碰撞可能发生在其与次优先的个体之间,也可能不是)。当我们找到了这两个个体后,剩下的工作就交给原来的碰撞处理部分解决即可了。
一旦我们更新了碰撞中的个体,下一步工作就又回到寻找优先权最高的个体的碰撞上来了。我们将一直重复这一步骤直到所有的碰撞都被解决。 你可以在两个地方使用这一系统:碰撞解决部分或碰撞预测系统中。碰撞解决的规则必须被修改以适应对于个体优先级的要求,这样的修改并不难,但会增加一定的代码量。或者你可以修改你的碰撞预测系统使得只会发生两个个体碰撞的情况,然而这样做你仍然需要先找出一次碰撞中的全部个体并作出操作。 解决堆叠峡谷问题(The Stacked Canyon Problem) 所有移动系统的最终目的都是要实现智能的移动效果,而所有处理中最能体现智能的就是处理堆叠峡谷问题了(什么是堆叠峡谷问题呢?事实上当一个个体要从一群排列紧凑的个体之间穿过时所需要解决的问题就是堆叠峡谷问题,图21就是一个例子)。虽然此类问题并不能简单的一次解决,但我们可以重用前面的一些简单方法来解决它。
一旦我们判断出将要解决一个堆叠峡谷问题,就可以采用一种简单的递归调用个体协调运动系统的方法来解决它。把第一个被推动的个体作为前进个体处理其于第二个个体之间的关系,并如此循环。每一个个体被它的前进个体推动直到它可以移动到一边而让出道路。当最后一个个体也从陆上让开后,原来的前进个体就可以继续移动了。 一个好的习惯是将已经移开的个体在移回原位。为了能够这样做,我们应该记录整个推动过程并在问题解决后倒序的执行该过程。另外如果负责移动的代码能够辨别出前进个体是否归属于一个组队,那就能保证组队中每个个体都能在原来阻碍道路的个体返回原位置之前通过。 注意 优化你的整体系统。如果你只是要做一个2D游戏,那就会有许多多余的计算是可以取消和简单化的。不论你是要做2D游戏还是3D的,你的碰撞检测系统都需要一个优秀的经过优化的个体分拣系统,这类系统已经不再仅仅用于绘图了。 对高级寻道和低级寻道使用不同的方法。过去大多数游戏对这两种寻道方式使用相同的算法。这样做的害处是如果对高级寻道使用低级寻道的算法将使高级寻道变得缓慢并且不能用于寻找长的通路;相反的,如果对低级寻道使用高级寻道的算法将会造成结果并没有将道路上的所有障碍物考虑在内或者造成一个个体能从其它个体之中穿过。一定要抓住要点制作两套寻道系统。 无论你做什么,个体总会交叠碰撞在一起。个体的交叠和碰撞是不可避免的,或者按最好情况说将是非常难以操作的。你最好尽早处理这些碰撞问题,这将使你的游戏更好一些。游戏的地图已经越来越复杂了,并且还会加入随机地图的处理。一个好的移动系统将能够很好的处理随机地图和相应的一切细节。 清楚地了解UL是怎样影响个体移动的。可变化的UL时间将是你的移动系统所必须解决的一大难题。可以使用一个简单的修正机制来解决此类大部分的问题。 只涉及单个UL的做法是过时的。没有计划的编制不可能解决好个体移动的协调问题,如果不记录上一次UL中的操作和将来要发生的问题又是不可能制作好的计划的。一个能够运作良好的移动协调系统必须在任何时候都能够参考以前的碰撞列表和预测碰撞的列表。切记解决碰撞的过程中出现的较小的变化是可以忽略的。
|
相关文章推荐
- 即时战略游戏中如何协调对象移动
- 即时战略游戏中如何协调对象移动(转帖)
- Cocos2d-x 游戏&如何修复在iPad2上对象移动时屏幕局部闪烁的问题
- [unity3d]如何实现游戏对象跟随鼠标方向移动
- [unity3d]如何实现游戏对象跟随鼠标方向移动
- 即时战略游戏中实用的寻路算法都有哪些,比较如何?
- 【干货推荐】如何改进移动游戏测试方法和发布策略
- 如何用cocos2d-x来开发简单的Uphone游戏:(二) 移动的精灵
- 关于unity动态添加游戏对象问题,如何动态添加一个菜单【一】
- [脚本]实现游戏对象跟随鼠标方向移动
- 如何在印度选择最好的移动游戏开发公司
- Unity中国技术总监刘钢:如何优化基于Unity开发的3D移动游戏
- cocos2dx-深度解析plist文件(一)(游戏对象的数据如何从plist创建获取)
- unity3d之进度条跟着游戏对象移动
- 在Unity3D中如何让游戏对象围绕某个特定的对象旋转
- android游戏中实现对象的触屏移动
- 关于unity动态添加游戏对象问题,如何动态添加一个菜单【二】
- [脚本]实现游戏对象跟随鼠标方向移动
- 移动平台化进程加速 网秦如何实现战略转型?
- Unity3d 如何找到游戏对象并改变其颜色