tetris tutorial para iniciantes

31
Tutorial tetris em C++ com plataforma independente, focada na lógica do jogo para iniciantes. Autor Original: Javier López López em GameTuto Traduzido por: Samuel Leonardo (Sam L.) Por Favor Acesse o Original Em Inglês: http://gametuto.com/tetris-tutorial-in-c-render-independent/ Nós iremos aprender como criar um clone do Tetris do inicio usando simples e limpo C++. E isto levará menos de uma hora! Este é o perfeito tutorial para iniciantes. Só se divertir e deixar um comentário, se quiser explicar-me a algo melhor. Sei que meu Inglês é uma porcaria, por isso, se você observar alguns erros, por favor, me diga. Vamos lá! Baixar codigo fonte Aqui está ele o codigo fonte completo. Plataforma Windows. O codigo fonte vem com os cabeçalhos da SDL e bibliotecas, pronto para compilar no Visual C++ Express Edition 2008. Na pasta “Realease” lá existe um arquivo executável, apenas no caso de você querer experimentá-lo diretamente.

Upload: leaors

Post on 31-Dec-2015

48 views

Category:

Documents


3 download

TRANSCRIPT

Page 1: Tetris Tutorial Para Iniciantes

Tutorial tetris em C++ com plataforma independente, focada na lógica do jogo para iniciantes.Autor Original:

Javier López López em GameTuto

Traduzido por:Samuel Leonardo (Sam L.)

Por Favor Acesse o Original Em Inglês:http://gametuto.com/tetris-tutorial-in-c-render-independent/

Nós iremos aprender como criar um clone do Tetris do inicio usando simples e limpo C++. E isto levará menos de uma hora! Este é o perfeito tutorial para iniciantes. Só se divertir e deixar um comentário, se quiser explicar-me a algo melhor. Sei que meu Inglês é uma porcaria, por isso, se você observar alguns erros, por favor, me diga. Vamos lá!

Baixar codigo fonteAqui está ele o codigo fonte completo.

Plataforma Windows.

O codigo fonte vem com os cabeçalhos da SDL e bibliotecas, pronto para compilar no Visual C++ Express Edition 2008.Na pasta “Realease” lá existe um arquivo executável, apenas no caso de você querer experimentá-lo diretamente.

Page 2: Tetris Tutorial Para Iniciantes

Outras plataformas(para usuario avançados).

Obrigado ao Imelior e à Javier Santana, por existir uma versão Linux deste tutorial.O código fonte é independete de plataforma e vem com um “makefile”.Contudo, sob Linux, você precisará da libsdl-gfx1.2-dev e libsdl1.2-dev, se você está usando Ubuntu você pode obtê-la por este meio:$ sudo apt-get install libsdl1.2-dev libsdlgfx1.2-dev

Teclas:• ESC = Terminar o jogo.• z = Rotacionar a peça. • x = deixar cair a peça• seta Esquerda, Direita e Baixo = Eu não ofenderei sua inteligência.

Page 3: Tetris Tutorial Para Iniciantes

Passo 0: Introdução

Nós vamos focalizar na lógica do jogo, utilizando apenas rectângulo primitivas (SDL_Rect), para a renderização.Toda a lógica do jogo é isolada do desenho, então 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 lógica do jogo. Esta é a forma como o seu protótipo ficará depois de terminar o tutorial:

Neste tutorial você vai aprender:

• Como conservar as peças e tabuleiro utilizando matrizes (arrays multidimensionais).

• Como resolver o problema de rotação no Tetris, em uma maneira muito fácil, sem usar a matemática complexa ou nada difícil, basta usar um truque inteligente.

• Como verificar colisões entre as peças e o tabuleiro.• Como o loop principal de um jogo Tetris trabalha(ou funciona).

O que suponho que você já conhece:

● C++● Um pouco de programação gráfica, se quiser ampliar o tutorial com

gráficos melhorados.Não se preocupe com o que se vê, só queira saber a lógica do jogo Tetris.

Page 4: Tetris Tutorial Para Iniciantes

O que você precisa?

• Um compilador ou uma IDE de programação. Eu usei Visual C++ Express Edition para este tutorial, que é uma IDE livre. Mas você pode usar uma de sua escolha é claro.

• Vontade de aprender :D

Qual é a licença do código fonte?

