domingo, 9 de junho de 2013

[Tutorial] [Java] Jogo da Cobrinha (Snake)

Olá pessoal, ontem realizei um tutorial bem simples de Tetris em java, hoje vamos realizar um jogo talvez mais simples ainda, porém com mais recursos ( sejam espertos para analisar hehe ). Inicialmente crie um novo projeto de aplicação Java no NetBeans, ou em um programa de preferência. Como nome do projeto coloque SNAKE, e a classe principal com o nome Snake.

Inicialmente vamos manipular a classe Grade que contém o ambiente do jogo. O funcionamento é bastante simples, existe a cobrinha que pode ser movimenta para todas posições (cima, baixo, esquerda, direita) mas nunca para posições opostas (esquerda e direita, cima e baixo), a cobrinha come a comida, e aumenta o tamanho do corpo. Se a cobrinha colidir com ela mesma, ou sobre as bordas do jogo, então é fim de jogo. Para implementação disso existe a classe que denominei de Grade, vamos observar abaixo. ( To feliz aprendi a carregar o código-fonte corretamente no blog :') hahahaha )

package SNAKE;

// Importações necessárias para rodar o jogo
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;

/*
 *
 * AUTOR: CAIQUE MONTEIRO ARAUJO
 * DATA: 09/06/2013
 * CLASSE: GRADE
 * OBJETIVO: AMBIENTE DO JOGO
 *
 */

public class Grade extends JPanel implements ActionListener
{

    // Tamanho do JPanel em Largura x Altura
    private final int WIDTH_ = 400;
    private final int HEIGHT_ = 400;

    // Tamanho de cada ponto na tela
    private final int TAMANHO_PONTO = 10;
    // Tamanho total de pontos, multiplicando a largura e altura
    private final int TODOS_PONTOS = 1600;

    // Um valor aleatória para gerar posição
    private final int RAND_POSICAO = 29;
    // Um delay para o tempo de execução do jogo
    private final int DELAY = 140;

    // Definição do plano cartesiano (x,y) do jogo
    private int[] x = new int[TODOS_PONTOS];
    private int[] y = new int[TODOS_PONTOS];

    // Pontos da cobrinha
    private int pontos;
    // Posição (x,y) da comida
    private int comida_x;
    private int comida_y;

    // Contar pontuação
    private int PONTUAÇÃO = 0;
    // Mensagem da pontuação
    String SCORE = "PONTUAÇÃO: " + PONTUAÇÃO;
    // Fonte para escrever a pontuação, estilo da fonte
    Font SCORE_FONT = new Font("Consolas", Font.BOLD, 12);
    // Tamanho total da escrita na tela
    FontMetrics SCORE_METRICA = this.getFontMetrics(SCORE_FONT);

    // Definição dos movimentos
    private boolean left = false;
    private boolean right = false;
    private boolean up = false;
    private boolean down = false;

    // Denifição do status do jogo
    private boolean estaJogando = true;

    // Tempo de execução do jogo
    private Timer tempo;

    // Imagens da cabeça e corpo da cobrinha, e comida
    private Image bola;
    private Image comida;
    private Image cabeça;

    // Método construtor da classe
    public Grade ()
    {
        // Cria uma instrução de teclado
        addKeyListener(new TAdapter());

        // Seta o plano de fundo como preto
        setBackground(Color.BLACK);

        // Cria um icone do arquivo png e seta na imagem correspondente
        ImageIcon bola_ = new ImageIcon("bola.png");
        bola = bola_.getImage();

        // Cria um icone do arquivo png e seta na imagem correspondente
        ImageIcon comida_ = new ImageIcon("comida.png");
        comida = comida_.getImage();

        // Cria um icone do arquivo png e seta na imagem correspondente
        ImageIcon cabeça_ = new ImageIcon("cabeça.png");
        cabeça = cabeça_.getImage();

        // Define o foco para o JPanel
        setFocusable(true);
        // Define o tamanho da tela
        setSize(WIDTH_, HEIGHT_);

        // Inicializa do jogo
        initJogo();
    }

    // Método para inicializar o jogo
    public void initJogo()
    {
        // Define quantidade de pontos iniciais
        pontos = 3;

        // Define a posição em (x,y) de cada ponto
        for (int i = 0; i < pontos; i++)
        {
            x[i] = 50 - i*10;
            y[i] = 50;
        }

        // Gera a primeira comida
        localComida();

        // Inicia o tempo de execução do jogo
        tempo = new Timer(DELAY, this);
        tempo.start();
    }

    // Método para desenhar elementos na tela do jogo
    @Override
    public void paint (Graphics g)
    {
        // Define o atribuito para a classe própria
        super.paint(g);

        // Analisa se o jogo esta em andamento, se estiver desenha na tela,
        // se não estiver, o jogo é dado como o fim
        if (estaJogando)
        {
            // Desenha a comida no plano (x,y) do jogo
            g.drawImage(comida, comida_x, comida_y, this);

            // Para cada ponto da cobrinha, desenha a cabeça e o corpo
            // em (x,y)
            for (int i = 0; i < pontos; i++)
            {
                if (i == 0)
                { g.drawImage(cabeça, x[i], y[i], this); }
                else
                { g.drawImage(bola, x[i], y[i], this); }
            }

            // Desenha a pontuação na tela
            desenharPontuacao(g);

            // Executa a sincronia de dados
            Toolkit.getDefaultToolkit().sync();

            // Pausa os gráficos
            g.dispose();
        }
        else
        {
            // Executa o fim de jogo
            FimDeJogo(g);
        }
    }

    // Método para desenhar a pontuação na tela
    public void desenharPontuacao (Graphics g)
    {
        // Define a frase para escrever
        SCORE = "PONTUAÇÃO: " + PONTUAÇÃO;
        // Define o tamanho da fonte
        SCORE_METRICA = this.getFontMetrics(SCORE_FONT);

        // Define a cor da fonte
        g.setColor(Color.white);
        // Seta a fonte para o gráfico
        g.setFont(SCORE_FONT);
        // Desenha a fonte na tela
        g.drawString(SCORE, (WIDTH_ - SCORE_METRICA.stringWidth(SCORE)) - 10, HEIGHT_ - 10);
    }

    public void FimDeJogo (Graphics g)
    {
        // Define a frase para escrever
        String msg = "FIM DE JOGO! Sua pontuação: " + PONTUAÇÃO;
        // Define o estilo da fonte
        Font pequena = new Font("Consolas", Font.BOLD, 14);
        // Define o tamanho da fonte
        FontMetrics metrica = this.getFontMetrics(pequena);

        // Define a cor da fonte
        g.setColor(Color.white);
        // Seta a fonte para o gráfico
        g.setFont(pequena);
        // Desenha a fonte na tela
        g.drawString(msg, (WIDTH_ - metrica.stringWidth(msg)) / 2, HEIGHT_ / 2);
    }

    // Método para checar se a cobrinha comeu a comida
    public void checarComida ()
    {
        // Se ele comer na mesma posição (x,y) então aumenta o corpo da cobrinha
        // aumenta a pontuação e gera uma nova comida
        if ((x[0] == comida_x) && (y[0] == comida_y))
        {
            pontos++;
            PONTUAÇÃO++;
            localComida();
        }
    }

    // Método para mover a cobrinha na tela
    public void mover ()
    {
        // Para cada ponto da cobrinha desenha em (x,y)
        for (int i = pontos; i > 0; i--)
        {
            x[i] = x[(i - 1)];
            y[i] = y[(i - 1)];
        }

        // Se for para esquerda decrementa em x
        if (left)
        {
            x[0] -= TAMANHO_PONTO;
        }

        // Se for para direita incrementa em x
        if (right)
        {
            x[0] += TAMANHO_PONTO;
        }

        // Se for para cima decrementa em y
        if (up)
        {
            y[0] -= TAMANHO_PONTO;
        }

        // Se for para baixo incrementa em y
        if (down)
        {
            y[0] += TAMANHO_PONTO;
        }
        
    }

    // Método para checar colisão entre a cobrinha e as bordas do jogo
    public void checarColisão ()
    {
        // Para cada ponto, verifica se este está em posição com outro ponto
        // se estiver ele avista que o jogador parou de jogar devido a colisão
        for (int i = pontos; i > 0; i--)
        {
            if ((i > 4) && (x[0] == x[i]) && (y[0] == y[i]))
            { estaJogando = false; }
            
        }

        // Verifica se a cabeça da cobrinha encostou em algum ponto (x,y)
        // nas bordas (width,height) da tela
        if (y[0] > HEIGHT_)
        { estaJogando = false; }

        if (y[0] < 0)
        { estaJogando = false; }

        if (x[0] > WIDTH_)
        { estaJogando = false; }

        if (x[0] < 0)
        { estaJogando = false; }
    }

    // Método que gera uma comida na tela
    public void localComida ()
    {
        // Define um valor aleatório e atribui a uma posição x na tela para a
        // comida
        int random = (int) (Math.random() * RAND_POSICAO);
        comida_x = (random * TAMANHO_PONTO);

        // Define um valor aleatório e atribui a uma posição y na tela para a
        // comida
        random = (int) (Math.random() * RAND_POSICAO);
        comida_y = (random * TAMANHO_PONTO);
    }

    // Método de ações durante a execução do jogo
    public void actionPerformed (ActionEvent e)
    {
        // Se estiver jogando então já realiza a checagem da comida, depois
        // verifica se existe colisão, só então depois, realiza o movimento
        // da cobrinha no jogo, por fim, redesenha os resultados
        if (estaJogando)
        {
            checarComida();
            checarColisão();
            mover();
        }

        repaint();
    }

    // Classe para analisar o teclado
    private class TAdapter extends KeyAdapter
    {

        // Método para verificar o que foi teclado
        @Override
        public void keyPressed (KeyEvent e)
        {
            // Obtém o código da tecla
            int key =  e.getKeyCode();

            // Verifica os movimentos e manipula as variáveis, para movimentar
            // corretamente sobre a tela
            if ((key == KeyEvent.VK_LEFT) && (!right))
            {
                left = true;
                up = false;
                down = false;
            }

            if ((key == KeyEvent.VK_RIGHT) && (!left))
            {
                right = true;
                up = false;
                down = false;
            }

            if ((key == KeyEvent.VK_UP) && (!down))
            {
                up = true;
                left = false;
                right = false;
            }

            if ((key == KeyEvent.VK_DOWN) && (!up))
            {
                down = true;
                left = false;
                right = false;
            }
        }
    }
 
}

Prontinhoooooooooooooooooooooo, uma Grade que engloba todos funcionamentos, eu comentei durante o código completo para que vocês conseguissem acompanhar o raciocínio. No trecho do código:

        // Cria um icone do arquivo png e seta na imagem correspondente
        ImageIcon bola_ = new ImageIcon("bola.png");
        bola = bola_.getImage();

        // Cria um icone do arquivo png e seta na imagem correspondente
        ImageIcon comida_ = new ImageIcon("comida.png");
        comida = comida_.getImage();

        // Cria um icone do arquivo png e seta na imagem correspondente
        ImageIcon cabeça_ = new ImageIcon("cabeça.png");
        cabeça = cabeça_.getImage();
É preciso criar as imagens na pasta principal do projeto, ou mude a url para a pasta que desejar. As images abaixo, foram as que criei para o jogo. O tamanho delas são 10x10 pixels, a primeira é para o corpo, a segunda é da cabeça da cobrinha e a terceira é da comida.

A Grade está pronta, mas agora é preciso exibi-la na tela, para isso vamos manipular a classe principal Snake que criamos no inicio do projeto, acompanhem.

package SNAKE;

// Importações necessárias para rodar o jogo
import javax.swing.JFrame;

/*
 *
 * AUTOR: CAIQUE MONTEIRO ARAUJO
 * DATA: 09/06/2013
 * CLASSE: SNAKE
 * OBJETIVO: JFRAME PRINCIPAL DO JOGO
 *
 */

public class Snake extends JFrame
{

    // Método construtor da classe
    public Snake ()
    {
        // Adiciona o JPanel do jogo
        add(new Grade());

        // Define a saida da aplicação
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // Define o tamanho da janela
        setSize(420, 440);
        // A localização
        setLocationRelativeTo(null);
        // O titulo da janela
        setTitle("Jogo da Cobrinha");

        // Impede o redimensionamento da janela
        setResizable(false);
        // Mostra a janela
        setVisible(true);
    }

    public static void main(String[] args) 
    {
        // Cria o novo JFrame
        new Snake();
    }

}

Muito bem!!!!!! Acabou, uhuuuuuu, e se rodarmos o jogo obtemos a tela abaixo:



TCHAAAAAAAARÃNNNNNN, final de mais um tutorial, espero que tenham aproveitado o conteúdo e entendido o código, criem novas ideias, implementem novos recursos, esse jogo não é orientado a objeto, entretanto é possível criar coisas interessantes. Até a próxima pessoal!


20 comentários:

  1. Ai CK presiso fazer um jogo de tiro ao alvo tipo aqueles de barraquinhas de exposição, aonde vc atira uma bolinha ou dardo e ganha um premio, estou usando evendos do mouse mas n cosigo fazer nada funcionar. tenho menos de 2 mese pra entrega e estou perdido queia saber se vc pode me ajudar com alguma coisa, um jogo parecido se- la qualque coisa.

    ResponderExcluir
  2. Vou dexar meu email pra vc entra em contato caso queria me ajudar
    phdesouza@live.com ou pauloh.ti.2012@gmail.com

    ResponderExcluir
  3. Muito legal, irei usar como base para um trabalho ^^, só vou precisar deixar ele mais orientado a objeto pro meu "querido" professor :P (alguma sugestão?), e tentarei adicionar umas funcionalidade diferente.
    Muito obrigado.

    ResponderExcluir
    Respostas
    1. Separe as classes: uma para jogador, uma para maça, uma para a tela, veja o tutorial de Invasores do Espaço!

      Excluir
  4. Show mano, eu consegui compilar o fonte mas a telinha não exibiu as fotos png, só aparece a telinha preta... Acho que coloquei as fotos erradas, vai uma ajudinha ai...

    OBG: To compilando no terminal do linux sem usar IDE

    ResponderExcluir
  5. Após uma revisadinha achei o problema, pura falta de atenção!, as fotos estavam numa pasta separada... CURTINDO O JOGUINHO AGORA... VLWW!!

    ResponderExcluir
  6. Cara, bom dia. Este código roda normal no Eclipse?

    ResponderExcluir
  7. CK, você poderia dispolibilizar o projeto java para download ?

    ResponderExcluir
  8. Deixe os código aqui pow livre pra gente já cópia e cola pow assim ajuda mt as pessoas 👏💪

    ResponderExcluir
  9. CK, você poderia disponibilizar o projeto java para download ?

    ResponderExcluir
  10. Aew CK, ficou maneiro seu programa, parabens

    ResponderExcluir
  11. Pensei que era um jogo no isso

    ResponderExcluir
  12. Galera, não foi ele quem fez, é copy/paste de: http://zetcode.com/tutorials/javagamestutorial/snake/ ele apenas renomeou algumas variáveis.

    ResponderExcluir
    Respostas
    1. É verdade! Mas, já estudou programação? Existem códigos comuns, que não fogem muito da lógica estrutural de desenvolvimento. Nem mesmo esse código do ZetCode é original, tem em livros de Java bem mais antigos com a mesma estrutura lógica. Bom, pelo menos, achei os comentários interessantes, não acho que invalide o conteúdo, principalmente para quem não entende inglês. Pelo menos, ele ainda adicionou um sistema de pontuação.

      Excluir
    2. Vai estudar baba ovo.

      Excluir
  13. Estou a utilizar o blueJ e quando crio uma instrução para o teclado, ele não reconhece a class do TAdapter.
    Sabes como poderei alterar o código para ele funcionar ?

    ResponderExcluir