«

OGRE+NXOGRE中实现简单AI赛车

在学习了O’REILLY出版的《游戏开发中的人工智能》之后,又在网上搜索到了基于Unity3D的一篇赛车游戏AI对手解决方案的一篇文章,结合书中的思路,利用视线追逐法,在赛道上布置一些线路点,让赛车逐个朝一个一个点行使,从而来实现赛车在赛道上自行行使。

下面我介绍一下这种思路在Ogre及NxOgre平台上的具体使用:

1、首先,我们在赛车所要行驶的赛道上布置线路点。

我们在3DMax中,在所建赛道的下方布置一个个小的立方体,用这些立方体的中心点位置来记录线路点的位置,并且能不让玩家看到这些立方体,这样使得赛车沿这些点连起来线便可行使完整条赛道。

2、我们在模型中设置了这些点,那程序中又如何读取这些点呢?

我们在导入整条赛道时,是先将赛道模型通过3DMax中OgreMax插件导出scene文件,然后通过OGRE官网wiki上提供的DotSceneLoader类来解析scene文件,通过此类的parseDotScene这个函数调用将赛道解析后渲染出了,下面是具体代码:

mSceneLoader.parseDotScene("road03.scene",Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,mSceneMgr);  

之后,原赛道场景的根节点便赋给了Ogre的根节点上,而Ogre的场景管理器也给赛道场景中的各个物体都分配了结点,且其命名的名称和3Dmax中物体的名称相同。 于是,我们可以在程序中定义相同个数的场景结点指针,来记录路径结点。

Ogre::SceneNode* mWayPoint[61];    //赛道标志点  
  //读取赛道的个标志点
for(int i=0 ; i<61; i++)  
{
   mWayPoint[i]= mSceneMan->getSceneNode("WayPoints"+
                       StringConverter::toString(i+1));
}
3、记录了线路点,接下来便是控制赛车朝这些线路点移动。

在简单的Ai赛车中,我们不需要让车知道什么时候前进,什么时候刹车,我们只需要让它一直保持前进状态,而后我们所要做的便是让它知道什么时候转弯,以及向那个方向转弯。在这项程序中,我不得不承认我让Ai作弊了,因为实在没有找到其他更好的解决办法。

怎么说是作弊呢?具体来说吧,在玩家控制赛车时,按左键(或右键),赛车的轮子是逐渐转向左侧(或右侧),直到转到20度。而我在设计Ai赛车时,是通过计算(赛车的速度方向向量)与(赛车朝线路点方向向量)的角度来直接控制Ai赛车轮胎的转向角度,从而实现赛车驶向线路点。

下面是具体代码:

Real mDistance;                  // 赛车与线路点之间的距离  
Vector3 mDirection;              // 赛车朝向线路点的方向向量  
NxOgre::Vec3 mCarV;              //赛车速度方向向量  
mCarV = newCar->mActor->getLinearVelocity();  //获取赛车速度方向向量  
float AngleV,AngleD;             //前者为赛车速度方向在xz坐标系内与x轴正方向的夹角  
                                 //后者为赛车朝向线路点方向与x轴正方向的夹角
float turnAngle;                 //赛车轮胎最终转向角度

//我们用mDirection来记录赛车朝向线路点的方向向量,
//其中mNextPoint表示下一个线路点,方向向量= 线路点坐标 — 赛车坐标
mDirection = mNextPoint->getPosition() - newCar->getSceneNode()->getPosition();  
mDistance = mDirection.normalise();           //获取赛车与线路点距离

//调用atan2函数计算两个夹角,角度为 PI 到 –PI之间
AngleV = atan2(mCarV.z ,mCarV.x )* 180.0f / 3.14f;  
AngleD = atan2(mDirection.z ,mDirection.x )* 180.0f / 3.14f;

//根据两个夹角的正负关系,设置相应轮胎转向角度,
//当初就是这里的角度计算情况没有考虑清楚让我卡了很久。  
if ( AngleV<-90 && AngleD>90)  
{
    turnAngle = ( 360 + AngleV - AngleD );
}else if( AngleV>90 && AngleD<-90) {
    turnAngle = -( 360 - AngleV + AngleD );
}else{
    turnAngle = (AngleV-AngleD) ;
}

//为了不使轮胎转向过度,给转向角度设置最大值
if( turnAngle>20 )  
    turnAngle = 20;
else if( turnAngle<-20 )  
    turnAngle = -20;
newCar->steerAngle ( turnAngle ); // 趋势赛车轮胎转向  
4、解决了赛车转向角度问题,还要考虑的是如何让赛车一个接一个的沿线路点行驶。

我们用mNextPoint来存储下一个线路点,用mDistance来记录赛车到线路点的距离,当赛车到达这个线路点时便获取下一个线路点。当然赛车在实际情况下并不一定会和线路点重合,所以我们要设定一个范围,当赛车达到这个范围之内,我们就说它已经到达目标线路点了。 具体代码如下:

if( mDistance > 5)  
{
    newCar->forword();
} else {
    WayPointNum = (WayPointNum+1)%61;
    mNextPoint = mWayPoint[WayPointNum];
}

组装好着这一切,一辆简单的Ai赛车终于诞生了,但它只能沿着赛道行驶,不能躲避其他车辆,而且转弯时摇头晃脑得厉害,这将是在接下去的时间里需要学习改进的。

分享