O código fonte está sob a “Creatives Commons – Attribution 3.0 Unported”. Isso significa que você pode copiar, distribuir e transmitir o trabalho e modificá-lo. Mas você deve atribuir o trabalho (mas não de qualquer forma que sugira que endossa você ou o seu uso do trabalho). A forma de atribuição é até você. Você pode apenas referir-me (Javier López). Um backlink seria igualmente apreciado.

Page 5: Tetris Tutorial Para Iniciantes

Passo 1: As peças.Primeiro, vamos criar uma classe para armazenar todas as peças. Há 7

tipos diferentes de peças: quadrado, I, L, L-espelhado, N, N-espelhados e T. Mas, como podemos definir cada peça? Apenas verifique a figura:

Como você pode ver, esta peça é definida em uma matriz de 5 × 5 células. 0 significa "nenhum bloco", 1 significa "normal bloco" e 2 significa "centro bloco". O centro bloco é o ponto de rotação: sim, o original Tetris jogo tem um ponto de rotação para cada peça:)

E como podemos armazená-la usando C++? Fácil: utilizando uma matriz bidimensional de 5 × 5 Ints (ou bytes, se você é um fanático por otimização). A peça é armazenado como o anterior, que:

1. {0, 0, 0, 0, 0}, 2. {0, 0, 0, 1, 0}, 3. {0, 0, 2, 1, 0}, 4. {0, 0, 1, 0, 0}, 5. {0, 0, 0, 0, 0}

Agora que temos a forma de armazenar cada peça vamos pensar sobre rotações. Podemos resolver o problema da rotação em um lote de diferentes maneiras. Em outros tutoriais, vi que eles utilizam uma complexa álgebra de rotação, a fim de girar a peça... mas nós podemos resolver este problema facilmente. Se podemos armazenar cada peça...por que não guardamos cada peça rotacionada também? Existem quatro possíveis rotações para cada peça:

Tetris Tutorial C++ - Peças e suas rotações

Como você pode ver, a peça mais longa tem apenas 4 blocos de largura. Mas estamos usando 5 blocos de matrizes, a fim de ser capaz de armazenar todas as rotações respeitando o centro do bloco.Em uma versão anterior deste tutorial, eu estava usando o 4 blocos de matrizes, mas, depois, foi necessário armazenar traduções do centro para a origem. Desta forma, estamos utilizando alguns bytes a mais, mas o código fonte está limpo. No total, só utilizaremos 448 bytes para armazenar todas as peças. Isso não é nada. :)

Então, a fim de armazenar toda esta informação, precisamos de um array de 4 dimensões (uau!), A fim de conservar as 4 possíveis rotações (matrizes de 5 × 5) de cada peça:

Page 6: Tetris Tutorial Para Iniciantes

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},

Page 7: Tetris Tutorial Para Iniciantes

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},

Page 8: Tetris Tutorial Para Iniciantes

110. {0, 1, 0, 0, 0}, 111. {0, 1, 2, 1, 0}, 112. {0, 0, 0, 0, 0}, 113. {0, 0, 0, 0, 0} 114. }, 115. { 116. {0, 0, 0, 0, 0}, 117. {0, 0, 1, 1, 0}, 118. {0, 0, 2, 0, 0}, 119. {0, 0, 1, 0, 0}, 120. {0, 0, 0, 0, 0} 121. }, 122. { 123. {0, 0, 0, 0, 0}, 124. {0, 0, 0, 0, 0}, 125. {0, 1, 2, 1, 0}, 126. {0, 0, 0, 1, 0}, 127. {0, 0, 0, 0, 0} 128. } 129. }, 130. // N 131. { 132. { 133. {0, 0, 0, 0, 0}, 134. {0, 0, 0, 1, 0}, 135. {0, 0, 2, 1, 0}, 136. {0, 0, 1, 0, 0}, 137. {0, 0, 0, 0, 0} 138. }, 139. { 140. {0, 0, 0, 0, 0}, 141. {0, 0, 0, 0, 0}, 142. {0, 1, 2, 0, 0}, 143. {0, 0, 1, 1, 0}, 144. {0, 0, 0, 0, 0} 145. }, 146. { 147. {0, 0, 0, 0, 0}, 148. {0, 0, 1, 0, 0}, 149. {0, 1, 2, 0, 0}, 150. {0, 1, 0, 0, 0}, 151. {0, 0, 0, 0, 0} 152. }, 153. { 154. {0, 0, 0, 0, 0}, 155. {0, 1, 1, 0, 0}, 156. {0, 0, 2, 1, 0}, 157. {0, 0, 0, 0, 0}, 158. {0, 0, 0, 0, 0} 159. } 160. },

