游戏开发论坛

 找回密码
 立即注册
搜索
楼主: lihai9917

[讨论] 新手请教怪物AI的设定问题

[复制链接]

23

主题

3388

帖子

6440

积分

论坛元老

Rank: 8Rank: 8

积分
6440
发表于 2007-8-23 14:40:00 | 显示全部楼层

Re: 新手请教怪物AI的设定问题

如果你要讨论具体的编码方式...好吧,虽然我对底层技术知道得不多,但我这里可以提供一段以前玩AI-CODE时写的坦克机器人的部分代码.那个机器人的AI都写在这段C代码里了,有兴趣可以看看.
#include "api\c\SimpleRobot.h"
#include "api\c\MathUtils.h"
#include "stdlib.h"
#include "stdio.h"
#include "math.h"
//全局变量
struct Bot * Enemy;
int needFight;
int aimMode;
int moveMode;
int fireFlag;
int bulletHit;
int hitByBullet;
int needDefense;
double velocity;
double firePower;//开火能量
double MbulletVelocity;//子弹速度
double possiblePower;//可能对敌造成伤害的子弹能量
double PFenergy;
double Ex;
double Ey;
double Edistance;
double Eheading;//敌机当前朝向
double Evelocity;
double EfirePower;
double Ebearing;//当前敌机方位
double aimHeading;//炮管所指方位
double eq[90];//直瞄误差量化数组,量化单位为10度角,从eq[0]~eq[8]表示0~90度的范围
double EPFvelocity;
double EPFacceleration;
double EacceleratingTime;
double EPFenergy;
double danger;//定义靠近敌人的危险程度
struct Mshoot
{
        struct Mshoot *pre;
        int am;//当前瞄准模式
        double FP;//子弹能量
        double BV;//子弹速度
        double fx;//开火位置
        double fy;
        double eb;//开火时的敌机方位(相对开火位置)
        double h0;//瞄准模式0的开火方向
        double x0;
        double y0;//对应模式0的子弹坐标
        double ld0;//对应模式0的子弹与敌机最小距离
        double h1;
        double x1;
        double y1;
        double ld1;
        double h2;
        double x2;
        double y2;
        double ld2;
        struct Mshoot *next;
};
struct Mshoot *head;
struct Mshoot *end;//已发射的某发子弹的数据及相关结构
double ar0,ar1,ar2;
//函数声明
void doRadar(void);
void doMove(void);
void changeDirection(void);
void doGun(void);
double aim_0();
double aim_1();
double aim_2();
void getAimMode();
//事件处理函数///////////////////////
////////////////////////////////////////////////
/**
* 当开始一场新的比赛时触发
*/
void onMatchBegin(struct MatchBeginAction* action){}
/**
* 当整场比赛结束时触发
*/
void onMatchFinish(struct MatchFinishAction* action){}
/**
* 当开始一轮新的比赛时触发
*/
void onRoundBegin(struct RoundBeginAction* action)
{
        int i;
        Enemy=getFirstOpponent();
        EPFvelocity=0;
        EPFenergy=100;
        velocity=8;
        fireFlag=0;
        bulletHit=0;
        hitByBullet=0;
        ar0=ar1=ar2=0;
        danger=0;
        if(Enemy==NULL)
                needFight=0;
        else
                needFight=1;
        needDefense=0;
        head=NULL;
        end=NULL;
        aimMode=2;
        moveMode=1;
        for(i=0;i<90;i++)
        {
                eq=0;
        }       
}
/**
* 当一轮比赛结束时触发。
*/
void onRoundFinish(struct RoundFinishAction* action){}

/**
* 当有队友向自己发送消息时触发
*/
void onMessageReceived(struct MessageReceivedAction* action){}
/**
* 当撞到其它机器人时触发
*/
void onHitRobot(struct HitRobotAction* action){}
/**
* 当撞到墙时触发
*/
void onHitWall(struct HitWallAction* action)
{
        changeDirection();
}
/**
* 当任意一个机器人开火时触发
*/
void onFire(struct FireAction* action){}
/**
* 当有机器人死亡时触发
*/
void onRobotDeath(struct RobotDeathAction* action){}
/**
* 当自己的子弹击中敌人时触发
*/
void onBulletHit(struct BulletHitAction* action)
{
        bulletHit=1;
}
/**
* 当被别人的子弹击中时触发
*/
void onHitedByBullet(struct HitedByBulletAction* action)
{
        hitByBullet=1;
        moveMode=-moveMode;
}


