俄罗斯方块的数据结构及实现 struct of a tetris

原创 2007年10月16日 09:11:00

最近对开源版本的QT中的俄罗斯方块的实现做了一些分析,顺便记录一下。本文遵守GNU GPL。

 

一直想做自己的游戏,俄罗斯方块或者五子棋这样的应该都是初学者想去完成的例子,最近找了一些关于tetris的代码,发现QT里面的例子做的比较好,它用C++实现了一个很好的类,我也用Win32的GDI重写了一个不用QT版本的。这里记录一下QT中tetrix的实现。

 

1、类结构:

 

QT的tetrix在其安装目录的examples/widgets/tetrix下(我使用的是QT4.2.3 for windows opensource mingw版本)。里面有这样两个类: TetrixPiece和TetrixBoard。其中TetrixPiece定义了tetrix中各种形状方块,TetrixBoard实现了整个游戏的实现以及逻辑。说实话,在看了几天QT的代码后,发现用QT写GUI的东西还是很方便地。里面的模块十分清晰,写出来的代码很漂亮,可读性极高,比用MFC向导生成那些工程要清晰很多,也比用WIN32写代码省好多笔墨。

 

2、数据结构:

 

在没有选择QT的tetrix之前,也从CodeGuru和CodeProject上找了一些tetrix的实现,但是,其中不是用了某些图形库(如cximage),就是数据结构定义很冗余。当然,这之前,我自己也想过其数据结构的实现,您也可以先在想想,对于里面的方块,我们该如何定义它的数据结构呢?

 

最初我的想法是,定义一个4X4的矩阵(简单的使用二维数组),并定义几个状态数来表示每一个图形。比如,要定义一个长条装的图形,我们可以需要这样一个二维数组:

 

1 0 0 0

1 0 0 0

1 0 0 0

1 0 0 0

 

这里面1表示有图形,0表示无,这样我们可以写出一个简单的图像驱动,在给他发送一个2维数组的样式后,帮助我们把它画出来。那么,对于一个长条状的图形,我们大概需要两个这样的矩阵。方块好一点,可能只需要一个,但是里面的Z形状以及T形状的图形,每一个都需要4个这样的阵来表示。这样的实现固然可行,只是在其数据结构的定义时,代码中需要一个长长的静态三维数组来表示这些图形。

 

对于tetrix的Board,实现起来应该简单一些,复杂的是它里面的逻辑控制。它只需要一个大的矩阵来存储整个图形区域的状态就好了。

 

当然,还有其他的表示方法,我们就不多讨论了。我们现在来看看QT例子中的实现: TetrixPiece.h and TetrixPiece.cpp

 

/****************************************************************************
** TetrixPiece.h

**
** Copyright (C) 2004-2007 Trolltech ASA. All rights reserved.
**
****************************************************************************/

#ifndef TETRIXPIECE_H
#define TETRIXPIECE_H

enum TetrixShape { NoShape, ZShape, SShape, LineShape, TShape, SquareShape,
                   LShape, MirroredLShape };

class TetrixPiece
{
public:
    TetrixPiece() { setShape(NoShape); }

    void setRandomShape();
    void setShape(TetrixShape shape);

    TetrixShape shape() const { return pieceShape; }
    int x(int index) const { return coords[index][0]; }
    int y(int index) const { return coords[index][1]; }
    int minX() const;
    int maxX() const;
    int minY() const;
    int maxY() const;
    TetrixPiece rotatedLeft() const;
    TetrixPiece rotatedRight() const;

private:
    void setX(int index, int x) { coords[index][0] = x; }
    void setY(int index, int y) { coords[index][1] = y; }

    TetrixShape pieceShape;
    int coords[4][2];
};

#endif

/****************************************************************************

** TetrixPiece.cpp

**
** Copyright (C) 2004-2007 Trolltech ASA. All rights reserved.
**
****************************************************************************/

#include <QtCore>

#include <stdlib.h>

#include "tetrixpiece.h"

void TetrixPiece::setRandomShape()
{
    setShape(TetrixShape(qrand() % 7 + 1));
}

void TetrixPiece::setShape(TetrixShape shape)
{
    static const int coordsTable[8][4][2] = {
        { { 0, 0 },   { 0, 0 },   { 0, 0 },   { 0, 0 } },
        { { 0, -1 },  { 0, 0 },   { -1, 0 },  { -1, 1 } },
        { { 0, -1 },  { 0, 0 },   { 1, 0 },   { 1, 1 } },
        { { 0, -1 },  { 0, 0 },   { 0, 1 },   { 0, 2 } },
        { { -1, 0 },  { 0, 0 },   { 1, 0 },   { 0, 1 } },
        { { 0, 0 },   { 1, 0 },   { 0, 1 },   { 1, 1 } },
        { { -1, -1 }, { 0, -1 },  { 0, 0 },   { 0, 1 } },
        { { 1, -1 },  { 0, -1 },  { 0, 0 },   { 0, 1 } }
    };

    for (int i = 0; i < 4 ; i++) {
        for (int j = 0; j < 2; ++j)
            coords[i][j] = coordsTable[shape][i][j];
    }
    pieceShape = shape;
}

