Black学Qt+俄罗斯方块

俄罗斯方块

需求分析

  • 方块样式

  • 界面设计

  • 需求
    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,使其具有一些用户所需要的特性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值