您的位置:首页 > 编程语言

游戏编程中的寻路算法研究

2013-08-03 04:30 1136 查看
近年来,游戏产业的快速发展带动了游戏中人工

智能(Artificial Intelligence,简称AI)的发展,越来越

多的游戏采用人工智能技术提高游戏的可玩性。在电

子游戏中,玩家操控主要角色,而其他角色的行为逻

辑由人工智能操纵,这些角色我们称之为NPC(Non-

Player Character,非玩家控制角色)。大部分游戏在开

发过程中都会遇到路径探索问题,快速、准确地计算

出游戏角色从地图中的A点到达B点的一条路径,一

直是游戏开发者追求的目标,同时也是游戏人工智能

研究的一个重要方面。

1 游戏编程中的简单寻路算法

在游戏关卡中常常会放置一些怪物(即NPC),这

些怪物通常在一个区域内走来走去,这个区域被称为

“巡逻区域”;一旦玩家的角色进入怪物的“视野”,怪

物就会发现玩家角色,并主动向其所在的位置移动,

这个区域称为“警戒区域”;当玩家角色和怪物更加靠

近时,会进入到怪物的“攻击区域”,这时怪物会对玩

家角色进行伤害。在某些RPG(Real-Time Strategy

Game,即时战略游戏)中,NPC在不利的情况下还会

选择主动逃跑。如何模拟这些行为逻辑,目前游戏业

已经有一些比较成熟的方法。

1.1 随机寻路算法

随机寻路算法适合模拟游戏中那些没有什么头脑

的生物,它们总是在场景中漫无目的地走来走去。可

以用以下的代码进行模拟:

npc_x_velocity=-5+rand()%10;

npc_y_velocity=-5+rand()%10;

int npc_move_count=0;

while(++npc_move_count<num){第4期8 5

npc_x+=npc_x_velocity;

npc_y+=npc_y_velocity;

}//endwhile

在上例中,NPC会选取一个随机方向和速率运动

一会儿,然后再选取另一个。当然,还可以加上更多

的随机性,如,改变运动方向的时间不是固定的num

个周期,或者更倾向于朝某个方向等。实际编程中还

必须考虑到碰撞检测,当NPC遇到障碍物后,会随机

选取一个前进的方向,继续行走。

1.2 跟踪算法

当游戏中的主角进入到NPC的“警戒区域”后,游

戏的AI可轻易获得目标的位置,然后控制NPC对象

移向被跟踪的对象。跟踪算法可以模拟这一行为:

voidBat_AI(void)

{

if(ghost.x>bat.x)

bat.x++;

else

if(ghost.x<bat.x)

bat.x--;

if(ghost.y>bat.y)

bat.y++;

else

if(ghost.y<bat.y)

bat.y--;

……

}//end Bat_AI

这段代码放到程序中实际运行时不难发现,N P C

会迅速地追踪到目标。这种跟踪非常精确,但是在游

戏中过于精确却不一定是一件好事,因为这会使NPC

的行为看上去显得有点假。一种更自然的跟踪方式是

使跟踪者的方向矢量与从跟踪目标的中心到跟踪者的

中心所定义的方向矢量靠拢。

这个算法可以这样设计:假设AI控制的对象称作

跟踪者(tracker)并有以下属性:

Position:(tracker.x,tracker.y)

Velocity:(tracker.xv,tracker.yv)

被跟踪对象称作跟踪目标(target),有如下属性:

Position:(target.x,target.y)

Velocity:(target.xv,target.yv)

基于上面的定义,下面是调整跟踪者的速度向量

的常用逻辑循环:

1)计算从跟踪者到跟踪目标的向量:

TV=(target.x-tracker.x,target.y-tracker.y)=(tvx,

tvy),规格化TV——也就是说(tvx,tvy)/Vector_Length

(tvx,tvy)使得最大长度为1.0,记其为TV*。记住

Vector_Length()只是计算从原点(0,0)开始的矢量长度。

2)调整跟踪者当前的速度向量,加上一个按rate

比例缩放过的TV*:

tracker.x+=rate*tvx;

tracker.y+=rate*tvy;