int TetrixPiece::minX() const
{
    int min = coords[0][0];
    for (int i = 1; i < 4; ++i)
        min = qMin(min, coords[i][0]);
    return min;
}

int TetrixPiece::maxX() const
{
    int max = coords[0][0];
    for (int i = 1; i < 4; ++i)
        max = qMax(max, coords[i][0]);
    return max;
}

int TetrixPiece::minY() const
{
    int min = coords[0][1];
    for (int i = 1; i < 4; ++i)
        min = qMin(min, coords[i][1]);
    return min;
}

int TetrixPiece::maxY() const
{
    int max = coords[0][1];
    for (int i = 1; i < 4; ++i)
        max = qMax(max, coords[i][1]);
    return max;
}

TetrixPiece TetrixPiece::rotatedLeft() const
{
    if (pieceShape == SquareShape)
        return *this;

    TetrixPiece result;
    result.pieceShape = pieceShape;
    for (int i = 0; i < 4; ++i) {
        result.setX(i, y(i));
        result.setY(i, -x(i));
    }
    return result;
}

TetrixPiece TetrixPiece::rotatedRight() const
{
    if (pieceShape == SquareShape)
        return *this;

    TetrixPiece result;
    result.pieceShape = pieceShape;
    for (int i = 0; i < 4; ++i) {
        result.setX(i, -y(i));
        result.setY(i, x(i));
    }
    return result;
}
 

首先看一下TetrixShape ,这个enum定义了一些图形的索引方法。用于表示各式的图形。我们可以在setShape方法中找到这个static const int coordsTable[8][4][2]。静态的三维数组,就是它,按照enum的顺序,定义了各个图形的真实表示。

 然后我们看看它到底是如何表示的,我们随便找一个ZShape吧,它的定义是:{ 0, -1 },  { 0, 0 },   { -1, 0 },  { -1, 1 }  用矩阵表示出来就是

 0   -1

 0    0

-1    0

-1    1

 

这个明显没有上面我们讨论的那个直观,当初我看这个数据结构时候也是迷惑了好一阵。这个矩阵是如何表示Z图形的呢?

 

了解了这个类中的成员函数以及后面TetrixBoard的实现才明白,TetrixPiece用4对坐标来表示一个图形。你看不管是哪种图形,其实它都是由4个方块组成的,所以,我们可以用4组坐标来表示一个图形。

看看这个Zshape,按照这个坐标来瞄点得到

   -1   0  1

-1      *

 0 *    *

 1 *

 

看,形状出来了吧。在这个二维数组中,前面的代表x坐标,后面的代表y坐标。理解了这个,那么下面的这些函数的意义就比较好懂了。

    int x(int index) const { return coords[index][0]; }  //返回图形的第index个坐标的x
    int y(int index) const { return coords[index][1]; }  //返回图形的第index个坐标的y
    int minX() const;                //最小x坐标值
    int maxX() const;               //最大x坐标值
    int minY() const;               //最小y坐标
    int maxY() const;               //最大y坐标  上面这4个函数,用于绘制图形时确定图形的外形大小。
    TetrixPiece rotatedLeft() const;  //坐标转换向左向右。
    TetrixPiece rotatedRight() const;

 

 

关于 TetrixPiece我们就聊这么多了。有了这些东西,看明白这个例子已经很容易鸟。

下面我们择一些有意思的地方说说:

 

void TetrixBoard::drawSquare(QPainter &painter, int x, int y, TetrixShape shape)

   这个函数,是用来画方块的,QPainter 类似于DGI中的DC,x和y指出在什么坐标上画,最后给出画的方块是什么图形的(这个tetrix里面不用方块用不同图形表示)。你可以看到在这个源代码中有两个这样静态的数组,他们都被定义到了成员函数里,一个是上面的coordsTable,一个是这个函数里面的colorTable。说实话,我对c++一知半解,并不知道这样的实现会不会有什么不好的地方,但是这种什么时候需要什么时候用,尽量把相关代码写到一起的风格,是哥们我很赞许地。

 

 bool TetrixBoard::tryMove(const TetrixPiece &newPiece, int newX, int newY)

   这个函数用于移动图形,给出try,说明它的返回值也很有用,在判断类似碰撞时起作用,就是让它不能随便Move。

 

        for (int i = 0; i < 4; ++i) {
            int x = curX + curPiece.x(i);
            int y = curY - curPiece.y(i);
            drawSquare(painter, rect.left() + x * squareWidth(),
                       boardTop + (BoardHeight - y - 1) * squareHeight(),
                       curPiece.shape());
        }
上面这段代码是用于绘制一个图形的代码,对于每一对坐标,计算了x和y的值,我们看到,它作了一些计算,主要是将坐标原点重新计算,让drawSquare在绘制的时候,可以按照一个合适的顺序完成。这里还有一个需要说明的,这组坐标,在通过计算max-min的值得到了它x和y坐标各自所占的大小。
还拿上面的ZShape做例子:

 0   -1

 0    0

