Qt练习项目--俄罗斯方块

一、开发环境

archlinux 更新到最新版
Qt 5.15.2+kde+r171-2
qt creator 4.14.2-1

二、项目实现

这个项目根据 《Qt及Qt Quick开发实战精解》第二章的内容编写,主要记录一些编程过程中遇到的问题。

1.游戏架构

在这里插入图片描述
游戏场景:800x500像素
方块移动区域:200x400像素
方块大小是20像素
所以整个区域可以看成是一个由20行10列的方块组成的网格。

2.实现游戏逻辑

(1)设计小方块

新建一个类,继承值QGraphicsObject。
为什么要选择这个类,因为它提供了图形项支持信号、槽和属性系统功能。这个类继承自 QObject and QGraphicsItem 。
自定义图形项:重新实现两个公共的纯虚函数boundingRect()和paint()。前者是用来返回绘制图形项的矩形区域,后者是执行实际的绘图操作。图形项的矩形区域包含一半的画笔宽度。
shape函数用来进行碰撞检测的。

#ifndef ONEBOX_H
#define ONEBOX_H

#include <QGraphicsObject>

class OneBox : public QGraphicsObject
{
public:
    OneBox(const QColor &color = Qt::red);
    QRectF boundingRect() const override;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
    QPainterPath shape() const override;

private:
    QColor brush_color_;
};

#endif  // ONEBOX_H

(2)设计方块组

方块组将下面7种形状的方块组成一个一个的图形项,便于移动和旋转。图形项的座标原点在其中心。
在这里插入图片描述
对每一个方块做碰撞检测的时候,因为已经和方块组发生了碰撞,所以自要碰撞的数量大于1,说明方块组有碰撞发生。

bool BoxGroup::IsColliding()
{
    QList<QGraphicsItem *> item_list = childItems();
    QGraphicsItem *item;
    foreach (item, item_list)
    {
        if (item->collidingItems().count() > 1)
        {
            return true;
        }
    }
    return false;
}

产生随机数不在使用qrand函数了,而是使用下面这个:

shape_id = QRandomGenerator::global()->bounded(7);

旋转方块组,我这里没有找到书中提供的那个函数rotate()。而是使用:

    case Qt::Key_Up:
        ++angle_;
        angle_ = angle_ > 4 ? 1 : angle_;
        setRotation(90 * angle_);
        if (IsColliding())
        {
            if (x() <= 220)
            {
                moveBy(20, 0);
            }
            else if (x() >= 380)
            {
                moveBy(-20, 0);
            }
            setRotation(-90 * angle_);
        }
        break;

使用一个变量来保存已经旋转的角度,便于计算上次选择的角度。还有一些问题都是因为旋转导致的,这里就是当方程组在移动区域两侧边缘旋转的时候,容易造成方块组卡在“墙”上。

此外还有就是创建方块组的函数中显示下一个方块组的区域容易发生碰撞,导致游戏终止。添加下面的条件判断:

    if (point.x() == 300 && IsColliding())
    {
        StopTimer();
        emit GameFinished();
    }

(3)添加游戏场景

新建一个类

#ifndef MYVIEW_H
#define MYVIEW_H

#include <QGraphicsView>
#include <QWidget>

class BoxGroup;

class MyView : public QGraphicsView
{
    Q_OBJECT
public:
    explicit MyView(QWidget *parent = nullptr);

public slots:
    void StartGame();
    void ClearFullRows();
    void MoveBox();
    void GameOver();

private:
    BoxGroup *box_group_;
    BoxGroup *next_box_group_;
    QGraphicsLineItem *top_line_;
    QGraphicsLineItem *bottom_line_;
    QGraphicsLineItem *left_line_;
    QGraphicsLineItem *right_line_;
    qreal game_speed_;
    QList<int> rows_;
    void InitView();
    void InitGame();
    void UpdateScore(const int full_row_num = 0);
};

#endif  // MYVIEW_H

scene->addItem(box_group_);
场景自动添加方块组函数addItem

Adds or moves the item and all its childen to this scene. This scene takes ownership of the item.
If the item is visible (i.e., QGraphicsItem::isVisible() returns true), QGraphicsScene will emit changed() once control goes back to the event loop.
If the item is already in a different scene, it will first be removed from its old scene, and then added to this scene as a top-level.

由于指针是指向的同一个内存区域,那么上一个方块组的旋转的角度这些信息也是保存了下来的,在新建方块组时,注意要重置。

3.游戏优化

(1)添加满行销毁动画

书中有代码,不在贴出

(2)添加游戏级别设置

(3)添加游戏控制按钮和模板

(4)添加背景音乐和音效

这部分没有使用书中的代码了,因为qt5开始使用multimedia模块了。
播放音频的核心代码:

QString kSoundPath = QDir(QDir::current()).canonicalPath();

background_music_ = new QMediaPlayer(this);
background_music_->setMedia(QUrl::fromLocalFile(kSoundPath + "/sounds/background.mp3"));
background_music_->play();

这里要将音频文件夹拷贝到编译后的那个文件夹中。因为获取的是应用程序的当前目录。
在这里插入图片描述

还有就是背景音乐的循环播放使用这个信号


void 
mediaStatusChanged(QMediaPlayer::MediaStatus status)

除此之外还要安装解码器
windows安装K-Lite_Codec_Pack_1612_Basic
linux安装的是gstream相关的

在Windows中运行遇到这个报错

doRender: Unknown error 0x80040200.

这是因为音频输出的问题,我这个电脑没有音响,在这里插入图片描述
插上耳机就好了

(5)添加程序启动动画

三、项目源代码

链接: https://pan.baidu.com/s/1nHAZJ5eQEySgwITY5Nbruw
提取码: nqg4

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值