161. // N mirrored 162. {

Page 9: Tetris Tutorial Para Iniciantes

163. { 164. {0, 0, 0, 0, 0}, 165. {0, 0, 1, 0, 0}, 166. {0, 0, 2, 1, 0}, 167. {0, 0, 0, 1, 0}, 168. {0, 0, 0, 0, 0} 169. }, 170. { 171. {0, 0, 0, 0, 0}, 172. {0, 0, 0, 0, 0}, 173. {0, 0, 2, 1, 0}, 174. {0, 1, 1, 0, 0}, 175. {0, 0, 0, 0, 0} 176. }, 177. { 178. {0, 0, 0, 0, 0}, 179. {0, 1, 0, 0, 0}, 180. {0, 1, 2, 0, 0}, 181. {0, 0, 1, 0, 0}, 182. {0, 0, 0, 0, 0} 183. }, 184. { 185. {0, 0, 0, 0, 0}, 186. {0, 0, 1, 1, 0}, 187. {0, 1, 2, 0, 0}, 188. {0, 0, 0, 0, 0}, 189. {0, 0, 0, 0, 0} 190. } 191. }, 192. // T 193. { 194. { 195. {0, 0, 0, 0, 0}, 196. {0, 0, 1, 0, 0}, 197. {0, 0, 2, 1, 0}, 198. {0, 0, 1, 0, 0}, 199. {0, 0, 0, 0, 0} 200. }, 201. { 202. {0, 0, 0, 0, 0}, 203. {0, 0, 0, 0, 0}, 204. {0, 1, 2, 1, 0}, 205. {0, 0, 1, 0, 0}, 206. {0, 0, 0, 0, 0} 207. }, 208. { 209. {0, 0, 0, 0, 0}, 210. {0, 0, 1, 0, 0}, 211. {0, 1, 2, 0, 0}, 212. {0, 0, 1, 0, 0}, 213. {0, 0, 0, 0, 0} 214. },

Page 10: Tetris Tutorial Para Iniciantes

215. { 216. {0, 0, 0, 0, 0}, 217. {0, 0, 1, 0, 0}, 218. {0, 1, 2, 1, 0}, 219. {0, 0, 0, 0, 0}, 220. {0, 0, 0, 0, 0} 221. } 222. } 223. };

Ótimo! Agora, para rodar uma peça, só temos de escolher as seguintes peças rotacionadas armazenadas.

Há algo importante que temos de ter em conta. Cada peça diferente deve ser corretamente posicionada cada vez que é criada no topo do ecrã(tela do seu jogo). Em outras palavras, ele precisa ser traduzido para a posição correta (a fim de mostrar apenas uma linha de blocos no tabuleiro e ser centrado, blocos superior devem estar fora do tabuleiro).

Como cada peça é diferente (algumas são mais baixas ou menor do que outras na matriz), cada uma tem uma tradução diferente cada vez que ela é criada. Vamos guardar essas traduções em outra matriz, uma tradução por peça rotacionada. Tome o seu tempo para entender isso.

Tetris Tutorial C++ - Tetris peças em boas posições e errado

Page 11: Tetris Tutorial Para Iniciantes

As traduções são dois números (traduções horizontais, traduções verticais) que temos de armazenar, para cada peça. Nós iremos usar esses números mais tarde, na classe “Jogo” ao criar as peças de cada vez que um elemento novo aparece, então ele será inicializado na posição correta. Esta é a matriz que armazena estes deslocamentos:

1. // Displacement of the piece to the position where it is first drawn in the board when it is created 2. int mPiecesInitialPosition [7 /*kind */ ][4 /* r2otation */ ][2 /* position */] = 3. { 4. /* Square */ 5. { 6. {-2, -3}, 7. {-2, -3}, 8. {-2, -3}, 9. {-2, -3} 10. }, 11. /* I */ 12. { 13. {-2, -2}, 14. {-2, -3}, 15. {-2, -2}, 16. {-2, -3} 17. }, 18. /* L */ 19. { 20. {-2, -3}, 21. {-2, -3}, 22. {-2, -3}, 23. {-2, -2} 24. }, 25. /* L mirrored */ 26. { 27. {-2, -3}, 28. {-2, -2}, 29. {-2, -3}, 30. {-2, -3} 31. }, 32. /* N */ 33. { 34. {-2, -3}, 35. {-2, -3}, 36. {-2, -3}, 37. {-2, -2} 38. }, 39. /* N mirrored */ 40. { 41. {-2, -3}, 42. {-2, -3}, 43. {-2, -3}, 44. {-2, -2} 45. },