//执行函数////////////////////////////////////////
//////////////////////////////////////////////////
/**
* 每个单位时间都会触发
*/
void onTick(struct TickAction* action)
{
        if(needFight)
        {
                doRadar();
                doMove();
                doGun();
        }
        else
                move(0);
        getAimMode();
        if(!isAlive(Enemy))
        {
                Enemy=getFirstOpponent();
                if(Enemy==NULL)
                        needFight=0;
                else
                        needFight=1;               
        }
}

void doRadar()
{
        double angle;
        //将后面计算所需的部分敌方数据计算出来存入全局变量
        Ex=Enemy->x;
        Ey=Enemy->y;
        Evelocity=Enemy->velocity;
        Eheading = Enemy->heading;
        Ebearing = heading( getX(),getY(),Ex, Ey);
        Edistance = distance(getX(),getY(),Ex,Ey);//计算敌我距离
        EPFacceleration = Evelocity-EPFvelocity;//估算敌加速度
        //计算敌加速度维持时间
        if(EPFacceleration == 0)
                EacceleratingTime = 0;//当敌加速度为0时认为加速度维持时间也为0
        else
        {
                if(EPFacceleration*Evelocity<=0)
                EacceleratingTime = (0-Evelocity)/EPFacceleration;//减速维持时间
                else
                {
                        if(Evelocity>0)
                        {
                                EacceleratingTime = 8-Evelocity;//正向加速维持时间
                                EPFacceleration = 1;//将正向加速度修正为1
                        }
                        else
                        {
                                EacceleratingTime = Evelocity+8;//负向加速维持时间
                                EPFacceleration =-1;//将负向加速度修正为-1
                        }
                }
        }
        danger=danger*(1-getEnergy()/300);
        if(hitByBullet)
                danger=danger+(PFenergy-getEnergy());
        if(danger>0.05)
                needDefense=1;
        else
                needDefense=0;
        if(bulletHit&&(!hitByBullet))
        {
                if(possiblePower>3)
                        EPFenergy=EPFenergy+(PFenergy-getEnergy())/4*6;
                else
                        EPFenergy=EPFenergy-possiblePower*6;
        }
        if((!bulletHit)&&hitByBullet)
        {
                EPFenergy=EPFenergy+(PFenergy-getEnergy())/6*4;
        }
        if(bulletHit&&hitByBullet)
        {
                EPFenergy=EPFenergy+((possiblePower*(8/3)-(getEnergy()-PFenergy)*(2/3))-6*possiblePower);
        }
        EfirePower = EPFenergy-Enemy->energy;
        PFenergy=getEnergy();
        EPFvelocity = Evelocity;
        EPFenergy = Enemy->energy;
        bulletHit=0;
        hitByBullet=0;
}

void doMove()
{
        //始终侧对着敌人
        double angle;
        long keepDistance;
        if(moveMode==1)
        {
                if( EfirePower>=0.1 ) changeDirection();
        }//如果moveMode为1,则探测到敌机开火就改变运动方向
        keepDistance=80;
    if((Edistance>keepDistance+40)&&!needDefense)
        {       
                if( velocity >= 0 )
            angle = Ebearing+PI/2-PI/4;
                else
                        angle = Ebearing+PI/2+PI/4;
        }//以PI/4靠近敌机
        if((Edistance<keepDistance)&&!needDefense)
    {
                if( velocity >= 0 )
                    angle = Ebearing+PI/2+PI/4;
                else
            angle = Ebearing+PI/2-PI/4;
    }//以PI/4远离敌机
        if((Edistance>=keepDistance)&&(Edistance<=keepDistance+40)||needDefense)
    {
                angle = Ebearing+PI/2;
    }//否则与敌机方位成90度角
        move(velocity);
        turnTo(angle);
}

void changeDirection()
{
        velocity=-velocity;
}       

