Jogo Snake em C++ - implementações iniciais

INTRODUÇÃO

Esse post tem o objetivo de apresentar a forma de iniciar o desenvolvimento do jogo snake em C++. Algumas características da linguagem serão utilizadas de forma contextualiza durante a criação do projeto, então o desenvolvimento do jogo busca ajudar, de forma divertida, na consolidação de conceitos, como: funcionalidades de POO (progração orientada à objetos), sintaxe do C++, bibliotecas (para windows) que não são usadas normalmente, manipulação de arquivos e estratégias para formatar um jogo. Segue, abaixo, um exemplo de como ficará o projeto após os três posts.

snake4

ESTRUTURANDO O PROJETO

Algumas bibliotecas precisam ser incluidas no projeto

   #include <stdlib.h>  // para usar o rand(), srand(), system()
   #include <windows.h> // para usar sleep(), Beep()
   #include <conio.h>   // para usar kbhit() e o getch()
   #include <time.h>    // para poder usar time(0)
   #include <cstring>   // para usar strcpy()
   #include <fstream>   // para usar a manipulacao de arquivo

Inicialmente será necessário criar uma classe, onde será definida todas características do projeto que estamos criando.

  class JogoSnake{
  public:
     //atributos

     //metodos
};

Os atributos necessários para definir essa classe precisam ser variáveis que auxiliem na criação da matriz do campo, inserção das frutas, contagem do tempo, marcação da pontuação, manipulação e criação da personagem. Entre os atributos também é necessario definir se o jogo pode permanecer executando ou deve ser finalizado, ou seja, teria ocorrido um game over por causa da cobra ter tocado a cauda, sendo essa a única forma de perder a partida.

A cabeça da cobra é criada com coordenadas (x,y) para ser situada na matriz do jogo, qualquer movimentação da cabeça é feita incrementado ou decrementando essas coordenadas. As frutas são criadas em posições aleatórias e utilizadas para marcar quando será necessário gerar novas frutas, incrementar a cauda e os pontos. A contagem do tempo é usada apenas para definir o momento em que a fruta extra permanecerá no campo.

    // atributos 
    bool perdeu;
    int const largura=20, altura=20; // a altura se refere as linhas e a largura as colunas. Essa e a dimensao da matriz do jogo
    int caudax[100], cauday[100]; // variaveis para as coordenadas em x e y dos fragmentos da cauda
    int x, y, frutaX, frutaY, extraX, tempo, extraY, contarCauda, pontos, auxPontos=0; /* variaveis para as coordenadas referentes a
                                                                             cabeca da cobra, a fruta, a fruta extra,
                                                                             pontuacao
                                                                            */

    enum direcao{parar=0, esquerda, direita, cima, baixo}; /* usado na movimentacao da cobra no jogo. Esse he um tipo de dado
    definido pelo usuário que define uma variável que vai receber apenas um conjunto restrito de valores */
   

    direcao pos; // pos pode ser parar, esquerda, direita, cima ou baixo
    bool tesquer, tdireita, tcima, tbaixo, acabouFextra=false, mostrarCauda; /* variaveis usadas para a cobra nao mudar de sentido (direita ou esquerda) quando estiver na horizontal e
                                                                                permancer na mesmo sentido (cima ou baixo) para o movimento vertical. acabouFextra auxilia quando o tempo
                                                                                da fruta extra termina e mostrarCauda ajuda para colocar espacos vazios quando nao esta sendo mostrada a
                                                                                cabeca da cobra, a fruta, a fruta extra e a cauda da cobra
                                                                               */ 

Os métodos são responsáveis por definir o comportamento da classe. No caso de JogoSnake serão criados seis métodos, sendo que entre eles estará o método construtor. Toda vez que um objeto da classe for instanciado o construtor é chamado e o objeto recebe as informações contidas nele, ou seja, é responsável por fazer a configuração inicial do jogo. A parte visual é criada no método desenho, isso inclui a moldura do campo, a cobra (com a extensão da cauda), a fruta, o placar de pontuação. As interações com o teclado para manipulação da cobra são feitas, no método entrada, por meio das teclas: a, w, s, d.

Toda a lógica de utilização do jogo é estruturado em um método, ajudando na criação e movimentação ordenada da cauda, ela precisa seguir a cabeça com fluidez. O método lógica também trata a passagem da cobra pelas paredes e o momento que ocorre a captura das frutas normais e extras. game over e gerarFrutasExtra são respectivamente responsáveis por: passar a variável que marca o estado de derrota para true e gerar as posições aleatórias para a fruta extra.

    // metodos 
         JogoSnake(); // construtor padrão da classe
    void desenho();
    void entrada();
    void logica();
    void gerarFrutaExtra();
    bool gameOver();
ANALISANDO O MÉTODO CONSTRUTOR

