[学习][笔记] qt5 从入门到入坟:<13>基于GraphicsViewFrame的贪吃蛇实现


贪吃蛇实现

对于游戏而言,
我们需要一个QGraphicsScene,作为游戏发生的舞台;
一个QGraphicsView,作为观察游戏舞台的组件;
以及若干元素,用于表示游戏对象,比如蛇、食物以及障碍物等。

绘制地图

Qt 学习之路 2(31):贪吃蛇游戏(1)

需求:
创建 400* 400 的20*20灰色格子的黑色边界的地图

成品代码:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class QGraphicsScene;
class QGraphicsView;

class GameController;

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();

//private slots:
//    void adjustViewSize();

private:
    void initScene();
    void initSceneBackground();

    QGraphicsScene *scene;
    QGraphicsView *view;

    GameController *game;
};

#endif // MAINWINDOW_H
#include <QGraphicsView>
#include <QTimer>
#include <qaction.h>
#include <qmenubar.h>
#include <qapplication.h>
#include <qmessagebox.h>


#include "mainwindow.h"
//#include "constant.h"
const int TILE_SIZE = 20;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    scene(new QGraphicsScene(this)),
    view(new QGraphicsView(scene, this))/*,
    game(new GameController(*scene, this))*/
{
    setCentralWidget(view);
    resize(600, 600);

    initScene();
    initSceneBackground();

    QTimer::singleShot(0, this, SLOT(adjustViewSize()));
}

void MainWindow::initScene()
{
    scene->setSceneRect(-100, -100, 200, 200);
}

void MainWindow::initSceneBackground()
{
    QPixmap bg(TILE_SIZE, TILE_SIZE);
    QPainter p(&bg);
    p.setBrush(QBrush(Qt::gray));
    p.drawRect(0, 0, TILE_SIZE, TILE_SIZE);

    view->setBackgroundBrush(QBrush(bg));
}


MainWindow::~MainWindow()
{

}

思路:
1.初始化地图大小 600600
2.初始矩形的逻辑坐标为(-100,-100)为原点
长宽逻辑长为200
200
3.初始化背景为QBrush为灰色,
QPixmap作为背景画刷,铺满整个视图,(默认是重复平铺),大小10*10

注意:

static void QTimer::singleShot(int msec, QObject * receiver, const char * member);

在 msec 毫秒之后,调用 receiver 的 member 槽函数。在我们的代码中,第一个参数传递的是 0,也就是 0ms 之后,调用this->adjustViewSize()

效果图:
在这里插入图片描述

设计蛇和食物

Qt 学习之路 2(32):贪吃蛇游戏(2)

设计食物

需求

绘制
一个红色的小圆饼,大小要比地图中的一个方格要小

实现

思路:
代码:

// food.h //
#ifndef FOOD_H
#define FOOD_H

#include <QGraphicsItem>



class Food : public QGraphicsItem
{
public:
    Food(qreal x, qreal y);

    QRectF boundingRect() const override;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override;

    QPainterPath shape() const override;
};

#endif // FOOD_H


// food.cpp //
#include <QPainter>
#include "constants.h"
#include "food.h"

static const qreal FOOD_RADIUS = 3.0;

Food::Food(qreal x, qreal y)
{
    setPos(x, y);
    setData(GD_Type, GO_Food);//该图形元素添加额外的数据信息
}

QRectF Food::boundingRect() const //返回一个用于包裹住图形元素的矩形
{
    return QRectF(-TILE_SIZE,    -TILE_SIZE,
                   TILE_SIZE * 2, TILE_SIZE * 2 );
}

void Food::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) //使用QPainter将图形元素绘制出来。
{
    painter->save();

    painter->setRenderHint(QPainter::Antialiasing);
    painter->fillPath(shape(), Qt::red);

    painter->restore();
}

QPainterPath Food::shape() const //矢量轮廓线,绘制路径
{
    QPainterPath p;
    p.addEllipse(QPointF(TILE_SIZE / 2, TILE_SIZE / 2), FOOD_RADIUS, FOOD_RADIUS); //draw circle
    return p;
}