Page 12: Tetris Tutorial Para Iniciantes

46. /* T */ 47. { 48. {-2, -3}, 49. {-2, -3}, 50. {-2, -3}, 51. {-2, -2} 52. }, 53. };

E temos como resolvido uma das mais complicadas partes deste tutorial.

Agora podemos criar a nossa classe Peças, esse arquivo é chamado "Pieces.h"://Pecas.h

1. #ifndef _PIECES_ 2. #define _PIECES_ 3. 4. // -------------------------------------------------------------------------------- 5. // Pieces 6. // -------------------------------------------------------------------------------- 7. 8. class Pieces 9. { 10. public: 11. 12. int GetBlockType (int pPiece, int pRotation, int pX, int pY); 13. int GetXInitialPosition (int pPiece, int pRotation); 14. int GetYInitialPosition (int pPiece, int pRotation); 15. }; 16. 17. #endif // _PIECES_

Os 3 métodos que você pode ver no cabeçalho retorna algumas informações que precisaremos mais tarde. A aplicação deles é trivial:

1. /* 2. ====================================== 3. Return the type of a block (0 = no-block, 1 = normal block, 2 = pivot block) 4. 5. Parameters: 6. 7. >> pPiece: Piece to draw 8. >> pRotation: 1 of the 4 possible rotations 9. >> pX: Horizontal position in blocks 10. >> pY: Vertical position in blocks 11. ====================================== 12. */ 13. int Pieces::GetBlockType (int pPiece, int pRotation, int pX, int pY) 14. { 15. return mPieces [pPiece][pRotation][pX][pY]; 16. }

Page 13: Tetris Tutorial Para Iniciantes

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. }

Page 14: Tetris Tutorial Para Iniciantes

Passo 2: O tabuleiro.Agora vamos aprender a guardar as peças no tabuleiro e verificar

colisões. Esta classe armazena um array bidimensional de N x N blocos que são inicializados para POS_FREE. As peças serão armazenadas por preencher estes blocos quando cair atualizando o bloco para POS_FILLED.

Este é o cabeçalho 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

Page 15: Tetris Tutorial Para Iniciantes

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 método diferente. Método 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;}

Page 16: Tetris Tutorial Para Iniciantes

Método StorePiece, armazena apenas uma peça no tabuleiro, enchendo os blocos como POS_FILLED. Existe um loop aninhado que itera através da peça matriz e armazenar os blocos do tabuleiro.

1. /* 2. 3. ====================================== 4. Store a piece in the board by filling the blocks 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. void Board::StorePiece (int pX, int pY, int pPiece, int pRotation) 15. { 16. // Store each block of the piece into the board 17. for (int i1 = pX, i2 = 0; i1 < pX + PIECE_BLOCKS; i1++, i2++) 18. { 19. for (int j1 = pY, j2 = 0; j1 < pY + PIECE_BLOCKS; j1++, j2++) 20. { 21. // Store only the blocks of the piece that are not holes 22. if (mPieces->GetBlockType (pPiece, pRotation, j2, i2) != 0) 23. mBoard[i1][j1] = POS_FILLED; 24. } 25. } 26. }

Método IsGameOver verifica se existem blocos na primeira fila. Isso significa que o jogo acaba.

1. /* 2. ====================================== 3. Check if the game is over becase a piece have achived the upper position 4. 5. Returns true or false 6. ====================================== 7. */ 8. bool Board::IsGameOver() 9. { 10. //If the first line has blocks, then, game over 11. for (int i = 0; i < BOARD_WIDTH; i++) 12. { 13. if (mBoard[i][0] == POS_FILLED) return true; 14. } 15. 16. return false; 17. }

Page 17: Tetris Tutorial Para Iniciantes

Método DeleteLine é o método que apaga uma linha e move todos os blocos de posições superiores uma linha para baixo. Ela só começa a partir da linha que tem de ser removida e, em seguida, iteração através 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 método que remove todas as linhas que devem ser apagadas do tabuleiro. Ele funciona em primeiro lugar, verificando quais linhas devem ser removidas (os que têm todos os seus blocos horizontais cheia). Então, ele usa o método 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. }