Esse método inicializa o jogo, fazendo com que a cabeça da cobra apareça, parada, no meio do campo, sem nenhum fragmento da cauda. A variável reponsável por marcar se o jogador perdeu ou não precisa ser false, porque inicialmente ele não foi derrotado. Nessa parte da configuração também são tratadas variáveis bool com a finalidade de evitar a mudança de direção da cobra. As coordenadas da fruta extra são geradas, mas só serão utilizadas se a pontuação chegar ao valor estipulado. O contador do tempo em que a fruta extra ficará em tela começa zerado.

 JogoSnake::JogoSnake(){ // metodo responsavel pelas configuracoes iniciais do jogo

    srand(time(0));   // usundo o relogio do computador para auxiliar na obtencao de numeros aleatorios
    perdeu=false; // essa variavel comeca em falso, porque o usuario ainda nao perdeu
    x=largura/2; // a cabeca da cobra inicia centralizada em x
    y=altura/2;  // a cabeca da cobra inicia centralizada em y
    frutaX=(rand()%(largura-3))+1; // gera uma coordenada aleatoria em x para a fruta
    frutaY=(rand()%(altura-3))+1;  // gera uma coordenada aleatoria em y para a fruta
    extraX=(rand()%(largura-3))+1; // gera uma coordenada aleatoria em x para a fruta extra
    extraY=(rand()%(altura-3))+1;  // gera uma coordenada aleatoria em y para a fruta extra
    contarCauda=0; //o crescimento da cauda comeca em 0
    pos=parar; // a cabeca da cobra comeca parada
    pontos=0; // o jogo inicia com a pontuacao zerada
    tempo=0; // o tempo da fruta extra inicia zerado, porque o jogador ainda nao esta com a pontuacao adequada
    tesquer=false;
    tdireita=false;
    tcima=false;
    tbaixo=false;

}
DESENHANDO A MATRIZ DO CAMPO DE JOGO
void JogoSnake::desenho(){ // metodo responsavel pela parte visual do jogo

    system("cls"); // limpa a tela
    cout<<"pontuacao: "<<pontos<<endl; // mostra a pontuacao
    cout<<"tempo da fruta extra: "<<tempo<<endl; // mostra o tempo da fruta extra
    cout<<endl;
    for(int i=0; i<largura+1; i++){ // mostra os quadrados da parte de cima da matriz
        cout<<char(176);
    }
    // percorre a matriz do jogo
    for(int i=0; i<altura; i++){
        for(int j=0; j<largura; j++){
            //formacao das paredes da matriz do jogo
            if(j==0 || j==largura-1){         // colocar um quadrado na primeira e na ultima posicao de cada linha
                if(i!=0){
                    cout<<char(176);
                }
            }
            if(j==x && i==y){                 //desenha a cabeca da cobra na posicao indicada por suas coordenadas
                cout<<char(219);
            }else if(j==frutaX && i==frutaY){ //desenha a fruta na posicao indicada por suas coordenadas
                cout<<char(184);
            }else if(pontos!=0 && pontos%50==0 && tempo<=50 && acabouFextra==false && j==extraX && i==extraY){  /*  condicoes para a fruta extra aparecer
                                                                                                                   (a cada 50 pontos, a contagem ser menor que 50 e a contagem nao ter estourado)
                                                                                                                */
                    cout<<char(206);
                    tempo++;                        // contagem do tempo em que a fruta extra fica disponivel

            }else{                                  /* quando nao estiver desenhando a cabeca da cobra, a fruta, a fruta extra
                                                       é verificado se a cauda da cobra precisa ser desenhada caso não precise é
                                                       colocado um espaco vazio na posicao, pois isso preenche o campo em torno da cobra,
                                                       fruta e fruta extra com espacos vazios dando forma a matriz que sera percorrida
                                                    */

                mostrarCauda=false;                /* inicialmente nao precisa mostrar a cauda, porque pode ser que ela nao exista
                                                      ou nao esteja no momento
                                                   */
                for(int p=0; p<contarCauda; p++){
                    if(caudax[p]==j && cauday[p]==i){
                        cout<<char(219);             // desenha a cauda da cobra
                        mostrarCauda=true;           // foi necessario mostrar a cauda

                    }
                }

                if(mostrarCauda==false){           // coloca espacos vazios quando nao e preciso mostar a cobra, fruta e fruta extra

                    cout<<" ";
                }
            }
        }
        cout<<endl;
    }
    for(int i=0; i<largura+1; i++){ // mostra os quadrados da parte de baixo da matriz do jogo
        cout<<char(176);
    }
    cout<<endl;
}

Após as implementações feitas é possível realizar o primeiro teste. Na função main() um objeto da classe JogoSnake precisa ser instanciado, com isso a chamada do método desenho mostra na tela o visual do nosso jogo. Não é possível fazer nenhuma movimentação com a cobra, porque as únicas funções da classe criadas até agora foram o construtor e desenho, respectivamente: inicializa características e cria a parte gráfica.

 int main(){
   JogoSnake *partida = new JogoSnake(); // instanciando um objeto, partida, da classe JogoSnake
   while(true){
      partida.desenho();
   }
   return 0;
}

Saída do jogo até o momento:

snake3

Comments