qt5实现俄罗斯方块(详细注释)

前言

一个礼拜前想做一个俄罗斯方块小游戏,因为想用c++实现,但又受制于界面,于是苦读了几天的Qt。昨天开工连带一个不眠夜,总算是大功告成,个中滋味,怕是只有自己知道。


简介

俄罗斯方块,c++,qt。

功能:常规俄罗斯方块具有的:方块旋转,左移,右移,下落加速,消行,提示下一块样式等等都已实现。同时实现了记分以及暂停的功能。


效果图


游戏过程效果图


暂停效果图


游戏结束效果图


实现思路


提到俄罗斯方块,稍微麻烦一些的地方只有三点


1. 方块旋转

#1##
#1##
#11#
####

####
##1#
111#
####

####
#11#
##1#
##1#

####
#111
#1##
####

上面四个小矩阵我们很容易看出,它是L型图案的四种不同形态,用4*4的矩阵来将它们统一表示,再细心一点的小伙伴还会发现,它们的顺序也是按照逆时针旋转进行排列的。

那么加入我们对上面四个小矩阵进行编号为1,2,3,4。

那么显然1-旋转-> 2 -旋转-> 3 -旋转-> 4 -旋转->1

如果我们开辟一NEXT数组用来保存方块对应的旋转后的方块编号。

则NEXT[1] = 2; NEXT[2] = 3; NEXT[3] = 4; NEXT[4] = 1;

举一反三,别的形状方块也是一样的。


2. 碰撞检测

上文,我们用一个四维小矩阵来表示方块,我们可以对它规定一个重心,索性就用左上角(0, 0)点来作为重心吧。

这个重心只是用来与实际地图相对应的一个相对点而已。

我们以将小矩阵忽略,只在意重心那个点,通过重心点所在地图的坐标,显然可以求出其他点位于地图的坐标。

这样一来,每次操作方块时,将方块将要变换到的位置与地图进行比较,通过简单的判断,可以得出是否有重复部分,若重复,则可以移动,否则不可以。


3. 方块自动下降

这个是最容易解决的,qt里有个QTimer类,有定时功能,设定一定时间间隔,触发timerEvent事件。在事件里,做你想做的即可。


代码构成

全部代码仅实现三个类

1. Board 游戏地图信息

class Board
{
public:
    int score;//当前分数
    int maxScore;//最高分
    int time;//每次下落的间隔时间
    int width;//地图宽
    int height;//地图高
    Block *block;//下落的方块
    char map[100][100];//保存地图信息

    Board(int, int);
    void confirm();//将下落到底的块更新到map
    bool isEnd();//判断是否游戏结束
};

2. Block 方块信息及操作

class Block
{
public:
    char BLOCKS[20][5][5];//各个类型的方块
    int NEXT[20];//模拟指向用于方块旋转
    Qt::GlobalColor COLOR[20];//各个类型方块的颜色
    int x;//块重心起始坐标
    int y;
    int type;//块id
    int nextType;//下一个块id
    Board *board;
    Block(Board *);//构造函数
    void toNext();//更改块id
    void moveUp();//变形
    void moveRight();//加速
    void moveLeft();//左移
    void moveDown();//右移
    bool detect(int);//碰撞检测
};

3. mainWindow 游戏界面部分

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    bool flag;//判断是否暂停状态
    QTimer *timer;//定时器
    Board *board;//游戏所用地图类

    MainWindow(Board *, QWidget *parent = 0);
    void paintEvent(QPaintEvent *event);//绘制界面
    void keyPressEvent(QKeyEvent *);//键盘事件处理

signals:

public slots:
    void timerEvent();//定时事件

private:
};

核心代码

粗略估计了下,代码总量约500行,所以这里就只贴自己认为核心的部分,完整项目文件及可执行程序会上传到git上,下面会给出链接。有兴趣的朋友去down一下即可。

碰撞检测

