English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Um, introdução ao experimento
1.1 Conteúdo do experimento
Nesta seção do experimento, implementaremos o design das principais funções do Tetris, completaremos as funções básicas e executaremos.
1.2 Conhecimentos do experimento
Desenho da janela
Design da classe bloco
Algoritmo de rotação
Funções de movimento e eliminação
1.3 Ambiente do experimento
xface terminal
g++ Compilador
Biblioteca ncurses
1.4 Compilar o programa
O comando de compilação deve incluir -l Opções para incluir a biblioteca ncurses:
g++ main.c -l ncurses
1.5 Executar o programa
./a.out
1.6 Resultados da execução
Dois, etapas do experimento
2.1 Arquivos de cabeçalho
Primeiro, inclui arquivos de cabeçalho e define uma função de troca e uma função de número aleatório, usadas posteriormente (a função de troca é usada para girar os blocos, e o número aleatório é usado para definir a forma dos blocos)
#include <iostream> #include <sys/time.h> #include <sys/types.h> #include <stdlib.h> #include <ncurses.h> #include <unistd.h> /* Troca a e b */ void swap(int &a, int &b){ int t=a; a = b; b = t; } /* Obtém um inteiro aleatório no intervalo (min, max) int getrand(int min, int max) { return(min+rand()%(max-min+1)); }
2.2 Definição de classe
Devido ao conteúdo do programa ser relativamente simples, aqui foi definida apenas uma classe Piece
class Piece { public: int score; //Pontuação int shape; //Representa a forma do bloco atual int next_shape; //Representa a forma do próximo bloco int head_x; //Posição do primeiro box do bloco atual, marcador de posição int head_y; int size_h; //Tamanho atual do bloco int size_w; int next_size_h; //Tamanho do próximo bloco int next_size_w; int box_shape[4][4]; //当前方块的shpe数组 4x4 int next_box_shape[4][4]; //下一个方块的shpe数组 4x4 int box_map[30][45]; //用来标记游戏框内的每个box bool game_over; //游戏结束的标志 public: void initial(); //初始化函数 void set_shape(int &cshape, int box_shape[][4],int &size_w, int & size_h); //设置方块形状 void score_next(); //显示下一个方块的形状以及分数 void judge(); //判断是否层满 void move(); //移动函数 通过 ← → ↓ 控制 void rotate(); //旋转函数 bool isaggin(); //判断下一次行动是否会越界或者重合 bool exsqr(int row); //判断当前行是否空 };
2.3 设置方块形状
这里通过 case 语句定义了7种方块的形状,在每次下一个方块掉落之前都要调用以设置好它的形状以及初始位置
void Piece::set_shape(int &cshape, int shape[][4],int &size_w,int &size_h) { /*首先将用来表示的4x4数组初始化为0*/ int i,j; para(i=0;i<4;i++); para(j=0;j<4;j++); shape[i][j]=0; /*设置7种初始形状并设置它们的size*/ switch(cshape) { case 0: size_h=1; size_w=4; shape[0][0]=1; shape[0][1]=1; shape[0][2]=1; shape[0][3]=1; break; case 1: size_h=2; size_w=3; shape[0][0]=1; shape[1][0]=1; shape[1][1]=1; shape[1][2]=1; break; case 2: size_h=2; size_w=3; shape[0][2]=1; shape[1][0]=1; shape[1][1]=1; shape[1][2]=1; break; case 3: size_h=2; size_w=3; shape[0][1]=1; shape[0][2]=1; shape[1][0]=1; shape[1][1]=1; break; case 4: size_h=2; size_w=3; shape[0][0]=1; shape[0][1]=1; shape[1][1]=1; shape[1][2]=1; break; case 5: size_h=2; size_w=2; shape[0][0]=1; shape[0][1]=1; shape[1][0]=1; shape[1][1]=1; break; case 6: size_h=2; size_w=3; shape[0][1]=1; shape[1][0]=1; shape[1][1]=1; shape[1][2]=1; break; } //设置完形状以后初始化方块的起始位置 head_x=game_win_width/2; head_y=1; //如果刚初始化就重合了,游戏结束~ if(isaggin()) /* GAME OVER ! */ game_over=true; }
2.4 旋转函数
这里使用了一个相对简单的算法对方块进行旋转,类似于矩阵的旋转,首先将 shape 数组进行斜对角线对称化,然后进行左右对称,便完成了旋转,需要注意的是要判断旋转后方块是否出界或重合,如果是,则取消本次旋转。
void Piece::rotate() { int temp[4][4]=0}; //variável temporária int temp_piece[4][4]=0}; //array de backup int i,j,tmp_size_h,tmp_size_w; tmp_size_w=size_w; tmp_size_h=size_h; para(int i=0; i<4;i++); para(int j=0;j<4;j++); temp_piece[i][j]=box_shape[i][j]; //faça backup do bloco atual, se a rotação falhar, volte ao formato atual para(i=0;i<4;i++); para(j=0;j<4;j++); temp[j][i]=box_shape[i][j]; //simétrico diagonalmente i=size_h; size_h=size_w; size_w=i; para(i=0;i<size_h;i++); para(j=0;j<size_w;j++); box_shape[i][size_w-1-j]=temp[i][j]; //simétrico horizontal e verticalmente /*Se, após a rotação, houver sobreposição, volte ao formato salvo no array de backup*/ if(isaggin()){ para(int i=0; i<4;i++); para(int j=0;j<4;j++); box_shape[i][j]=temp_piece[i][j]; size_w=tmp_size_w; //Lembre-se de que size também precisa voltar ao tamanho original size_h=tmp_size_h; } /*Se a rotação for bem-sucedida, então exiba na tela*/ else{ para(int i=0; i<4;i++); para(int j=0;j<4;j++} se(temp_piece[i][j]==1} mvwaddch(game_win,head_y+i,head_x+j,' '); //mover para um determinado ponto de coordenadas na janela game_win e imprimir o caractere wrefresh(game_win); } } para(int i=0; i<size_h;i++); for(int j=0;j<size_w;j++} if(this->box_shape[i][j]==1} mvwaddch(game_win,head_y+i,head_x+j,'#'); wrefresh(game_win); } } } }
2.5 função de movimento
Se o jogador não pressionar nenhuma tecla, o bloco precisa cair lentamente, então não podemos bloquear na entrada de tecla esperando por getch(), aqui usamos select() para cancelar o bloqueio.
/* aqui está apenas uma parte do programa, a implementação específica, consulte o código-fonte */ struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec= 500000; se (select(1, &set, NULL, NULL, &timeout) == 0)
timeout é o tempo máximo que podemos esperar por uma tecla, aqui foi configurado 500000us, se passar desse tempo, não aguardaremos mais a entrada de getch(), e passaremos diretamente ao próximo passo.
Se, dentro do tempo limite, for detectado um tecla, a condição if a seguir será verdadeira, obtendo o valor de entrada do key, e realizando operações como mover para a esquerda, direita, para baixo, girar, etc., com base em diferentes valores de key.
se (FD_ISSET(0, &set))
enquanto ((key = getch()) == -1) ;
os tratamentos das funções de movimento para esquerda, direita e para baixo são básicamente os mesmos, aqui apenas explicamos a função de movimento para baixo
/* aqui está apenas uma parte do programa, a implementação específica, consulte o código-fonte */ /* se a tecla de entrada for ↓ */ if(key==KEY_DOWN){ head_y++; //coordenada y do bloco+1 if(isaggin()){ //se houver sobreposição ou saída, cancele essa movimentação head_y--; /*já que parou, defina o box correspondente no mapa como ocupado, usando1indica, 0 indica não ocupado for(int i=0;i<size_h;i++); for(int j=0;j<size_w;j++); if(box_shape[i][j]==1); box_map[head_y+i][head_x+j]=1; score_next(); //exibir a pontuação e o aviso do próximo bloco } /*se puder cair para baixo, remova a exibição atual do bloco, caia uma linha para a exibição, aqui, note que o loop for deve começar do fundo para cima else{ for(int i=size_h-1; i>=0;i--); for(int j=0;j<size_w;j++} if(this->box_shape[i][j]==1} mvwaddch(game_win,head_y-1+i,head_x+j,' '); mvwaddch(game_win,head_y+i,head_x+j,'#'); } } wrefresh(game_win); }
2.6 função repetitiva
função a ser executada após cada movimento ou rotação, se a função retornar verdadeiro, não pode haver ação, se retornar falso, pode prosseguir para o próximo passo.
bool Piece::isaggin(){ for(int i=0;i<size_h;i++); for(int j=0;j<size_w;j++} if(box_shape[i][j]==1} if(head_y+i > game_win_height-2); //saída para baixo return true; if(head_x+j > game_win_width-2 || head_x+i-1<0) //saída para os lados return true; if(box_map[head_y+i][head_x+j]==1); //se sobrepuser ao box ocupado return true; } } return false; }
2.7 função de camada cheia
A função mais importante é eliminar as linhas cheias dos blocos, que deve ser feita sempre que um bloco parar de cair.
void Piece::judge(){ int i,j; int line=0; //usado para registrar o número de linhas cheias da camada bool full; for(i=1;i<game_win_height-1;i++} //exceto as bordas full=true; for(j=1;j<game_win_width-1;j++} if(box_map[i][j]==0) //existe um box não ocupado full=false; //indica que a camada não está cheia } if(full){ //se a camada estiver cheia line++; //linha cheia+1 score+=50; //Avaliação adicional~ for(j=1;j<game_win_width-1;j++); box_map[i][j]=0; //Limpe essa camada (marque como não utilizada) } } /*Depois de verificar, observe o valor de line. Se não for 0, significa que uma camada está cheia e precisa ser eliminada*/ if(line!=0){ for(i=game_win_height-2;i>=2;i--} int s=i; if(exsqr(i)==0){ while(s>1 && exsqr(--s)==0); //Encontre as linhas onde há blocos e mova它们 inferiormente for(j=1;j<game_win_width-1;j++} box_map[i][j]=box_map[s][j]; //Deslocamento inferior box_map[s][j]=0; //Limpeza superior } } } /*Após limpar e mover as marcas, é necessário atualizar a tela, reimprimir game_win*/ for(int i=1;i<game_win_height-1;i++); for(int j=1;j<game_win_width-1;j++} if(box_map[i][j]==1} mvwaddch(game_win,i,j,'#'); wrefresh(game_win); } else{ mvwaddch(game_win,i,j,' '); wrefresh(game_win); } } } }
III. Resumo do Experimento
Aqui, a introdução a alguns dos principais funções está completa. Entenda o funcionamento dessas funções e realize, referenciando o código-fonte, o restante das funções e a função main para executar! Claro, há muitos métodos de implementação do Tetris, e a lógica e o método de cada pessoa podem ser diferentes. Talvez você escreva um Tetris mais simples e mais fluído! Aproveite-o! :)
Declaração: O conteúdo deste artigo é de rede, pertence ao autor original, o conteúdo é contribuído e carregado voluntariamente pelos usuários da Internet, o site não possui direitos de propriedade, não foi editado artificialmente e não assume responsabilidade legal relevante. Se você encontrar conteúdo suspeito de direitos autorais, envie e-mail para: notice#oldtoolbag.com (ao enviar e-mail, substitua # por @ para denunciar e forneça provas. Atingido, o site deletará imediatamente o conteúdo suspeito de infringir direitos autorais.)