#ifndef GAMECONTROLLER_H
#define GAMECONTROLLER_H
#include <QObject>
#include <QGraphicsScene>
#include <QTimer>
#include <QAction>

class Snake;
class Food;

class GameController : public QObject
{
    Q_OBJECT
public:
    GameController();
    GameController(QGraphicsScene *scene, QObject *parent);
    virtual ~GameController() {}

public slots:
    void pause();
    void resume();

private:
    QAction * resumeAction;
    QTimer timer;
    QGraphicsScene *scene;
    Snake *snake;
};
#endif // GAMECONTROLLER_H



//gamecontroller.cpp
#include <QEvent>
#include <QGraphicsScene>
#include <QKeyEvent>
#include <QMessageBox>
#include <QAction>

#include "gamecontroller.h"
#include "food.h"
//#include "snake.h"

GameController::GameController(QGraphicsScene *scene, QObject *parent) :
    QObject(parent),
    scene(scene)
    /*,snake(new Snake(this))*/
{
    timer.start(1000/33);

    Food *a1 = new Food(0, -50);
    scene->addItem(a1);

    //scene->addItem(snake);

    scene->installEventFilter(this);

    resume();
}



void GameController::pause()
{
    disconnect(&timer, SIGNAL(timeout()),
               scene,  SLOT(advance()));
}

void GameController::resume()
{
    connect(&timer, SIGNAL(timeout()),
            scene,  SLOT(advance()));
}

GameController的工作是,初始化场景中的游戏对象,开始游戏循环。每一个游戏都需要有一个游戏循环,类型于事件循环。想象一个每秒滴答 30 次的表。每次响起滴答声,游戏对象才有机会执行相应的动作:移动、检查碰撞、攻击或者其它一些游戏相关的活动。为方便起见,我们将这一次滴答成为一帧,那么,每秒 30 次滴答,就是每秒 30 帧。游戏循环通常使用定时器实现,因为应用程序不仅仅是一个游戏循环,还需要响应其它事件,比如游戏者的鼠标键盘操作。正因为如此,我们不能简单地使用无限的 for 循环作为游戏循环。

在 Graphics View Framework 中,每一帧都应该调用一个称为advance()的函数。QGraphicsScene::advance()会调用场景中每一个元素自己的advance()函数。所以,如果图形元素需要做什么事,必须重写QGraphicsItem的advance(),然后在游戏循环中调用这个函数。

设计蛇

需求

1.蛇具有复杂得多的形状。因为蛇的形状随着游戏者的控制而不同,因此,我们必须找出一个能够恰好包含蛇头和所有身体块的矩形。这也是 boundingRect() 函数所要解决的问题。

2.蛇会长大(比如吃了食物之后)。因此,我们需要在蛇对象中增加一个用于代表蛇身体长度的growing变量:当growing为正数时,蛇的身体增加一格;当growing为负数时,蛇的身体减少一格。

3.advance()函数用于编码移动部分,这个函数会在一秒内调用 30 次(这是我们在GameController的定时器中决定的)。

实现

思路:
1.绘制一个黄色方框 添加到scene里面
2.控制小蛇

绘制蛇

代码:

#ifndef SNAKE_H
#define SNAKE_H

#include <QGraphicsItem>

#include "gamecontroller.h"

class Snake :public QGraphicsItem
{
public:
    Snake();

    Snake(GameController *controller);

    QRectF boundingRect() const override;

    QPainterPath shape() const override;

    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;

private:
    QPointF  head;
    QList<QPointF> tail;
};


//snake.cpp 


#include <QPainter>
#include <constants.h>
#include "snake.h"



static const qreal SNAKE_SIZE = TILE_SIZE;


Snake::Snake(GameController *controller) :
    head(0, 0)/*,
    growing(7),
    speed(5),
    moveDirection(NoMove),
    controller(controller)*/
{
}