注意:当rate>1.0时,跟踪向量会合得更快,跟踪

算法对目标跟踪得更紧密,并更快地修正目标的运动。

3)跟踪者的速度向量修改过之后,有可能向量的

速度会溢出最大值,就是说,跟踪者一旦锁定了目标的

方向,就会继续沿着该方向加速。所以,需要设置一个

上界,让跟踪者的速度从某处慢下来。可做如下改进:

tspeed=Vector_Length(tracker.xv,tracker.yv);

if(tspeed>max_SPEED){

tracker.xv*=0.7;

tracker.yv*=0.7;

}

也可以选择其它的边界值0.5或0.9等均可。如果

追求完美,甚至可以计算出确切的溢出,并从向量中

缩去相应的数量。追踪过程中同样也会遇到障碍物,

因此,碰撞检测是必不可少的。程序员可以根据不同

的游戏类型设计碰撞后的行为逻辑。

1.3 闪避算法

这个技术是让游戏的N P C能避开玩家角色的追

击,跟前面的跟踪代码很相似,跟踪算法的对立面就

是闪避算法,只要把上例中的等式翻转,闪避算法就

成了,下面是转换后的代码:

if(ghost.x>bat.x)

bat.x--;

else

if(ghost.x<bat.x)

bat.x++;

if(ghost.y>bat.y)

bat.y--;

else

if(ghost.y<bat.y)

bat.y++;

……

以上介绍的3个算法可以模拟NPC的一些简单的寻

路、跟踪和闪避行为,在小游戏中会经常用到。但是,

在较大型的游戏中使用这样简单的算法就会大大影响游

戏效果了。因此,大型游戏的人工智能算法都较复杂。

2 遗传算法在路径探索中的应用

改进优化后的A*算法可以很好地胜任游戏中的路

径搜索[1],一直以来被游戏界认为是最好、最成熟的寻

路算法之一,因而被广泛应用。由于A*算法是按照寻

找最低耗费的路径来设计,A*寻路会找到最短,最直接

付朝晖,丁梦,喻昕 游戏编程中的寻路算法研究8 6 湖南工业大学 学 报2007年

的路径,当算法具体实现后,得到的这条路径也是唯一

的路径[2]。于是,当游戏重来时,玩家会发现NPC总是

只有一条路可走,这样就显得不够真实。玩家希望NPC

有足够的智力能找到一条适合的路径,也要有不同的选

择。如果能够为游戏中的角色设置一个智能系统来进行

控制,这个系统能通过自身不断地学习,逐渐适应复杂

的环境,自己找到一条“较好”的路径,并且有较高的

效率,就会使游戏角色的行为逻辑看上去更真实一些。

这样就需要借助人工智能的一些技术,如遗传算法等。

2.1 遗传算法

遗传算法是模拟生物进化的步骤,将繁殖、杂交、

变异、竞争和选择等概念引入到算法中,通过维持一

组可行解,并通过对可行解的重新组合,改进可行解

在多维空间内的移动轨迹或趋向,最终走向最优解。

它克服了传统优化方法容易陷入局部极值的缺点,是

一种全局优化算法[3,4]。

遗传算法的步骤如下:

1)对待解决问题进行编码——生物的性状是由生

物遗传基因的编码所决定的,使用遗传算法时,需要

把问题的每一解编码成一个基因编码,一个基因编码

就代表问题的一个解,每个基因编码有时被称作是一

个个体,有时也把基因编码称作染色体[5];

2)随机初始化群体X(0)=(x1,x2,…,xn);

3)对当前群体X(t)中每个个体xi计算其适应度

F(xi),适应度表示了该个体的性能好坏;

4)从当前群体选出2个成员,选出的概率正比于

染色体的适应性,适应分愈高,被选中概率也愈大;

5)按照预先设定的杂交率,从每个选中染色体的

一个随机确定的点上进行杂交;

6)按照预定的变异率,通过对被选染色体的位的

循环,把相应的位实行翻转;

7)如果不满足终止条件继续3)。

2.2路径探索的遗传算法实现

在这个路径探索例子中,首先创建1个地图,它

