Graphics View Framework之坦克大战(二)

上篇博文完成了玩家坦克的自由移动,这篇博文将介绍一下如何完成坦克的发射导弹,导弹飞行,坦克过草地,导弹炸砖块等等。。。。

坦克发射导弹:
每一辆坦克,包括玩家坦克和敌方坦克都有导弹发射速率,就是在一段时间内只能发射一次,其余发射请求无效。这个可以由shotRate和shotDelay属性完成,shotDelay就是一个延时属性,当延时属性满足一定条件时才允许发射,每次定时器溢出就去检查这个shotDelay如果满足条件就发射然后归0,不满足就加一个基于shotRate的值。
导弹飞行:
首先导弹要根据坦克的朝向和拥有的导弹属性赋值,然后进行坐标移动。

坦克过草地:
先创建坦克,再创建草地,就可以实现坦克过草地了,否则你的坦克会压在草地上。

导弹炸砖块:
导弹飞行的时候进行碰撞检测,碰到砖块进行判定,能炸就炸。

这里要介绍一个函数:

void setData(int key,QVariant& data);

这个函数可以帮你的类省去很多那种数据写入和读出的函数,你只需要对键值规定一下就好了。比如坦克有很多属性,那么你把这些属性枚举一下就好了,否则你还要为每一个属性写2个函数。
下面上一下代码

class Missile : public QGraphicsItem
{

public:
    Missile(QPixmap *pic, QGraphicsItem * parent = 0);
    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);


    typedef   enum{up,down,left,right} _movedir;
    typedef   enum{flying,stop} _state;  //导弹的状态,暂时无用貌似
    typedef   enum{movedir=1,state,speed,power,index} key;
    void setPic(QPixmap* pic);

private:
    QPixmap* pic;  //导弹图片

};

class Tank :public QGraphicsItem
{
public:
    Tank(QPixmap* picU,QPixmap* picD,QPixmap* picL,QPixmap* picR,QGraphicsItem *parent = 0);
    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

    typedef enum{up,down,left,right} _movedir;
    typedef enum{moving,stop} _state;
    typedef enum{isShot =1,shotRate,moveSpeed,missilePower,
                missileSpeed,movedir,state,blood,defency,shotDelay,index} key;

    void setPic(QPixmap* picU,QPixmap* picD,QPixmap* picL,QPixmap* picR);


private:
    QPixmap   *picLeft,
              *picRight,
              *picUp,
              *picDown;

};

这是导弹和坦克的类,砖和河就比较简单不放了。坦克类被我修改了,所有属性都使用Data函数进行读写。玩家坦克继承了Tank类,加了2个按键检测和释放事件,和上一篇博文的实现是一样的,只是现在分了2个类,不贴实现CPP了。敌方坦克直接使用的就是Tank类,AI准备放在gameController类实现。现在贴一下,导弹发射,坦克移动,和碰撞检测函数:

void gameController::tankMove()
{
    QList<QGraphicsItem*> items = scene->items();
    foreach(QGraphicsItem* item,items){
        if(item->data(0).toString().contains("Tank")){
            Tank* tank = (Tank*)item;
            if(tank->data(Tank::state).toInt() == Tank::moving){
               QPointF srcPos = tank->pos();
               QPointF orgPos = tank->pos();
               switch(tank->data(Tank::movedir).toInt()){
               case  userTank::down:
                   srcPos.setY(qMin(srcPos.y()+tank->data(Tank::moveSpeed).toInt(),scene->sceneRect().height()-tank->boundingRect().height()));
                   break;
               case  userTank::up:
                   srcPos.setY(qMax(srcPos.y()-tank->data(Tank::moveSpeed).toInt(),0.0));
                   break;
               case  userTank::right:
                   srcPos.setX(qMin(srcPos.x()+tank->data(Tank::moveSpeed).toInt(),scene->sceneRect().width()-tank->boundingRect().width()));
                   break;
               default:
                   srcPos.setX(qMax(srcPos.x()-tank->data(Tank::moveSpeed).toInt(),0.0));
               }

               tank->setPos(srcPos);
               if(handleColliding(tank)){   //如果检测到坦克碰撞 墙壁就无法行动,也就是退回
                   tank->setPos(orgPos);
               }

           }
        }
    }


}

