俄羅斯方塊的資料結構及實現 struct of a tetris
最近對開源版本的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的事情。
相關文章
- Tetris 俄羅斯方塊遊戲遊戲
- canvas實現俄羅斯方塊Canvas
- [分享]純python3手寫Tetris(俄羅斯方塊)遊戲Python遊戲
- c#實現簡單的俄羅斯方塊C#
- 使用JavaScript實現一個俄羅斯方塊JavaScript
- Flutter Web 實戰 - 俄羅斯方塊FlutterWeb
- 俄羅斯方塊聯機小遊戲的實現遊戲
- 俄羅斯方塊練習
- 俄羅斯方塊(JS+CSS)JSCSS
- 初學者——Java之實現簡易俄羅斯方塊Java
- 【Java遊戲】java俄羅斯方塊!Java遊戲
- 300行Python程式碼實現俄羅斯方塊,致敬逝去的童年Python
- 最新《 java實戰開發俄羅斯方塊教程》Java
- Python 實戰開發俄羅斯方塊遊戲Python遊戲
- Python:遊戲:300行程式碼實現俄羅斯方塊Python遊戲行程
- 基於Flutter的俄羅斯方塊小遊戲Flutter遊戲
- Python3+pygame實現的俄羅斯方塊 程式碼完整 有演示效果PythonGAM
- 基於MonoGame重製《俄羅斯方塊》遊戲MonoGAM遊戲
- 如何讓 Emacs 俄羅斯方塊變得更難Mac
- wxpython入門第十一步(俄羅斯方塊)Python
- 【補檔STM32】STM32F103俄羅斯方塊遊戲實現遊戲
- 如何讓AI教機器自己玩俄羅斯方塊?AI
- 使用C#和MonoGame開發俄羅斯方塊遊戲C#MonoGAM遊戲
- 俄羅斯方塊歷史發展與變革創新
- 用React、Redux、Immutable做俄羅斯方塊 | 掘金技術徵文ReactRedux
- 從俄羅斯方塊,邁向強化學習大門強化學習
- 用 SQL 寫的俄羅斯方塊遊戲「GitHub 熱點速覽」SQL遊戲Github
- python開發俄羅斯方塊小遊戲程式碼例項Python遊戲
- “漢字俄羅斯方塊”《一字不落》今日上架Steam
- 為了上班摸魚我用Python製作了俄羅斯方塊?Python
- 函數語言程式設計嘗試之俄羅斯方塊函數程式設計
- 回顧「俄羅斯方塊」曾經的一段蒸汽波時代
- 超越《俄羅斯方塊》後,《我的世界》想打造一個更具野心的世界
- pyqt5製作俄羅斯方塊小遊戲-----原始碼解析QT遊戲原始碼
- 俄羅斯玩偶
- 俄羅斯:‘區塊鏈屬於我們’區塊鏈
- 《俄羅斯方塊》系列 35 週年 一窺遊戲歷年玩法演變遊戲
- 俄羅斯軍方正在建設區塊鏈研究實驗室區塊鏈
- 恰逢35歲生日 俄羅斯方塊的一切都變得井井有條