有1个入口,1个出口。地图中放置一些障碍物,在入

口处放置1个NPC。地图可以用1个二维整数数组Map

[][]来表示,其中用0来表示可以通行的空间,1代表

墙壁或障碍物,8为入口,9为出口。游戏开发者通常

是借助地图编辑器之类的工具来生成这个数组。

接下来要使它能找到出口,并避免与所有障碍物相

碰撞。这种地图设计方法被封装在一个称为CNpcMap

的类中,只需要以常量的形式来保存地图数组以及起点

和终点就行了。除了存储地图,这个CNpcMap类中需

要1个数组NpcPath[][],用来记录NPC在地图中行走的

路径。可以用由1(UP)、2(DOWN)、3(LEFT)、4(RIGHT)

所组成的动作方向序列来检测NPC走了多远,并计算出

NPC能到达的最远位置,然后返回1个适应性分数,它

正比于NPC最终位置离出口的距离。NPC所到达的位置

与出口越近,给NPC的适应性分数就越高。如果NPC实

际已到达了出口,将得到满分1,这时,循环就会自动

结束,此时得到问题的一个解。

根据遗传算法的步骤,第1步是为染色体编码,染

色体把NPC的每一个动作方向编入代码中。NPC有上、

下、左、右4个动作方向,编码后的染色体是代表这4

个动作方向的一个数组DirAction[]。首先通过程序生成

由1234组成的整型随机数数组,就能根据它得到NPC

行动时的方向。例如染色体{3,2,1,2,4,3,2,1,…}。

第2步要做的是将NPC置于地图的入口,然后指

示NPC根据DirAction[]数组中所列的方向指令序列一

步步地走。如果有一个方向使NPC碰到了墙壁或障碍

物,则忽略该指令序列并继续按下一条指令序列去走

就行了。这样不断走下去,直到用完所有方向或NPC

到达出口为止。遗传算法以整型随机数数组作为初始

群体,一般要求产生几百个这样的随机染色体,测试

它们每一个能让NPC走到离出口有多么近,然后让其

中最好的那些作为种子产生后代,期望它们的“子孙”

中能有走得离出口更近一点。这样继续下去,直到找

出一个解。因此,需要定义一种结构,其中包含一个

染色体,以及一个与该染色体相联系的适应性分数。

这个结构的定义如下:

struct Chromosome

{

int DirAction[];

double FitnessScore;

Chromosome():FitnessScore(0){}

Chromosome(int num):FitnessScore(0)

{

for(int i=0;i<num;++i)

{

//创造随机字符数组

}

}

}

在创建Chromosome对象时,把一个整型数作为参

数传递给构造函数,则它就会自动创建一个以此整数

为长度的随机数字数组,并将其适应性分数初始化为

零,完成对基因组的设置。

第3步,也是遗传算法类中最为关键的一步,就

是测试染色体群中每一个体的适应性分数,数。函数

CalFitSco()根据代表上、下、左、右4个方向的数组

DirAction[],计算出NPC离开出口的最终距离,返回

一个适应性分数。计算适应性分数程序如下:

int Dist_X=abs(Npc_X-End_X);

int Dist_Y=abs(Npc_Y-End_Y);第4期8 7

return 1/(Dist_X+Dist_Y+1);//加1避免分子为0

这里的Dist_X和Dist_Y就是NPC所在的位置相对

于地图出口的水平和垂直偏离值。如果N P C到达出

口,则Dist_X+Dist_Y=0。CalFitSco()保持对每一代

中适应性分数最高的基因组以及与所有基因组相关的

适应性分数的跟踪。每当一个新的基因组群被创建出

来时,需要将它们保存下来。

第4步,从当前群体选出2个个体以备杂交。赌

轮选择是常用的一种方法,被选中的几率和它们的适

应性分数成比例,适应性分数愈高的染色体,被选中

的概率也愈大。但这不是说适应性分数最高的成员一

定能选入下一代,它只是有最大的概率被选中。

while(Parents<V_Num)

{

//用赌轮法选择

Chromosome Parent1=RSele();

Chromosome Parent2=RSele();

……

}

在每次迭代过程中,需选择2个染色体作为“后