void gameController::missileFly()
{
    QList<QGraphicsItem*>  items = scene->items() ;
    foreach(QGraphicsItem* item,items){
        if(item->data(0).toString().contains( "Missile")){  //判断是否是 子弹
            Missile* missile = (Missile*)item;
            missile->setVisible(true);
            if(missile->data(Missile::state).toInt() == Missile::flying){
               QPointF srcPos = missile->pos();
                switch(missile->data(Missile::movedir).toInt()){
                case Missile::down:
                    srcPos.setY(srcPos.y()+missile->data(Missile::speed).toInt());
                    break;
                case Missile::up:
                    srcPos.setY(srcPos.y()-missile->data(Missile::speed).toInt());
                    break;
                case Missile::left:
                    srcPos.setX(srcPos.x()-missile->data(Missile::speed).toInt());
                    break;
                default:
                    srcPos.setX(srcPos.x()+missile->data(Missile::speed).toInt());
                }
                if(!scene->sceneRect().contains(srcPos)){
                    removeMissile(missile);  //飞出场景外直接作废
                }

                missile->setPos(srcPos);
                handleColliding(missile);

           }
        }
    }


}
void gameController::tankShot()
{
    QList<QGraphicsItem*>  items = scene->items() ;
    foreach(QGraphicsItem* item,items){
        if(item->data(0).toString().contains("Tank")){
            int rate = item->data(Tank::shotDelay).toInt();
            if(rate < 12){
                item->setData(Tank::shotDelay,rate+item->data(Tank::shotRate).toInt());  //延时未满
            }
            else{
                Tank* tank = (Tank*)item;
                if(tank->data(Tank::isShot).toBool()){
                    Missile* missile;
                    if(tank->data(0).toString() == "userTank"){
                        missile = addMissiles(0,tank->data(Tank::missileSpeed),
                                              tank->data(Tank::missilePower),
                                              tank->data(Tank::movedir));
                    }
                    else{
                        missile = addMissiles(1,tank->data(Tank::missileSpeed),
                                              tank->data(Tank::missilePower),
                                              tank->data(Tank::movedir));
                    }  //导弹加入场景

                    switch(tank->data(Tank::movedir).toInt()){
                    case Tank::up:
                        missile->setPos(tank->x()+tank->boundingRect().width()/2-missile->boundingRect().width()/2,
                                        tank->y()-missile->boundingRect().height());
                        break;
                    case Tank::down:
                        missile->setPos(tank->x()+tank->boundingRect().width()/2-missile->boundingRect().width()/2,
                                        tank->y()+tank->boundingRect().height());
                        break;
                    case Tank::left:
                        missile->setPos(tank->x()-missile->boundingRect().width(),
                                        tank->y()+tank->boundingRect().height()/2-missile->boundingRect().height()/2);
                        break;
                    default:
                        missile->setPos(tank->x()+tank->boundingRect().width(),
                                        tank->y()+tank->boundingRect().height()/2-missile->boundingRect().height()/2);
                    }
                    tank->setData(Tank::isShot,false);

                }
            }
        }
    }

}

bool gameController::handleColliding(QGraphicsItem* item)
{
   QList<QGraphicsItem*> collidingItems = scene->collidingItems(item);
   foreach(QGraphicsItem* collidingItem,collidingItems){
       if(item->data(0).toString().contains("Tank")){
           if(collidingItem->data(0).toString().contains(QRegExp("wall|River"))){
               item->setData(Tank::state,Tank::stop);  //坦克碰到河或者墙壁 无法向前进
               return true;
               //qDebug()<<"碰到障碍物";
           }
           else if(collidingItem->data(0).toString().contains("Tank")){  //坦克撞坦克
               if(item->data(0).toString().contains("enemy") && collidingItem->data(0).toString().contains("enemy")){
                   item->setData(Tank::state,Tank::stop);
               }
               else{
                   int itemBlood= item->data(Tank::blood).toInt();
                   int itemDefency = item ->data(Tank::defency).toInt();
                   int collidingItemBlood= collidingItem->data(Tank::blood).toInt();
                   int collidingItemDefency = collidingItem ->data(Tank::defency).toInt();
                   item->setData(Tank::blood,itemBlood+itemDefency-collidingItemBlood);
                   collidingItem->setData(Tank::blood,collidingItemBlood+collidingItemDefency-itemBlood);
               }
           }

       }
       else if(item->data(0).toString().contains("Missile")){   //子弹碰撞处理
           if(collidingItem->data(0).toString().contains("wall")){  //碰墙
               if(item->data(Missile::power).toInt() >2){
                   scene->removeItem(collidingItem);
               }
               else{
                   if(collidingItem->data(Wall::Type).toInt() == Wall::normall){
                       scene->removeItem(collidingItem);
                   }
               }
               removeMissile((Missile*)item);
           }

           else if(collidingItem->data(0).toString().contains("Tank")){  //子弹碰坦克
              QString str = collidingItem->data(0).toString()+item->data(0).toString();
              if(str.contains("enemy") && str.contains("user")){
                  int blood = collidingItem->data(Tank::blood).toInt();
                  int defency = collidingItem->data(Tank::defency).toInt();
                  int power = item->data(Missile::power).toInt();
                  collidingItem->setData(Tank::blood,blood+defency-power*20);
                  removeMissile((Missile*)item);
              }
           }
           else if(collidingItem->data(0).toString() == "Grass"){
               item->setVisible(false);
           }
           return true;

       }
   }
   return false;
}

