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;