void doGun()
{
        double angle;
        firePower=(1000/Edistance-1)/(300/Edistance);
        if(firePower*6>Enemy->energy)
                firePower=(Enemy->energy)/5;//当敌机能量不多时以恰好能击毁敌机的能量开火
    if(firePower<0.1)
        {
                firePower = 0.1;
        }
        else
        {
                if(firePower>3)
                        firePower = 3;
        }//使开火能量处于规定范围内
        MbulletVelocity=20-3*firePower;//得到子弹速度
        if(aimMode==0)//根据aimMode的值选择瞄准模式
                aimHeading=aim_0();
        if(aimMode==1)
                aimHeading=aim_1();
        if(aimMode==2)
                aimHeading=aim_2();
        if(getFirePrepareTime()==0)
        {
                fireFlag=1;
                fire(aimHeading,firePower);
        }
        else
        {
                fireFlag=0;
                fire(aimHeading,0);
        }
}

double aim_0()//瞄准模式0:直线预测瞄准。以迭代法计算
{
        double x,y;//接触点坐标
        double S;//从开炮到弹敌接触前敌机行使距离估测值
        double D;
        double impactTime;
        double AH;
        D=Edistance;
        do
        {
            impactTime = D/MbulletVelocity;//根据前次估算距离计算弹敌接触时间
                if(EacceleratingTime>=impactTime)
                {
                        S = Evelocity*impactTime+EPFacceleration*impactTime*impactTime/2;
                        x = Ex+cos(Eheading)*S;
                        y = Ey+sin(Eheading)*S;
                }
                else
                {
                        S = Evelocity*EacceleratingTime+
                                EPFacceleration*EacceleratingTime*EacceleratingTime/2+
                                (Evelocity+EPFacceleration*EacceleratingTime)*(impactTime-EacceleratingTime);
                        x = Ex+cos(Eheading)*S;
                        y = Ey+sin(Eheading)*S;
                }
                D = distance(getX(),getY(),x,y);//重新估算弹敌接触时的敌我距离
    }
        while(fabs(D-MbulletVelocity*impactTime)>=1);//循环条件——此次估算距离与前次估算距离之差大于等于1像素
        AH=heading(getX(),getY(),x,y);
        return(AH);
}

double aim_1()//瞄准模式1:根据敌当前位置直接开火。
{
        double AH;
        AH=Ebearing;
        return(AH);
}

double aim_2()//瞄准模式2:采取直接瞄准加修正角的方式
{
        double AH;
        double modiAngle;
        double p1,p2,p3;
        int unit;//由敌我距离决定的量化单位
        int i,j,k;
        int angle;
        p1=p2=0;
        unit=(int) ((40/Edistance)/PI*180);//该量化单位内只能容纳不到1车身
        if(unit==0)
                unit=1;
        else if(unit>90)
                unit=90;
        for(i=0;i<=90-unit;i++)
        {
                for(j=0;j<unit;j++)
                {
                        k=i+j;
                        p1=p1+eq[k];
                }
                if(p1>p2)
                {
                        p2=p1;
                        angle=i;
                }
                p1=0;
        }
        if(p2==0)
                modiAngle=0;
        else
                modiAngle=(angle+unit/2-45)*PI/180;
        AH=Ebearing+modiAngle;
        if(AH>2*PI)
                AH=AH-2*PI;
        if(AH<0)
                AH=AH+2*PI;
        return(AH);
}