-1    0

-1    1


程序通过下面的规则来计算:MAX(x)-MIN(x)+1 为x轴所占的大小即0-(-1)+1=2   ,MAX(y)-MIN(y)+1 为y轴所占的大小即1-(-1)+1=3。

我们可以跟一下对于绘制ZShape时候的情况:假设Zshape刚从最上面掉下来。
    curX = BoardWidth / 2 + 1=10/2+1=6  //居中
    curY = BoardHeight - 1 + curPiece.minY()= 22-1+(-1)=22

    x=curX+curPiece.x(i)=5[6]

    y=curY - curPiece.y(i)=23[22][21]


   drawSquare(painter, rect.left() + x * squareWidth(), //最左边坐标+x坐标
                          boardTop + (BoardHeight - y - 1) * squareHeight(), //最上端坐标 +相对最高处的修正 图形只显示一个方块.
                          curPiece.shape());



还有一个比较有意思的函数

TetrixShape &shapeAt(int x, int y) { return board[(y * BoardWidth) + x]; }

 

这个函数在使用的时候可以作为一个表达式的左值,like:shapeAt(1,2)=3; 我不熟悉c++的语法,但这个的确是我第一次遇见。shapeAt(1,2)=3;

相当于board[(2* BoardWidth) + 1]=3;

 

 大概就能想到这么多要说的东西了,如果大家有兴趣可以email和我讨论tetrix的事情。

 

 

HTML5边玩边学(8):俄罗斯方块就是这么简单 之 数据模型篇

HTML5边玩边学(9):俄罗斯方块就是这么简单 之 数据模型篇 特别提示: 本文中的运行效果需要 Chrome 浏览器或者 Firefox 浏览器。   一、从数据出发还是从...
  • houjun1988325
  • houjun1988325
  • 2014年08月14日 17:09
  • 1880

俄罗斯方块的源码实现

问题概述 这是一个使用java swing实现的小游戏,和之前的贪吃蛇以及五子棋类似,闲来无聊的作品,不过,这次的作品相较于前两个游戏在代码水平有很大的提高,结论是,编程能力的提高是写更多的代码写出来...
  • u011233383
  • u011233383
  • 2016年04月04日 21:43
  • 445

俄罗斯方块源程序和流程图

  • 2007年01月17日 17:06
  • 1000KB
  • 下载

基于Qt5的俄罗斯方块的开发学习(1)

以此篇博文纪念走过的2014年以及我刚刚起步的编程学习生涯。 首先声明这篇关于俄罗斯方块的文章也许不是很好但是我会尽自己的努力去说清重点,而细枝末节希望大家下载下来共同学习,所以这里只展示核心代码。...
  • yrh159357
  • yrh159357
  • 2015年01月20日 23:18
  • 2138

史上最短小精悍的javascript编写的俄罗斯方块游戏,仅仅60行代码

史上最短小精悍的javascript编写的俄罗斯方块游戏,仅仅60行代码,代码下载地址:http://www.zuidaima.com/share/1759652641295360.htm...
  • yaerfeng
  • yaerfeng
  • 2014年04月28日 09:52
  • 52073

使用Java实现小游戏:俄罗斯方块

使用Java实现小游戏:俄罗斯方块使用一个二维数组保存游戏的地图:// 游戏地图格子,每个格子保存一个方块,数组纪录方块的状态 private State map[][] = new State[ro...
  • zhliro
  • zhliro
  • 2015年05月15日 16:43
  • 3095

俄罗斯方块的 C++实现

最近想写一个俄罗斯方块,正好电脑里面有个以前下的代码,于是就看了看思路,然后自己加了一些东西,要是忘了就不好了 先做7个不同类型的方块,分别为 长条形、方形、L形、反L形、Z形、反Z形、土字形。用...
  • LT_lover
  • LT_lover
  • 2015年04月21日 10:49
  • 12827

C语言 WIN32窗口实现 俄罗斯方块Tetris 源代码

最近有空闲时间,就想编一个俄罗斯方块试试,期间也碰到了一些问题,在网上查找了很多资料。       现在代码已经完成了,游戏支持开始(包括从暂停恢复和结束后重新开始)、暂停、空格键落地及基本功能,玩起...
  • goldentimes1
  • goldentimes1
  • 2012年09月17日 14:49
  • 12818

JavaScript jQuery实现Tetris(俄罗斯方块)游戏代码

原文:JavaScript jQuery实现Tetris(俄罗斯方块)游戏代码 源代码下载地址:http://www.zuidaima.com/share/1882054152457216....
  • yaerfeng
  • yaerfeng
  • 2016年12月15日 10:17
  • 1219

C语言 WIN32窗口实现 俄罗斯方块Tetris 源代码

  • 2012年09月22日 12:30
  • 27KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章: 俄罗斯方块的数据结构及实现 struct of a tetris
举报原因:
原因补充:

(最多只允许输入30个字)