Graphics View Framework之坦克大战(一)

Qt的 Graphics View的框架非常强大,框架由Scene Item 和View组成,场景管理所有的Item,View用来展示。详细的介绍请看Qt自带的帮助文档。为了学习这个框架,博主决定自己写个坦克大战试试。首先我们来完成坦克的移动。
玩家控制的坦克作为场景中的一个元素必然是继承QGraphicsItem类,我们来看下这个类,发现里面有:

virtual void    paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0) = 0

virtual QRectF  boundingRect() const = 0

这两个纯虚函数是必须我们自己定义的。
BoudingRect指的是用来绘制元素的那个矩形范围,这有别于元素的形状(shape),shape()用来作碰撞检测,shape()默认返回的是BoudingRect,这需要注意一下。
paint()函数用来绘制元素,我们需要用这个函数来绘制坦克的图片。

class userTank :public QGraphicsItem
{

public:
    userTank(QGraphicsItem * parent = 0);    
    QRectF boundingRect() const;   //pure virtual
    void paint(QPainter * painter,
               const QStyleOptionGraphicsItem * option,
               QWidget * widget = 0) ;  //virtual
    enum{up,down,left,right}movedir;  //坦克移动方向
    enum{moving,stop}state; //移动状态
    QSize    size;
protected:
     void   keyPressEvent(QKeyEvent * event) ;
     void   keyReleaseEvent(QKeyEvent * event);
private:
    QPixmap  *picLeft,
             *picRight,
             *picUp,
             *picDown;  //玩家坦克的4个运动方向的图片

static int objectType;  //目标类型为玩家坦克

};

int userTank::objectType = 0;
userTank::userTank(QGraphicsItem *parent):QGraphicsItem(parent)
{

    picLeft = new QPixmap(":/image/usertank/img/p1tankL.gif");
    picRight = new QPixmap(":/image/usertank/img/p1tankR.gif");
    picUp = new QPixmap(":/image/usertank/img/p1tankU.gif");
    picDown = new QPixmap(":/image/usertank/img/p1tankD.gif");
    size = picDown->size();
    movedir = up;
    state = stop;
    QTimer timer;
    timer.setInterval(100);


}

QRectF userTank::boundingRect() const
{
   return QRectF(QPoint(0,0),size);
}

void userTank::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
   QPixmap* pic;
   switch(movedir){
   case up:
       pic = picUp;
       break;
   case down:
       pic = picDown;
       break;
   case left:
       pic = picLeft;
       break;
   default:
       pic = picRight;
   }
   painter->drawPixmap(0,0,*pic);
}

void userTank::keyPressEvent(QKeyEvent *event)
{
    switch(event->key()){
    case Qt::Key_Up:
        movedir = up;
        state = moving;
        break;
    case Qt::Key_Down:
        movedir = down;
        state = moving;
        break;
    case Qt::Key_Left:
        movedir = left;
        state = moving;
        break;
    case Qt::Key_Right:
        movedir= right;
        state = moving;
        break;
    default: ;

    }

}
void userTank::keyReleaseEvent(QKeyEvent *event)
{
    if( state == moving){
        switch(event->key()){
        case Qt::Key_Up:
            if(movedir != up) return;
            break;
        case Qt::Key_Down:
            if(movedir != down) return;
            break;
        case Qt::Key_Left:
            if(movedir != left) return;
            break;
        case Qt::Key_Right:
            if(movedir != right) return;
            break;
        default:
            return;

        }
        state = stop;
    }
}

原理比较简单,就是获取按键,然后确定移动方向 和移动状态。有个地方要注意一下,就是按键释放的时候可能我们已经按下了别的方向键,然后松开了原来那个键,所以要检测一下当前释放的键是否是移动方向,不然会出现调转方向卡顿的情况。

一个游戏通常都有一个核心控制类,为了测试我们的坦克移动,我们先简单的写一下gamecontroller

class QGraphicsView;
class QGraphicsScene;
class userTank;
class gameController :public QObject
{
    Q_OBJECT
public:
    gameController(QObject* parent =0,QWidget* widget=0);
private:
    QGraphicsView* view;
    QGraphicsScene* scene;
    userTank* tank;
private slots:
    void userTankMove();
};
gameController::gameController(QObject *parent, QWidget *widget) :QObject(parent)
{
     scene  = new QGraphicsScene(QRectF(0,0,300,300));
     tank =  new userTank;
     scene->addItem(tank);
     tank->grabKeyboard();

     view = new QGraphicsView(scene,widget);
     QTimer* timer = new QTimer;
     timer->setInterval(100);
     connect(timer,SIGNAL(timeout()),SLOT(userTankMove()));
     timer->start();
}

void gameController::userTankMove()
{
    static int speed = 10;
    if(tank->state == userTank::moving){
       QPointF srcPos = tank->pos();

       switch(tank->movedir){
       case  userTank::down:
           srcPos.setY(qMin(srcPos.y()+speed,scene->sceneRect().height()-tank->size.rheight()));
           break;
       case  userTank::up:
           srcPos.setY(qMax(srcPos.y()-speed,0.0));
           break;
       case  userTank::right:
           srcPos.setX(qMin(srcPos.x()+speed,scene->sceneRect().width()-tank->size.rwidth()));
           break;
       default:
           srcPos.setX(qMax(srcPos.x()-speed,0.0));
       }
       tank->setPos(srcPos);

   }
}

有个地方要注意一下:我们的tank必须开启grabkeybord才能接收到按键事件。坦克移动由定时器完成,可以控制speed来控制坦克的移动速度。
以下是运行结果:
这里写图片描述
这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_16952303/article/details/52912773