bool Block::detect(int flag)
{
    //获得当前操作的目标状态:目标坐标,目标方块类型。
    int nextX, nextY, nextType;
    // 0,1,2,3对应上下左右
    switch(flag)
    {
        case 0: nextX = x; nextY = y; nextType = NEXT[type]; break;
        case 1: nextX = x+1; nextY = y; nextType = type; break;
        case 2: nextX = x; nextY = y-1; nextType = type; break;
        case 3: nextX = x; nextY = y+1; nextType = type; break;
    }

    for(int i=0; i<4; i++)
        for(int j=0; j<4; j++)
        {
            //tx,ty表示i,j所对应的地图的实际位置。
            int tx = nextX+i;
            int ty = nextY+j;
            //边界处理
            if(tx < 0 || tx > board->height+1 || ty < 0 || ty > board->width+1)
                continue;
            //如果块与地图墙相重合,则发生碰撞
            if(BLOCKS[nextType][i][j] != ‘#‘ && (ty == 0 || ty == board->width+1))
                return false;
            if(BLOCKS[nextType][i][j] != ‘#‘ && board->map[tx][ty] != ‘#‘)
                return false;
        }
    return true;
}

方块沉底后的地图信息更新及消行记分操作

void Board::confirm()
{
    //将块更新到map
    for(int i=0; i<4; i++)
        for(int j=0; j<4; j++)
        {
            int tx = block->x + i;
            int ty = block->y + j;
            if(tx<1 || tx > height || ty < 1 || ty > width)
                continue;
            if(block->BLOCKS[block->type][i][j] != ‘#‘)
                map[tx][ty] = block->BLOCKS[block->type][i][j];
        }

    //消去完整的行并计算行个数
    int cnt = 0;
    for(int i=height; i>=1; i--)
    {
        bool flag = false;
        for(int j=1; j<=width; j++)
            if(map[i][j] == ‘#‘)
            {
                flag = true;
                break;
            }
        if(flag)
            continue;
        cnt++;
        for(int j=i; j>=1; j--)
            for(int k=1; k<=width; k++)
                map[j][k] = map[j-1][k];
    }

    //每下落一个块加5分
    score += 5;
    //根据同时消去的行的数量指数型记分
    //1-10 2-20 3-40 4-80
    if(cnt)
        score += 10*(1<<cnt);

    //实时更新最大值
    maxScore = std::max(maxScore, score);

    //每下落一个块,时间间隔减2
    time -= 2;
    if(time < 0)
        time = 0;

    //更新块
    block->toNext();
}

完整代码

我的github链接:

https://github.com/shiyi1996/algorithm/tree/master/eluosi

哈哈,分享成果的过程总是令人快乐的。

最后,如果有道友发现其中有出错的地方,还望不吝指出。

若有更好的写法也可交流一二,大家一起进步嘛!

时间: 2024-08-10 16:36:17

qt5实现俄罗斯方块(详细注释)的相关文章

Qt5_简易画板_详细注释

代码下载链接:  http://pan.baidu.com/s/1hsc41Ek 密码: 5hdg 显示效果如下: 代码附有详细注释(代码如下) 1 /*** 2 * 先新建QMainWindow, 项目名称: DrawWidget 基类选择: QMainWindow, 3 * 类名默认, 然后在DrawWidget项目名上新建c++class文件, 选择基类: QWidget 4 */ 5 //先完成绘图区的实现 6 //如下为: drawwidget.h 7 #ifndef DRAWWIDG

codevs 2924 数独挑战 x(三种做法+超详细注释~)

2924 数独挑战 时间限制: 1 s 空间限制: 1000 KB 题目等级 : 钻石 Diamond 题目描述 Description “芬兰数学家因卡拉,花费3个月时间设计出了世界上迄今难度最大的数独游戏,而且它只有一个答案.因卡拉说只有思考能力最快.头脑最聪明的人才能破解这个游戏.”这是英国<每日邮报>2012年6月30日的一篇报道.这个号称“世界最难数独”的“超级游戏”,却被扬州一位69岁的农民花三天时间解了出来. 看到这个新闻后,我激动不已,证明我们OI的实力的机会来了,我们虽然不是

多线程实现生产者消费者问题 详细注释 事件+临界区 信号量+临界区2种方法

生产者消费者问题:  该问题描述了两个共享固定大小缓冲区的线程--即所谓的"生产者"和"消费者"--在实际运行时会发生的问题.生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程.与此同时,消费者也在缓冲区消耗这些数据.该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据.具体我就不解释了   应该都懂 不懂请百度一下 我是用事件实现生产者消费者问题的同步  用临界区实现互斥    我的这个做法与经典做法不同 即用信号量

30多个iOS常用动画,带详细注释

// //  CoreAnimationEffect.h //  CoreAnimationEffect // //  Created by VincentXue on 13-1-19. //  Copyright (c) 2013年 VincentXue. All rights reserved. // #import <Foundation/Foundation.h> /**  !  导入QuartzCore.framework  *  *  Example:  *  *  Step.1

ABP+Zero+Metronic+Redis的完美结合快速启动模板(超级代码详细注释版本)

微信扫一扫并支付成功,联系QQ:770628656获取所有源码(超级代码详细注释版本) 原文地址:https://www.cnblogs.com/abpbasic/p/8124792.html

Light OJ - 1026 - Critical Links(图论-Tarjan算法求无向图的桥数) - 带详细注释

 原题链接   无向连通图中,如果删除某边后,图变成不连通,则称该边为桥. 也可以先用Tajan()进行dfs算出所有点 的low和dfn值,并记录dfs过程中每个 点的父节点:然后再把所有点遍历一遍, 看其low和dfn,满足dfn[ fa ]<low[ i ](0<i<=n, i 的 father为fa) -- 则桥为fa-i. 找桥的时候,要注意看有没有重边:有重边,则不是桥. 另外,本题的题意及测试样例中没有重边,所以不用考虑重边. 带详细注释的题解: #include<s

降水量与蒸发量统计报表详细注释

//降水量与蒸发量统计报表详细注释 <template> <div id="main" :style="{width:'1000px',height:'500px' }"></div> </template> <!--1 为ECharts准备一个具备大小(宽高)的Dom--> <!-- <div id="mainBar" style="height:500px;bo

SAP CRM BOL编程基础,代码+详细注释

网络上可以找到一些使用BOL查询.维护数据的DEMO,但几乎都是单纯的代码,缺乏说明,难以理解.本文除了代码外,还给出了详细的注释,有助于理解BOL编程中的一些基本概念. 这是一篇翻译的文章,你可能会发现部分内容不是很好理解,这时可以直接阅读原文. 原文所在的sapcrmwebui.com是一个不错的博客,然而网站不是很稳定,偶尔会连接不上,建议使用Internet Archive访问. 如果你访问不了Internet Archive,说明你需要一点过墙的手段. 本文链接:http://www.

【转】IOS 30多个iOS常用动画,带详细注释

原文: http://blog.csdn.net/zhibudefeng/article/details/8691567 // //  CoreAnimationEffect.h //  CoreAnimationEffect // //  Created by VincentXue on 13-1-19. //  Copyright (c) 2013年 VincentXue. All rights reserved. // #import <Foundation/Foundation.h>