Tetris Tutorial para Iniciantes
Tetris Tutorial para Iniciantes
z = Rotacionar a pea.
Passo 0: Introduo
Ns vamos focalizar na lgica do jogo, utilizando apenas rectngulo
primitivas (SDL_Rect), para a renderizao.
Toda a lgica do jogo isolada do desenho, ento voc pode expandir o
tutorial facilmente.
Estou fazendo um planejamento do segundo tutorial de como melhorar
este Colne do Tetris usando sprites, background, efeitos, etc.
Mas agora, vamos nos concentrar na lgica do jogo. Esta a forma como o
seu prottipo ficar depois de terminar o tutorial:
C++
Um pouco de programao grfica, se quiser ampliar o tutorial com
grficos melhorados.No se preocupe com o que se v, s queira saber
a lgica do jogo Tetris.
Passo 1: As peas.
Primeiro, vamos criar uma classe para armazenar todas as peas. H 7
tipos diferentes de peas: quadrado, I, L, L-espelhado, N, N-espelhados e T.
Mas, como podemos definir cada pea? Apenas verifique a figura:
{0, 0, 0, 0, 0},
{0, 0, 0, 1, 0},
{0, 0, 2, 1, 0},
{0, 0, 1, 0, 0},
{0, 0, 0, 0, 0}
1. // Pieces definition
2. char mPieces [7 /*kind */ ][4 /* rotation */ ][5 /* horizontal blocks */ ][5 /* vertical
blocks */ ] =
3. {
4. // Square
5. {
6. {
7.
{0, 0, 0, 0, 0},
8.
{0, 0, 0, 0, 0},
9.
{0, 0, 2, 1, 0},
10.
{0, 0, 1, 1, 0},
11.
{0, 0, 0, 0, 0}
12.
},
13. {
14.
{0, 0, 0, 0, 0},
15.
{0, 0, 0, 0, 0},
16.
{0, 0, 2, 1, 0},
17.
{0, 0, 1, 1, 0},
18.
{0, 0, 0, 0, 0}
19.
},
20. {
21.
{0, 0, 0, 0, 0},
22.
{0, 0, 0, 0, 0},
23.
{0, 0, 2, 1, 0},
24.
{0, 0, 1, 1, 0},
25.
{0, 0, 0, 0, 0}
26.
},
27. {
28.
{0, 0, 0, 0, 0},
29.
{0, 0, 0, 0, 0},
30.
{0, 0, 2, 1, 0},
31.
{0, 0, 1, 1, 0},
32.
{0, 0, 0, 0, 0}
33.
}
34. },
35.
36. // I
37. {
38. {
39.
{0, 0, 0, 0, 0},
40.
{0, 0, 0, 0, 0},
41.
{0, 1, 2, 1, 1},
42.
{0, 0, 0, 0, 0},
43.
{0, 0, 0, 0, 0}
44.
},
45. {
46.
{0, 0, 0, 0, 0},
47.
{0, 0, 1, 0, 0},
48.
{0, 0, 2, 0, 0},
49.
{0, 0, 1, 0, 0},
50.
{0, 0, 1, 0, 0}
51.
},
52. {
53.
{0, 0, 0, 0, 0},
54.
{0, 0, 0, 0, 0},
55.
{1, 1, 2, 1, 0},
56.
{0, 0, 0, 0, 0},
57.
{0, 0, 0, 0, 0}
58.
},
59. {
60.
{0, 0, 1, 0, 0},
61.
{0, 0, 1, 0, 0},
62.
{0, 0, 2, 0, 0},
63.
{0, 0, 1, 0, 0},
64.
{0, 0, 0, 0, 0}
65.
}
66. }
67. ,
68. // L
69. {
70. {
71.
{0, 0, 0, 0, 0},
72.
{0, 0, 1, 0, 0},
73.
{0, 0, 2, 0, 0},
74.
{0, 0, 1, 1, 0},
75.
{0, 0, 0, 0, 0}
76.
},
77. {
78.
{0, 0, 0, 0, 0},
79.
{0, 0, 0, 0, 0},
80.
{0, 1, 2, 1, 0},
81.
{0, 1, 0, 0, 0},
82.
{0, 0, 0, 0, 0}
83.
},
84. {
85.
{0, 0, 0, 0, 0},
86.
{0, 1, 1, 0, 0},
87.
{0, 0, 2, 0, 0},
88.
{0, 0, 1, 0, 0},
89.
{0, 0, 0, 0, 0}
90.
},
91. {
92.
{0, 0, 0, 0, 0},
93.
{0, 0, 0, 1, 0},
94.
{0, 1, 2, 1, 0},
95.
{0, 0, 0, 0, 0},
96.
{0, 0, 0, 0, 0}
97.
}
98. },
99. // L mirrored
100. {
101. {
102.
{0, 0, 0, 0, 0},
103.
{0, 0, 1, 0, 0},
104.
{0, 0, 2, 0, 0},
105.
{0, 1, 1, 0, 0},
106.
{0, 0, 0, 0, 0}
107.
},
108. {
109.
{0, 0, 0, 0, 0},
110.
{0,
111.
{0,
112.
{0,
113.
{0,
114.
},
115. {
116.
{0,
117.
{0,
118.
{0,
119.
{0,
120.
{0,
121.
},
122. {
123.
{0,
124.
{0,
125.
{0,
126.
{0,
127.
{0,
128.
}
129. },
130. // N
131. {
132. {
133.
{0,
134.
{0,
135.
{0,
136.
{0,
137.
{0,
138.
},
139. {
140.
{0,
141.
{0,
142.
{0,
143.
{0,
144.
{0,
145.
},
146. {
147.
{0,
148.
{0,
149.
{0,
150.
{0,
151.
{0,
152.
},
153. {
154.
{0,
155.
{0,
156.
{0,
157.
{0,
158.
{0,
159.
}
160. },
1, 0, 0,
1, 2, 1,
0, 0, 0,
0, 0, 0,
0},
0},
0},
0}
0, 0, 0,
0, 1, 1,
0, 2, 0,
0, 1, 0,
0, 0, 0,
0},
0},
0},
0},
0}
0, 0, 0,
0, 0, 0,
1, 2, 1,
0, 0, 1,
0, 0, 0,
0},
0},
0},
0},
0}
0, 0, 0,
0, 0, 1,
0, 2, 1,
0, 1, 0,
0, 0, 0,
0},
0},
0},
0},
0}
0, 0, 0,
0, 0, 0,
1, 2, 0,
0, 1, 1,
0, 0, 0,
0},
0},
0},
0},
0}
0, 0, 0,
0, 1, 0,
1, 2, 0,
1, 0, 0,
0, 0, 0,
0},
0},
0},
0},
0}
0, 0, 0,
1, 1, 0,
0, 2, 1,
0, 0, 0,
0, 0, 0,
0},
0},
0},
0},
0}
161. // N mirrored
162. {
163. {
164.
{0,
165.
{0,
166.
{0,
167.
{0,
168.
{0,
169.
},
170. {
171.
{0,
172.
{0,
173.
{0,
174.
{0,
175.
{0,
176.
},
177. {
178.
{0,
179.
{0,
180.
{0,
181.
{0,
182.
{0,
183.
},
184. {
185.
{0,
186.
{0,
187.
{0,
188.
{0,
189.
{0,
190.
}
191. },
192. // T
193. {
194. {
195.
{0,
196.
{0,
197.
{0,
198.
{0,
199.
{0,
200.
},
201. {
202.
{0,
203.
{0,
204.
{0,
205.
{0,
206.
{0,
207.
},
208. {
209.
{0,
210.
{0,
211.
{0,
212.
{0,
213.
{0,
214.
},
0, 0, 0,
0, 1, 0,
0, 2, 1,
0, 0, 1,
0, 0, 0,
0},
0},
0},
0},
0}
0, 0, 0,
0, 0, 0,
0, 2, 1,
1, 1, 0,
0, 0, 0,
0},
0},
0},
0},
0}
0, 0, 0,
1, 0, 0,
1, 2, 0,
0, 1, 0,
0, 0, 0,
0},
0},
0},
0},
0}
0, 0, 0,
0, 1, 1,
1, 2, 0,
0, 0, 0,
0, 0, 0,
0},
0},
0},
0},
0}
0, 0, 0,
0, 1, 0,
0, 2, 1,
0, 1, 0,
0, 0, 0,
0},
0},
0},
0},
0}
0, 0, 0,
0, 0, 0,
1, 2, 1,
0, 1, 0,
0, 0, 0,
0},
0},
0},
0},
0}
0, 0, 0,
0, 1, 0,
1, 2, 0,
0, 1, 0,
0, 0, 0,
0},
0},
0},
0},
0}
215. {
216.
{0,
217.
{0,
218.
{0,
219.
{0,
220.
{0,
221.
}
222. }
223. };
0, 0, 0,
0, 1, 0,
1, 2, 1,
0, 0, 0,
0, 0, 0,
0},
0},
0},
0},
0}
46. /* T */
47. {
48.
{-2,
49.
{-2,
50.
{-2,
51.
{-2,
52. },
53. };
-3},
-3},
-3},
-2}
17.
18. /*
19. ======================================
20. Returns the horizontal displacement of the piece that has to be applied in order
to create it in the
21. correct position.
22.
23. Parameters:
24.
25. >> pPiece: Piece to draw
26. >> pRotation: 1 of the 4 possible rotations
27. ======================================
28. */
29. int Pieces::GetXInitialPosition (int pPiece, int pRotation)
30. {
31.
return mPiecesInitialPosition [pPiece][pRotation][0];
32. }
33.
34. /*
35. ======================================
36. Returns the vertical displacement of the piece that has to be applied in order to
create it in the
37. correct position.
38.
39. Parameters:
40.
41. >> pPiece: Piece to draw
42. >> pRotation: 1 of the 4 possible rotations
43. ======================================
44. */
45. int Pieces::GetYInitialPosition (int pPiece, int pRotation)
46. {
47.
return mPiecesInitialPosition [pPiece][pRotation][1];
48. }
Passo 2: O tabuleiro.
Agora vamos aprender a guardar as peas no tabuleiro e verificar
colises. Esta classe armazena um array bidimensional de N x N blocos que
so inicializados para POS_FREE. As peas sero armazenadaspor preencher
estes blocos quando cair atualizando o bloco para POS_FILLED.
Este o cabealho da classe (Board.h):
1. #ifndef _BOARD_
2. #define _BOARD_
3.
4. // ------ Includes ----5.
6. #include "Pieces.h"
7.
8. // ------ Defines ----9.
10. #define BOARD_LINE_WIDTH 6
// Width of each of the two lines that delimit
the board
11. #define BLOCK_SIZE 16
// Width and Height of each block of a piece
12. #define BOARD_POSITION 320
// Center position of the board from the left
of the screen
13. #define BOARD_WIDTH 10
// Board width in blocks
14. #define BOARD_HEIGHT 20
// Board height in blocks
15. #define MIN_VERTICAL_MARGIN 20
// Minimum vertical margin for the board
limit
16. #define MIN_HORIZONTAL_MARGIN 20 // Minimum horizontal margin for the
board limit
17. #define PIECE_BLOCKS 5
// Number of horizontal and vertical blocks of a
matrix piece
18.
19. // -------------------------------------------------------------------------------20. //
Board
21. // -------------------------------------------------------------------------------22.
23. class Board
24. {
25. public:
26.
27.
Board
(Pieces *pPieces, int pScreenHeight);
28.
29.
int GetXPosInPixels
(int pPos);
30.
int GetYPosInPixels
(int pPos);
31.
bool IsFreeBlock
(int pX, int pY);
32.
bool IsPossibleMovement (int pX, int pY, int pPiece, int pRotation);
33.
void StorePiece
(int pX, int pY, int pPiece, int pRotation);
34.
void DeletePossibleLines ();
35.
bool IsGameOver
();
36.
37. private:
38.
enum { POS_FREE, POS_FILLED };
// POS_FREE = free position of the
board; POS_FILLED = filled position of the board
39.
int mBoard [BOARD_WIDTH][BOARD_HEIGHT]; // Board that contains the pieces
40.
Pieces *mPieces;
41.
int mScreenHeight;
42.
43.
void InitBoard();
44.
void DeleteLine (int pY);
45. };
46.
47. #endif // _BOARD_
Agora, vamos ver cada mtodo diferente.
Mtodo InitBoard apenas um loop aninhado que inicializa todos os blocos
do tabuleiro POS_FREE.
1. /*
2. ======================================
3. Init the board blocks with free positions
4. ======================================
5. */
6. void Board::InitBoard()
7. {
8.
for (int i = 0; i < BOARD_WIDTH; i++)
9.
for (int j = 0; j < BOARD_HEIGHT; j++)
10.
mBoard[i][j] = POS_FREE;
11. }
1. /*
2. ======================================
3. Init the board blocks with free positions
4. ======================================
5. */
6. void Board::InitBoard()
7. {
8.
for (int i = 0; i < BOARD_WIDTH; i++)
9.
for (int j = 0; j < BOARD_HEIGHT; j++)
10.
mBoard[i][j] = POS_FREE;
11. }
/*
======================================
Init the board blocks with free positions
======================================
*/
void Board::InitBoard()
{
for (int i = 0; i < BOARD_WIDTH; i++)
for (int j = 0; j < BOARD_HEIGHT; j++)
mBoard[i][j] = POS_FREE;
}
Mtodo DeleteLine o mtodo que apaga uma linha e move todos os blocos
de posies superiores uma linha para baixo. Ela s comea a partir da linha
que tem de ser removida e, em seguida, iterao atravs da placa em um
loop aninhado, move todos os blocos das linhas superiores de uma linha
feita.
1. /*
2. ======================================
3. Delete a line of the board by moving all above lines down
4.
5. Parameters:
6.
7. >> pY:
Vertical position in blocks of the line to delete
8. ======================================
9. */
10. void Board::DeleteLine (int pY)
11. {
12.
// Moves all the upper lines one row down
13.
for (int j = pY; j > 0; j--)
14.
{
15.
for (int i = 0; i < BOARD_WIDTH; i++)
16.
{
17.
mBoard[i][j] = mBoard[i][j-1];
18.
}
19.
}
20. }
DeletePossibleLines um mtodo que remove todas as linhas que devem ser
apagadas do tabuleiro. Ele funciona em primeiro lugar, verificando quais
linhas devem ser removidas (os que tm todos os seus blocos horizontais
cheia). Ento, ele usa o mtodo DeleteLine a fim de apagar essa linha e
mover todas as linhas de uma linha superior para baixo.
1. /*
2. ======================================
3. Delete all the lines that should be removed
4. ======================================
5. */
6. void Board::DeletePossibleLines ()
7. {
8.
for (int j = 0; j < BOARD_HEIGHT; j++)
9.
{
10.
int i = 0;
11.
while (i < BOARD_WIDTH)
12.
{
13.
if (mBoard[i][j] != POS_FILLED) break;
14.
i++;
15.
}
16.
17.
if (i == BOARD_WIDTH) DeleteLine (j);
18.
}
19. }
1. /*
2. ======================================
3. Check if the piece can be stored at this position without any collision
4. Returns true if the movement is possible, false if it not possible
5.
6. Parameters:
7.
8. >> pX:
Horizontal position in blocks
9. >> pY:
Vertical position in blocks
10. >> pPiece: Piece to draw
11. >> pRotation: 1 of the 4 possible rotations
12. ======================================
13. */
14. bool Board::IsPossibleMovement (int pX, int pY, int pPiece, int pRotation)
15. {
16.
// Checks collision with pieces already stored in the board or the board limits
17.
// This is just to check the 5x5 blocks of a piece with the appropriate area in
the board
18.
for (int i1 = pX, i2 = 0; i1 < pX + PIECE_BLOCKS; i1++, i2++)
19.
{
20.
for (int j1 = pY, j2 = 0; j1 < pY + PIECE_BLOCKS; j1++, j2++)
21.
{
22.
// Check if the piece is outside the limits of the board
23.
if ( i1 < 0
||
24.
i1 > BOARD_WIDTH - 1 ||
25.
j1 > BOARD_HEIGHT - 1)
26.
{
27.
if (mPieces->GetBlockType (pPiece, pRotation, j2, i2) != 0)
28.
return 0;
29.
}
30.
31.
// Check if the piece have collisioned with a block already stored in the
map
32.
if (j1 >= 0)
33.
{
34.
if ((mPieces->GetBlockType (pPiece, pRotation, j2, i2) != 0) &&
35.
(!IsFreeBlock (i1, j1)) )
36.
return false;
37.
}
38.
}
39.
}
40.
41.
// No collision
42.
return true;
43.}
Passo 3: O jogo.
Agora vamos implementar uma classe geral, chamada "Game", que
inicialize o jogo, desenha o tabuleiro e as peas, aproveitando cada bloco
como um retngulo (usando uma outra classe que veremos mais tarde
chamada "IO", que usa SDL) e cria novas peas aleatrias.
Este o cabealho, Game.h:
1. #ifndef _GAME_
2. #define _GAME_
3.
4. // ------ Includes ----5.
6. #include "Board.h"
7. #include "Pieces.h"
8. #include "IO.h"
9. #include <time.h>
10.
11. // ------ Defines ----12.
13. #define WAIT_TIME 700
// Number of milliseconds that the piece remains
before going 1 block down */
14.
15. // -------------------------------------------------------------------------------16. //
Game
17. // -------------------------------------------------------------------------------18.
19. class Game
20. {
21. public:
22.
23.
Game
(Board *pBoard, Pieces *pPieces, IO *pIO, int pScreenHeight);
24.
25.
void DrawScene ();
26.
void CreateNewPiece ();
27.
28.
int mPosX, mPosY;
// Position of the piece that is falling down
29.
int mPiece, mRotation;
// Kind and rotation the piece that is falling down
30.
31. private:
32.
33.
int mScreenHeight;
// Screen height in pixels
34.
int mNextPosX, mNextPosY;
// Position of the next piece
35.
int mNextPiece, mNextRotation; // Kind and rotation of the next piece
36.
37.
Board *mBoard;
38.
Pieces *mPieces;
39.
IO *mIO;
40.
41.
int GetRand (int pA, int pB);
42.
void InitGame();
43.
void DrawPiece (int pX, int pY, int pPiece, int pRotation);
44.
void DrawBoard ();
45. };
46. #endif // _GAME_
Como voc pode ver, a pea atual definida usando 4 variaveis:
mPosX, mPosY (a posio da pea no bloco), mPiece (o tipo de pea),
mRotation (a matriz atual que define a pea, como vimos, cada pea tem
quatro matrizes, uma para cada rotao).
Vamos ver a implementao dos mtodos.
GetRand trivial um mtodo que retorna um nmero aleatrio entre dois
limites.
1. /*
2. ======================================
3. Get a random int between to integers
4.
5. Parameters:
6. >> pA: First number
7. >> pB: Second number
8. ======================================
9. */
10. int Game::GetRand (int pA, int pB)
11. {
12.
return rand () % (pB - pA + 1) + pA;
13. }
InitGame, cuidada da inicializao do jogo, selecionando a primeira e a
prxima pea aleatoriamente.A prxima pea apresentada por isso o
jogador pode ver o que vai aparecer prxima pea. Este mtodo tambm
apresenta a posio em blocos de peas.
Usamos dois mtodos que vimos antes na classe "Pieces":
GetXInitialPosition e GetYInitialPosition em ordem para inicializar a pea na
posio correta.
1. /*
2. ======================================
3. Initial parameters of the game
4. ======================================
5. */
6. void Game::InitGame()
7. {
8.
// Init random numbers
9.
srand ((unsigned int) time(NULL));
10.
11.
// First piece
12.
mPiece
= GetRand (0, 6);
13.
mRotation
= GetRand (0, 3);
14.
mPosX
= (BOARD_WIDTH / 2) + mPieces->GetXInitialPosition (mPiece,
mRotation);
15.
mPosY
= mPieces->GetYInitialPosition (mPiece, mRotation);
16.
17.
18.
19.
20.
21.
22. }
// Next piece
mNextPiece
mNextRotation
mNextPosX
mNextPosY
19.
int mPixelsY = mBoard->GetYPosInPixels (pY);
20.
21.
// Travel the matrix of blocks of the piece and draw the blocks that are filled
22.
for (int i = 0; i < PIECE_BLOCKS; i++)
23.
{
24.
for (int j = 0; j < PIECE_BLOCKS; j++)
25.
{
26.
// Get the type of the block and draw it with the correct color
27.
switch (mPieces->GetBlockType (pPiece, pRotation, j, i))
28.
{
29.
case 1: mColor = GREEN; break; // For each block of the piece except
the pivot
30.
case 2: mColor = BLUE; break; // For the pivot
31.
}
32.
33.
if (mPieces->GetBlockType (pPiece, pRotation, j, i) != 0)
34.
mIO->DrawRectangle (mPixelsX + i * BLOCK_SIZE,
35.
mPixelsY + j * BLOCK_SIZE,
36.
(mPixelsX + i * BLOCK_SIZE) + BLOCK_SIZE - 1,
37.
(mPixelsY + j * BLOCK_SIZE) + BLOCK_SIZE - 1,
38.
mColor);
39.
}
40.
}
41. }
DrawBoard semelhante ao mtodo anterior. Ela chama duas colunas azuis,
que so utilizadas como os limites do tabuleiro. Em seguida, desenham-se os
blocos do tabuleiro que so sinalizados como POS_FILLED em um loop
aninhado.
1. /*
2. ======================================
3. Draw board
4.
5. Draw the two lines that delimit the board
6. ======================================
7. */
8. void Game::DrawBoard ()
9. {
10.
11.
// Calculate the limits of the board in pixels
12.
int mX1 = BOARD_POSITION - (BLOCK_SIZE * (BOARD_WIDTH / 2)) - 1;
13.
int mX2 = BOARD_POSITION + (BLOCK_SIZE * (BOARD_WIDTH / 2));
14.
int mY = mScreenHeight - (BLOCK_SIZE * BOARD_HEIGHT);
15.
16.
// Check that the vertical margin is not to small
17.
//assert (mY > MIN_VERTICAL_MARGIN);
18.
19.
// Rectangles that delimits the board
20.
mIO->DrawRectangle (mX1 - BOARD_LINE_WIDTH, mY, mX1, mScreenHeight 1, BLUE);
21.
22.
mIO->DrawRectangle (mX2, mY, mX2 + BOARD_LINE_WIDTH, mScreenHeight 1, BLUE);
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42. }
37.
void UpdateScreen
38.
39. };
40. #endif // _IO_
();
libsdl1.2-dev em Linux.
Se voc gostou deste tutorial, por favor, ponha um backlink de seu blog e v
ao ttulo e clique no Digg, Reddit e Dzone. Isso me d energia, a fim de criar
mais tutoriais.
Bnus
No se esquea de brincar com o "define".Exemplo maluco:
1. #define BLOCK_SIZE 5
2. #define BOARD_WIDTH 90
3. #define BOARD_HEIGHT 90