Page 18: Tetris Tutorial Para Iniciantes

IsFreeBlock é um método trivial que verifica se um bloco do tabuleiro é preenchido ou não.

1. /* 2. ====================================== 3. Returns 1 (true) if the this block of the board is empty, 0 if it is filled 4. 5. Parameters: 6. 7. >> pX: Horizontal position in blocks 8. >> pY: Vertical position in blocks 9. ====================================== 10. */ 11. bool Board::IsFreeBlock (int pX, int pY) 12. { 13. if (mBoard [pX][pY] == POS_FREE) return true; else return false; 14. }

Até agora, temos sempre a falado de "blocos". Mas, a fim de chamar-lhes a tela, precisamos determinar a posição em pixels. Então, temos dois métodos (GetXPosInPixels e GetYPosInPixels), a fim de obter a posição horizontal e vertical em pixels de um determinado bloco.

1. /* 2. ====================================== 3. Returns the horizontal position (in pixels) of the block given like parameter 4. 5. Parameters: 6. 7. >> pPos: Horizontal position of the block in the board 8. ====================================== 9. */ 10. int Board::GetXPosInPixels (int pPos) 11. { 12. return ( ( BOARD_POSITION - (BLOCK_SIZE * (BOARD_WIDTH / 2)) ) + (pPos * BLOCK_SIZE) ); 13. } 14. 15. /* 16. ====================================== 17. Returns the vertical position (in pixels) of the block given like parameter 18. 19. Parameters: 20. 21. >> pPos: Horizontal position of the block in the board 22. ====================================== 23. */ 24. int Board::GetYPosInPixels (int pPos) 25. { 26. return ( (mScreenHeight - (BLOCK_SIZE * BOARD_HEIGHT)) + (pPos * BLOCK_SIZE) ); 27. }

Page 19: Tetris Tutorial Para Iniciantes

IsPossibleMovement é o último e mais complexo método da classe tabuleiro. Este método será utilizado mais tarde no loop principal para verificar se o movimento de uma peça é possível ou não. O método compara todos os blocos de uma peça com os blocos já armazenados no tabuleiro e com o limite do tabuleiro. Esta comparação é feita por meio de iteração da peça matriz e comparando com os 5 × 5 espaço no tabuleiro. Se houver uma colisão que significa que o movimento não é possível, então ele retorna false. Se não houver colisão, o movimento é possível, e ela retorna true.

Tetris Tutorial C++ - Colisões com os blocos armazenados e os limites do tabuleiro

Page 20: Tetris Tutorial Para Iniciantes

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.}

Page 21: Tetris Tutorial Para Iniciantes

Passo 3: O jogo.Agora vamos implementar uma classe geral, chamada "Game", que

inicialize o jogo, desenha o tabuleiro e as peças, aproveitando cada bloco como um retângulo (usando uma outra classe que veremos mais tarde chamada "IO", que usa SDL) e cria novas peças aleatórias.

Este é o cabeçalho, “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);

Page 22: Tetris Tutorial Para Iniciantes

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 peça atual é definida usando 4 variaveis: mPosX, mPosY (a posição da peça no bloco), mPiece (o tipo de peça),mRotation (a matriz atual que define a peça, como vimos, cada peça tem quatro matrizes, uma para cada rotação).

Vamos ver a implementação dos métodos.GetRand é trivial um método que retorna um número aleatório 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 inicialização do jogo, selecionando a primeira e a próxima peça aleatoriamente.A próxima peça é apresentada por isso o jogador pode ver o que vai aparecer próxima peça. Este método também apresenta a posição em blocos de peças.Usamos dois métodos que vimos antes na classe "Pieces": GetXInitialPosition e GetYInitialPosition em ordem para inicializar a peça na posição 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.

Page 23: Tetris Tutorial Para Iniciantes

17. // Next piece 18. mNextPiece = GetRand (0, 6); 19. mNextRotation = GetRand (0, 3); 20. mNextPosX = BOARD_WIDTH + 5; 21. mNextPosY = 5; 22. }