代”染色体的“父辈”,一个染色体的适应性分数越高,

其被赌轮方法选择作为“父辈”的概率就越大。

第5步杂交操作

Chromosome Cbaby1,Cbaby2;

Hybridize  (Parent1.DirAction, 

Parent2.DirAction , Cbaby1.DirAction ,

Cbaby2.DirAction);

创建2个新的染色体,它们与所选的父辈一起传

递给杂交函数Hybridize()。这一函数执行了杂交,并把

新的染色体的二进制位串存放到Cbaby1和Cbaby2中。

第6步变异操作

Differentiation(Cbaby1.DirAction);

Differentiation(Cbaby2.DirAction);

以上这2步实现对染色体后代杂交变异,完成后

把2个后代染色体加入新的群体,这样就完成了一次

迭代过程。该过程不断重复,原有的群体由新生一代

所组成的群体代替,直到染色体收敛到了一个解。

程序运行中有可能不是总能找到一条通往出口的

路径,这时有可能会导致NPC会在一个角落不确定地

走来走去,原因是群体太快地收敛到一个特殊类型的

染色体,由于群体中的成员变得相似,Hybridize算子

的优势这时实际上已经不能发挥作用,只有很少的变

异操作在起作用。但变异率设置得很低,当染色体类

型的差异消失后,仅仅依靠变异本身已不能去发现一

个解[6]。选择适当的参数可以改善这个问题,但是目

前还没有一个有效的规则,不同的问题需要不同的

值,用户只能通过自己的实践选取合适的值,在具体

实现中把杂交率选为0.6,变异率取为0.05,而基因组

数目取为染色体长度的2倍,可得到较理想的解。

2.3 遗传算法寻路的优缺点

与其他寻路算法相比,遗传算法具有如下优点:

1)在搜索中用到的是随机的变换规则,而不是确

定的规则。它在搜索时采用启发式的搜索,而不是盲

目的穷举,因而具有很高的搜索效率。

2)遗传算法给出的是一组优化解,而不是一个优

化解,这样,在游戏中可表现出更多的行为空间。

3)遗传算法具有很强的可并行性,可通过并行计

算来提高计算速度,因而更适用于大规模复杂问题的

优化。

但是,遗传算法毕竟是一种较新的算法,实际运

用中存在一些问题,主要集中在以下几个方面:

1)遗传算法的理论研究较滞后。由于遗传算法本

身是一种仿生的思想,尽管实践效果较好,但理论证

明比较困难,而且,这种算法提出来的时间还不是太

长,因此,其理论和实践的研究几乎是平行进行的。

2)算法本身的参数还缺乏定量的标准,目前采用

的都是经验数值,而且,不同编码、不同遗传技术都会

影响到遗传参数的选取,因而会影响到算法的通用性。

3)遗传寻路算法还算不上真正的实时算法,在游

戏中的应用会受到一些限制,在实时性很强的游戏中

不宜使用。

3 结语

近年来,游戏的AI发展很快,各种AI技术被引

入到游戏中,如遗传算法、人工神经网络计算、地形

分析技术、团队寻径算法、A*算法等,这些技术出色

地解决了游戏中一些基本问题。有理由相信,未来游

戏的AI将会给玩家带来更多的惊喜。

参考文献:

[1] 何国辉,陈家琪.游戏开发中智能路径搜索的算法研究[J].

计算机工程与设计,2006,27(13):2334-2337.

[2] 陈和平,张前哨.A*算法在游戏地图寻径中的应用与实现

[J].计算机应用与软件,2005,22(10):118-120.

[3] 王小平,曹立明.遗传算法-理论、应用与软件实现[M].

西安:西安交通大学出版社,2002.

[4] 阎平凡,张长水.人工神经网络与模拟进化计算[M].北

京:清华大学出版社,2000.

[5] 王淑琴.神经网络和遗传算法在游戏设计中的应用研究[D].

长春:东北师范大学,2004.

[6] 金朝红,吴汉松,李腊梅,等.一种基于自适应遗传算法

的神经网络学习算法[J].微型机信息,2005,2(110-11):

49-51.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: