Ubuntu平台俄罗斯方块系统的设计与实现

Ubuntu平台俄罗斯方块系统的设计与实现

学生姓名:                          

学    号:                              

班    级:                               

  • 系统分析与总体设计
  1. 选题的意义

在当下生活节奏越来越快的情况下,人们的生活压力也越来越大,对于休闲娱乐的需求也逐步上升。在这种情况下,一款休闲娱乐的小游戏成了最佳的选择,既不需要付出金钱、同时游戏简单也不用花时间琢磨。俄罗斯方块正符合这2点要求,玩家只需要按几个键就可以玩,非常容易上手。并且本系统的适用性很广,除了年轻人,儿童和老人都可以玩,不仅娱乐身心,还可以锻炼大脑。

  1. 需求分析

本系统是一款适合大众的休闲小游戏,通过键盘上下左右和空格键进行操作方块。按上键可以旋转方块、按下键可以下速方块下降、按左键方块向左移动、按右键方块向右移动、按下空格键可以让方块直接落下。同时系统具有随机生成下一方块、计分和自动判断游戏是否结束的功能,对于随机生成的下一块方块,将呈现在界面的右上角,呈现蓝色;对于正在下落的方块呈现红色;对于已经落定的方块呈现绿色。

  1. 总体设计

系统开发环境:

       操作系统:ubuntu 10.10

       开发软件:Qt

系统总体功能:系统具有监听用户按键并做相应操作、随机生成下一方块、计分、判断游戏是否结束的功能。

系统结构模块图:

 

 

  • 系统的详细设计

1、生成随机方块功能设计

输入:系统随机生成的数字(0-6)

输出:根据随机数生成对应形状方块

描述:系统一开始定义了7种原始方块并以01的形式存在二维数组中,系统生成随机方块时只需要随机生成数字编号传给生成方块的方法即可。

算法:inline void block_cpy(int dblock[4][4],int sblock[4][4])

{

    for(int i=0;i<4;i++)

        for(int j=0;j<4;j++)

            dblock[i][j]=sblock[i][j];

}

2、方块旋转功能

输入:要旋转的方块

输入:旋转后的方块

描述:系统根据旋转算法将传入方块进行旋转。

算法:void Widget::BlockRotate(int block[4][4])

{

    int temp_block[4][4];

    for(int i=0;i<4;i++)

        for(int j=0;j<4;j++)

            temp_block[3-j][i]=block[i][j];

    for(int i=0;i<4;i++)

        for(int j=0;j<4;j++)

            block[i][j]=temp_block[i][j];

}

3、方块左移功能、

输入:LEFT,代表向左移

输出:左移后的方块

描述:用户按下左键后,系统监听到会调用移动方法同时传入LEFT参数。

算法:case LEFT:

        //到左边界或者碰撞不再往左       if(block_pos.pos_x+cur_border.lbound==0||IsCollide(block_pos.pos_x,block_pos.pos_y,LEFT))

            break;

        //恢复方块右场景,为了清除移动过程中的方块残留

        for(int i=cur_border.ubound;i<=cur_border.dbound;i++)

            game_area[block_pos.pos_y+i][block_pos.pos_x+3]=0;

        block_pos.pos_x-=1;

        //方块左移一格,拷贝到场景

        for(int i=cur_border.ubound;i<=cur_border.dbound;i++)

            for(int j=0;j<4;j++)             if(block_pos.pos_x+j>=0&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2) //注意场景数组不越界

          game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];

        break;

4、方块下速下滑功能

输入:DOWN,代表向下移

输出:下移后的方块

描述:用户按下向下键后,系统监听到会调用移动方法同时传入DOWN参数。

算法:case DOWN:

        //方块到达边界则不再移动

        if(block_pos.pos_y+cur_border.dbound==AREA_ROW-1)

        {

            ConvertStable(block_pos.pos_x,block_pos.pos_y);

            ResetBlock();

            break;

        }

        //碰撞检测,只计算上下左右边界,先尝试走一格,如果碰撞则稳定方块后跳出

        if(IsCollide(block_pos.pos_x,block_pos.pos_y,DOWN))

        {

            //只有最终不能下落才转成稳定方块

            ConvertStable(block_pos.pos_x,block_pos.pos_y);

            ResetBlock();

            break;

        }

        //恢复方块上场景,为了清除移动过程中的方块残留

        for(int j=cur_border.lbound;j<=cur_border.rbound;j++)

            game_area[block_pos.pos_y][block_pos.pos_x+j]=0;

        //没有碰撞则下落一格

        block_pos.pos_y+=1;

        //方块下降一格,拷贝到场景,注意左右边界

        for(int i=0;i<4;i++) //必须是0到4而不是边界索引,考虑到旋转后边界重新计算

            for(int j=cur_border.lbound;j<=cur_border.rbound;j++)

                if(block_pos.pos_y+i<=AREA_ROW-1&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2) //注意场景数组不越界,而且不会擦出稳定的方块                 game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];

        break;

5、方块右移功能

输入:RIGHT,代表向右移

输出:右移后的方块

描述:用户按下右键后,系统监听到会调用移动方法同时传入RIGHT参数。

算法:case RIGHT:

        if(block_pos.pos_x+cur_border.rbound==AREA_COL-1||IsCollide(block_pos.pos_x,block_pos.pos_y,RIGHT))

            break;

        //恢复方块左场景,为了清除移动过程中的方块残留

        for(int i=cur_border.ubound;i<=cur_border.dbound;i++)

            game_area[block_pos.pos_y+i][block_pos.pos_x]=0;

        block_pos.pos_x+=1;

        //方块右移一格,拷贝到场景

        for(int i=cur_border.ubound;i<=cur_border.dbound;i++)

            for(int j=0;j<4;j++)

                if(block_pos.pos_x+j<=AREA_COL-1&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2) //注意场景数组不越界                  game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];

        break;

6、方块直接落定功能

输入:SPACE,代表直接落定

输出:落定后的方块

描述:用户按下空格键后,系统监听到会调用移动方法同时传入SPACE参数。

算法:case SPACE: //一次到底

        //一格一格下移,直到不能下移

        while(block_pos.pos_y+cur_border.dbound<AREA_ROW-1&&!IsCollide(block_pos.pos_x,block_pos.pos_y,DOWN))

        {

            //恢复方块上场景,为了清除移动过程中的方块残留

            for(int j=cur_border.lbound;j<=cur_border.rbound;j++)

                game_area[block_pos.pos_y][block_pos.pos_x+j]=0;

            //没有碰撞则下落一格

            block_pos.pos_y+=1;

            //方块下降一格,拷贝到场景,注意左右边界

            for(int i=0;i<4;i++) //必须是0到4

                for(int j=cur_border.lbound;j<=cur_border.rbound;j++)

                    if(block_pos.pos_y+i<=AREA_ROW-1&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2) //注意场景数组不越界,而且不会擦出稳定的方块                       game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];

        }

        ConvertStable(block_pos.pos_x,block_pos.pos_y);

        ResetBlock();

        break;

    default:

        break;

7、计分功能

输入:原始分数、铺满行数

输出:新分数

描述:用户每铺满一行方块,该行方块会自动消失,同时增加分数。

算法://处理消行,整个场景上面的行依次下移

    int i=AREA_ROW-1;

    int line_count=0; //记消行数

    while(i>=1)

    {

        bool is_line_full=true;

        for(int j=0;j<AREA_COL;j++)

            if(game_area[i][j]==0)

            {

                is_line_full=false;

                i--;

                break;

            }

        if(is_line_full)

        {

            for(int k=i;k>=1;k--)

                for(int j=0;j<AREA_COL;j++)

                    game_area[k][j]=game_area[k-1][j];

            line_count++;//每次增加消行的行数

        }

    }

score+=line_count*10; //得分

      8、判断游戏结束功能

      输入:当前移动后方块坐标

输出:判断结果

描述:每次移动后,系统会判断移动后的方块有没有触及最顶端,如果有,      则游戏结束。

算法://判断游戏是否结束

    for(int j=0;j<AREA_COL;j++)

        if(game_area[0][j]==2) //最顶端也有稳定方块

            GameOver();

  1. 各模块具体实现
  1. 生成随机方块功能模块实现:

void Widget::CreateBlock(int block[4][4],int block_id)

{

    switch (block_id)

    {

    case 0:

        block_cpy(block,item1);

        break;

    case 1:

        block_cpy(block,item2);

        break;

    case 2:

        block_cpy(block,item3);

        break;

    case 3:

        block_cpy(block,item4);

        break;

    case 4:

        block_cpy(block,item5);

        break;

    case 5:

        block_cpy(block,item6);

        break;

    case 6:

        block_cpy(block,item7);

        break;

    default:

        break;

    }

}

//拷贝方块图案

inline void block_cpy(int dblock[4][4],int sblock[4][4])

{

    for(int i=0;i<4;i++)

        for(int j=0;j<4;j++)

            dblock[i][j]=sblock[i][j];

}

  1. 方块旋转功能模块实现

void Widget::BlockRotate(int block[4][4])

{

    int temp_block[4][4];

    for(int i=0;i<4;i++)

        for(int j=0;j<4;j++)

            temp_block[3-j][i]=block[i][j];

    for(int i=0;i<4;i++)

        for(int j=0;j<4;j++)

            block[i][j]=temp_block[i][j];

}

  1. 方块移动功能模块实现

void Widget::BlockMove(Direction dir)

{

    switch (dir) {

    case UP:

        if(IsCollide(block_pos.pos_x,block_pos.pos_y,UP))

            break;

        //逆时针旋转90度

        BlockRotate(cur_block);

        //防止旋转后bug,i和j从0到4重新设置方块

        for(int i=0;i<4;i++)

            for(int j=0;j<4;j++)               game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];

        //重新计算边界

        GetBorder(cur_block,cur_border);

        break;

    case DOWN:

        //方块到达边界则不再移动

        if(block_pos.pos_y+cur_border.dbound==AREA_ROW-1)

        {

            ConvertStable(block_pos.pos_x,block_pos.pos_y);

            ResetBlock();

            break;

        }

        //碰撞检测,只计算上下左右边界,先尝试走一格,如果碰撞则稳定方块后跳出

        if(IsCollide(block_pos.pos_x,block_pos.pos_y,DOWN))

        {

            //只有最终不能下落才转成稳定方块

            ConvertStable(block_pos.pos_x,block_pos.pos_y);

            ResetBlock();

            break;

        }

        //恢复方块上场景,为了清除移动过程中的方块残留

        for(int j=cur_border.lbound;j<=cur_border.rbound;j++)

            game_area[block_pos.pos_y][block_pos.pos_x+j]=0;

        //没有碰撞则下落一格

        block_pos.pos_y+=1;

        //方块下降一格,拷贝到场景,注意左右边界

        for(int i=0;i<4;i++) //必须是0到4而不是边界索引,考虑到旋转后边界重新计算

            for(int j=cur_border.lbound;j<=cur_border.rbound;j++)

                if(block_pos.pos_y+i<=AREA_ROW-1&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2) //注意场景数组不越界,而且不会擦出稳定的方块                   game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];

        break;

    case LEFT:

        //到左边界或者碰撞不再往左

if(block_pos.pos_x+cur_border.lbound==0||IsCollide(block_pos.pos_x,block_pos.pos_y,LEFT))

            break;

        //恢复方块右场景,为了清除移动过程中的方块残留

        for(int i=cur_border.ubound;i<=cur_border.dbound;i++)

            game_area[block_pos.pos_y+i][block_pos.pos_x+3]=0;

        block_pos.pos_x-=1;

        //方块左移一格,拷贝到场景

        for(int i=cur_border.ubound;i<=cur_border.dbound;i++)

            for(int   j=0;j<4;j++)            if(block_pos.pos_x+j>=0&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2) //注意场景数组不越界                    game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];

        break;

    case RIGHT:

        if(block_pos.pos_x+cur_border.rbound==AREA_COL-1||IsCollide(block_pos.pos_x,block_pos.pos_y,RIGHT))

            break;

        //恢复方块左场景,为了清除移动过程中的方块残留

        for(int i=cur_border.ubound;i<=cur_border.dbound;i++)

            game_area[block_pos.pos_y+i][block_pos.pos_x]=0;

        block_pos.pos_x+=1;

        //方块右移一格,拷贝到场景

        for(int i=cur_border.ubound;i<=cur_border.dbound;i++)

            for(int j=0;j<4;j++)

                if(block_pos.pos_x+j<=AREA_COL-1&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2) //注意场景数组不越界                   game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];

        break;

    case SPACE: //一次到底

        //一格一格下移,直到不能下移

        while(block_pos.pos_y+cur_border.dbound<AREA_ROW-1&&!IsCollide(block_pos.pos_x,block_pos.pos_y,DOWN))

        {

            //恢复方块上场景,为了清除移动过程中的方块残留

            for(int j=cur_border.lbound;j<=cur_border.rbound;j++)

                game_area[block_pos.pos_y][block_pos.pos_x+j]=0;

            //没有碰撞则下落一格

            block_pos.pos_y+=1;

            //方块下降一格,拷贝到场景,注意左右边界

            for(int i=0;i<4;i++) //必须是0到4

                for(int j=cur_border.lbound;j<=cur_border.rbound;j++)

                    if(block_pos.pos_y+i<=AREA_ROW-1&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2) //注意场景数组不越界,而且不会擦出稳定的方块                      game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];

        }

        ConvertStable(block_pos.pos_x,block_pos.pos_y);

        ResetBlock();

        break;

    default:

        break;

    }

}

  1. 计分和判断游戏结束功能模块实现

//处理消行,整个场景上面的行依次下移

    int i=AREA_ROW-1;

    int line_count=0; //记消行数

    while(i>=1)

    {

        bool is_line_full=true;

        for(int j=0;j<AREA_COL;j++)

            if(game_area[i][j]==0)

            {

                is_line_full=false;

                i--;

                break;

            }

        if(is_line_full)

        {

            for(int k=i;k>=1;k--)

                for(int j=0;j<AREA_COL;j++)

                    game_area[k][j]=game_area[k-1][j];

            line_count++;//每次增加消行的行数

        }

    }

    score+=line_count*10; //得分

    //判断游戏是否结束

    for(int j=0;j<AREA_COL;j++)

        if(game_area[0][j]==2) //最顶端也有稳定方块

            GameOver();

void Widget::GameOver()

{

    //游戏结束停止计时器

    killTimer(game_timer);

    killTimer(paint_timer);

    QMessageBox::information(this,"failed","game over");

}

  1. 调试过程
  1. 调试旋转功能,在旋转代码处打上断点(图3.2.1),观察程序运行情况。程序如期在断点处停止,方块未旋转(图3.2.2)。放行后,程序继续运行,方块旋转(图3.2.3)。

 

 

图3.2.1

 

 

图3.2.2

 

 

图3.2.3

  1. 调试左右移动,在向左移动代码处打上断点(图3.2.4),观察程序运行情况。程序如期在断点处停止,方块未移动(图3.2.5)。放行后,程序继续运行,方块向左移动(图3.2.6)。

 

 

 

图3.2.4

 

 

图3.2.5

 

 

图3.2.6

  1. 调试立即落定功能,在立即落定代码处打上断点(图3.2.7),观察程序运行情况。程序如期在断点处停止,方块未移动(图3.2.8)。放行后,程序继续运行,方块落定(图3.2.9)。

图3.2.7

图3.2.8

图3.2.9

  1. 系统运行的结果

  • 程序代码

1、头文件目录下widget.h

#ifndef WIDGET_H

#define WIDGET_H

#include <QWidget>

const int BLOCK_SIZE=25; //单个方块单元的边长

const int MARGIN=5; //场景边距

const int AREA_ROW=20; //场景行数

const int AREA_COL=12; //场景列数

//方向

enum Direction

{

    UP,

    DOWN,

    LEFT,

    RIGHT,

    SPACE

};

//定义边界信息

struct Border

{

    int ubound;

    int dbound;

    int lbound;

    int rbound;

};

//坐标

struct block_point

{

    int pos_x;

    int pos_y;

    //    block_point(int x,int y):pos_x(x),pos_y(y){}

};

namespace Ui {

class Widget;

}

class Widget : public QWidget

{

    Q_OBJECT

public:

    void InitGame(); //初始化

    void StartGame(); //开始游戏

    void GameOver(); //游戏结束

    void ResetBlock(); //重置方块

    void BlockMove(Direction dir); //方块变动

    void BlockRotate(int block[4][4]); //方块旋转

    void CreateBlock(int block[4][4],int block_id); //产生方块

    void GetBorder(int block[4][4],Border &border); //计算边界

    void ConvertStable(int x,int y); //转换为稳定方块

    bool IsCollide(int x,int y,Direction dir); //判断是否会碰撞

public:

    explicit Widget(QWidget *parent = 0);

    ~Widget();

    virtual void paintEvent(QPaintEvent *event); //场景刷新

    virtual void timerEvent(QTimerEvent *event); //定时器事件

    virtual void keyPressEvent(QKeyEvent *event); //键盘响应

private:

    Ui::Widget *ui;

private:

    int game_area[AREA_ROW][AREA_COL]; //场景区域,1表示活动的方块,2表示稳定的方块,0表示空

    block_point block_pos; //当前方块坐标

    int cur_block[4][4]; //当前方块形状

    Border cur_border; //当前方块边界

    int next_block[4][4]; //下一个方块形状

    bool isStable; //当前方块是否稳定了

    int score;  //游戏分数

    int game_timer; //方块下落计时器

    int paint_timer; //渲染刷新计时器

    int speed_ms; //下落时间间隔

    int refresh_ms; //刷新时间间隔

};

#endif // WIDGET_H

2、Sources目录下widget.cpp

#include <time.h>

#include <QMessageBox>

#include <QDebug>

#include <QPainter>

#include <QKeyEvent>

#include "widget.h"

#include "ui_widget.h"

//定义图案代码和边界

//田字

int item1[4][4]=

{

    {0,0,0,0},

    {0,1,1,0},

    {0,1,1,0},

    {0,0,0,0}

};

//右L

int item2[4][4]=

{

    {0,1,0,0},

    {0,1,0,0},

    {0,1,1,0},

    {0,0,0,0}

};

//左L

int item3[4][4]=

{

    {0,0,1,0},

    {0,0,1,0},

    {0,1,1,0},

    {0,0,0,0}

};

//右S

int item4[4][4]=

{

    {0,1,0,0},

    {0,1,1,0},

    {0,0,1,0},

    {0,0,0,0}

};

//左S

int item5[4][4]=

{

    {0,0,1,0},

    {0,1,1,0},

    {0,1,0,0},

    {0,0,0,0}

};

//山形

int item6[4][4]=

{

    {0,0,0,0},

    {0,0,1,0},

    {0,1,1,1},

    {0,0,0,0}

};

//长条

int item7[4][4]=

{

    {0,0,1,0},

    {0,0,1,0},

    {0,0,1,0},

    {0,0,1,0}

};

//拷贝方块图案

inline void block_cpy(int dblock[4][4],int sblock[4][4])

{

    for(int i=0;i<4;i++)

        for(int j=0;j<4;j++)

            dblock[i][j]=sblock[i][j];

}

Widget::Widget(QWidget *parent) :

    QWidget(parent),

    ui(new Ui::Widget)

{

    ui->setupUi(this);

    //调整窗口尺寸布局   resize(AREA_COL*BLOCK_SIZE+MARGIN*4+4*BLOCK_SIZE,AREA_ROW*BLOCK_SIZE+MARGIN*2);

    //初始化游戏

    InitGame();

}

Widget::~Widget()

{

    delete ui;

}

void Widget::paintEvent(QPaintEvent *event)

{

    QPainter painter(this);

    //画游戏场景边框

    painter.setBrush(QBrush(Qt::white,Qt::SolidPattern));    painter.drawRect(MARGIN,MARGIN,AREA_COL*BLOCK_SIZE,AREA_ROW*BLOCK_SIZE);

    //画方块预告

    painter.setBrush(QBrush(Qt::blue,Qt::SolidPattern));

    for(int i=0;i<4;i++)

        for(int j=0;j<4;j++)

            if(next_block[i][j]==1)                painter.drawRect(MARGIN*3+AREA_COL*BLOCK_SIZE+j*BLOCK_SIZE,MARGIN+i*BLOCK_SIZE,BLOCK_SIZE,BLOCK_SIZE);

    //绘制得分

    painter.setPen(Qt::black);

    painter.setFont(QFont("Arial",14));    painter.drawText(QRect(MARGIN*3+AREA_COL*BLOCK_SIZE,MARGIN*2+4*BLOCK_SIZE,BLOCK_SIZE*4,BLOCK_SIZE*4),Qt::AlignCenter,"score: "+QString::number(score));

    //绘制下落方块和稳定方块,注意方块边线的颜色是根据setPen来的,默认黑色

    for(int i=0;i<AREA_ROW;i++)

        for(int j=0;j<AREA_COL;j++)

        {

            //绘制活动方块

            if(game_area[i][j]==1)

            {

                painter.setBrush(QBrush(Qt::red,Qt::SolidPattern));                painter.drawRect(j*BLOCK_SIZE+MARGIN,i*BLOCK_SIZE+MARGIN,BLOCK_SIZE,BLOCK_SIZE);

            }

            //绘制稳定方块

            else if(game_area[i][j]==2)

            {

                painter.setBrush(QBrush(Qt::green,Qt::SolidPattern));       painter.drawRect(j*BLOCK_SIZE+MARGIN,i*BLOCK_SIZE+MARGIN,BLOCK_SIZE,BLOCK_SIZE);

            }

        }

}

void Widget::timerEvent(QTimerEvent *event)

{

    //方块下落

    if(event->timerId()==game_timer)

        BlockMove(DOWN);

    //刷新画面

    if(event->timerId()==paint_timer)

        update();

}

void Widget::keyPressEvent(QKeyEvent *event)

{

    switch(event->key())

    {

    case Qt::Key_Up:

        BlockMove(UP);

        break;

    case Qt::Key_Down:

        BlockMove(DOWN);

        break;

    case Qt::Key_Left:

        BlockMove(LEFT);

        break;

    case Qt::Key_Right:

        BlockMove(RIGHT);

        break;

    case Qt::Key_Space:

        BlockMove(SPACE);

        break;

    default:

        break;

    }

}

void Widget::CreateBlock(int block[4][4],int block_id)

{

    switch (block_id)

    {

    case 0:

        block_cpy(block,item1);

        break;

    case 1:

        block_cpy(block,item2);

        break;

    case 2:

        block_cpy(block,item3);

        break;

    case 3:

        block_cpy(block,item4);

        break;

    case 4:

        block_cpy(block,item5);

        break;

    case 5:

        block_cpy(block,item6);

        break;

    case 6:

        block_cpy(block,item7);

        break;

    default:

        break;

    }

}

void Widget::GetBorder(int block[4][4],Border &border)

{

    //计算上下左右边界

    for(int i=0;i<4;i++)

        for(int j=0;j<4;j++)

            if(block[i][j]==1)

            {

                border.dbound=i;

                break; //直到计算到最后一行有1

            }

    for(int i=3;i>=0;i--)

        for(int j=0;j<4;j++)

            if(block[i][j]==1)

            {

                border.ubound=i;

                break;

            }

    for(int j=0;j<4;j++)

        for(int i=0;i<4;i++)

            if(block[i][j]==1)

            {

                border.rbound=j;

                break;

            }

    for(int j=3;j>=0;j--)

        for(int i=0;i<4;i++)

            if(block[i][j]==1)

            {

                border.lbound=j;

                break;

            }

//    qDebug()<<cur_border.ubound<<cur_border.dbound<<cur_border.lbound<<cur_border.rbound;

}

void Widget::InitGame()

{

    for(int i=0;i<AREA_ROW;i++)

        for(int j=0;j<AREA_COL;j++)

            game_area[i][j]=0;

    speed_ms=800;

    refresh_ms=30;

    //初始化随机数种子

    srand(time(0));

    //分数清0

    score=0;

    //开始游戏

    StartGame();

}

void Widget::ResetBlock()

{

    //产生当前方块

    block_cpy(cur_block,next_block);

    GetBorder(cur_block,cur_border);

    //产生下一个方块

    int block_id=rand()%7;

    CreateBlock(next_block,block_id);

    //设置初始方块坐标,以方块左上角为锚点

    block_point start_point;

    start_point.pos_x=AREA_COL/2-2;

    start_point.pos_y=0;

    block_pos=start_point;

}

void Widget::StartGame()

{

    game_timer=startTimer(speed_ms); //开启游戏timer

    paint_timer=startTimer(refresh_ms); //开启界面刷新timer

    //产生初始下一个方块

    int block_id=rand()%7;

    CreateBlock(next_block,block_id);

    ResetBlock(); //产生方块

}

void Widget::GameOver()

{

    //游戏结束停止计时器

    killTimer(game_timer);

    killTimer(paint_timer);

    QMessageBox::information(this,"failed","game over");

}

void Widget::BlockRotate(int block[4][4])

{

    int temp_block[4][4];

    for(int i=0;i<4;i++)

        for(int j=0;j<4;j++)

            temp_block[3-j][i]=block[i][j];

    for(int i=0;i<4;i++)

        for(int j=0;j<4;j++)

            block[i][j]=temp_block[i][j];

}

void Widget::ConvertStable(int x,int y)

{

    for(int i=cur_border.ubound;i<=cur_border.dbound;i++)

        for(int j=cur_border.lbound;j<=cur_border.rbound;j++)

            if(cur_block[i][j]==1)

                game_area[y+i][x+j]=2; //x和y别搞反

}

bool Widget::IsCollide(int x,int y,Direction dir)

{

    //用拷贝的临时方块做判断

    int temp_block[4][4];

    block_cpy(temp_block,cur_block);

    Border temp_border;

    GetBorder(temp_block,temp_border);

    //先尝试按照某方向走一格

    switch(dir)

    {

    case UP:

        BlockRotate(temp_block);

        GetBorder(temp_block,temp_border); //旋转后要重新计算边界

        break;

    case DOWN:

        y+=1;

        break;

    case LEFT:

        x-=1;

        break;

    case RIGHT:

        x+=1;

        break;

    default:

        break;

    }

    for(int i=temp_border.ubound;i<=temp_border.dbound;i++)

        for(int j=temp_border.lbound;j<=temp_border.rbound;j++)            if(game_area[y+i][x+j]==2&&temp_block[i][j]==1||x+temp_border.lbound<0||x+temp_border.rbound>AREA_COL-1)

                return true;

    return false;

}

void Widget::BlockMove(Direction dir)

{

    switch (dir) {

    case UP:

        if(IsCollide(block_pos.pos_x,block_pos.pos_y,UP))

            break;

        //逆时针旋转90度

        BlockRotate(cur_block);

        //防止旋转后bug,i和j从0到4重新设置方块

        for(int i=0;i<4;i++)

            for(int j=0;j<4;j++)                game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];

        //重新计算边界

        GetBorder(cur_block,cur_border);

        break;

    case DOWN:

        //方块到达边界则不再移动

        if(block_pos.pos_y+cur_border.dbound==AREA_ROW-1)

        {

            ConvertStable(block_pos.pos_x,block_pos.pos_y);

            ResetBlock();

            break;

        }

        //碰撞检测,只计算上下左右边界,先尝试走一格,如果碰撞则稳定方块后跳出

        if(IsCollide(block_pos.pos_x,block_pos.pos_y,DOWN))

        {

            //只有最终不能下落才转成稳定方块

            ConvertStable(block_pos.pos_x,block_pos.pos_y);

            ResetBlock();

            break;

        }

        //恢复方块上场景,为了清除移动过程中的方块残留

        for(int j=cur_border.lbound;j<=cur_border.rbound;j++)

            game_area[block_pos.pos_y][block_pos.pos_x+j]=0;

        //没有碰撞则下落一格

        block_pos.pos_y+=1;

        //方块下降一格,拷贝到场景,注意左右边界

        for(int i=0;i<4;i++) //必须是0到4而不是边界索引,考虑到旋转后边界重新计算

            for(int j=cur_border.lbound;j<=cur_border.rbound;j++)

                if(block_pos.pos_y+i<=AREA_ROW-1&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2) //注意场景数组不越界,而且不会擦出稳定的方块                    game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];

        break;

    case LEFT:

        //到左边界或者碰撞不再往左

        if(block_pos.pos_x+cur_border.lbound==0||IsCollide(block_pos.pos_x,block_pos.pos_y,LEFT))

            break;

        //恢复方块右场景,为了清除移动过程中的方块残留

        for(int i=cur_border.ubound;i<=cur_border.dbound;i++)

            game_area[block_pos.pos_y+i][block_pos.pos_x+3]=0;

        block_pos.pos_x-=1;

        //方块左移一格,拷贝到场景

        for(int i=cur_border.ubound;i<=cur_border.dbound;i++)

            for(int j=0;j<4;j++)                if(block_pos.pos_x+j>=0&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2) //注意场景数组不越界                    game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];

        break;

    case RIGHT:

        if(block_pos.pos_x+cur_border.rbound==AREA_COL-1||IsCollide(block_pos.pos_x,block_pos.pos_y,RIGHT))

            break;

        //恢复方块左场景,为了清除移动过程中的方块残留

        for(int i=cur_border.ubound;i<=cur_border.dbound;i++)

            game_area[block_pos.pos_y+i][block_pos.pos_x]=0;

        block_pos.pos_x+=1;

        //方块右移一格,拷贝到场景

        for(int i=cur_border.ubound;i<=cur_border.dbound;i++)

            for(int j=0;j<4;j++)

                if(block_pos.pos_x+j<=AREA_COL-1&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2) //注意场景数组不越界                    game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];

        break;

    case SPACE: //一次到底

        //一格一格下移,直到不能下移

        while(block_pos.pos_y+cur_border.dbound<AREA_ROW-1&&!IsCollide(block_pos.pos_x,block_pos.pos_y,DOWN))

        {

            //恢复方块上场景,为了清除移动过程中的方块残留

            for(int j=cur_border.lbound;j<=cur_border.rbound;j++)

                game_area[block_pos.pos_y][block_pos.pos_x+j]=0;

            //没有碰撞则下落一格

            block_pos.pos_y+=1;

            //方块下降一格,拷贝到场景,注意左右边界

            for(int i=0;i<4;i++) //必须是0到4

                for(int j=cur_border.lbound;j<=cur_border.rbound;j++)

                    if(block_pos.pos_y+i<=AREA_ROW-1&&game_area[block_pos.pos_y+i][block_pos.pos_x+j]!=2) //注意场景数组不越界,而且不会擦出稳定的方块                        game_area[block_pos.pos_y+i][block_pos.pos_x+j]=cur_block[i][j];

        }

        ConvertStable(block_pos.pos_x,block_pos.pos_y);

        ResetBlock();

        break;

    default:

        break;

    }

    //处理消行,整个场景上面的行依次下移

    int i=AREA_ROW-1;

    int line_count=0; //记消行数

    while(i>=1)

    {

        bool is_line_full=true;

        for(int j=0;j<AREA_COL;j++)

            if(game_area[i][j]==0)

            {

                is_line_full=false;

                i--;

                break;

            }

        if(is_line_full)

        {

            for(int k=i;k>=1;k--)

                for(int j=0;j<AREA_COL;j++)

                    game_area[k][j]=game_area[k-1][j];

            line_count++;//每次增加消行的行数

        }

    }

    score+=line_count*10; //得分

    //判断游戏是否结束

    for(int j=0;j<AREA_COL;j++)

        if(game_area[0][j]==2) //最顶端也有稳定方块

            GameOver();

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

.wei-upup

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值