void getAimMode()
{
        struct Mshoot *p,*c,*n;
        double erroAngle;//直瞄误差(弧度,以开火时的敌机方位为0度,逆时针为正方向)
        double EA;
        double crld;//当前考察的那次射击实际子弹与敌机最小距离
        int i;
        if(fireFlag)
        {
                if(head==NULL)
                {
                        end=head=(struct Mshoot *)malloc(sizeof(struct Mshoot));
                        head->pre=NULL;
                        head->next=NULL;
                }
                else
                {
                        end->next=(struct Mshoot *)malloc(sizeof(struct Mshoot));
                        p=end;
                        end=end->next;
                        end->pre=p;
                        end->next=NULL;
                }//建立命中精度统计链表
                if(aimMode==0)
                {
                        end->h0=aimHeading;
                        end->h1=aim_1();
                        end->h2=aim_2();
                }
                if(aimMode==1)
                {
                        end->h0=aim_0();
                        end->h1=aimHeading;
                        end->h2=aim_2();
                }
                if(aimMode==2)
                {
                        end->h0=aim_0();
                        end->h1=aim_1();
                        end->h2=aimHeading;
                }
                end->am=aimMode;
                end->FP=firePower;
                end->BV=MbulletVelocity;//得到子弹速度;
                end->x0=end->x1=end->x2=end->fx=getX();
                end->y0=end->y1=end->y2=end->fy=getY();
                end->eb=Ebearing;
                end->ld0=end->ld1=end->ld2=Edistance;//记录本次射击的预测状态
        }
        possiblePower=0;
        for(c=head;c!=NULL;)
        {
                c->x0=(c->x0)+c->BV*cos(c->h0);
                c->y0=(c->y0)+c->BV*sin(c->h0);
                if((c->ld0)>distance(c->x0,c->y0,Ex,Ey))
                        c->ld0=distance(c->x0,c->y0,Ex,Ey);
                c->x1=(c->x1)+c->BV*cos(c->h1);
                c->y1=(c->y1)+c->BV*sin(c->h1);
                if((c->ld1)>distance(c->x1,c->y1,Ex,Ey))
                        c->ld1=distance(c->x1,c->y1,Ex,Ey);
                c->x2=(c->x2)+c->BV*cos(c->h2);
                c->y2=(c->y2)+c->BV*sin(c->h2);
                if((c->ld2)>distance(c->x2,c->y2,Ex,Ey))
                        c->ld2=distance(c->x2,c->y2,Ex,Ey);
                if(c->am==0)
                        crld=c->ld0;
                if(c->am==1)
                        crld=c->ld1;
                if(c->am==2)
                        crld=c->ld2;
                if(crld<(20+(c->BV)*2))
                {
                        possiblePower+=c->FP;
                }
                if(distance(c->x0,c->y0,c->fx,c->fy)>(distance(Ex,Ey,c->fx,c->fy)+20))
                {
                        for(i=0;i<90;i++)
                        {
                                eq=eq*0.65;
                        }
                        erroAngle=bearing(heading(c->fx,c->fy,Ex,Ey),c->eb);
                        EA=45+erroAngle/PI*180;//将erroAngle转换到以敌机方位为+45度的角度坐标系中
                        if(EA<0)
                                EA=0;
                        else if(EA>=90)
                                EA=89;
                        i=(int) EA;
                        eq=eq+4;//量化erroAngle,并统计入量化数组
                        ar0=ar0*0.65;
                        ar1=ar1*0.65;
                        ar2=ar2*0.65;
                        if(c->ld0<20)
                                ar0=ar0+4;
                        /*else if(c->ld0<33)
                                ar0=ar0+1;*/
                        if(c->ld1<20)
                                ar1=ar1+4;
                        /*else if(c->ld1<33)
                                ar1=ar1+1;*/
                        if(c->ld2<20)
                                ar2=ar2+4;
                        /*else if(c->ld2<33)
                                ar2=ar2+1;*/
                        if(ar0>=ar1)
                        {
                                if(ar0>=ar2)
                                        aimMode=0;
                                else
                                        aimMode=2;
                        }
                        else
                        {
                                if(ar1>=ar2)
                                        aimMode=1;
                                else
                                        aimMode=2;
                        }
                        p=c->pre;
                        n=c->next;
                        if(p==NULL && n==NULL)
                        {
                                head=NULL;
                                end=NULL;
                                free(c);
                                c=n;
                        }
                        if(p==NULL && n!=NULL)
                        {
                                head=n;
                                n->pre=NULL;
                                free(c);
                                c=n;
                        }
                        if(p!=NULL && n==NULL)
                        {
                                end=p;
                                p->next=NULL;
                                free(c);
                                c=n;
                        }
                        if(p!=NULL && n!=NULL)
                        {
                                p->next=c->next;
                                n->pre=c->pre;
                                free(c);
                                c=n;
                        }
                }
                else
                        c=c->next;
        }
}

76

主题

1100

帖子

1107

积分

金牌会员

Rank: 6Rank: 6

积分
1107
发表于 2007-8-23 14:46:00 | 显示全部楼层

Re:新手请教怪物AI的设定问题

差不多都.AI就是判定->行为.有限状态机就行;

不过c是面向过程地哦;

23

主题

3388

帖子

6440

积分

论坛元老

Rank: 8Rank: 8

积分
6440
发表于 2007-8-23 14:47:00 | 显示全部楼层

Re:新手请教怪物AI的设定问题

里面没有什么特别复杂的算法,基本就是有限状态机+模糊统计(不知道是不是这样称呼的,具体算法是我自己琢磨的).

