' ============================================================================== ' ' Jogo: Tetris ' Versão: 1.0.0 ' Autor: Fábio Moura de Oliveira ' Data: 26/01/2018 ' ' Teclas usadas no jogo: ' ' * Seta pra esquerda: Move peça pra esquerda, se possível. ' * Seta pra direita: Move peça pra direita, se possível. ' * Seta pra cima: Rotaciona peça 90 graus. ' * Seta pra baixo: Manter tecla pressionada, desloca a peça pra baixo ' mais rápido, cessando o pressionamento peça desloca ' pra baixo na velocidade normal. ' ' =============================================================================== Namespace myapp #Import "" #Import "" Using std.. Using mojo.. Class Tetris Extends Window ' Aqui, largura e altura da célula terão o mesmo comprimento. Const CELULA_LARGURA: Int = 25 Const CELULA_ALTURA : Int = CELULA_LARGURA ' Comprimento da borda que circunda a célula Const BORDA_TAMANHO: Int = 2 ' Aqui, indica a largura/altura que uma célula ocupa. Const CELULA_LARGURA_COM_BORDA: Int = CELULA_LARGURA + BORDA_TAMANHO Const CELULA_ALTURA_COM_BORDA: Int = CELULA_ALTURA + BORDA_TAMANHO ' Indica a quantidade de células por linhas e por coluna Const CELULAS_POR_LINHA : Int = 20 Const CELULAS_POR_COLUNA: Int = 10 ' Indica as coordenadas x e y, de onde o tabuleiro comecará a ser renderizado. Const TABULEIRO_LEFT_X: Int = CELULA_LARGURA_COM_BORDA Const TABULEIRO_TOP_Y: Int = CELULA_ALTURA_COM_BORDA Const TABULEIRO_RIGHT_X: Int = TABULEIRO_LEFT_X + CELULA_LARGURA_COM_BORDA * CELULAS_POR_COLUNA Const TABULEIRO_BOTTOM_Y: Int = TABULEIRO_TOP_Y + CELULA_ALTURA_COM_BORDA * CELULAS_POR_LINHA ' Cores usadas no jogo Const TABULEIRO_COR_DE_FUNDO: Color = New Color(128, 128, 128) ' Cor da célula Const CELULA_COR_DE_FUNDO: Color = Color.Red Const CELULA_COR: Color = Color.White ' Arranjo bidimensional, onde cada célula armazena o valor 1 ou um 1 valor diferente ' de 0 que indica que a célula está preenchida. Field tabuleiro: Int[,] ' Indica o último indice da linha e coluna do tabuleiro Field tabuleiro_linha_ultimo_indice: Int Field tabuleiro_coluna_ultimo_indice: Int ' Arranjo que armazena todas as peças do jogo. Field pecas: Int[][,] ' Armazena em um arranjo multidimensional a peça atual Field peca_atual: Int[,] ' Indica o último índice da linha e coluna do arranjo peca_atual. Field peca_atual_coluna_ultimo_indice: Int Field peca_atual_linha_ultimo_indice: Int ' ' Indica a posição da peça atual, relativo ao tabuleiro ' Field peca_left_x: Int = 0 Field peca_right_x: Int = 0 Field peca_top_y: Int = 0 Field peca_bottom_y: Int = 0 ' ' Armazena o índice da peça atual ' Se é a primeira vez que a peça está sendo sorteada, ' ambas as variáveis terão o valor -1 ' Field peca_atual_indice : Int = -1 Field peca_proxima_indice: Int = -1 ' Indica a cor das peças atual e da próxima peça Field peca_atual_cor : Color Field peca_proxima_cor: Color ' Indica o índice da cor que foi escolhido no arranjo ' peca_cores, este índice será armazenado no arranjo ' tabuleiro, quando a peça for gravada no tabuleiro. Field peca_atual_indice_cor: Int Field peca_proxima_indice_cor: Int ' Uma lista de cores pra Field peca_cores: Color[] = New Color[](Color.None, Color.Black, Color.Blue, Color.Red, Color.Yellow, Color.Green, Color.Violet) ' Indica a quantidade de linhas e colunas da peça atual e a próxima. Field peca_atual_linhas: Int Field peca_atual_colunas: Int Field peca_proxima_linhas: Int Field peca_proxima_colunas: Int ' Indica a quantidade de tempo pra a peça descer. Field peca_tempo_pra_descer: Double = 0.5 ' Indica a quantidade de tempo anterior. Field tempo_anterior: Double ' Pra suaviar o deslocamento pra esquerda e direita ' iremos determinar um tempo mínimo pra ocorrer outro deslocamento. Field bn_mover_pra_esquerda : Bool = False Field bn_mover_pra_direita: Bool = False Field mover_pra_esquerda_tempo: Double = 0.1 Field mover_pra_esquerda_tempo_anterior : Double Field mover_pra_direita_tempo: Double = 0.1 Field mover_pra_direita_tempo_anterior : Double Field bn_mover_pra_baixo_mais_rapido: Bool Field mover_pra_baixo_mais_rapido_tempo: Double Field mover_pra_baixo_mais_rapido_tempo_anterior: Double Field girar_tempo: Double Field girar_tempo_anterior: Double Field bn_girar_peca: Bool = False Field peca_girar_tempo: Double = 1.5 Field peca_girar_tempo_anterior: Double = 0.0 ' Taxa que indica quantas vezes por segundo a função do timer ' será chamada. Field hertz_por_segundo: Double = 1 ' Timer que serve pra mover a peça pra baixo. Field timer_descer_peca: Timer ' Timer que serve pra mover a peça mais rápido pra baixo. Field timer_descer_peca_mais_rapido: Timer Field strTexto: String Method New( title:String="Tetris",width:Int=800,height:Int=600,flags:WindowFlags=Null ) Super.New( title,width,height,flags ) ' Aqui, haverá um timer que será chamado, a cada hertz por segundo, quanto ' maior a quantidade de herts, mais rápido a peça se move pra baixo. hertz_por_segundo = 4 timer_descer_peca = New Timer(hertz_por_segundo, Peca_Mover_Pra_Baixo) mover_pra_baixo_mais_rapido_tempo = hertz_por_segundo / 60 * 2 Iniciar_Tabuleiro() Iniciar_Peca() Sortear_Peca() End Method Iniciar_Tabuleiro:Void() tabuleiro = New Int[CELULAS_POR_LINHA, CELULAS_POR_COLUNA] tabuleiro_linha_ultimo_indice = CELULAS_POR_LINHA - 1 tabuleiro_coluna_ultimo_indice = CELULAS_POR_COLUNA - 1 End ' ' Exibir as células do tabuleiro ' Method Exibir_Tabuleiro:Void(canvas: Canvas) ' Pega cor anterior Local cor_anterior : Color = canvas.Color ' Desenha o fundo do tabuleiro canvas.Color = TABULEIRO_COR_DE_FUNDO canvas.DrawRect(TABULEIRO_LEFT_X, TABULEIRO_TOP_Y, CELULAS_POR_COLUNA * CELULA_LARGURA_COM_BORDA, CELULAS_POR_LINHA * CELULA_ALTURA_COM_BORDA) canvas.Color = CELULA_COR ' Agora, desenha as células do tabuleiro For Local linha := 0 Until CELULAS_POR_LINHA For Local coluna := 0 Until CELULAS_POR_COLUNA ' Desenha a célula atual do tabuleiro. ' Observe abaixo, o deslocamento pra a célula é 2 pixels maior a largura ' da célula, isto foi feito, pois ao desenhar a célula ' Veremos a grade da mesma. If tabuleiro[linha, coluna] = 0 canvas.Color = CELULA_COR Else Local cor_indice: Int = tabuleiro[linha, coluna] canvas.Color = peca_cores[cor_indice] Endif canvas.DrawRect(TABULEIRO_LEFT_X + CELULA_LARGURA_COM_BORDA * coluna + 1, TABULEIRO_TOP_Y + CELULA_ALTURA_COM_BORDA * linha + 1, CELULA_LARGURA, CELULA_ALTURA) Next Next ' Reseta pra a cor anterior canvas.Color = cor_anterior End Method Tabuleiro_Verificar_Linhas_Completas() Local linha_tabuleiro : Int = tabuleiro_linha_ultimo_indice Local coluna_tabuleiro : Int = 0 Local linha_completa_tabuleiro: Int = -1 While linha_tabuleiro >= 0 Local bn_linha_completa : Int = True For Local coluna_tabuleiro: Int = 0 To tabuleiro_coluna_ultimo_indice If tabuleiro[linha_tabuleiro, coluna_tabuleiro] = 0 bn_linha_completa = False 'Exit Endif Next If bn_linha_completa = True ' A linha atual está completa, entretanto, é a primeira vez ' que tem uma linha completa, neste caso, devemos definir esta ' linha como completa If linha_completa_tabuleiro = -1 ' Devemos apagar a linha completa. For Local coluna_tabuleiro : Int = 0 To tabuleiro_coluna_ultimo_indice tabuleiro[linha_tabuleiro, coluna_tabuleiro] = 0 Next linha_completa_tabuleiro = linha_tabuleiro Else ' Se chegarmos aqui, quer dizer, que está não é a primeira ' vez que tem linha completa, então devemos zera todas ' as células desta linha For Local coluna_tabuleiro: Int = 0 To tabuleiro_coluna_ultimo_indice tabuleiro[linha_tabuleiro, coluna_tabuleiro] = 0 Next Endif Else ' A linha não está completa e já houve linhas completas anteriormente ' então, devemos copiar a linha atual que não está completa ' pra a linha que está completa, pois ela foi esvaziada. If linha_completa_tabuleiro <> -1 For Local coluna_tabuleiro: Int = 0 To tabuleiro_coluna_ultimo_indice tabuleiro[linha_completa_tabuleiro, coluna_tabuleiro] = tabuleiro[linha_tabuleiro, coluna_tabuleiro] tabuleiro[linha_tabuleiro, coluna_tabuleiro] = 0 ' Como copiarmos a linha não completa, pra a posição da linha completa ' a linha atual torna-se uma linha vazia. Next linha_completa_tabuleiro = linha_completa_tabuleiro - 1 End If End If linha_tabuleiro -= 1 Wend End Method ' ' Inicia as peças que serão utilizadas no jogo ' Method Iniciar_Peca: Void() Local str_pecas: String = "" 'L' : peca_0 str_pecas += "10" str_pecas += "10" str_pecas += "11" 'L INVERTIDO': peca_1 str_pecas += "01" str_pecas += "01" str_pecas += "11" '' : peca_2 str_pecas += "10" str_pecas += "11" str_pecas += "01" '' : peca_3 str_pecas += "01" str_pecas += "11" str_pecas += "10" 'T' : peca_4 str_pecas += "111" str_pecas += "010" '' : peca_5 str_pecas += "1" str_pecas += "1" str_pecas += "1" '' : peca_6 str_pecas += "11" str_pecas += "11" Local peca_dimensoes: Int[] peca_dimensoes = New Int[](3, 2, 3, 2, 3, 2, 3, 2, 2, 3, 3, 1, 2, 2) pecas = New Int[7][,] Local indice_str_peca: Int = 0 Local indice_peca: Int = 0 Local linha: Int = 0 Local coluna: Int = 0 Local uA: Int = 0 Local peca_linhas: Int = 0 Local peca_colunas: Int = 0 indice_str_peca = 0 indice_peca = 0 For uA = 0 Until peca_dimensoes.Length Step 2 ' O arranjo peca_dimensoes armazena as dimensoes de cada arranjo. peca_linhas = peca_dimensoes[uA] peca_colunas = peca_dimensoes[uA + 1] ' Cria o arranjo multidimensional dinamicamente. pecas[indice_peca] = New Int[peca_linhas, peca_colunas] Local qt_linhas: Int = pecas[indice_peca].GetSize(0) Local qt_colunas: Int = pecas[indice_peca].GetSize(1) 'DebugStop() strTexto = "" For linha = 0 Until peca_linhas For coluna = 0 Until peca_colunas pecas[indice_peca][linha,coluna] = Cast(str_pecas.Slice(indice_str_peca, indice_str_peca + 1)) strTexto += pecas[indice_peca][linha, coluna] indice_str_peca += 1 Next Next ' Serve pra ir pra a próxima peça indice_peca += 1 Next End ' ' A peça se move 1 célula por vez ' Devemos verificar se a célula da peça ' que tem o valor 1 não está ocupada ' na nova célula correspondente. ' ' A colisão ocorre se a célula da peça ' no arranjo peca_atual tem o valor 1 ' o local onde está célula será armazenada ' no tabuleiro está ocupada. ' ' Um detalhe a observar é que uma célula ' da posição anterior pode se sobrepor ' a uma célula da nova posição pois ' a quantidade de linhas da peça é maior ' que a quantidade de deslocamento pra ' baixo da célula ' Então, não podemos analisar por linha e sim ' por coluna. ' Method Peca_Mover_Pra_Baixo() Local linha_destino_top_y: Int = peca_top_y + 1 Local linha_destino_bottom_y: Int = linha_destino_top_y + peca_atual_linhas - 1 ' Verifica se passou da última linha do tabuleiro If linha_destino_bottom_y > tabuleiro_linha_ultimo_indice Peca_Gravar_No_Tabuleiro() Tabuleiro_Verificar_Linhas_Completas() Sortear_Peca() Return Endif ' Apaga a posição da peça atual, pra evitar a peça ' atual, sobrepor a peça na nova posição pois a peça ' move pra baixo 1 linha por vez, entretanto, a peça ' ocupa mais de 1 linha. Peca_Apagar_do_Tabuleiro() ' ' Compara cada célula da linha de destino com ' a célula correspondente no tabuleiro e verificar se há alguma ' colisão ' Local peca_linha : Int = 0 Local peca_coluna : Int = 0 For Local tabuleiro_linha: Int = linha_destino_top_y To linha_destino_bottom_y ' Sempre resetar a coluna da peça pra começar da esquerda pra direita. peca_coluna = 0 For Local tabuleiro_coluna: Int = peca_left_x To peca_right_x ' Se há alguma colisão, sortea uma nova peça e retorna. If tabuleiro[tabuleiro_linha, tabuleiro_coluna] <> 0 And peca_atual[peca_linha, peca_coluna] = 1 Peca_Gravar_No_Tabuleiro() Tabuleiro_Verificar_Linhas_Completas() Sortear_Peca() Return Endif peca_coluna += 1 Next peca_linha += 1 Next ' Apaga o rastro da peça na posição anterior 'Peca_Apagar_do_Tabuleiro() ' Move a coordena pra baixo peca_top_y += 1 peca_bottom_y = peca_top_y + peca_atual_linhas - 1 Peca_Gravar_No_Tabuleiro() End Method Method Peca_Mover_Pra_Esquerda() Local coluna_destino_left_x: Int = peca_left_x - 1 Local coluna_destino_right_x: Int = coluna_destino_left_x + peca_atual_colunas - 1 If coluna_destino_left_x < 0 Return Endif ' ' Apaga a posição atual da peça armazenada ' no arranjo tabuleiro, pra evitar comparar ' células que se sobrepõe pois, a célula ' sempre se move 1 célula por vez, entretanto ' todas as peças é composta de mais de uma célula. Peca_Apagar_do_Tabuleiro() ' ' Compara cada célula da linha de destino com ' a célula correspondente no tabuleiro e verificar se há alguma ' colisão ' Local peca_linha : Int = 0 Local peca_coluna : Int = 0 For Local tabuleiro_linha: Int = peca_top_y To peca_bottom_y ' Sempre resetar a coluna da peça pra começar da esquerda pra direita. peca_coluna = 0 For Local tabuleiro_coluna: Int = coluna_destino_left_x To coluna_destino_right_x ' Se na nova posição, há uma colisão devemos continuar na posição que estávamos If tabuleiro[tabuleiro_linha, tabuleiro_coluna] <> 0 And peca_atual[peca_linha, peca_coluna] = 1 Peca_Gravar_No_Tabuleiro() Return Endif peca_coluna += 1 Next peca_linha += 1 Next ' Move a peça pra esquerda. peca_left_x = coluna_destino_left_x peca_right_x = coluna_destino_right_x Peca_Gravar_No_Tabuleiro() End Method Method Peca_Mover_Pra_Direita() Local coluna_destino_left_x: Int = peca_left_x + 1 Local coluna_destino_right_x: Int = coluna_destino_left_x + peca_atual_colunas - 1 If coluna_destino_right_x > tabuleiro_coluna_ultimo_indice Return Endif ' ' Apaga a posição atual da peça armazenada ' no arranjo tabuleiro, pra evitar comparar ' células que se sobrepõe pois, a célula ' sempre se move 1 célula por vez, entretanto ' todas as peças é composta de mais de uma célula. Peca_Apagar_do_Tabuleiro() ' ' Compara cada célula da linha de destino com ' a célula correspondente no tabuleiro e verificar se há alguma ' colisão ' Local peca_linha : Int = 0 Local peca_coluna : Int = 0 For Local tabuleiro_linha: Int = peca_top_y To peca_bottom_y ' Sempre resetar a coluna da peça pra começar da esquerda pra direita. peca_coluna = 0 For Local tabuleiro_coluna: Int = coluna_destino_left_x To coluna_destino_right_x ' Se na nova posição, há uma colisão devemos continuar na posição que estávamos If tabuleiro[tabuleiro_linha, tabuleiro_coluna] <> 0 And peca_atual[peca_linha, peca_coluna] = 1 Peca_Gravar_No_Tabuleiro() Return Endif peca_coluna += 1 Next peca_linha += 1 Next ' Move a peça pra esquerda. peca_left_x = coluna_destino_left_x peca_right_x = coluna_destino_right_x Peca_Gravar_No_Tabuleiro() End Method Method Peca_Girar:Void() ' Aqui, a linha da peça atual, será a coluna da peça girada ' e a coluna da peça atual, será a linha da peça girada. Local peca_girada_linhas: Int = peca_atual_colunas Local peca_girada_colunas: Int = peca_atual_linhas Local peca_girada_linha_ultimo_indice : Int = peca_atual_coluna_ultimo_indice Local peca_girada_coluna_ultimo_indice: Int = peca_atual_linha_ultimo_indice ' Centraliza a peça dentro da peça anterior. Local deslocamento: Int = 0 Local peca_girada_left_x: Int = 0 Local peca_girada_right_x: Int = 0 Local peca_girada_top_y: Int = 0 Local peca_girada_bottom_y: Int = 0 ' Aqui, devemos girar a peça corretamente If peca_atual_colunas > peca_atual_linhas deslocamento = peca_atual_colunas / peca_atual_linhas - 1 ' Antes de mover: ' ****************** ' 0 1 2 3 4 5 ' 0 ' 1 # # # # ' 2 # ' 3 ' ****************** ' Após mover: ' 0 1 2 3 4 5 ' 0 # # ' 1 # ' 2 # ' 3 # ' ****************** ' No exemplo acima, pode-se observar ' antes de mover, a posição left_x da peça ' estava na coluna 0 ' Após mover, ela foi pra a posição 1 ' Pra descobrir a nova posição é bem simples ' é só dividir o maior lado pelo menor lado ' o quociente obtido, é a posição left_x ' da nova posição da peça que foi gerada, ' no caso, a posição é baseada em zero, então ' devemos subtrair 1, pra saber qual a posição ' No exemplo acima: ' A largura é 4 e a altura é 2, ' então, 4 / 2 = 2 - 1 = 1 ' Observando no exemplo acima, ' o deslocamento é positivo pra o eixo x ' e é negativo pra o eixo y. peca_girada_left_x = peca_left_x + deslocamento peca_girada_right_x = peca_girada_left_x + peca_girada_coluna_ultimo_indice peca_girada_top_y = peca_top_y - deslocamento peca_girada_bottom_y = peca_girada_top_y + peca_girada_linha_ultimo_indice Elseif peca_atual_colunas < peca_atual_linhas deslocamento = peca_atual_linhas / peca_atual_colunas - 1 ' Aqui, o deslocamento é negativo pra o eixo x e ' positivo pra o eixo y. peca_girada_left_x = peca_left_x - deslocamento peca_girada_right_x = peca_girada_left_x + peca_girada_coluna_ultimo_indice peca_girada_top_y = peca_top_y + deslocamento peca_girada_bottom_y = peca_girada_top_y + peca_girada_linha_ultimo_indice Else peca_girada_left_x = peca_left_x peca_girada_right_x = peca_girada_left_x + peca_girada_coluna_ultimo_indice peca_girada_top_y = peca_top_y peca_girada_bottom_y = peca_girada_top_y + peca_girada_linha_ultimo_indice Endif ' Valida os limites da peça girada dentro do tabuleiro. If peca_girada_left_x < 0 Return Endif ' Se ao girar a peça, o lado direito, ultrapassar o lado ' direito do tabuleiro, tentar mover o mesmo pra esquerda. If peca_girada_right_x > tabuleiro_coluna_ultimo_indice Local peca_girada_left_x_temp: Int = peca_girada_left_x - 1 For Local linha_tabuleiro : Int = peca_girada_top_y To peca_girada_bottom_y If linha_tabuleiro >= 0 If tabuleiro[linha_tabuleiro, peca_girada_left_x_temp] <> 0 Return Endif End If Next ' Se chegarmos aqui, podemos mover pra esquerda peca_girada_left_x -= 1 peca_girada_right_x -= 1 Endif If peca_girada_top_y < 0 Return Endif If peca_girada_bottom_y > tabuleiro_linha_ultimo_indice Return Endif ' Cria a nova peça girada Local peca_girada: Int[,] = New Int[peca_girada_linhas, peca_girada_colunas] ' Neste caso, iremos girar a peça pra esquerda, veja: ' Antes de mover: ' ****************** ' 0 1 2 3 4 5 ' 0 5 6 7 8 ' 1 9 ' 2 ' 3 ' ****************** ' Após mover: ' 0 1 2 3 4 5 ' 0 9 5 ' 1 6 ' 2 7 ' 3 8 ' ****************** ' ' Observe que a primeira linha da peça original ' irá pra a última coluna da peça girada. ' A segunda linha, irá pra a penúltima coluna da peça girada. ' Local peca_girada_linha : Int Local peca_girada_coluna: Int peca_girada_coluna = peca_girada_coluna_ultimo_indice For Local peca_linha: Int = 0 Until peca_atual_linhas For Local peca_coluna: Int = peca_atual_colunas - 1 To 0 Step -1 peca_girada[peca_coluna, peca_girada_coluna] = peca_atual[peca_linha, peca_coluna] Next peca_girada_coluna -= 1 Next ' ' Apaga as coordenadas da peça atual do tabuleiro, pra ' evitar que a peça girada sobreponha a peça atual. ' Peca_Apagar_do_Tabuleiro() ' Vamos verificar por colisão. ' Primeiro, se a peça antes de girar está na borda direita do tabuleiro ' ou se está à esquerda de uma peça, devemos verificar que ao girar ' a peça o lado direito irá colidir com a peça, se sim, devemos tentar ' mover a peça pra esquerda, pois detectei isto, pois em outros jogos ' de Tetris, se uma peça está a esquerda de uma outra peça, ao girar ' a peça move pra esquerda se a coordenada da nova peça sobre a peça da direita. Local peca_linha : Int = 0 Local peca_coluna : Int = 0 Local peca_colisao_a_direita: Bool = False Local peca_colisao_a_esquerda: Bool = False ' Aqui, iremos verificar duas coisas, se o último lado da peça ' girada, colide com alguma célula já preenchida do tabuleiro ' Se houver colisão, iremos também dentro do mesmo loop, verificar ' se o lado esquerdo da peça tem uma coluna vazia, se sim, iremos ' deslocar pra esquerda. For Local tabuleiro_linha: Int = peca_girada_top_y To peca_girada_bottom_y ' Sempre retornar a coluna da peça peca_coluna = peca_girada_coluna_ultimo_indice ' Aqui, ire If tabuleiro[tabuleiro_linha, peca_girada_right_x] <> 0 And peca_girada[peca_linha, peca_coluna] = 1 peca_colisao_a_direita = True Endif ' Aqui, iremos verificar se há espaço não ocupado a esquerda, pra ' mover caso, o lado direito colide. If tabuleiro[tabuleiro_linha, peca_girada_left_x - 1] <> 0 And peca_colisao_a_esquerda = True End If Next ' Se a última coluna da peça colide com célula ocupada no tabuleiro ' e do lado esquerdo da peça girada há células ocupada que colide ' não podemos mover, devemos sair If peca_colisao_a_direita And peca_colisao_a_esquerda Return Endif ' Se houve colisão a direita, tentar mover pra esquerda If peca_colisao_a_direita = True And peca_colisao_a_esquerda = False peca_girada_left_x -= 1 peca_girada_right_x -= 1 End If For Local tabuleiro_linha: Int = peca_girada_top_y To peca_girada_bottom_y ' Sempre resetar a coluna da peça pra começar da esquerda pra direita. peca_coluna = 0 For Local tabuleiro_coluna: Int = peca_girada_left_x To peca_girada_right_x ' Se na nova posição, há uma colisão devemos continuar na posição que estávamos If tabuleiro[tabuleiro_linha, tabuleiro_coluna] <> 0 And peca_girada[peca_linha, peca_coluna] = 1 Peca_Gravar_No_Tabuleiro() Return Endif peca_coluna += 1 Next peca_linha += 1 Next ' ' Se chegarmos aqui, quer dizer, que a peça girada, não colidiu com nada ' devemos gravar ' peca_atual = Null peca_atual = peca_girada peca_atual_colunas = peca_girada_colunas peca_atual_linhas = peca_girada_linhas peca_atual_linha_ultimo_indice = peca_girada_linha_ultimo_indice peca_atual_coluna_ultimo_indice = peca_girada_coluna_ultimo_indice peca_left_x = peca_girada_left_x peca_right_x = peca_girada_right_x peca_top_y = peca_girada_top_y peca_bottom_y = peca_girada_bottom_y Peca_Gravar_No_Tabuleiro() End Method ' ' Cada vez que a peça se move pra baixo ' e ela colide com alguma peça, uma nova ' peça é sorteada ' Method Sortear_Peca:Void() Local total_de_pecas: Int = pecas.Length ' Se é a primeira vez que a peça será sorteada, ' ambas variáveis terão o valor -1, então, devemos ' sortear duas peças. If peca_atual_indice = -1 And peca_proxima_indice = -1 peca_atual_indice = Rnd(total_de_pecas) peca_proxima_indice = Rnd(total_de_pecas) peca_atual_indice_cor = Rnd(1, peca_cores.Length) peca_proxima_indice_cor = Rnd(1, peca_cores.Length) Else ' Se não for a primeira vez, devemos pega a próxima ' peça e armazenar na peça atual e sortear a próxima ' peça peca_atual_indice = peca_proxima_indice peca_proxima_indice = Rnd(total_de_pecas) peca_atual_indice_cor = peca_proxima_indice_cor peca_proxima_indice_cor = Rnd(1, peca_cores.Length) Endif ' Em seguida, iremos armazena os dados da peça atual sorteada ' no arranjo peca_atual. peca_atual_linhas = pecas[peca_atual_indice].GetSize(0) peca_atual_colunas = pecas[peca_atual_indice].GetSize(1) ' Vamos armazenar o último índice da linha e coluna, pra facilitar peca_atual_linha_ultimo_indice = peca_atual_linhas - 1 peca_atual_coluna_ultimo_indice = peca_atual_colunas - 1 peca_atual = New Int[peca_atual_linhas, peca_atual_colunas] ' Copia os dados da peça sorteada, dentro da peça atual. For Local linha := 0 Until peca_atual_linhas For Local coluna := 0 Until peca_atual_colunas peca_atual[linha, coluna] = pecas[peca_atual_indice][linha, coluna] Next Next ' Em seguida, define as coordenadas da nova peça, dentro do tabuleiro ' Observe abaixo que subtraírmos 1 pois, por exemplo, ' se a peça tem 2 colunas, e começa na posição 3, ao somarmos 3 + 2, é igual a 5 ' entretanto, a peça ocupa a coluna 3 e 4, então devemos subtrair 1 peca_left_x = 3 peca_right_x = peca_left_x + peca_atual_colunas - 1 ' A peça sempre começa fora do tabuleiro peca_top_y = -peca_atual_linhas peca_top_y = -1 'peca_top_y = 0 peca_bottom_y = peca_top_y + peca_atual_linhas - 1 ' Sortea aleatoriamente uma cor pra peça peca_atual_indice_cor = Rnd(peca_cores.Length) Peca_Gravar_No_Tabuleiro() End ' ' Grava a peça na nova posição. ' Method Peca_Gravar_No_Tabuleiro:Void() Local peca_coluna : Int = 0 Local peca_linha : Int = 0 ' ' Este 2 loops é bem simples, iremos percorrer, todas as células ' da peça, se a célula tem o valor 1, quer dizer, que ' a célula do tabuleiro deve ser marcada como ocupada. ' For Local tabuleiro_linha : Int = peca_top_y To peca_bottom_y If tabuleiro_linha < 0 peca_linha += 1 Continue Endif peca_coluna = 0 For Local tabuleiro_coluna : Int = peca_left_x To peca_right_x If peca_atual[peca_linha, peca_coluna] = 1 tabuleiro[tabuleiro_linha, tabuleiro_coluna] = peca_atual_indice_cor End If peca_coluna += 1 Next peca_linha += 1 Next End Method ' ' Toda vez que a peça se move, devemos apagar ' o rastro da mesma. ' Method Peca_Apagar_do_Tabuleiro:Void() Local peca_coluna : Int = 0 Local peca_linha : Int = 0 ' ' Este 2 loops é bem simples, iremos percorrer, todas as células ' da peça, se a célula tem o valor 1, quer dizer, que ' a célula do tabuleiro deve ser marcada como ocupada. ' For Local tabuleiro_linha : Int = peca_top_y To peca_bottom_y If tabuleiro_linha < 0 peca_linha += 1 Continue Endif peca_coluna = 0 For Local tabuleiro_coluna : Int = peca_left_x To peca_right_x If peca_atual[peca_linha, peca_coluna] = 1 tabuleiro[tabuleiro_linha, tabuleiro_coluna] = 0 End If peca_coluna += 1 Next peca_linha += 1 Next End Method Method OnKeyEvent( event:KeyEvent ) Override End Method Verificar_Teclas:Void() ' Evitar a tecla se repetir. If Keyboard.KeyHit(Key.Up, False) bn_girar_peca = True girar_tempo_anterior = Now() End If If Keyboard.KeyDown(Key.Left) If bn_mover_pra_esquerda = False bn_mover_pra_esquerda = True mover_pra_esquerda_tempo_anterior = Now() End If End If If Keyboard.KeyDown(Key.Right) If bn_mover_pra_direita = False bn_mover_pra_direita = True mover_pra_direita_tempo_anterior = Now() Endif Endif ' Se o usuário mantêm a tecla pressionada, devemos ' alterar o tempo anterior pra agora. If Keyboard.KeyDown(Key.Down) If bn_mover_pra_baixo_mais_rapido = False bn_mover_pra_baixo_mais_rapido = True mover_pra_baixo_mais_rapido_tempo_anterior = Now() Endif Endif End Method Method OnRender( canvas:Canvas ) Override App.RequestRender() Verificar_Teclas() Local tempo_atual : Double = Now() If tempo_atual - mover_pra_baixo_mais_rapido_tempo_anterior > mover_pra_baixo_mais_rapido_tempo If bn_mover_pra_baixo_mais_rapido bn_mover_pra_baixo_mais_rapido = False Peca_Mover_Pra_Baixo() Endif End If If tempo_atual - mover_pra_esquerda_tempo_anterior > mover_pra_esquerda_tempo If bn_mover_pra_esquerda Peca_Mover_Pra_Esquerda() bn_mover_pra_esquerda = False End If Endif If tempo_atual - mover_pra_direita_tempo_anterior > mover_pra_direita_tempo If bn_mover_pra_direita Peca_Mover_Pra_Direita() bn_mover_pra_direita = False Endif Endif If tempo_atual - girar_tempo_anterior > girar_tempo If bn_girar_peca Peca_Girar() bn_girar_peca = False girar_tempo_anterior = Now() Endif Endif Exibir_Tabuleiro(canvas) End End Function Main() New AppInstance New Tetris App.Run() End