Qt-俄罗斯方块

本文记录了作者使用Qt开发俄罗斯方块游戏的过程,包括遇到的冲突检测、坐标系转换、消除规则等问题,以及解决方案和开发日志。在冲突检测上,作者放弃了Qt的内置方法,选择自定义算法。此外,还提到了Qt应用的图标设置、编码问题和窗口移动的优化。
摘要由CSDN通过智能技术生成

1.前言

整个程序的完成花了我不少时间, 有许多知识细节不够清楚,边学边做,断断续续完成的。之前有用C++直接做过一次俄罗斯方块,界面简陋,是在控制台运行的。这次用Qt实现,沿用了之前的总体思想,技术细节有所改动。


2.效果展示

  刚开始:
这里写图片描述
开始游戏:
这里写图片描述

这里写图片描述
背景界面随着分数进行随机的切换
这里写图片描述


3.主要代码
项目构架:
这里写图片描述
这里写图片描述

这里写图片描述

**基本单元方块类(Elem):**

#ifndef ELEM_H
#define ELEM_H
#include <QGraphicsObject>
#include <QPainter>
#include <QTime>

enum elemColor {
    red, yellow, blue, green, lightBlue, purple,gray,randomColor
};
const QColor ElemColorArray[] = {
    QColor(255,0,0,100),  QColor(255,255,0,100),QColor(0,0,255,100),
    QColor(0,255,0,100),QColor(0,255,255,100),QColor(255,0,255,100),
    QColor(150,100,100,100)
};
class Elem : public QGraphicsObject
{
public:
    Elem(QPointF pos = QPointF(0,0), int colorId = elemColor::randomColor, QGraphicsItem *parent = Q_NULLPTR);
    void paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget);
    QRectF boundingRect() const;
    int getCurrentStatus();
    void setCurrentStatus(int status);
private:
    int colorId;
    int currentStatus;
};
#endif // ELEM_H

**基本单元方块类Elem 实现部分:**

#include "elem.h"

Elem::Elem(QPointF pos,int colorId, QGraphicsItem *parent):QGraphicsObject(parent)
{
   this->currentStatus = 0;

   this->setPos(this->mapFromParent(pos));

   this->colorId = colorId;

   if (this->colorId == elemColor::randomColor) {
       qsrand(QTime().currentTime().second());
       this->colorId = qrand() % 7;
   }
}

void Elem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->drawPixmap(0,0,20,20,QPixmap(":/image/images/box.gif"));
    painter->setBrush(QBrush(ElemColorArray[this->colorId]));
    painter->setPen(QPen(ElemColorArray[this->colorId]));
    painter->drawRect(0,0,19,19);
}

QRectF Elem::boundingRect() const
{
    return QRectF(-0.5,-0.5,21,21);
}

int Elem::getCurrentStatus()
{
    return this->currentStatus;
}

void Elem::setCurrentStatus(int status)
{
    this->currentStatus = status;
}



**形状基类MyShape:**

#ifndef MYSHAPE_H
#define MYSHAPE_H
#include <QGraphicsItemGroup>
#include <QKeyEvent>
#include <QObject>
#include <QTime> // 为了引入 随机数
#include "elem.h"

enum shapeCode {
    shapeT,shapeL,shapeMirrorL,shapeSquare,shapeZ,shapeMirrorZ,shapeLine,shapeRandom
};// 这里面的值  0~7

enum shapeCoorId {
  // 形状坐标id
    LeftTop, RightTop,RightBottom,LeftBottom
};// 分别是 左上, 右上, 右下, 左下 为原点      在旋转的时候坐标系整体旋转

class MyShape : public QObject,public QGraphicsItemGroup
{
    Q_OBJECT
public:
    MyShape(QPoint origin = QPoint(300,40));
    QPoint getOrigin();
    virtual void rolate();
    virtual void rolateBack();
    int getCurrentStatus();

protected:
    QPoint origin;

    // 下面的变量用来标明当前使用的是哪一个形状, 在体现 旋转中心不同 等形状个性 的时候需要用到
protected:
    int currentStatus;// 指明了当前的旋转角度, 同时也表明了当前坐标系的状态,也说明了自己当前属于自己的形态的哪一种
    int colorId;

    Elem * elem[4];
};

#endif // MYSHAPE_H


**MyShape实现部分**:

#include "myshape.h"

MyShape::MyShape(QPoint origin)
{
    this->origin = origin;
    this->setPos(this->origin);
}

QPoint MyShape::getOrigin()
{
    return this->origin;
}

void MyShape::rolate()
{
    this->currentStatus = (this->currentStatus + 90) % 360;
    this->setTransformOriginPoint(20,20);
    this->setRotation(this->currentStatus);

    elem[0]->setCurrentStatus(this->currentStatus);
    elem[1]->setCurrentStatus(this->currentStatus);
    elem[2]->setCurrentStatus(this->currentStatus);
    elem[3]->setCurrentStatus(this->currentStatus);
}

void MyShape::rolateBack()
{
    this->currentStatus = (this->currentStatus-90 + 360) % 360;
    this->setTransformOriginPoint(20,20);
    this->setRotation(this->currentStatus);

    elem[0]->setCurrentStatus(this->currentStatus);
    elem[1]->setCurrentStatus(this->currentStatus);
    elem[2]->setCurrentStatus(this->currentStatus);
    elem[3]->setCurrentStatus(this->currentStatus);
}