这个机器人具有一定的学习能力,能在一定程度上学习对方的移动规律做出预测,并根据预测打提前量.当然这个机器人很简陋,学习能力比不上使用模式匹配算法甚至神经网络的机器人,但用来给新手看看大致的结构应该是足够了.

最近正在尝试加入一点简单的遗传算法原理,以优化机器人的策略组合.

76

主题

1100

帖子

1107

积分

金牌会员

Rank: 6Rank: 6

积分
1107
发表于 2007-8-23 14:51:00 | 显示全部楼层

Re:新手请教怪物AI的设定问题

其实ai什么语言实现都可以,毕竟游戏也不是全部都是c/C++开发的.比如java,或C#,basic,但不同语言里对ai设计会存在不少差异,比如C和C++、C#,实际去写时思想就不太一样;

你说的有理.至于纯ai原理,有限状态机那套东西就ok了,大部分书籍都有.国内网络游戏也少见复杂的ai效果,值得一读,继续学习;

23

主题

3388

帖子

6440

积分

论坛元老

Rank: 8Rank: 8

积分
6440
发表于 2007-8-23 14:54:00 | 显示全部楼层

Re: Re:新手请教怪物AI的设定问题

windtiger: Re:新手请教怪物AI的设定问题

差不多都.AI就是判定->行为.有限状态机就行;

不过c是面向过程地哦;

AI本身就是过程,而非对象.个人认为C这种面向过程的语言在描述AI时,比C++更为清晰.

其实最好的AI编程语言是面向逻辑的语言.

不要以为面向对象就一定比面向过程优秀,看需要的.

76

主题

1100

帖子

1107

积分

金牌会员

Rank: 6Rank: 6

积分
1107
发表于 2007-8-23 14:59:00 | 显示全部楼层

Re:新手请教怪物AI的设定问题

其实研究纯ai不要和 开发语言挂钩,就是做有限状态机那套纯理论研究足够;

而具体实现用什么语言写都可以;国内网络游戏也少见复杂ai的概念,基本就那些,只能当你业余爱好兴趣学习时用在单机里的研究兴趣吧;

23

主题

3388

帖子

6440

积分

论坛元老

Rank: 8Rank: 8

积分
6440
发表于 2007-8-23 15:03:00 | 显示全部楼层

Re: Re:新手请教怪物AI的设定问题

windtiger: Re:新手请教怪物AI的设定问题

其实ai什么语言实现都可以,毕竟游戏也不是全部都是c/C++开发的.比如java,或C#,basic,但不同语言里对ai设计会...

你的观念真让人失望...使用不同的语言编写AI代码,仅仅是表现形式的不同而已,但思想上都是一致的.不然也不会有那么多的被广泛接受的AI算法了.

编程语言是什么?工具而已!代码真正的灵魂是算法.

其实现在很多游戏开发都使用内嵌的脚本语言(比如:lua/python等等)来描述AI.这些语言都不算严格意义上的OO语言.

21

主题

639

帖子

674

积分

高级会员

Rank: 4

积分
674
发表于 2007-8-23 15:05:00 | 显示全部楼层

Re:新手请教怪物AI的设定问题

建议新立主题,就叫《AI的定义到语言的优劣及实现方法初探》  

76

主题

1100

帖子

1107

积分

金牌会员

Rank: 6Rank: 6

积分
1107
发表于 2007-8-23 16:35:00 | 显示全部楼层

Re: Re: Re:新手请教怪物AI的设定问题

卡特铁角: Re: Re:新手请教怪物AI的设定问题


你的观念真让人失望...使用不同的语言编写AI代码,仅仅是表现形式的不同而已,但思想上都是一致的.不然也不...

其实我们一直都在探讨不同的东西,所以很早就说了不探讨ai,而是探讨ai实现的需求,所以你还是没明白我们探讨什么,所以大家一直在讲两个不同的东西,呵呵;

ai原理自然是统一的了;

23

主题

3388

帖子

6440

积分

论坛元老

Rank: 8Rank: 8

积分
6440
发表于 2007-8-23 18:04:00 | 显示全部楼层

Re:新手请教怪物AI的设定问题

那你到底是在说什么嘛?

不讨论ai原理,也不讨论实际编码,你要讨论什么?

讨论AI的需求?你那段代码表现出什么需求了?你用一段代码来表现你的需求?
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-6-10 03:47

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表