QRectF Snake::boundingRect() const
{
    qreal minX = head.x();
    qreal minY = head.y();
    qreal maxX = head.x();
    qreal maxY = head.y();

    foreach (QPointF p, tail) {
        maxX = p.x() > maxX ? p.x() : maxX;
        maxY = p.y() > maxY ? p.y() : maxY;
        minX = p.x() < minX ? p.x() : minX;
        minY = p.y() < minY ? p.y() : minY;
    }

    QPointF tl = mapFromScene(QPointF(minX, minY));
    QPointF br = mapFromScene(QPointF(maxX, maxY));

    QRectF bound = QRectF(tl.x(),  // x
                          tl.y(),  // y
                          br.x() - tl.x() + SNAKE_SIZE,      // width
                          br.y() - tl.y() + SNAKE_SIZE       //height
                          );
    return bound;
}


QPainterPath Snake::shape() const
{
    QPainterPath path;
    path.setFillRule(Qt::WindingFill);

    path.addRect(QRectF(0, 0, SNAKE_SIZE, SNAKE_SIZE));

    foreach (QPointF p, tail) {
        QPointF itemp = mapFromScene(p);
        path.addRect(QRectF(itemp.x(), itemp.y(), SNAKE_SIZE, SNAKE_SIZE));
    }

    return path;
}

void Snake::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
    painter->save();
    painter->fillPath(shape(), Qt::yellow);
    painter->restore();
}



#endif // SNAKE_H


//gamecontroller.cpp
#include "gamecontroller.h"
#include "food.h"
#include "snake.h"

GameController::GameController(QGraphicsScene *scene, QObject *parent) :
    QObject(parent),
    scene(scene),
    snake(new Snake(this))
{
    timer.start(1000/33);

    Food *a1 = new Food(0, -50);
    scene->addItem(a1);

    scene->addItem(snake);

    scene->installEventFilter(this);

    resume();
}
控制蛇

详解 QT Event 以及 Event Filter 事件处理

代码实现:

#ifndef GAMECONTROLLER_H
#define GAMECONTROLLER_H
#include <QObject>
#include <QGraphicsScene>
#include <QTimer>
#include <QAction>

class Snake;
class Food;

class GameController : public QObject
{
    Q_OBJECT
public:
    GameController();
    GameController(QGraphicsScene &scene, QObject *parent);
    virtual ~GameController() {}

public slots:
    void pause();
    void resume();

protected:
     bool eventFilter(QObject *object, QEvent *event);
private:
     void handleKeyPressed(QKeyEvent *event);
     void addNewFood();
     void setResume();
private:
     QAction * resumeAction;
     QTimer timer;
     QGraphicsScene &scene;
     Snake *snake;
     bool isPause;
};


#endif // GAMECONTROLLER_H

//gamecontroller.cpp
#include <QEvent>
#include <QGraphicsScene>
#include <QKeyEvent>
#include <QMessageBox>
#include <QAction>

#include "gamecontroller.h"
#include "food.h"
#include "snake.h"
#include "mainwindow.h"

GameController::GameController(QGraphicsScene &scene, QObject *parent) :
    QObject(parent),
    scene(scene),
    snake(new Snake(*this))
{
    timer.start(1000/33);

    Food *a1 = new Food(0, -50);
    scene.addItem(a1);

    scene.addItem(snake);

    scene.installEventFilter(this);

    resume();
}



void GameController::()
{
    disconnect(&timer, SIGNAL(timeout()),
               &scene,  SLOT(advance()));
}

void GameController::resume()
{
    connect(&timer, SIGNAL(timeout()),
            &scene,  SLOT(advance()));
}

bool GameController::eventFilter(QObject *object, QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
        handleKeyPressed((QKeyEvent *)event);
        return true;
    } else {
        return QObject::eventFilter(object, event);
    }
}

void GameController::handleKeyPressed(QKeyEvent *event)
{
    if (!isPause)
        switch (event->key()) {
            case Qt::Key_Left:
                snake->setMoveDirection(Snake::MoveLeft);
                break;
            case Qt::Key_Right:
                snake->setMoveDirection(Snake::MoveRight);
                break;
            case Qt::Key_Up:
                snake->setMoveDirection(Snake::MoveUp);
                break;
            case Qt::Key_Down:
                snake->setMoveDirection(Snake::MoveDown);
                break;
            case Qt::Key_Space:
                ();
                break;
        }
    else resume();
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二进制怪兽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值