qt实现坦克大战

  • Post author:
  • Post category:其他




前言

前一阵由于一些原因需要做一个坦克大战,想了好几天,终于算是大致实现了,由于时间紧,做得很粗糙,只能在这将一些大致的想法讲给大家,希望有所帮助



地图的实现

这一部分我建议是建立一个地图类,在类中定义一个二维数组,二维数组的行列号对应地图的行列号,再声明并实现一个为这个数组赋值的函数,该函数带参,内部就是一个switch语句,通过参数来知道你需要绘制第几关的图像,然后在每一case里为你的数组赋值,比如0代表空地,1代表草之类,数组中数值的不同,最终地图也就不同。

class ditu
{
public:
 void guankashu(int gunaka);
     int map1[17][16];

};

关卡数的实现如下,我只实现了一关,可以进行添加

void ditu::guankashu(int guanka){
    switch(guanka){
    case 0:{
        int mapl[17][16]={{2,0,1,1,1,1,1,1,0,3,4,5,0,1,1,2},
                          {0,0,1,1,1,1,1,1,1,3,4,5,0,1,1,2},
                          {0,0,1,1,1,1,1,1,1,3,0,5,0,1,1,2},
                          {0,0,1,1,1,1,1,1,1,3,4,5,0,1,1,2},
                          {0,0,1,1,1,1,1,1,1,3,4,0,0,1,1,2},
                          {0,0,1,1,1,1,1,1,1,0,0,5,0,1,1,0},
                          {0,0,1,1,1,1,1,1,1,3,0,1,0,1,1,2},
                          {0,0,1,1,1,1,1,1,1,3,0,5,0,1,6,2},
                          {0,0,1,1,1,1,1,1,1,3,4,0,0,1,1,2},
                          {0,0,1,1,1,1,1,1,1,3,4,5,0,1,1,2},
                          {0,0,1,1,1,1,1,1,1,3,4,5,0,1,1,2},
                          {0,0,1,1,1,1,1,1,2,3,4,5,0,1,1,2},
                          {0,0,1,1,1,1,1,1,2,3,4,5,0,1,1,2},
                          {0,0,1,1,1,1,1,1,1,3,4,5,0,1,1,2},
                          {0,0,1,1,1,1,1,1,1,3,4,5,0,1,1,2},
                          {0,0,1,1,1,1,1,1,2,3,4,5,0,1,1,2},
                          {0,0,1,1,1,1,1,1,2,3,4,5,0,1,1,2}};
        for(int qq=0;qq<17;qq++){
            for(int ww=0;ww<16;ww++){
                map1[qq][ww]=mapl[qq][ww];
            }
           }





    break;
    }

    }

这之后,你可以在widget.cpp中通过类的对象来调用这个数组及这个函数(在绘图事件中)进行绘制,另外还需要一个将数组中元素转化为对应图片的函数,该函数需要引入数组对应元素,以及该元素的横纵坐标,并且在该函数中将对应图片进行绘制,该函数如下:

void  Widget::ditumoxing(QPainter*p,int heng,int zong,int tuxing){
    QPixmap pix;
    switch (tuxing) {
    case 1:
        pix=QPixmap(":/tuxiang/1_1.png");
        break;
    case 2:
        pix=QPixmap(":/tuxiang/2_1.png");
        break;
    case 3:
        pix=QPixmap(":/tuxiang/3.png");
        break;
    case 4:
        pix=QPixmap(":/tuxiang/4.png");
        break;
    case 5:
        pix=QPixmap(":/tuxiang/5.png");
        break;
    case 6:
        pix=QPixmap(":/tuxiang/boss.png");
        break;
    default:
        break;
    }
    QRect r(heng*60,zong*60,60,60);
    p->drawPixmap(r,pix );


}

于是,当你的地图数组在绘图事件中得到了对应赋值后,如果不加限制,由于每次调用绘图事件都会调用地图赋值函数为地图重新赋值,所以需要添加一个if条件,使得该函数的调用全场只有一次,这样才能使之后的地图被破坏的部分得到保留,赋值完毕后就是利用上述地图模型函数在程序里进行绘画,得到地图。



关于开始界面与地图界面的切换

坦克大战有一个开始界面,点击按钮后才会进入真正的游戏画面。我的想法是做一个全局变量,程序一开始时,在绘图事件里满足这个变量,绘制开始界面。

if(iii==1){
        QPixmap map(":/tuxiang/start.png");
        QRect q(0,0,600,408);
        QRect q2(0,0,1020,960);
        painter.drawPixmap(q2,map,q);
        a3=0;

    }

然后,利用ui制作一个按钮,利用connect与拉姆达表达式,当按钮按下,该全局变量变成0,并且让按钮的尺寸变为0(使按钮消失)。之后重绘图像,进入地图绘制,背景图绘制(全黑即可),以及之后游戏的设计



己方坦克的实现

由于己方坦克是一个独立的单位,能够听从键盘进行移动,并且还有相应的碰撞检测等等,所以我是用了一个坦克数组,同样的,数组横纵坐标就代表坦克横纵坐标,数组每个元素的值(1,0,2,3,4)代表该坐标下是否有己方坦克,以及炮口朝向,当然我们不需要每次都遍历这个数组才能找出坦克位置,由于己方坦克出生是有固定位置的,我们可以运用两个全局变量(假设为i1,j1)作为横纵坐标,,先拿出坦克的出生位置,之后就能实时得到坦克当前位置,不需要遍历。之后,就是在键盘事件中对i1,j1进行实时修改,修改完毕后,重绘图像,在绘图事件中,在坐标(i1,j1)位置,绘制坦克图像,当然,此处也需要一个坦克绘制函数,与上述地图模型函数类似,它能接收i1,j1,以及对应数组元素(1代表炮口朝上,2下,3左,4右),然后绘制相应图像

坦克绘制函数如下:

void Widget::tankemoxing(QPainter*p,int heng,int zong,int tuxing){
    QPixmap pix;
    switch(tuxing){
    case 1:{
        pix=QPixmap(":/tuxiang/p1tankU.png");
        break;
    }
    case 2:{
        pix=QPixmap(":/tuxiang/p1tankD.png");
        break;
    }
    case 3:{
        pix=QPixmap(":/tuxiang/p1tankL.png");
        break;
    }
    case 4:{
        pix=QPixmap(":/tuxiang/p1tankR.png");
        break;
    }
    default:
        break;
    }
    QRect r(heng*60,zong*60,60,60);
    p->drawPixmap(r,pix);
}

这之后,就是在键盘事件中对i1,j1进行实时修改以及对碰撞的检测了,另外,坦克在转向的过程中,比如前一步还在朝前方走,后一步改为朝右时,应该先原地转向,之后在按下右键时才会朝右前进。

下方ditu2为地图数组(其中0,3,5是可穿过单位),i2,j2用于记录上一步坦克位置以便于获得上一步炮口朝向。

具体实现如下:

void Widget::keyPressEvent(QKeyEvent *event){
int i2=i1,j2=j1;
        //qDebug()<<event->key();
        if(event->key()==Qt::Key_A&&i1>0){
            //qDebug()<<event->key();
            if(tanke1[i2][j2]==3){
                if(ditu2[i1-1][j1]==0||ditu2[i1-1][j1]==3||ditu2[i1-1][j1]==5){
                    i1=i1-1;
                    tanke1[i1][j1]=tanke1[i2][j2];
                    tanke1[i2][j2]=0;
                }

            }
            else{
                tanke1[i2][j2]=3;
            }


            repaint();
        }
        else if(event->key()==Qt::Key_W&&j1>0){
            //qDebug()<<event->key();
            if(tanke1[i2][j2]==1){

                if(ditu2[i1][j1-1]==0||ditu2[i1][j1-1]==3||ditu2[i1][j1-1]==5){
                    j1=j1-1;
                    tanke1[i1][j1]=tanke1[i2][j2];
                    tanke1[i2][j2]=0;
                }

            }
            else{
                tanke1[i2][j2]=1;
            }

            repaint();
        }
        else if(event->key()==Qt::Key_S&&j1<15){
            if(tanke1[i2][j2]==2){
                if(ditu2[i1][j1+1]==0||ditu2[i1][j1+1]==3||ditu2[i1][j1+1]==5){
                    j1=j1+1;
                    tanke1[i1][j1]=tanke1[i2][j2];
                    tanke1[i2][j2]=0;
                }

            }
            else{
                tanke1[i2][j2]=2;
            }

            repaint();
        }
        else if(event->key()==Qt::Key_D&&i1<16){
            if(tanke1[i2][j2]==4){
                if(ditu2[i1+1][j1]==0||ditu2[i1+1][j1]==3||ditu2[i1+1][j1]==5){
                    i1=i1+1;

                    tanke1[i1][j1]=tanke1[i2][j2];
                    tanke1[i2][j2]=0;
                }

            }
            else{
                tanke1[i2][j2]=4;
            }

            repaint();
        }
        if(event->key()==Qt::Key_J){
            glb[b3].fzd=1;
            glb[b3].hzb=i1;
            glb[b3].zzb=j1;
            if(tanke1[i1][j1]==1)
                glb[b3].wz=1;
            else if(tanke1[i1][j1]==2)
                glb[b3].wz=2;
            else if(tanke1[i1][j1]==3)
                glb[b3].wz=3;
            else if(tanke1[i1][j1]==4)
                glb[b3].wz=4;
            b3++;
        }


}

上述最后的按键 j 是用来发射子弹的,可以先不管



己方子弹的实现

子弹的实现,是这个游戏的核心及难点(我这个实现的不太好,但可以给大家提供参考)

我的想法是构建一个结构体,并以数组方式实现,该结构体能够记录按下发射按键时坦克的位置以及炮口的朝向。该数组就是一个弹匣(无法弹出弹壳),按下发射按键,就往数组中空位置(已有子弹或者子弹已经发射完毕的是非空位置,这样做就限制了一局里可发射子弹的数量)添加一颗子弹,即令fzd=1

子弹结构体:

struct bullet1{
  int hzb;
  int zzb;
  int wz;
  int fzd;
  int kongke;
  QPixmap map;
};
bullet1 glb[70];

最后的map是用来存子弹图片的

wz用来记录炮口朝向,fzd用来记录当前位置被放入一颗子弹,kongke用来记录该子弹是否在地图上已经消失

if(event->key()==Qt::Key_J){
            glb[b3].fzd=1;
            glb[b3].hzb=i1;
            glb[b3].zzb=j1;
            if(tanke1[i1][j1]==1)
                glb[b3].wz=1;
            else if(tanke1[i1][j1]==2)
                glb[b3].wz=2;
            else if(tanke1[i1][j1]==3)
                glb[b3].wz=3;
            else if(tanke1[i1][j1]==4)
                glb[b3].wz=4;
            b3++;
        }

之后,就是构建计时器,我是做了一个0.2秒的计时器(每隔0.2秒调用一次计时器事件),由于之后敌方坦克的制作也需要用到计时器,而0.2秒一次的刷新会使敌方坦克的移动过快,所以可以在计时器事件中使用if条件,只有是0.2秒的计时器调用时,才会进入子弹的运作。具体操作为,每隔0.2秒就视察一遍弹匣,如果弹匣里有fzd=1(fzd=1代表了这发子弹要么是刚刚装填,要么是正在地图上飞行,只有子弹死亡或者还未装填时fzd=0)的元素,先判断这颗子弹内部记录的炮口位置,以及该子弹所处位置是否超过边界,通过记录的炮口位置就知道了子弹需要前进的方向,然后对其内部的hzb(横坐标)或者zzb(纵坐标)进行修改即可,这之后是子弹即将出现的新位置的碰撞判断,将子弹内部坐标更改后,我们需要一个碰撞判断函数,如果子弹当前位置有地图建筑物(只判断墙和铁),则判断该建筑物是否可击毁,可击毁,则将地图数组对应位置改为0,之后,无论可不可击毁,只要该位置有墙或铁,该子弹就要消失,则令fzd=0,kongke=-1(kongke=-1只会出现在弹匣该位置曾有过子弹,但现在已经消失),

坐标的修改以及碰撞的判断完毕后,重绘图像。绘图事件里的操作在下面说

计时器制作如下:

eventid1=startTimer(200);
    eventid2=startTimer(1000);

eventid1,id2都是int型

计时器事件如下:

void Widget::timerEvent(QTimerEvent *event){
    if(event->timerId()==eventid1){
        enemyappear();
        for(int y=0;y<70;y++){
            if(glb[y].fzd==1){
                //glb[y].fzd=0;

                if(glb[y].wz==1&&glb[y].zzb>=0){
                    glb[y].zzb=glb[y].zzb-1;
                    defeatjianzhu(glb[y].hzb,glb[y].zzb,y);
                    if(glb[y].zzb<0){
                        glb[y].kongke=-1;
                        glb[y].fzd=0;
                    }
                    repaint();
                }
                else if(glb[y].wz==2&&glb[y].zzb<=15){
                    glb[y].zzb=glb[y].zzb+1;
                    defeatjianzhu(glb[y].hzb,glb[y].zzb,y);
                    if(glb[y].zzb>15){
                        glb[y].kongke=-1;
                        glb[y].fzd=0;
                    }
                    repaint();
                }
                else if(glb[y].wz==3&&glb[y].hzb>=0){
                    glb[y].hzb=glb[y].hzb-1;
                    defeatjianzhu(glb[y].hzb,glb[y].zzb,y);
                    if(glb[y].hzb<0){
                        glb[y].kongke=-1;
                        glb[y].fzd=0;
                    }
                    repaint();
                }
                else if(glb[y].wz==4&&glb[y].hzb<=16){
                    glb[y].hzb=glb[y].hzb+1;
                    defeatjianzhu(glb[y].hzb,glb[y].zzb,y);
                    if(glb[y].hzb>16){
                        glb[y].kongke=-1;
                        glb[y].fzd=0;
                    }
                    repaint();
                }
            }

        }

子弹碰撞函数如下(heng,zong为该发子弹此时坐标,danshu为判断是哪一发子弹)最后的for是用来击毁敌方坦克的,先不用管:

void Widget::defeatjianzhu(int heng,int zong,int danshu){
    if(ditu2[heng][zong]==1){
        ditu2[heng][zong]=0;
        glb[danshu].kongke=-1;
        glb[danshu].fzd=0;
    }
    else if(ditu2[heng][zong]==2){
        glb[danshu].kongke=-1;
        glb[danshu].fzd=0;
    }
    for(int y=0;y<10;y++){
        if(etank[y].nowhzb==heng&&etank[y].nowzzb==zong&&etank[y].cunhuo&&etank[y].zwz!=0){
            if(etank[y].ming>0){
                etank[y].ming--;
                glb[danshu].kongke=-1;
                glb[danshu].fzd=0;
            }
            if(etank[y].ming<=0){
               etank[y].cunhuo=false;
               etank[y].siwang=1;
               a3--;

            }

        }
    }
}

己方子弹的绘图(只要弹匣中该子弹还未移动完毕,即kongke!=-1,就让它继续放):

for(int y=0;y<70;y++){
        if(glb[y].kongke!=-1){
            //defeatjianzhu(glb[y].hzb,glb[y].zzb,y);
            QRect r3(glb[y].hzb*60,glb[y].zzb*60,60,60);
            painter.drawPixmap(r3,glb[y].map);
        }
    }



敌方坦克的实现

条件:敌方共有十个坦克,开局出现三个,每死一个再出现一个,十个都死后,游戏胜利。

由于敌方坦克的出生地点是随机的,等级,出生时炮口朝向都是随机的,所以,我们也可以建立一个敌方坦克结构体,这个结构体能够记录坦克出生时的位置以及之后在地图中移动的位置,出生时炮口朝向,该坦克等级,生命值,是否还存活,以及该坦克的弹匣。

敌方坦克结构体构建如下:

struct enemy{
  int starthzb;//随机出生横坐标
  int startzzb;//随机出生纵坐标
  int nowhzb;//当前位置横坐标
  int nowzzb;//当前位置纵坐标
  int zwz;//炮口朝向
  int life;//等级
  bool cunhuo;//是否存活
  int ming;//生命值
  int siwang;//用于记录坦克死亡时间,坦克死亡那一瞬间,其值为1,在之后的0.6秒内,它会最终变为4,目的是实现0.6秒爆炸特效
  bullet1 ebullet[50];//弹匣

};
enemy etank[10];//构建十个坦克


敌方坦克出现函数

(其中,a3为全局变量,开始为0,用于记录当前地图中一共有多少敌方坦克还存活,一个地图中同时出现的坦克最大为3,zwz为0时,代表了该位置还未出现坦克,因为坦克炮口朝向只有1,2,3,4,对应上下左右,其为0时,代表无坦克,可以存放):

void Widget::enemyappear(){
    srand((unsigned)time(NULL));

    for(int y=0;y<10;y++){
       if(a3!=3&&etank[y].zwz==0){
        int xheng=rand()%17+0;
        int xzong=rand()%2+0;
        int zwz=rand()%4+1;
        int life=rand()%3+1;
        etank[y].starthzb=xheng;//获得开始位置
        etank[y].startzzb=xzong;//获得开始位置
        etank[y].zwz=zwz;//获得开始炮口朝向
        etank[y].life=life;//获得等级
        if(life==1){
            etank[y].ming=1;//每个等级对应一种生命值
        }
        else if(life==2){
            etank[y].ming=2;
        }
        else if(life==3){
            etank[y].ming=3;
        }
        a3++;

      }
    }

}

敌方坦克有了开始位置,有了炮口朝向后就可以开始绘图了,同样,也需要一个坦克模型函数,每种等级的坦克,有不同的图形,每种等级坦克也有四个方向图片


敌方坦克模型函数如下:

void Widget::enemymoxing(QPainter*p,int heng,int zong,int zwz,int life){
    QPixmap pix;
    if(life==1){
        switch(zwz){
        case 1:{
            pix=QPixmap(":/tuxiang/enemy1U.png");
            break;
        }
        case 2:{
            pix=QPixmap(":/tuxiang/enemy1D.png");
            break;
        }
        case 3:{
            pix=QPixmap(":/tuxiang/enemy1L.png");
            break;
        }
        case 4:{
            pix=QPixmap(":/tuxiang/enemy1R.png");
            break;
    }
    default:
        break;
       }
    }
    else if(life==2){
        switch(zwz){
            case 1:{
                pix=QPixmap(":/tuxiang/enemy2U.png");
                break;
            }
            case 2:{
                pix=QPixmap(":/tuxiang/enemy2D.png");
                break;
            }
            case 3:{
                pix=QPixmap(":/tuxiang/enemy2L.png");
                break;
            }
            case 4:{
                pix=QPixmap(":/tuxiang/enemy2R.png");
                break;
            }
        }
        }
    else if(life==3){
        switch(zwz){
            case 1:{
                pix=QPixmap(":/tuxiang/enemy3U.png");
                break;
            }
            case 2:{
                pix=QPixmap(":/tuxiang/enemy3D.png");
                break;
            }
            case 3:{
                pix=QPixmap(":/tuxiang/enemy3L.png");
                break;
            }
            case 4:{
                pix=QPixmap(":/tuxiang/enemy3R.png");
                break;
            }
        }
        }


    QRect r(heng*60,zong*60,60,60);
    p->drawPixmap(r,pix );
}

接下来就需要让敌方坦克动起来,一是让坦克的位置能在计时器事件里被实时更改,二是制定敌方坦克移动的规则,即它每一步前进或者转向的概率

以下是敌方坦克移动函数,第一个为移动判断(向前还是转向,向前的概率为0.7,炮口的位置决定了坦克的前方是哪方,转向概率为0.3,),第二个为转向函数(该转向函数是坦克已经决定向前,但是前方无法通行时进行转向,直接转向无障碍物的一方)

void Widget::yidongpanduan(int y){//输入的y代表是第几辆坦克

    int xheng=rand()%10+1;
    int xzong=rand()%4+1;
    if(xheng<=7){
        if(etank[y].zwz==1){//根据其炮口朝向确定其直行前进方向
            if(ditu2[etank[y].nowhzb][etank[y].nowzzb-1]==1||ditu2[etank[y].nowhzb][etank[y].nowzzb-1]==2||ditu2[etank[y].nowhzb][etank[y].nowzzb-1]==4){
                zhuanxiang(xzong,y);//前路不通,转向
            }
            else{
                if(etank[y].nowzzb>0)
            etank[y].nowzzb--;//前路通,前进
            }
        }
        else if(etank[y].zwz==2){
            if(ditu2[etank[y].nowhzb][etank[y].nowzzb+1]==1||ditu2[etank[y].nowhzb][etank[y].nowzzb+1]==2||ditu2[etank[y].nowhzb][etank[y].nowzzb+1]==4){
                zhuanxiang(xzong,y);
            }
            else{
                if(etank[y].nowzzb<=15)
            etank[y].nowzzb++;
            }
        }
        else if(etank[y].zwz==3){
            if(ditu2[etank[y].nowhzb-1][etank[y].nowzzb]==1||ditu2[etank[y].nowhzb-1][etank[y].nowzzb]==2||ditu2[etank[y].nowhzb-1][etank[y].nowzzb]==4){
                zhuanxiang(xzong,y);
            }
            else{
                if(etank[y].nowhzb>0)
            etank[y].nowhzb--;
            }
        }
        else if(etank[y].zwz==4){
            if(ditu2[etank[y].nowhzb+1][etank[y].nowzzb]==1||ditu2[etank[y].nowhzb+1][etank[y].nowzzb]==2||ditu2[etank[y].nowhzb+1][etank[y].nowzzb]==4){
                zhuanxiang(xzong,y);
            }
            else{
                if(etank[y].nowhzb<=16)
            etank[y].nowhzb++;
            }
        }
    }
    else if(xheng>7){//转向
        if(ditu2[etank[y].nowhzb][etank[y].nowzzb-1]!=1&&ditu2[etank[y].nowhzb][etank[y].nowzzb-1]!=2&&(etank[y].nowzzb-1)>=0&&ditu2[etank[y].nowhzb][etank[y].nowzzb-1]!=4){
            etank[y].zwz=1;
        }
        else if(ditu2[etank[y].nowhzb][etank[y].nowzzb+1]!=1&&ditu2[etank[y].nowhzb][etank[y].nowzzb+1]!=2&&(etank[y].nowzzb+1)<16&&ditu2[etank[y].nowhzb][etank[y].nowzzb+1]!=4){
            etank[y].zwz=2;
        }
        else if(ditu2[etank[y].nowhzb-1][etank[y].nowzzb]!=1&&ditu2[etank[y].nowhzb-1][etank[y].nowzzb]!=2&&(etank[y].nowhzb-1)>=0&&ditu2[etank[y].nowhzb-1][etank[y].nowzzb]!=4){
            etank[y].zwz=3;
        }
        else if(ditu2[etank[y].nowhzb+1][etank[y].nowzzb]!=1&&ditu2[etank[y].nowhzb+1][etank[y].nowzzb]!=2&&(etank[y].nowhzb+1)<17&&ditu2[etank[y].nowhzb+1][etank[y].nowzzb]!=4){
            etank[y].zwz=4;
        }
    }



}
void Widget::zhuanxiang(int x,int y){
    if(x==1){
        etank[y].zwz=1;
    }
    else if(x==2){
        etank[y].zwz=2;
    }
    else if(x==3){
        etank[y].zwz=3;
    }
    else if(x==4){
        etank[y].zwz=4;
    }
}

综上,将坦克出现函数放在0.2秒的计时器里,每0.2秒查看一次,若地图上有坦克死亡,就补上。

然后,坦克移动函数放入每一秒调用一次的计时器里,使其每隔一秒移动一次(防止速度过快)

eventid2=startTimer(1000);
else if(event->timerId()==eventid2){
        if(f==1){
            zhuangdan();//敌方子弹装弹函数,之后解释
        }

        srand((unsigned)time(NULL));
        if(f==1){
            for(int y=0;y<10;y++){
                if(etank[y].cunhuo&&etank[y].zwz!=0)//判断当前位置是否有坦克在地图上
                yidongpanduan(y);

            }

        }

    }

一切完毕后,就在绘图函数里,将坦克绘制出来,我下述这一部分代码实际上包含了三部分,第一个for循环是用来给刚出生的敌方坦克赋予位置和生命,因为敌方坦克需要一个初始位置,之后的位置变化都是在这基础上加减,故nowhzb,nowzzb的用处就是记录当前坐标,cunhuo表示该坦克活了,之后就可以绘制他了

第二个for是用来绘制爆炸特效的,上述已经提过,siwang最初为0,只有当该坦克死后,其变为1,之后才通过计时器在0.6秒内一直绘制爆炸图像(可以是多张)以实现爆炸特效。

最后的enemymoxing才是绘制当前坦克图像的

for(int y=0;y<10;y++){
        if(etank[y].nowhzb==-2&&etank[y].zwz!=0&&!etank[y].cunhuo){
        enemymoxing(&painter,etank[y].starthzb,etank[y].startzzb,etank[y].zwz,etank[y].life);
        etank[y].nowhzb=etank[y].starthzb;
        etank[y].nowzzb=etank[y].startzzb;
        etank[y].cunhuo=true;
        //qDebug()<<etank[y].starthzb;
        }
    }

    
    for(int y=0;y<10;y++){
        if(etank[y].siwang>0&&etank[y].siwang<=3){
            if(etank[y].siwang==1){
                QRect Q(0,0,28,28);
                QRect Q2(etank[y].nowhzb*60,etank[y].nowzzb*60,60,60);
                QPixmap MAP2(":/tuxiang/Explode1.bmp");
                painter.drawPixmap(Q2,MAP2,Q);
            }
            else{
                QRect Q(0,0,64,64);
                QRect Q2(etank[y].nowhzb*60,etank[y].nowzzb*60,60,60);
                QPixmap MAP2(":/tuxiang/Explode2.bmp");
                painter.drawPixmap(Q2,MAP2,Q);
            }
        }
        if(etank[y].cunhuo){
        enemymoxing(&painter,etank[y].nowhzb,etank[y].nowzzb,etank[y].zwz,etank[y].life);
        }



敌方子弹的实现

敌方子弹其实和己方子弹的实现很类似,它的飞行,碰撞,消失条件都与己方子弹相同,不同之处只有己方子弹的装弹是通过按键实现,敌方子弹装弹是通过装弹函数

装弹函数如下:

void Widget::zhuangdan(){
srand((unsigned)time(NULL));
for(int y=0;y<10;y++){
    int xheng=rand()%11;//装弹概率为0-4,只有这几个数能装弹
    int bb=0;
    if(etank[y].zwz!=0){//该位置有坦克(无论死活)
     while(true){
        if(etank[y].ming>0&&xheng<5&&etank[y].ebullet[bb].kongke!=-1&&etank[y].ebullet[bb].fzd!=1&&etank[y].cunhuo){//细化条件,坦克要存活,该坦克的该位置弹匣为空
            etank[y].ebullet[bb].fzd=1;
            etank[y].ebullet[bb].hzb=etank[y].nowhzb;
            etank[y].ebullet[bb].zzb=etank[y].nowzzb;
            etank[y].ebullet[bb].wz=etank[y].zwz;
            break;

        }//循环,直到找到空位置装上子弹。
        bb++;
        if(bb>49){
            break;
        }
     }
    }

}
}

敌方子弹碰撞函数,与己方子弹极度类似

void Widget::enemydefeat(int heng,int zong,int tankshu,int danshu){
    if(ditu2[heng][zong]==1){
        ditu2[heng][zong]=0;
        etank[tankshu].ebullet[danshu].kongke=-1;
       etank[tankshu].ebullet[danshu].fzd=0;
    }
    else if(ditu2[heng][zong]==2){
        etank[tankshu].ebullet[danshu].kongke=-1;
       etank[tankshu].ebullet[danshu].fzd=0;
    }

    if(etank[tankshu].ebullet[danshu].hzb==i1&&etank[tankshu].ebullet[danshu].zzb==j1){
            sh1=i1;
            sz1=j1;
            i1=5;
            j1=15;
            sishu++;
            siqu=1;

    }

}

同样的,敌方子弹也放入0.2秒的计时器中,让其不断改变位置,再在绘图事件中进行绘制,该步骤与己方子弹相同,不再重复。



敌方与己方坦克的死去与复活以及特效

己方坦克的死去就是全局变量(横纵坐标)i1,j1又变成了初始位置,即在敌方子弹的碰撞函数里用一个if判断敌方子弹当前位置是否与己方坦克重合,一旦重合,就将i1,j1初始化,并且记录下i1,j1此时位置,在当前位置绘制爆炸图像,同样的,也是让一个值由0变1,绘制爆炸图,并且在计时器事件里让它在接下来0.6秒里都持续绘制爆炸图像,达到特效。敌方的爆炸类似于此,但不同的是还需要在己方子弹碰撞函数里减1记录当前地图中敌方坦克数量的全局变量的值,好让下一辆敌方坦克出现。

至于己方复活特效,也是类似上述爆炸,但就是在0.6秒的持续绘图中,让重生特效一直绘制在当前坦克所在位置即可(即让其在0.6秒内可以和坦克一起移动)



胜利与失败条件

在胜利函数里不断遍历敌方坦克数组中坦克死亡数,当其达到10时,就绘制胜利图像(该函数在绘图事件里调用)

失败函数同样,用一个全局变量记录己方坦克死亡次数,当其达到三时,绘制失败图像



结语

这个坦克大战实现完毕之后,我觉得核心就是两个结构体,一个子弹结构体,一个敌方坦克结构体,只要能把这两个结构体理解以及完善,坦克游戏的实现就可以成功(我的想法中有很多不足之处,也希望大家指出,共同学习,共同进步)



版权声明:本文为luojio原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。