int MyShape::getCurrentStatus()
{
    return this->currentStatus;
}


**各个具体的形状这里我只举例一个ShapeLine类:**
#ifndef SHAPELINE_H
#define SHAPELINE_H
#include "myshape.h"

class ShapeLine : public MyShape
{
public:
    ShapeLine(int currentStatus = 0,QPoint origin = QPoint(300,40),int colorId = elemColor::gray);
    virtual void rolate();
};

#endif // SHAPELINE_H

**ShapeLine类实现部分:**

#include "shapeline.h"

ShapeLine::ShapeLine(int currentStatus, QPoint origin,int colorId) : MyShape(origin)
{
    //this->shapeId = shapeCode::shapeLine;
    if (currentStatus % 90 || currentStatus < 0) {
        currentStatus = 0;
        this->currentStatus = currentStatus % 180;
    }
    else {
        qsrand(QTime::currentTime().second());
        this->currentStatus = (qrand() % 2) * 90;
    }
    this->colorId = colorId;

    elem[0] = new Elem(QPointF(0,0),this->colorId,this);
    elem[1] = new Elem(QPointF(0,20),this->colorId,this);
    elem[2] = new Elem(QPointF(0,40),this->colorId,this);
    elem[3] = new Elem(QPointF(0,60),this->colorId,this);

    this->addToGroup(elem[0]);
    this->addToGroup(elem[1]);
    this->addToGroup(elem[2]);
    this->addToGroup(elem[3]);

    while (this->currentStatus != 0) {
        this->rolate();
        this->currentStatus -= 90;
    }

    elem[0]->setCurrentStatus(this->currentStatus);
    elem[1]->setCurrentStatus(this->currentStatus);
    elem[2]->setCurrentStatus(this->currentStatus);
    elem[3]->setCurrentStatus(this->currentStatus);
}

void ShapeLine::rolate()
{
    this->currentStatus = (this->currentStatus + 90) % 180;
    this->setTransformOriginPoint(20,40);
    this->setRotation(this->currentStatus);

    elem[0]->setCurrentStatus(this->currentStatus);
    elem[1]->setCurrentStatus(this->currentStatus);
    elem[2]->setCurrentStatus(this->currentStatus);
    elem[3]->setCurrentStatus(this->currentStatus);
}



**负责核心操控的Game类:**

#ifndef GAME_H
#define GAME_H
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QGuiApplication>
#include <QLabel>
#include <QPushButton>
#include "myshape.h"
#include <QTimer>
#include <QList>
#include <QPalette>
#include <QLabel>
#include <QGraphicsWidget>
#include <QMediaPlayer>
#include <QScreen>
#include <QDir>
#include "shapet.h"
#include "shapel.h"
#include "shapemirrorl.h"
#include "shapez.h"
#include "shapemirrorz.h"
#include "shapeline.h"
#include "shapesquare.h"
#include <QVector>
#include <iostream>
#include <QMessageBox>

using namespace std;

class Game : public QGraphicsView
{
    Q_OBJECT
public:
    Game();
    void keyPressEvent(QKeyEvent *event);
private slots:
    void init(); // 需要写一个初始化函数,明明已经有了构造函数,构造函数不久可一实现初始化吗,为什么还要重写一个init函数呢? 因为游戏会重新开局!!这个重新开局不是关闭游戏再重新打开游戏,而构造函数在整个过程中只是会执行一次,除非关闭程序重新运行才可以执行构造函数,显然着不符合用户需求,用户希望的是在不需要重新打开游戏的前提下,重新游戏
private:
    void startMenu();
    bool isShapeColliding(); // 方块之间冲突
    bool isBorderColliding(); // 方块和边界冲突
    bool moveDownOneStep();// 只是单纯的下降一步,返回值表示下降是否成功
    void clearUpAndRenewShape(); // 消除判断 和 新生一个形状
    void clearShapeWithNoChild(); // 清除没有孩子的形状
    MyShape * newShape(int shapeId = shapeCode::shapeRandom,int status = -1,QPoint landedPoint = QPoint(300,40));// shapeId 用于选择哪个形状, status 用于确定形状初始的形态 -1 表示随机, landedPoint 指定该形状的落点
    QRectF getShapeCurrentBoundingRectInScene();//这个是用来获取当前形状,无论是否被旋转过,都一律返回当前在scene中的以左上角为中心点的包围矩形
    void gameOver(); // 当消除后,当时形状的上界能够触碰到方块活动区域上边界,那么游戏结束,调用gameOver,做好一些后续处理工作
    void setFlag(bool flag);
protected slots:
    void timeSlice();
    void pause();
    void screenShotSlot(); // 保存游戏界面截图
protected:
    void mousePressEvent(QMouseEvent * event);
    void mouseMoveEvent(QMouseEvent * event);
    void mouseReleaseEvent(QMouseEvent *event);
private:
    MyShape * myshape;
    // 下面变量和用来指定方块活动区域范围
    QPoint areaOrigin; // 区域原点
    int areaWidth; //  区域宽
    int areaHeight; 
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值