CreateNewPiece método que define a "próxima peça" como a atual e um redefine a sua posição, em seguida, escolhe uma nova "próxima peça".

1. /* 2. ====================================== 3. Create a random piece 4. ====================================== 5. */ 6. void Game::CreateNewPiece() 7. { 8. // The new piece 9. mPiece = mNextPiece; 10. mRotation = mNextRotation; 11. mPosX = (BOARD_WIDTH / 2) + mPieces->GetXInitialPosition (mPiece, mRotation); 12. mPosY = mPieces->GetYInitialPosition (mPiece, mRotation); 13. 14. // Random next piece 15. mNextPiece = GetRand (0, 6); 16. mNextRotation = GetRand (0, 3); 17. }

DrawPiece é realmente um método fácil que itera por meio da peça matriz e chama cada bloco da peça. Ele usa a cor verde para os blocos normais e azul para o centro bloco. Para desenhar rectângulos é chamada DrawRectangle, método da classe "IO", que veremos mais tarde.

1. /* 2. ====================================== 3. Draw piece 4. 5. Parameters: 6. 7. >> pX: Horizontal position in blocks 8. >> pY: Vertical position in blocks 9. >> pPiece: Piece to draw 10. >> pRotation: 1 of the 4 possible rotations 11. ====================================== 12. */ 13. void Game::DrawPiece (int pX, int pY, int pPiece, int pRotation) 14. { 15. color mColor; // Color of the block 16. 17. // Obtain the position in pixel in the screen of the block we want to draw 18. int mPixelsX = mBoard->GetXPosInPixels (pX);

Page 24: Tetris Tutorial Para Iniciantes

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 método anterior. Ela chama duas colunas azuis, que são utilizadas como os limites do tabuleiro. Em seguida, desenham-se os blocos do tabuleiro que são 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);

Page 25: Tetris Tutorial Para Iniciantes

23. 24. // Check that the horizontal margin is not to small 25. //assert (mX1 > MIN_HORIZONTAL_MARGIN); 26. 27. // Drawing the blocks that are already stored in the board 28. mX1 += 1; 29. for (int i = 0; i < BOARD_WIDTH; i++) 30. { 31. for (int j = 0; j < BOARD_HEIGHT; j++) 32. { 33. // Check if the block is filled, if so, draw it 34. if (!mBoard->IsFreeBlock(i, j)) 35. mIO->DrawRectangle ( mX1 + i * BLOCK_SIZE, 36. mY + j * BLOCK_SIZE, 37. (mX1 + i * BLOCK_SIZE) + BLOCK_SIZE - 1, 38. (mY + j * BLOCK_SIZE) + BLOCK_SIZE - 1, 39. RED); 40. } 41. } 42. }

DrawScene, apenas solicita os métodos anteriores, em ordem para desenhar tudo.

1. /* 2. ====================================== 3. Draw scene 4. 5. Draw all the objects of the scene 6. ====================================== 7. */ 8. void Game::DrawScene () 9. { 10. DrawBoard (); // Draw the delimitation lines and blocks stored in the board 11. DrawPiece (mPosX, mPosY, mPiece, mRotation); // Draw the playing piece 12. DrawPiece (mNextPosX, mNextPosY, mNextPiece, mNextRotation); // Draw the next piece 13. }

Page 26: Tetris Tutorial Para Iniciantes

Passo 4: desenhando fácil, gerenciamento de janela e entradas do teclado usando SDL, isolado da lógica do jogo.

"IO.cpp" e "IO.h" são os arquivos que implementam a classe "IO". Ele usa SDL, a fim de criar a janela, é claro, atualizar a tela e ter cuidado com as entradas do teclado.

Você pode verificar que "IO.cpp" e "IO.h" são arquivos a fim de ver a sua execução. Eu não vou para explicar os métodos que estão relacionados com SDL. Você pode alterar esta classe, a fim de usar um renderizador diferentes (como IndieLib, Allegro, OpenGL, Direct3D, etc.)

Este é o cabeçalho “IO.h”:

1. #ifndef _IO_ 2. #define _IO_ 4. // ------ Includes ----- 5. 6. #ifndef LINUX 7. #include "SDL/include/SDL.h" 8. #include "SDL/SDL_GfxPrimitives/SDL_gfxPrimitives.h" 9. #else 10. #include <SDL/SDL.h> 11. #include "SDL/SDL_GfxPrimitives/sdl_gfxprimitives.h" 12. #endif 13. #pragma comment (lib, "SDL/lib/SDL.lib") 14. #pragma comment (lib, "SDL/SDL_GfxPrimitives/SDL_GfxPrimitives_Static.lib") 15. 16. // ------ Enums ----- 17. 18. enum color {BLACK, RED, GREEN, BLUE, CYAN, MAGENTA, YELLOW, WHITE, COLOR_MAX}; // Colors 19. 20. // -------------------------------------------------------------------------------- 21. // IO 22. // -------------------------------------------------------------------------------- 23. 24. class IO 25. { 26. public: 27. 28. IO (); 29. 30. void DrawRectangle (int pX1, int pY1, int pX2, int pY2, enum color pC); 31. void ClearScreen (); 32. int GetScreenHeight (); 33. int InitGraph (); 34. int Pollkey (); 35. int Getkey (); 36. int IsKeyDown (int pKey);

Page 27: Tetris Tutorial Para Iniciantes

37. void UpdateScreen (); 38. 39. }; 40. #endif // _IO_

Passo 5: O loop Principal

O loop principal é bastante simples. Em cada frame nós desenhamos tudo. Depois, nós usamos as entradas do teclado a fim de mover a peça. Antes de cada movimento, primeiro verifique se o movimento é possível. Nós também medimos o tempo, a fim de mover a peça para baixo a cada n milissegundos. Quando a peça descer um bloco, verificamos se esse movimento é possível, se não, guarde a peça no tabuleiro. Também verifique se há blocos na linha superior, em caso afirmativo, o jogo acaba.

Vamos ver o “Main.cpp” passo-a-passo:

Primeiro nós inicializamos todas as classes. Então, nós obtemos o atual milissegundos, que serão utilizados para determinar quando a peça deve mover para baixo.

1. #include "Game.h" 2. #ifndef LINUX 3. #include <windows.h> 4. #endif 5. 6. /* 7. ================== 8. Main 9. ================== 10. */ 11. int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 12. { 13. // ----- Vars ----- 14. 15. // Class for drawing staff, it uses SDL for the rendering. Change the methods of this class 16. // in order to use a different renderer 17. IO mIO; 18. int mScreenHeight = mIO.GetScreenHeight(); 19. 20. // Pieces 21. Pieces mPieces; 22. 23. // Board 24. Board mBoard (&mPieces, mScreenHeight); 25. 26. // Game 27. Game mGame (&mBoard, &mPieces, &mIO, mScreenHeight); 28. 29. // Get the actual clock milliseconds (SDL) 30. unsigned long mTime1 = SDL_GetTicks();

Page 28: Tetris Tutorial Para Iniciantes

Este é o loop principal. Nós podemos sair apertando ESC. Em cada frame (loop) nós limpamos e atualizamos a tela e desenhamos tudo de novo.

1. // ----- Main Loop ----- 2. 3. while (!mIO.IsKeyDown (SDLK_ESCAPE)) 4. { 5. // ----- Draw ----- 6. 7. mIO.ClearScreen (); // Clear screen 8. mGame.DrawScene (); // Draw staff 9. mIO.UpdateScreen (); // Put the graphic context in the screen

Nós começamos com a entrada do teclado. Se nós pressionarmos seta para esquerda, seta para baixo ou seta para direita, nós movemos a peça naquela direção. Nós só moveremos a peça se o movimento for possivel.

1. // ----- Input ----- 2. 3. int mKey = mIO.Pollkey(); 4. 5. switch (mKey) 6. { 7. case (SDLK_RIGHT): 8. { 9. if (mBoard.IsPossibleMovement (mGame.mPosX + 1, mGame.mPosY, mGame.mPiece, mGame.mRotation)) 10. mGame.mPosX++; 11. break; 12. } 13. 14. case (SDLK_LEFT): 15. { 16. if (mBoard.IsPossibleMovement (mGame.mPosX - 1, mGame.mPosY, mGame.mPiece, mGame.mRotation)) 17. mGame.mPosX--; 18. break; 19. } 20. 21. case (SDLK_DOWN): 22. { 23. if (mBoard.IsPossibleMovement (mGame.mPosX, mGame.mPosY + 1, mGame.mPiece, mGame.mRotation)) 24. mGame.mPosY++; 25. break; 26. }

Page 29: Tetris Tutorial Para Iniciantes

Pressionando "x", a peça vai cair diretamente no chão. Isto é realmente fácil de implementar, tentar mover a peça para baixo até que o movimento não seja possível. Então armazene a peça, delete possíveis linhas e verifique se o jogo acabou, se não, nós criamos uma nova peça.

1. case (SDLK_x): 2. { 3. // Check collision from up to down 4. while (mBoard.IsPossibleMovement(mGame.mPosX, mGame.mPosY, mGame.mPiece, mGame.mRotation)) { mGame.mPosY++; } 5. 6. mBoard.StorePiece (mGame.mPosX, mGame.mPosY - 1, mGame.mPiece, mGame.mRotation); 7. 8. mBoard.DeletePossibleLines (); 9. 10. if (mBoard.IsGameOver()) 11. { 12. mIO.Getkey(); 13. exit(0); 14. } 15. 16. mGame.CreateNewPiece(); 17. 18. break; 19. }

Pressionando "z" rotacionamos a peça. Com os métodos que já implementamos, essa é uma tarefa fácil. A rotação é de fato uma mudança para a próxima peça armazenada. Nós primeiro, devemos verificar se a peça rodada será desenhada sem colidir, em caso afirmativo, definimos esta rotação como a atual.

1. case (SDLK_z): 2. { 3. if (mBoard.IsPossibleMovement (mGame.mPosX, mGame.mPosY, mGame.mPiece, (mGame.mRotation + 1) % 4)) 4. mGame.mRotation = (mGame.mRotation + 1) % 4; 5. 6. break; 7. } 8. }

Page 30: Tetris Tutorial Para Iniciantes

Se WAIT_TIME for passado, a peça deve cair um bloco. Temos de verificar se o movimento é possível, se não, a peça deve ser armazenada e temos de verificar se podemos excluir linhas. Nós também devemos ver se o jogo acabou, se não, nós criamos uma nova peça.

1. // ----- Vertical movement ----- 2. 3. unsigned long mTime2 = SDL_GetTicks(); 4. 5. if ((mTime2 - mTime1) > WAIT_TIME) 6. { 7. if (mBoard.IsPossibleMovement (mGame.mPosX, mGame.mPosY + 1, mGame.mPiece, mGame.mRotation)) 8. { 9. mGame.mPosY++; 10. } 11. else 12. { 13. mBoard.StorePiece (mGame.mPosX, mGame.mPosY, mGame.mPiece, mGame.mRotation); 14. 15. mBoard.DeletePossibleLines (); 16. 17. if (mBoard.IsGameOver()) 18. { 19. mIO.Getkey(); 20. exit(0); 21. } 22. 23. mGame.CreateNewPiece(); 24. } 25. 26. mTime1 = SDL_GetTicks(); 27. } 28. } 29. 30. return 0;

E isso é tudo! Por favor, deixe um comentário, aqui:http://gametuto.com/tetris-tutorial-in-c-render-independent/se você observar alguns erros, erros linguísticos ou se você tiver quaisquer dúvidas ... ou simplesmente para dizer muito obrigado! :)

Créditos:• Javier López López (criador do tutorial) * Um agradecimento especial: Imelior, quem corrigu erros de inglês e compilou o tutorial em Linux. * Um agradecimento especial: Javier Santana, que acrescentou setenças #ifndef ponteiros que era necessário utilizar libsdlgfx1.2-dev e

Page 31: Tetris Tutorial Para Iniciantes

libsdl1.2-dev em Linux.

Se você gostou deste tutorial, por favor, ponha um backlink de seu blog e vá ao título e clique no Digg, Reddit e Dzone. Isso me dá energia, a fim de criar mais tutoriais.

Bônus Não se esqueça de brincar com o "define".Exemplo maluco:

1. #define BLOCK_SIZE 5 // Width and Height of each block of a 2. #define BOARD_WIDTH 90 // Board width in blocks 3. #define BOARD_HEIGHT 90 // Board height in blocks

Esta postagem foi escrita por Javier López, publicada em 14 de dezembro de 2008 às 5:33, arquivada sob Uncategorized. Bookmark o permalink. Siga quaisquer comentários aqui com o feed RSS para este post. Postar um comentário ou deixar um trackback: Trackback URL.