在内存管理方面,我使用了半自动的方法。也就是场景中删除的对象不删除,而是将它的序号保存起来,下次场景需要对象优先使用废弃掉的对象,这样可以避免频繁的申请和释放内存。实现这一功能只需2个List,一个用来保存创建的对象,另一个保存废弃的对象的序号。我对敌方坦克和子弹运用了这种管理的方式。

Missile *gameController::addMissiles(int type,QVariant speed,QVariant power,QVariant movedir)
{
   QString str;
   QPixmap* pic;
   switch(type){
   case 0 :
       str = "userMissile";
       pic = picUTMissile;
       break;
   default:
       str = "enemyMissile";
       pic = picETMissile;
   }
   Missile* missile;
   if(!FMIndex.isEmpty()){
       missile = missiles[FMIndex.last()];
       missile->setData(0,str);
       missile->setPic(pic);
       FMIndex.pop_back();  //退出最后一个序号
   }
   else{
       missile = new Missile(pic);
       missile->setData(0,str);
       missile->setData(Missile::index,missiles.size());
       missiles.append(missile);

   }
   missile->setData(Missile::power,power);
   missile->setData(Missile::speed,speed);
   missile->setData(Missile::movedir,movedir);
   scene->addItem(missile);
   return missile;

}
void gameController::addEnemyTank(int type, QVariant moveSpeed, QVariant movedir, QVariant blood,
                                  QVariant defency, QVariant shotRate, QVariant missileSpeed, QVariant missilePower)
{
    QString str;
    QPixmap *picU,*picD,*picL,*picR;
    switch(type){
    case 0:
        picU = eT1picU;
        picD = eT1picD;
        picL = eT1picL;
        picR = eT1picR;
        str = "enemyTank1";
        break;
    case 1:
        picU = eT2picU;
        picD = eT2picD;
        picL = eT2picL;
        picR = eT2picR;
        str = "enemyTank2";
        break;
    default:
        picU = eT3picU;
        picD = eT3picD;
        picL = eT3picL;
        picR = eT3picR;
        str = "enemyTank3";
    }
    Tank* etank;
    if(!FTIndex.isEmpty()){
        etank = enemyTanks[FTIndex.last()];
        etank->setData(0,str);
        etank->setPic(picU,picD,picL,picR);
        FTIndex.pop_back();  //退出最后一个序号
    }
    else{
        etank = new Tank(picU,picD,picL,picR);
        etank->setData(0,str);
        etank->setData(Tank::index,enemyTanks.size());
        enemyTanks.append(etank);
    }
    etank->setData(Tank::moveSpeed,moveSpeed);
    etank->setData(Tank::movedir,movedir);
    etank->setData(Tank::blood,blood);
    etank->setData(Tank::defency,defency);
    etank->setData(Tank::shotRate,shotRate);
    etank->setData(Tank::missileSpeed,missileSpeed);
    etank->setData(Tank::missilePower,missilePower);
    scene->addItem(etank);
}


void gameController::removeMissile(Missile *missile)
{
  FMIndex.append(missile->data(Missile::index).toInt());
  scene->removeItem(missile);
}

void gameController::removeEnemyTank(Tank *tank)
{
  FTIndex.append(tank->data(Tank::index).toInt());
  scene->removeItem(tank);
}

下面是运行测试,由于还没写AI,就没放敌方坦克
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值