http://blog.csdn.net/ityuany/archive/2010/04/21/5509750.aspx
近年来,游戏产业的快速发展带动了游戏中人工
智能(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.