俄罗斯方块
需求分析
- 方块样式
- 界面设计
- 需求
1、方块随机出现不同形状,不同颜色
2、有一个界面显示下一个要出现的方块
3、能够消去填满的行,并根据同时消掉的行数以及它的颜色建立得分机制
4、显示得分
5、根据得分情况逐步加快速度
6、按键控制:“Key_Up”——顺时针旋转,“Key_Right”——右移,“Key_Left”——左移,“Key_Down”——加速下降
7、游戏结束:当方块堆积到最顶层的时候,游戏结束
8、方块出现时有一个逐个进入的效果
9、有开始,暂停和重新开始按钮 - 最终实现效果
实现过程
1、界面设计:
在界面设计的时候,开始时采用Qt Designer直接设计Ui,问题是直接使用Ui,子窗口直接使用的是基类QWidget。果断放弃。
在将子窗口(游戏界面gamewidget.h与下一方块界面nextwidget.h)和控件(QPushbutton与QLabel)确定好位置,再对两个窗口进行区域分配:
struct WIDGETPROPERTY{
unsigned int rectSize;//每个方块的尺寸
unsigned int rectNum_X;//窗口X方向方块的数量
unsigned int rectNum_Y;//窗口Y方向方块的数量
unsigned int rectDistance;//方块间距
unsigned int rectDistanceWithBorder;//方块与边界的距离
unsigned int X_Size;//窗口X方向上的尺寸
unsigned int Y_Size;//窗口Y方向上的尺寸
};
struct RECTPROPERTY{
unsigned int x_coordinate;//横轴坐标
unsigned int y_coordinate;//纵轴坐标
bool light;//是否点亮
int color;//点亮颜色
};//坐标
enum myColor{
RED,YELLOW,GREEN,BLUE,PRIMARY_COLOR
};
QRgb getColor(int color);
enum myShape{
StripShape_1,StripShape_2,
Diamonds,
TankShape_1,TankShape_2,TankShape_3,TankShape_4,
Chair_1_Shape_1,Chair_1_Shape_2,
Chair_2_Shape_1,Chair_2_Shape_2,
Soft_1_Shape_1,Soft_1_Shape_2,Soft_1_Shape_3,Soft_1_Shape_4,
Soft_2_Shape_1,Soft_2_Shape_2,Soft_2_Shape_3,Soft_2_Shape_4,
Nothing//什么都不显示
};
struct myTetris{//在(x_coordinate,y_coordinate)位置显示颜色为color,形状为color的方块
unsigned int x_coordinate[4];
unsigned int y_coordinate[4];
myShape shape;
myColor color;
};
gameWidget与nextWidget的实现机制是一样的,区别主要在于两个窗口的大小不一样,且gameWidget要实现动画效果。
定义了一个结构体 WIDGETPROPERTY 来存储窗口的大小信息。
定义了一个 WIDGETPROPERTY 结构体来存储每个方块的横纵坐标,是否点亮以及点亮颜色。
定义了两个枚举类型分别表示颜色myColor,形状myShape。
定义了一个结构体myTetris存储显示的四个方块的位置,形状,颜色。
区域分配函数:
WidgetProperty::RECTPROPERTY **widgetArea;
void setWidgetProperty(int rectSize,int rectNum_X,int rectNum_Y,
int rectDistance,int rectDistanceWithBorder);
void zoneAllocation();//区域分配
void GameWidget::setWidgetProperty(int rectSize,int rectNum_X,int rectNum_Y,
int rectDistance,int rectDistanceWithBorder)
{
widgetProperty.rectSize = rectSize;
widgetProperty.rectNum_X = rectNum_X;
widgetProperty.rectNum_Y = rectNum_Y;
widgetProperty.rectDistance = rectDistance;
widgetProperty.rectDistanceWithBorder = rectDistanceWithBorder;
widgetProperty.X_Size = rectSize * rectNum_X;
widgetProperty.Y_Size = rectSize * rectNum_Y;
}
void GameWidget::zoneAllocation()
{
widgetArea = new WidgetProperty::RECTPROPERTY *[widgetProperty.rectNum_Y];//多分配3行,以便方块刚出来的时候产生进入效果
for(unsigned int i=0;i<widgetProperty.rectNum_Y;i++){ //且多出来的3行的真实区域与第4行相同
widgetArea[i] = new WidgetProperty::RECTPROPERTY[widgetProperty.rectNum_X];
}
for(unsigned int i=3;i<widgetProperty.rectNum_Y;i++){
for(unsigned int j=0;j<widgetProperty.rectNum_X;j++){
widgetArea[i][j].x_coordinate =widgetProperty.rectDistanceWithBorder + j * (widgetProperty.rectSize + widgetProperty.rectDistance);
widgetArea[i][j].y_coordinate =widgetProperty.rectDistanceWithBorder + (i-3) * (widgetProperty.rectSize + widgetProperty.rectDistance);
widgetArea[i][j].light = false;
widgetArea[i][j].color = WidgetProperty::PRIMARY_COLOR;
}
}
for(unsigned int i=0;i<3;i++){
for(unsigned int j=0;j<widgetProperty.rectNum_X;j++){
widgetArea[i][j].x_coordinate = widgetArea[3][j].x_coordinate;
widgetArea[i][j].y_coordinate = widgetArea[3][j].y_coordinate;
widgetArea[i][j].light = widgetArea[3][j].light;
widgetArea[i][j].color = widgetArea[3][j].color;
}
}
}
对界面进行区域分配后,整个游戏界面就分为rectNum_Y(行)*rectNum_X(列)个方块,每个方块都有自己的位置信息,颜色,以及是否显示属性。
使用QPainter来绘制界面。这里重新编写了一个类mypainter,继承QPainter,以便能够直接在界面对应为显示任意颜色任意形状的方块。
class MyPainter : public QPainter
{
public:
explicit MyPainter(QPaintDevice *canvas,WidgetProperty::WIDGETPROPERTY &widgetProperty,
WidgetProperty::RECTPROPERTY **widgetArea);
~MyPainter();
public:
WidgetProperty::WIDGETPROPERTY widgetProperty;
void setWidgetProperty(WidgetProperty::WIDGETPROPERTY &widgetProperty);
WidgetProperty::RECTPROPERTY **widgetArea;//使用二级指针,在构造函数中对其初始化
public:
void drawPicture();
void drawWidgetRect(unsigned int row,unsigned int column,WidgetProperty::myColor color);
void switchShape(WidgetProperty::myTetris *tetris);
void drawShape(WidgetProperty::myTetris *tetris);
void drawWidgetStripShape_1(WidgetProperty::myTetris *tetris);//绘制长条
void drawWidgetStripShape_2(WidgetProperty::myTetris *tetris);//绘制横条
void drawWidgetDiamonds(WidgetProperty::myTetris *tetris);//绘制方块
void drawWidgetTankShape_1(WidgetProperty::myTetris *tetris);//绘制“坦克”形状:1
void drawWidgetTankShape_2(WidgetProperty::myTetris *tetris);//绘制“坦克”形状:2
void drawWidgetTankShape_3(WidgetProperty::myTetris *tetris);//绘制“坦克”形状:3
void drawWidgetTankShape_4(WidgetProperty::myTetris *tetris);//绘制“坦克”形状:4
void drawWidgetChair_1_Shape_1(WidgetProperty::myTetris *tetris);//绘制1“椅子”形状:1
void drawWidgetChair_1_Shape_2(WidgetProperty::myTetris *tetris);//绘制1“椅子”形状:2
void drawWidgetChair_2_Shape_1(WidgetProperty::myTetris *tetris);//绘制2“椅子”形状:1
void drawWidgetChair_2_Shape_2(WidgetProperty::myTetris *tetris);//绘制2“椅子”形状:2
void drawWidgetSoft_1_Shape_1(WidgetProperty::myTetris *tetris);//绘制1“沙发”形状:1
void drawWidgetSoft_1_Shape_2(WidgetProperty::myTetris *tetris);
void drawWidgetSoft_1_Shape_3(WidgetProperty::myTetris *tetris);
void drawWidgetSoft_1_Shape_4(WidgetProperty::myTetris *tetris);
void drawWidgetSoft_2_Shape_1(WidgetProperty::myTetris *tetris);//绘制2“沙发”形状:1
void drawWidgetSoft_2_Shape_2(WidgetProperty::myTetris *tetris);
void drawWidgetSoft_2_Shape_3(WidgetProperty::myTetris *tetris);
void drawWidgetSoft_2_Shape_4(WidgetProperty::myTetris *tetris);
void clearAll();//清除界面
};
MyPainter::MyPainter(QPaintDevice *canvas,WidgetProperty::WIDGETPROPERTY &widgetProperty,
WidgetProperty::RECTPROPERTY **widgetArea) : QPainter(canvas)
{
setWidgetProperty(widgetProperty);
this->widgetArea = widgetArea;
}
MyPainter::~MyPainter()
{
}
void MyPainter::setWidgetProperty(WidgetProperty::WIDGETPROPERTY &widgetProperty)
{
this->widgetProperty.rectSize = widgetProperty.rectSize;
this->widgetProperty.rectNum_X = widgetProperty.rectNum_X;
this->widgetProperty.rectNum_Y = widgetProperty.rectNum_Y;
this->widgetProperty.rectDistance = widgetProperty.rectDistance;
this->widgetProperty.rectDistanceWithBorder = widgetProperty.rectDistanceWithBorder;
this->widgetProperty.X_Size = widgetProperty.X_Size;
this->widgetProperty.Y_Size = widgetProperty.Y_Size;
}
QRgb WidgetProperty::getColor(int color)
{
switch(color)
{
case myColor::RED : return qRgb(192,96,96);
case myColor::YELLOW : return qRgb(192,192,96);
case myColor::GREEN : return qRgb(96,192,96);
case myColor::BLUE : return qRgb(96,192,192);
case myColor::PRIMARY_COLOR : return qRgb(240,240,240);
default : return qRgb(240,240,240);
}
}
void MyPainter::drawPicture()
{
for(int i=0;i<widgetProperty.rectNum_Y;i++){
for(int j=0;j<widgetProperty.rectNum_X;j++){
if(widgetArea[i][j].light == 1){
this->setPen(QPen(Qt::black,1,Qt::SolidLine,Qt::RoundCap));
this->setBrush(QBrush(QColor(WidgetProperty::getColor(widgetArea[i][j].color))));
this->drawRect(widgetArea[i][j].x_coordinate,widgetArea[i][j].y_coordinate,
widgetProperty.rectSize,widgetProperty.rectSize);
}
if(widgetArea[i][j].light == 0){
this->setPen(QPen(Qt::black,1,Qt::NoPen,Qt::RoundCap));
this->setBrush(QBrush(QColor(WidgetProperty::getColor(widgetArea[i][j].color))));
this->drawRect(widgetArea[i][j].x_coordinate,widgetArea[i][j].y_coordinate,
widgetProperty.rectSize,widgetProperty.rectSize);
}
}
}
}
void MyPainter::drawWidgetRect(unsigned int row,unsigned int column,WidgetProperty::myColor color)
{
if(row>widgetProperty.rectNum_Y-1) row = row%widgetProperty.rectNum_Y;
if(column>widgetProperty.rectNum_X-1) column = column%widgetProperty.rectNum_X;
if(color == WidgetProperty::PRIMARY_COLOR){
widgetArea[row][column].light = false;
}
else{
widgetArea[row][column].light = true;
}
widgetArea[row][column].color = color;
}
void MyPainter::drawShape(WidgetProperty::myTetris *tetris)
{
switchShape(tetris);
drawWidgetRect(tetris->y_coordinate[0],tetris->x_coordinate[0],tetris->color);
drawWidgetRect(tetris->y_coordinate[1],tetris->x_coordinate[1],tetris->color);
drawWidgetRect(tetris->y_coordinate[2],tetris->x_coordinate[2],tetris->color);
drawWidgetRect(tetris->y_coordinate[3],tetris->x_coordinate[3],tetris->color);
}
void MyPainter::switchShape(WidgetProperty::myTetris *tetris)
{
switch (tetris->shape) {
case WidgetProperty::myShape::StripShape_1:
drawWidgetStripShape_1(tetris);
break;
case WidgetProperty::myShape::StripShape_2:
drawWidgetStripShape_2(tetris);
break;
case WidgetProperty::myShape::Diamonds:
drawWidgetDiamonds(tetris);
break;
case WidgetProperty::myShape::TankShape_1:
drawWidgetTankShape_1(tetris);
break;
case WidgetProperty::myShape::TankShape_2:
drawWidgetTankShape_2(tetris);
break;
case WidgetProperty::myShape::TankShape_3:
drawWidgetTankShape_3(tetris);
break;
case WidgetProperty::myShape::TankShape_4:
drawWidgetTankShape_4(tetris);
break;
case WidgetProperty::myShape::Chair_1_Shape_1:
drawWidgetChair_1_Shape_1(tetris);
break;
case WidgetProperty::myShape::Chair_1_Shape_2:
drawWidgetChair_1_Shape_2(tetris);
break;
case WidgetProperty::myShape::Chair_2_Shape_1:
drawWidgetChair_2_Shape_1(tetris);
break;
case WidgetProperty::myShape::Chair_2_Shape_2:
drawWidgetChair_2_Shape_2(tetris);
break;
case WidgetProperty::myShape::Soft_1_Shape_1:
drawWidgetSoft_1_Shape_1(tetris);
break;
case WidgetProperty::myShape::Soft_1_Shape_2:
drawWidgetSoft_1_Shape_2(tetris);
break;
case WidgetProperty::myShape::Soft_1_Shape_3:
drawWidgetSoft_1_Shape_3(tetris);
break;
case WidgetProperty::myShape::Soft_1_Shape_4:
drawWidgetSoft_1_Shape_4(tetris);
break;
case WidgetProperty::myShape::Soft_2_Shape_1:
drawWidgetSoft_2_Shape_1(tetris);
break;
case WidgetProperty::myShape::Soft_2_Shape_2:
drawWidgetSoft_2_Shape_2(tetris);
break;
case WidgetProperty::myShape::Soft_2_Shape_3:
drawWidgetSoft_2_Shape_3(tetris);
break;
case WidgetProperty::myShape::Soft_2_Shape_4:
drawWidgetSoft_2_Shape_4(tetris);
break;
default:
break;
}
}
void MyPainter::drawWidgetStripShape_1(WidgetProperty::myTetris *tetris)//绘制长条
{
if(tetris->y_coordinate[0] < 1) tetris->y_coordinate[0] = 1;
if(tetris->y_coordinate[0] > widgetProperty.rectNum_Y-3) tetris->y_coordinate[0] = widgetProperty.rectNum_Y-3;
if(tetris->x_coordinate[0] > widgetProperty.rectNum_X-1) tetris->x_coordinate[0] = widgetProperty.rectNum_X-1;
tetris->y_coordinate[1] = tetris->y_coordinate[0] - 1;
tetris->x_coordinate[1] = tetris->x_coordinate[0];
tetris->y_coordinate[2] = tetris->y_coordinate[0] + 1;
tetris->x_coordinate[2] = tetris->x_coordinate[0];
tetris->y_coordinate[3] = tetris->y_coordinate[0] + 2;
tetris->x_coordinate[3] = tetris->x_coordinate[0];
}
void MyPainter::drawWidgetStripShape_2(WidgetProperty::myTetris *tetris)//绘制横条
{
if(tetris->y_coordinate[0] < 3) tetris->y_coordinate[0] = 3;
if(tetris->x_coordinate[0] < 1) tetris->x_coordinate[0] = 1;
if(tetris->y_coordinate[0] > widgetProperty.rectNum_Y-1) tetris->y_coordinate[0] = widgetProperty.rectNum_Y-1;
if(tetris->x_coordinate[0] > widgetProperty.rectNum_X-3) tetris->x_coordinate[0] = widgetProperty.rectNum_X-3;
tetris->y_coordinate[1] = tetris->y_coordinate[0];
tetris->x_coordinate[1] = tetris->x_coordinate[0] - 1;
tetris->y_coordinate[2] = tetris->y_coordinate[0];
tetris->x_coordinate[2] = tetris->x_coordinate[0] + 1;
tetris->y_coordinate[3] = tetris->y_coordinate[0];
tetris->x_coordinate[3] = tetris->x_coordinate[0] + 2;
}
void MyPainter::drawWidgetDiamonds(WidgetProperty::myTetris *tetris)//绘制方块
{
if(tetris->y_coordinate[0] < 2) tetris->y_coordinate[0] = 2;
if(tetris->y_coordinate[0] > widgetProperty.rectNum_Y-2) tetris->y_coordinate[0] = widgetProperty.rectNum_Y-2;
if(tetris->x_coordinate[0] > widgetProperty.rectNum_X-2) tetris->x_coordinate[0] = widgetProperty.rectNum_X-2;
tetris->y_coordinate[1] = tetris->y_coordinate[0];
tetris->x_coordinate[1] = tetris->x_coordinate[0] + 1;
tetris->y_coordinate[2] = tetris->y_coordinate[0] + 1;
tetris->x_coordinate[2] = tetris->x_coordinate[0];
tetris->y_coordinate[3] = tetris->y_coordinate[0] + 1;
tetris->x_coordinate[3] = tetris->x_coordinate[0] + 1;
}
void MyPainter::drawWidgetTankShape_1(WidgetProperty::myTetris *tetris)//绘制“坦克”形状:1
{
if(tetris->y_coordinate[0] < 3) tetris->y_coordinate[0] = 3;
if(tetris->y_coordinate[0] > widgetProperty.rectNum_Y-1) tetris->y_coordinate[0] = widgetProperty.rectNum_Y-1;
if(tetris->x_coordinate[0] < 1) tetris->x_coordinate[0] = 1;
if(tetris->x_coordinate[0] > widgetProperty.rectNum_X-2) tetris->x_coordinate[0] = widgetProperty.rectNum_X-2;
tetris->y_coordinate[1] = tetris->y_coordinate[0] - 1;
tetris->x_coordinate[1] = tetris->x_coordinate[0];
tetris->y_coordinate[2] = tetris->y_coordinate[0];
tetris->x_coordinate[2] = tetris->x_coordinate[0] - 1;
tetris->y_coordinate[3] = tetris->y_coordinate[0];
tetris->x_coordinate[3] = tetris->x_coordinate[0] + 1;
}
void MyPainter::drawWidgetTankShape_2(WidgetProperty::myTetris *tetris)//绘制“坦克”形状:2
{
if(tetris->y_coordinate[0] < 2) tetris->y_coordinate[0] = 2;
if(tetris->y_coordinate[0] > widgetProperty.rectNum_Y-2) tetris->y_coordinate[0] = widgetProperty.rectNum_Y-2;
if(tetris->x_coordinate[0] > widgetProperty.rectNum_X-2) tetris->x_coordinate[0] = widgetProperty.rectNum_X-2;
tetris->y_coordinate[1] = tetris->y_coordinate[0] - 1;
tetris->x_coordinate[1] = tetris->x_coordinate[0];
tetris->y_coordinate[2] = tetris->y_coordinate[0] + 1;
tetris->x_coordinate[2] = tetris->x_coordinate[0];
tetris->y_coordinate[3] = tetris->y_coordinate[0];
tetris->x_coordinate[3] = tetris->x_coordinate[0] + 1;
}
void MyPainter::drawWidgetTankShape_3(WidgetProperty::myTetris *tetris)//绘制“坦克”形状:3
{
if(tetris->y_coordinate[0] < 2) tetris->y_coordinate[0] = 2;
if(tetris->y_coordinate[0] > widgetProperty.rectNum_Y-2) tetris->y_coordinate[0] = widgetProperty.rectNum_Y-2;
if(tetris->x_coordinate[0] < 1) tetris->x_coordinate[0] = 1;
if(tetris->x_coordinate[0] > widgetProperty.rectNum_X-2) tetris->x_coordinate[0] = widgetProperty.rectNum_X-2;
tetris->y_coordinate[1] = tetris->y_coordinate[0];
tetris->x_coordinate[1] = tetris->x_coordinate[0] - 1;
tetris->y_coordinate[2] = tetris->y_coordinate[0];
tetris->x_coordinate[2] = tetris->x_coordinate[0] + 1;
tetris->y_coordinate[3] = tetris->y_coordinate[0] + 1;
tetris->x_coordinate[3] = tetris->x_coordinate[0];
}
void MyPainter::drawWidgetTankShape_4(WidgetProperty::myTetris *tetris)//绘制“坦克”形状:4
{
if(tetris->y_coordinate[0] < 2) tetris->y_coordinate[0] = 2;
if(tetris->y_coordinate[0] > widgetProperty.rectNum_Y-2) tetris->y_coordinate[0] = widgetProperty.rectNum_Y-2;
if(tetris->x_coordinate[0] < 1) tetris->x_coordinate[0] = 1;
if(tetris->x_coordinate[0] > widgetProperty.rectNum_X-1) tetris->x_coordinate[0] = widgetProperty.rectNum_X-1;
tetris->y_coordinate[1] = tetris->y_coordinate[0] - 1;
tetris->x_coordinate[1] = tetris->x_coordinate[0];
tetris->y_coordinate[2] = tetris->y_coordinate[0];
tetris->x_coordinate[2] = tetris->x_coordinate[0] - 1;
tetris->y_coordinate[3] = tetris->y_coordinate[0] + 1;
tetris->x_coordinate[3] = tetris->x_coordinate[0];
}
void MyPainter::drawWidgetChair_1_Shape_1(WidgetProperty::myTetris *tetris)//绘制“椅子”形状:1
{
if(tetris->y_coordinate[0] < 2) tetris->y_coordinate[0] = 2;
if(tetris->y_coordinate[0] > widgetProperty.rectNum_Y-2) tetris->y_coordinate[0] = widgetProperty.rectNum_Y-2;
if(tetris->x_coordinate[0] > widgetProperty.rectNum_X-2) tetris->x_coordinate[0] = widgetProperty.rectNum_X-2;
tetris->y_coordinate[1] = tetris->y_coordinate[0] - 1;
tetris->x_coordinate[1] = tetris->x_coordinate[0];
tetris->y_coordinate[2] = tetris->y_coordinate[0];
tetris->x_coordinate[2] = tetris->x_coordinate[0] + 1;
tetris->y_coordinate[3] = tetris->y_coordinate[0] + 1;
tetris->x_coordinate[3] = tetris->x_coordinate[0] + 1;
}
void MyPainter::drawWidgetChair_1_Shape_2(WidgetProperty::myTetris *tetris)//绘制“椅子”形状:2
{
if(tetris->y_coordinate[0] < 2) tetris->y_coordinate[0] = 2;
if(tetris->y_coordinate[0] > widgetProperty.rectNum_Y-2) tetris->y_coordinate[0] = widgetProperty.rectNum_Y-2;
if(tetris->x_coordinate[0] < 1) tetris->x_coordinate[0] = 1;
if(tetris->x_coordinate[0] > widgetProperty.rectNum_X-2) tetris->x_coordinate[0] = widgetProperty.rectNum_X-2;
tetris->y_coordinate[1] = tetris->y_coordinate[0];
tetris->x_coordinate[1] = tetris->x_coordinate[0] + 1;
tetris->y_coordinate[2] = tetris->y_coordinate[0] + 1;
tetris->x_coordinate[2] = tetris->x_coordinate[0];
tetris->y_coordinate[3] = tetris->y_coordinate[0] + 1;
tetris->x_coordinate[3] = tetris->x_coordinate[0] - 1;
}
void MyPainter::drawWidgetChair_2_Shape_1(WidgetProperty::myTetris *tetris)//绘制2“椅子”形状:1
{
if(tetris->y_coordinate[0] < 2) tetris->y_coordinate[0] = 2;
if(tetris->y_coordinate[0] > widgetProperty.rectNum_Y-2) tetris->y_coordinate[0] = widgetProperty.rectNum_Y-2;
if(tetris->x_coordinate[0] < 1) tetris->x_coordinate[0] = 1;
if(tetris->x_coordinate[0] > widgetProperty.rectNum_X-1) tetris->x_coordinate[0] = widgetProperty.rectNum_X-1;
tetris->y_coordinate[1] = tetris->y_coordinate[0] - 1;
tetris->x_coordinate[1] = tetris->x_coordinate[0];
tetris->y_coordinate[2] = tetris->y_coordinate[0];
tetris->x_coordinate[2] = tetris->x_coordinate[0] - 1;
tetris->y_coordinate[3] = tetris->y_coordinate[0] + 1;
tetris->x_coordinate[3] = tetris->x_coordinate[0] - 1;
}
void MyPainter::drawWidgetChair_2_Shape_2(WidgetProperty::myTetris *tetris)//绘制2“椅子”形状:2
{
if(tetris->y_coordinate[0] < 2) tetris->y_coordinate[0] = 2;
if(tetris->y_coordinate[0] > widgetProperty.rectNum_Y-2) tetris->y_coordinate[0] = widgetProperty.rectNum_Y-2;
if(tetris->x_coordinate[0] < 1) tetris->x_coordinate[0] = 1;
if(tetris->x_coordinate[0] > widgetProperty.rectNum_X-2) tetris->x_coordinate[0] = widgetProperty.rectNum_X-2;
tetris->y_coordinate[1] = tetris->y_coordinate[0];
tetris->x_coordinate[1] = tetris->x_coordinate[0] - 1;
tetris->y_coordinate[2] = tetris->y_coordinate[0] + 1;
tetris->x_coordinate[2] = tetris->x_coordinate[0];
tetris->y_coordinate[3] = tetris->y_coordinate[0] + 1;
tetris->x_coordinate[3] = tetris->x_coordinate[0] + 1;
}
void MyPainter::drawWidgetSoft_1_Shape_1(WidgetProperty::myTetris *tetris)//绘制1“沙发”形状:1
{
if(tetris->y_coordinate[0] < 3) tetris->y_coordinate[0] = 3;
if(tetris->y_coordinate[0] > widgetProperty.rectNum_Y-1) tetris->y_coordinate[0] = widgetProperty.rectNum_Y-1;
if(tetris->x_coordinate[0] > widgetProperty.rectNum_X-3) tetris->x_coordinate[0] = widgetProperty.rectNum_X-3;
tetris->y_coordinate[1] = tetris->y_coordinate[0] - 1;
tetris->x_coordinate[1] = tetris->x_coordinate[0];
tetris->y_coordinate[2] = tetris->y_coordinate[0];
tetris->x_coordinate[2] = tetris->x_coordinate[0] + 1;
tetris->y_coordinate[3] = tetris->y_coordinate[0];
tetris->x_coordinate[3] = tetris->x_coordinate[0] + 2;
}
void MyPainter::drawWidgetSoft_1_Shape_2(WidgetProperty::myTetris *tetris)
{
if(tetris->y_coordinate[0] < 1) tetris->y_coordinate[0] = 1;
if(tetris->y_coordinate[0] > widgetProperty.rectNum_Y-3) tetris->y_coordinate[0] = widgetProperty.rectNum_Y-3;
if(tetris->x_coordinate[0] > widgetProperty.rectNum_X-2) tetris->x_coordinate[0] = widgetProperty.rectNum_X-2;
tetris->y_coordinate[1] = tetris->y_coordinate[0];
tetris->x_coordinate[1] = tetris->x_coordinate[0] + 1;
tetris->y_coordinate[2] = tetris->y_coordinate[0] + 1;
tetris->x_coordinate[2] = tetris->x_coordinate[0];
tetris->y_coordinate[3] = tetris->y_coordinate[0] + 2;
tetris->x_coordinate[3] = tetris->x_coordinate[0];
}
void MyPainter::drawWidgetSoft_1_Shape_3(WidgetProperty::myTetris *tetris)
{
if(tetris->y_coordinate[0] < 2) tetris->y_coordinate[0] = 2;
if(tetris->y_coordinate[0] > widgetProperty.rectNum_Y-2) tetris->y_coordinate[0] = widgetProperty.rectNum_Y-2;
if(tetris->x_coordinate[0] < 2) tetris->x_coordinate[0] = 2;
if(tetris->x_coordinate[0] > widgetProperty.rectNum_X-1) tetris->x_coordinate[0] = widgetProperty.rectNum_X-1;
tetris->y_coordinate[1] = tetris->y_coordinate[0];
tetris->x_coordinate[1] = tetris->x_coordinate[0] - 1;
tetris->y_coordinate[2] = tetris->y_coordinate[0];
tetris->x_coordinate[2] = tetris->x_coordinate[0] - 2;
tetris->y_coordinate[3] = tetris->y_coordinate[0] + 1;
tetris->x_coordinate[3] = tetris->x_coordinate[0];
}
void MyPainter::drawWidgetSoft_1_Shape_4(WidgetProperty::myTetris *tetris)
{
if(tetris->y_coordinate[0] < 3) tetris->y_coordinate[0] = 3;
if(tetris->y_coordinate[0] > widgetProperty.rectNum_Y-1) tetris->y_coordinate[0] = widgetProperty.rectNum_Y-1;
if(tetris->x_coordinate[0] < 1) tetris->x_coordinate[0] = 1;
if(tetris->x_coordinate[0] > widgetProperty.rectNum_X-1) tetris->x_coordinate[0] = widgetProperty.rectNum_X-1;
tetris->y_coordinate[1] = tetris->y_coordinate[0] - 1;
tetris->x_coordinate[1] = tetris->x_coordinate[0];
tetris->y_coordinate[2] = tetris->y_coordinate[0] - 2;
tetris->x_coordinate[2] = tetris->x_coordinate[0];
tetris->y_coordinate[3] = tetris->y_coordinate[0];
tetris->x_coordinate[3] = tetris->x_coordinate[0] - 1;
}
void MyPainter::drawWidgetSoft_2_Shape_1(WidgetProperty::myTetris *tetris)//绘制2“沙发”形状:1
{
if(tetris->y_coordinate[0] < 3) tetris->y_coordinate[0] = 3;
if(tetris->y_coordinate[0] > widgetProperty.rectNum_Y-1) tetris->y_coordinate[0] = widgetProperty.rectNum_Y-1;
if(tetris->x_coordinate[0] < 2) tetris->x_coordinate[0] = 2;
if(tetris->x_coordinate[0] > widgetProperty.rectNum_X-1) tetris->x_coordinate[0] = widgetProperty.rectNum_X-1;
tetris->y_coordinate[1] = tetris->y_coordinate[0] - 1;
tetris->x_coordinate[1] = tetris->x_coordinate[0];
tetris->y_coordinate[2] = tetris->y_coordinate[0];
tetris->x_coordinate[2] = tetris->x_coordinate[0] - 1;
tetris->y_coordinate[3] = tetris->y_coordinate[0];
tetris->x_coordinate[3] = tetris->x_coordinate[0] - 2;
}
void MyPainter::drawWidgetSoft_2_Shape_2(WidgetProperty::myTetris *tetris)
{
if(tetris->y_coordinate[0] < 3) tetris->y_coordinate[0] = 3;
if(tetris->y_coordinate[0] > widgetProperty.rectNum_Y-1) tetris->y_coordinate[0] = widgetProperty.rectNum_Y-1;
if(tetris->x_coordinate[0] > widgetProperty.rectNum_X-2) tetris->x_coordinate[0] = widgetProperty.rectNum_X-2;
tetris->y_coordinate[1] = tetris->y_coordinate[0] - 1;
tetris->x_coordinate[1] = tetris->x_coordinate[0];
tetris->y_coordinate[2] = tetris->y_coordinate[0] - 2;
tetris->x_coordinate[2] = tetris->x_coordinate[0];
tetris->y_coordinate[3] = tetris->y_coordinate[0];
tetris->x_coordinate[3] = tetris->x_coordinate[0] + 1;
}
void MyPainter::drawWidgetSoft_2_Shape_3(WidgetProperty::myTetris *tetris)
{
if(tetris->y_coordinate[0] < 2) tetris->y_coordinate[0] = 2;
if(tetris->y_coordinate[0] > widgetProperty.rectNum_Y-2) tetris->y_coordinate[0] = widgetProperty.rectNum_Y-2;
if(tetris->x_coordinate[0] > widgetProperty.rectNum_X-3) tetris->x_coordinate[0] = widgetProperty.rectNum_X-3;
tetris->y_coordinate[1] = tetris->y_coordinate[0];
tetris->x_coordinate[1] = tetris->x_coordinate[0] + 1;
tetris->y_coordinate[2] = tetris->y_coordinate[0];
tetris->x_coordinate[2] = tetris->x_coordinate[0] + 2;
tetris->y_coordinate[3] = tetris->y_coordinate[0] + 1;
tetris->x_coordinate[3] = tetris->x_coordinate[0];
}
void MyPainter::drawWidgetSoft_2_Shape_4(WidgetProperty::myTetris *tetris)
{
if(tetris->y_coordinate[0] < 1) tetris->y_coordinate[0] = 1;
if(tetris->y_coordinate[0] > widgetProperty.rectNum_Y-3) tetris->y_coordinate[0] = widgetProperty.rectNum_Y-3;
if(tetris->x_coordinate[0] < 1) tetris->x_coordinate[0] = 1;
if(tetris->x_coordinate[0] > widgetProperty.rectNum_X-1) tetris->x_coordinate[0] = widgetProperty.rectNum_X-1;
tetris->y_coordinate[1] = tetris->y_coordinate[0];
tetris->x_coordinate[1] = tetris->x_coordinate[0] - 1;
tetris->y_coordinate[2] = tetris->y_coordinate[0] + 1;
tetris->x_coordinate[2] = tetris->x_coordinate[0];
tetris->y_coordinate[3] = tetris->y_coordinate[0] + 2;
tetris->x_coordinate[3] = tetris->x_coordinate[0];
}
void MyPainter::clearAll()
{
for(int i=0;i<widgetProperty.rectNum_Y;i++){
for(int j=0;j<widgetProperty.rectNum_X;j++){
widgetArea[i][j].light = false;
widgetArea[i][j].color = WidgetProperty::PRIMARY_COLOR;
}
}
}
2、动画实现:
实现动画的过程比较原始。使用一个定时器进行定时,当然定时器的时间可以设置以控制下降的速度。每次定时时间到,就将原本位置的方块显示为底色,属性设置为不点亮,再将原本位置向下(向右)(向左)移动一格,再在更新后的位置重新显示。需要提及的是,这里使用的更新函数是QPainter中的repaint()函数而不是update()函数,Qt为了优化速度和防止闪烁,写了update()函数,其在几次调用update()时只执行一次paintEvent(),而repaint()函数调用一次则执行一次paintEvent()函数。
3、方块状态切换实现:
方块切换本身比较容易实现,同动画移动是一样的,将原位置的方块消除,再替换上转换后需要的形状。这里要注意的问题是,当方块旁边有其他的方块存在且占住了该方块转换后形状的位置,此时应不能转换。故需要做一个判断函数。
bool GameWidget::judgeNextTetris()//判断下一个变换位置是否为空
{
WidgetProperty::myTetris *tempTetris = new WidgetProperty::myTetris;
tempTetris->x_coordinate[0] = currentTetris->x_coordinate[0];
tempTetris->y_coordinate[0] = currentTetris->y_coordinate[0];
tempTetris->shape = currentTetris->shape;
tempTetris->color = currentTetris->color;
tempTetris->shape = shapeChange(tempTetris->shape);
switchShape(tempTetris);//获得变换后的4个位置
for(int i=0;i<=3;i++){
qDebug()<<"judgeNextTetris:: current "<<i<<" "<<currentTetris->x_coordinate[i]<<" "<<currentTetris->y_coordinate[i];
}
qDebug()<<"\n";
for(int i=0;i<=3;i++){
qDebug()<<"judgeNextTetris:: temp "<<i<<" "<<tempTetris->x_coordinate[i]<<" "<<tempTetris->y_coordinate[i];
}
qDebug()<<"\n";
for(int i=0;i<=3;i++){
bool flag = false;
for(int j=0;j<4;j++){
if(tempTetris->x_coordinate[i] == currentTetris->x_coordinate[(i+j)%4] &&
tempTetris->y_coordinate[i] == currentTetris->y_coordinate[(i+j)%4]){
flag = true;
break;
}
}
if(flag == false && widgetArea[tempTetris->y_coordinate[i]][tempTetris->x_coordinate[i]].light == true){
qDebug()<<"judgeNextTetris:: true "<<"\njudgeNextTetris:: temp "
<<i<<" "<<" "<<tempTetris->y_coordinate[i]
<<" "<<tempTetris->x_coordinate[i]<<"\n";
return true;
}
}
return false;
}
方法是提前获取变换后的四个位置,找到跟之前不同的位置,判断这些位置是否被点亮,如果被点亮则意味着有阻碍而不能变换。
4、按键控制实现:
通过按键实现方块的移动,快速下降同动画的机制是一样的,区别就是动画是通过定时器自动改变位置,而按键则是手动改变位置。同样要注意移动时存在的障碍。
bool GameWidget::judgeBarrier(Direction direction)//判断direction方向是否有障碍物
{
int plusRow = 0;
int plusColumn = 0;
switch(direction){
case LEFT : plusColumn = -1; break;
case RIGHT : plusColumn = 1;break;
case DOWN : plusRow = 1;break;
}
unsigned int nearX_coordinate,nearY_coordinate;
for(int i = 0;i < 4;i++){
//获得direction方向上相邻的位置
//如果该方向上当前位置抵达边界,则相邻位置取其自身
nearX_coordinate = currentTetris->x_coordinate[i];
nearY_coordinate = currentTetris->y_coordinate[i];
if(currentTetris->x_coordinate[i] > 0 ||
currentTetris->x_coordinate[i] < widgetProperty.rectNum_X-1){
nearX_coordinate += plusColumn;
}
if(currentTetris->y_coordinate[i] < widgetProperty.rectNum_Y-1){
nearY_coordinate += plusRow;
}
//如果4个方块在direction方向上相邻位置有一个为其自身
//则说明抵达direction方向的边界,返回true,表示碰到阻碍
if(nearX_coordinate == currentTetris->x_coordinate[i] &&
nearY_coordinate == currentTetris->y_coordinate[i])
return true;
//如果该方块在direction方向上相邻位置为其他3个方块中的一个
//则继续判断下一个
if((nearX_coordinate == currentTetris->x_coordinate[(i+1)%4] && nearY_coordinate == currentTetris->y_coordinate[(i+1)%4]) ||
(nearX_coordinate == currentTetris->x_coordinate[(i+2)%4] && nearY_coordinate == currentTetris->y_coordinate[(i+2)%4]) ||
(nearX_coordinate == currentTetris->x_coordinate[(i+3)%4] && nearY_coordinate == currentTetris->y_coordinate[(i+3)%4]))
continue;
else if(widgetArea[nearY_coordinate][nearX_coordinate].light == true)
return true;
}
return false;
}
4、方块堆积实现:
当方块本身向下移动时,不断判断下方是否有阻碍,没有则继续向下移动,有的话则停止。获取下一个形状和颜色,从头开始。
5、消行实现:
从底部一行一行地不断地检测该行是否全部被点亮,如果全部被点亮则将上面的方块全部向下挪动一个,再次从底部开始检测,同样可以判断该行颜色是否相同,以进行积分。
void GameWidget::clearRow(unsigned int row)//清除某一行,该行上面的方块下降一格
{
if(row > widgetProperty.rectNum_Y-1) return;
if(row > 0){
for(unsigned int i = row;i > 0;i--){
for(unsigned int j = 0;j<widgetProperty.rectNum_X;j++){
widgetArea[i][j].light = widgetArea[i-1][j].light;
widgetArea[i][j].color = widgetArea[i-1][j].color;
}
}
}
for(unsigned int j = 0;j<widgetProperty.rectNum_X;j++){
widgetArea[0][j].light = false;
widgetArea[0][j].color = WidgetProperty::PRIMARY_COLOR;
}
qDebug()<<"消除成功";
}
void GameWidget::checkClearRow()
{
int clearRowNum = 0,clearColorNum = 0;
for(int i = widgetProperty.rectNum_Y-1;i >= 0;i--){
int rowStatus = 0;
int colorStatus = 0;
for(unsigned int j = 0;j<widgetProperty.rectNum_X;j++){
rowStatus += (int)(widgetArea[i][j].light);
}
if(rowStatus == widgetProperty.rectNum_X){//这一行是否全点亮
clearRow(i);
clearRowNum++;//对消去的行进行计数
for(unsigned int j = 0;j<widgetProperty.rectNum_X;j++)
colorStatus += qAbs((int)(widgetArea[i][j].color - widgetArea[i][0].color));
if(colorStatus == 0) clearColorNum++;//这一行的颜色是否相同
i = widgetProperty.rectNum_Y;//最后还会执行一次i--,使i = widgetProperty.rectNum_Y-1,再次从最底部开始检查
}
}
countScore(clearRowNum,clearColorNum);
emit changeScore(currentScore);
emit changeLevel(currentLevel + 1);
}
6、方块渐入实现:
实现方块渐入采用的方法是:在第一行的的上面再加虚假的三行,这三行的地址同第一行的地址完全相同。
7、游戏结束实现:
当方块抵达底部时,检测方块当前位置是否为第一行,是的话则游戏结束。
8、开始、暂停、重新开始按钮实现:
这里要注意的是,开始与暂停两个按钮应该有一个互锁,当开始按钮按下时,再次按下开始按钮应该不起作用(不能再次开始),按下暂停按钮后再次按下也不起作用。
遇到的问题和能够改进的地方
- 问题:
1、在子窗口的类中声明Q_OBJECT后,子窗口的边框会莫名消失(或者说子窗口不会显示出来),以及底色的方块边界会覆盖掉相邻方块的边界线,十分突兀。后期只能在界面分配区域的时候使每个方块的位置间隔2px,也就出现了最终实现效果中的灰色线条(由于子窗口的背景没有显示出来,而只显示出来方块,那么主窗口的背景颜色就构成了方块之间的线条)。
2、方块渐入所采用的方法导致的缺点是:有一些方块会在渐入过程中闪烁,如图:
3、在程序中在类间使用指针进行地址传递,使两个类变量共享一个地址。这种做法危险。应该考虑多使用信号与槽。
4、在界面设计的时候,直接将子窗口和控件的大小位置固定死了,位置计算过程复杂且原始,不够灵活,界面大小也没办法变化。布局管理了解一下。 - 改进:
1、加入菜单栏,可手动设置难度级别,窗口大小,背景颜色,加入背景音效。
2、动画处理。在本次设计中采用定时器加画面切换的方法,过于原始。帧缓冲技术了解一下。
学习和思考
1、Qt中的事件管理机制。
2、Qt中的多线程。
3、Qt中的Ui设计,能否将自己编写的窗口或者控件类加入到Qt Designer,使其具有一些用户所需要的特性。