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

简介: [学习][笔记] 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();
}



相关文章
|
2月前
|
存储 SQL 数据库连接
【QT速成】半小时入门QT6之QT前置知识扫盲(二)
【QT速成】半小时入门QT6之QT前置知识扫盲(二)
128 13
|
6月前
|
XML 开发框架 JSON
【Qt】Qt入门
【Qt】Qt入门
|
2月前
|
数据可视化 编译器 API
【QT速成】半小时入门QT6之QT前置知识扫盲(一)
【QT速成】半小时入门QT6之QT前置知识扫盲(一)
141 2
|
2月前
|
存储 SQL 数据库连接
【QT速成】半小时入门QT6之QT前置知识扫盲(二)
【QT速成】半小时入门QT6之QT前置知识扫盲(二)
101 2
|
3月前
|
定位技术 Go 开发工具
dynamic-situational-awareness-qt学习记录
本文是作者yantuguiguziPGJ关于dynamic-situational-awareness-qt学习记录的分享,介绍了在Qt学习过程中发现的qml资源丰富的代码仓库,并提供了资源路径和相关的安装、配置步骤,涉及的内容有数字地球、GIS纹理等,同时提供了相关链接和git命令来克隆代码仓库和ArcGIS Runtime SDK for Qt的安装说明。
|
2月前
|
编译器 API C语言
【QT速成】半小时入门QT6之QT前置知识扫盲(一)
【QT速成】半小时入门QT6之QT前置知识扫盲(一)
176 0
|
4月前
【Qt项目专栏】贪吃蛇小游戏1.0
【Qt项目专栏】贪吃蛇小游戏1.0
109 5
|
6月前
|
计算机视觉
【轻松入门】OpenCV4.8 + QT5.x开发环境搭建
【轻松入门】OpenCV4.8 + QT5.x开发环境搭建
102 0
【轻松入门】OpenCV4.8 + QT5.x开发环境搭建
|
5月前
|
C++
Qt中的信号与槽如何学习?(包括自定义信号)这篇文章告诉你
以现实中的事件来举例的话,例如有两把不同颜色的信号枪,分别是红色,绿色,打响不通颜色的信号枪会触发不同的槽发生,比如说打响红色这个人就跑步,绿色就走步,但是还有一个很重要的机制,那就是连接,我们需要把信号枪去跟这个人的动作连接起来。 如果上面理解没问题的话我们可以把信号和槽看成两个工具,我们最重要的是如何去把这两个工具连接起来。 它的作用可以让我们更加灵活的去使用不同窗口间的切换以及某些事件的连接。
112 0
|
5月前
|
开发者
Qt中的事件该如何学习?(附带案例)
事件是Qt中比较重要的一部分,在初期如果理解不当学习可能会比较困难,这里提一嘴当初教我的那位老师水平是真的高,让我很轻易的就理解了事件的概念。 在平时我们见到那些界面上的某些快捷键就有可能是事件做的,例如ESC关闭窗口,Enter提交或者登录这种类似的,这也是事件的强大之处。
136 0