Hello people! Aqui estamos com a parte 2 de criação do nosso mini-jogo estilo Atari ( hehehe ). Nesta parte, vamos abordar duas classes, a principal e a Grade do jogo, a gigante, a enorme, a complexa! Acompanhem com calma que no fundo é bem fácil.
InvasoresDoEspaco (principal, JFrame) - Exibe o jogo na tela
Grade (JPanel) - Controla todos processos do jogo
CLASSE GRADE
A classe
GRADE, é a principal do jogo, que manipula todos gráficos na tela, assim como toda interação entre os objetos, eu poderia fazer um texto gigante para explicar ela inteira, mas o código já é maior ainda, e tem muitos comentários, então aproveitem todo código.
package INVASORESDOESPACO;
// Importações necessárias para a classe
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
/*
*
* AUTOR: CAIQUE MONTEIRO ARAUJO
* DATA: 09/06/2013
* CLASSE: GRADE
* OBJETIVO: AMBIENTE DO JOGO
*
*/
// EXTENDE A CLASSE AOS ATRIBUTOS E MÉTODOS DA CLASSE JPANEL
// IMPLEMENTA A PROCESSO DE EXECUÇÃO E A CLASSE COMUNS
public class Grade extends JPanel implements Runnable, Comuns
{
// DEFINE A DIMENSÃO DO AMBIENTE
private Dimension dimensão;
// CRIA UMA ARRAY COM OS ALIENS DO JOGO
private ArrayList aliens;
// CRIA UM JOGADOR PARA O JOGO
private Jogador jogador;
// CRIA UM TIRO PARA O JOGADOR
private Tiro tiro;
// DEFINE AS POSIÇÕES DE (x,y) PARA O ALIEN
private int alienX = 150;
private int alienY = 5;
// DEFINE A DIREÇÃO DO MOVIMENTO PADRÃO
private int direção = -1;
// DEFINE A QUANTIDADE DE MORTES DOS ALIENS
private int mortes = 0;
// DEFINE A QUANTIDADE DE VIDAS DO JOGADOR
private int vidas = 3;
// VALIDA QUE O JOGO ESTÁ EM EXECUÇÃO
private boolean estaJogando = true;
// DEFINE A IMAGEM PARA A EXPLOSÃO
private final String explosao = "explosão.png";
// DEFINE A IMAGEM PARA OS ALIENS
private final String alienimg = "alien.png";
// MENSAGEM DO JOGO
private String mensagem = "FIM DE JOGO!";
// THREAD PARA ANIMAR O JOGO
private Thread animador;
// MÉTODO CONSTRUTOR DA CLASSE
public Grade ()
{
// POSSIBILIDADE DE INTERAÇÃO COM O TECLADO
addKeyListener(new TAdapter());
// DEFINE O FOCO PARA ESSE JPANEL
setFocusable(true);
// DEFINE SUA DIMENSÃO
dimensão = new Dimension(LARGURA, ALTURA);
// SETA A COR DO PLANO DE FUNDO
setBackground(Color.BLACK);
// INICIA O JOGO
initJogo();
}
// CONTROLA AS NOTIFICAÇÕES DO THREAD
@Override
public void addNotify()
{
// DEFINE ESTE COMO ELE MESMO
super.addNotify();
// INICIA O JOGO
initJogo();
}
// MÉTODO PARA INICIAR O JOGO
public void initJogo ()
{
// CRIA UMA NOVA LISTA DE ALIENS
aliens = new ArrayList();
// CARREGA A IMAGEM DO ALIEN
ImageIcon alien_ = new ImageIcon(alienimg);
// MAPA DE ALIENS, DISPOSTOS EM 4 LINHAS X 6 COLUNAS
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 6; j++)
{
// CRIA UM NOVO ALIEN PASSANDO COMO PARÂMETOS SUAS POSIÇÕES NA TELA
Alieniginas alien = new Alieniginas(alienX + 18*j, alienY + 18*i);
// SETA A IMAGEM DO ALIEN CRIADO
alien.setImagem(alien_.getImage());
// ADICIONA O ALIEN NA ARRAY DO JOGO
aliens.add(alien);
}
}
// CRIA UM NOVO JOGADOR
jogador = new Jogador();
// CRIA UM NOVO TIRO
tiro = new Tiro();
// SE O THREAD ESTIVER NULL, OU O JOGO NÃO ESTIVER RODANDO
if (animador == null || !estaJogando)
{
// INSTÂNCIA PARA O THREAD ESTA CLASSE
animador = new Thread(this);
// INICIA A EXECUÇÃO DO THREAD
animador.start();
}
}
// MÉTODO PARA DESENHAR ALIENS NA TELA
public void desenharAliens (Graphics g)
{
// CRIA UM CURSOR DE POSICIONAMENTO NA ARRAY DOS ALIENS
Iterator it = aliens.iterator();
// ENQUANDO EXISTIR UM PRÓXIMO CURSOR, CAPTURA UM ALIEN DA ARRAY,
// SE ESTE ALIEN ESTIVER VISÍVEL ENTÃO DESENHA ELE NA TELA, COM SUA
// POSIÇÃO (x,y), SE ELE ESTIVER MORRENDO ENTÃO MATA O ALIEN NA TELA
while (it.hasNext())
{
Alieniginas alien = (Alieniginas) it.next();
if (alien.isVisivel())
{ g.drawImage(alien.getImagem(), alien.getX(), alien.getY(), this); }
if (alien.isMorrendo())
{ alien.morrer(); }
}
}
// MÉTODO PARA DESENHAR JOGADOR NA TELA
public void desenharJogador (Graphics g)
{
// SE O JOGADOR ESTIVER VISÍVEL ENTÃO DESENHA ELE NA TELA, COM SUA
// POSIÇÃO (x,y), SE ELE ESTIVER MORRENDO ENTÃO MATA O JOGADOR NA TELA
// E FINALIZA O JOGO
if (jogador.isVisivel())
{ g.drawImage(jogador.getImagem(), jogador.getX(), jogador.getY(), this); }
if (jogador.isMorrendo())
{ jogador.morrer(); estaJogando = false; }
}
// MÉTODO PARA DESENHAR TIRO NA TELA
public void desenharTiro (Graphics g)
{
// SE O TIRO ESTIVER VISÍVEL ENTÃO DESENHA ELE NA TELA, COM SUA
// POSIÇÃO (x,y)
if (tiro.isVisivel())
{ g.drawImage(tiro.getImagem(), tiro.getX(), tiro.getY(), this); }
}
// MÉTODO PARA DESENHAR BOMBA NA TELA
public void desenharBomba (Graphics g)
{
// CRIA UM CURSOR DE POSICIONAMENTO NA ARRAY DOS ALIENS
Iterator it = aliens.iterator();
// ENQUANDO EXISTIR UM PRÓXIMO CURSOR, CAPTURA UM ALIEN DA ARRAY,
// E OBTEM A BOMBA DESSE ALIEN, SE A BOMBA NÃO ESTÁ DESTRUIDA
// ENTÃO DESENHA ELA NA TELA COM SUAS POSIÇÕES (x,y)
while (it.hasNext())
{
Alieniginas alien = (Alieniginas) it.next();
Alieniginas.Bomba bomba = alien.getBomba();
if (!bomba.isDestruido())
{ g.drawImage(bomba.getImagem(), bomba.getX(), bomba.getY(), this); }
}
}
// MÉTODO PARA DESENHAR A PONTUAÇÃO NA TELA
public void desenharScore (Graphics g)
{
// DEFINE A STRING DE EXIBIÇÃO
String score = "Pontuação: " + mortes;
// CRIA UM ESTILO DE FONTE
Font estilo = new Font("Consolas", Font.BOLD, 12);
// SETA A COR DA FONTE
g.setColor(Color.white);
// SETA O ESTILO DE FONTE
g.setFont(estilo);
// DESENHA A STRING COM A POSIÇÃO (x,y)
g.drawString(score, 10, 20);
}
// MÉTODO PARA DESENHAR VIDAS NA TELA
public void desenharVidas (Graphics g)
{
// DEFINE A STRING DE EXIBIÇÃO
String vida = "VIDAS";
// CRIA UM ESTILO DE FONTE
Font estilo = new Font("Consolas", Font.BOLD, 12);
// CRIA A MÉTRICA DA FONTE
FontMetrics metrica = this.getFontMetrics(estilo);
// OBTÉM O TAMANHO DA STRING NA TELA
int width = metrica.stringWidth(vida);
// CALCUPA A DISTÂNCIA DA BORDA PARA POSICIONAR A STRING
int distancia = (15 * vidas) + (5 * vidas) + 25 + width;
// PARA CADA VIDA DO JOGADOR, DESENHA UMA IMAGEM DA VIDA,
// ALTERANDO A POSIÇÃO COM BASE NOS CALCULOS PARA DEFINIR
// A POSIÇÃO DE CADA UMA EM (x,y)
for (int i = 0; i < vidas; i++)
{
g.drawImage(jogador.getImagem(), LARGURA - width, 10, this);
width += JOGADOR_WIDTH + 5;
}
// SETA A COR DA FONTE
g.setColor(Color.white);
// SETA O ESTILO DE FONTE
g.setFont(estilo);
// DESENHA A STRING COM A POSIÇÃO (x,y)
g.drawString(vida, LARGURA - distancia, 20);
}
// MÉTODO PARA DESENHAR NA TELA COM ATUALIZAÇÃO
@Override
public void paint (Graphics g)
{
// DEFINE O PAINT COMO ELE MESMO
super.paint(g);
// SETA UMA COR
g.setColor(Color.black);
// DESENHA UM RETÂNGULO DO TAMANHO DA TELA
g.fillRect(0, 0, dimensão.width, dimensão.height);
// SETA UMA COR
g.setColor(Color.green);
if (estaJogando)
{
// DESENHA UMA LINHA PARA REPRESENTAR A BASE
g.drawLine(0, BASE, LARGURA, BASE);
// DESENHA TODOS OBJETOS DO JOGO CHAMANDO OS MÉTODOS CRIADOS
// ANTERIORMENTE
desenharAliens(g);
desenharJogador(g);
desenharTiro(g);
desenharBomba(g);
desenharVidas(g);
desenharScore(g);
}
// ATIVA A SINCRONIA
Toolkit.getDefaultToolkit().sync();
// PAUSA
g.dispose();
}
// DEFINE O CICLO DE ANIMAÇÃO E EXECUÇÃO DO JOGO NA TELA
public void cicloAnimacao()
{
// VERIFICA SE JÁ MORRERAM TODOS OS ALIENS, CASO ISSO TENHA ACONTECIDO
// PARA O JOGO, E DEFINE A MENSAGEM
if (mortes == NUMERO_DE_ALIENS)
{
estaJogando = false;
mensagem = "VOCÊ VENCEU!";
}
// JOGADOR COMEÇA A OPERAR
jogador.atuar();
// CONTROLA O TIRO SE ESTE ESTIVER VISÍVEL
if (tiro.isVisivel())
{
// CRIA UM CURSOR DE POSICIONAMENTO NA ARRAY DOS ALIENS
Iterator it = aliens.iterator();
// OBTEM AS POSIÇÕES (x,y) DO TIRO
int tiroX = tiro.getX();
int tiroY = tiro.getY();
// ENQUANTO O EXISTIR UM PRÓXIMO CURSOR, ELE CAPTURA UM ALIEN
// DA ARRAY, RESGATA SEU POSICIONAMENTO EM (x,y) VERIFICA SE
// O ALIEN ESTÁ VISÍVEL E O TIRO TAMBÉM, SE ESTIVER VERIFICA SE
// O TIRO ATINGIU O ALIEN CAPTURADO, CASO ISSO ACONTEÇA, DEFINE
// UMA NOVA IMAGEM PARA O ALIEN, INFORMA QUE O ALIEN ESTÁ MORRENDO,
// ACRESCENTA UMA MORTE NO JOGO, E TERMINA O TIRO
while (it.hasNext())
{
Alieniginas alien = (Alieniginas) it.next();
int alienX_ = alien.getX();
int alienY_ = alien.getY();
if (alien.isVisivel() && tiro.isVisivel())
{
if (tiroX >= alienX_ &&
tiroX <= (alienX_ + ALIEN_WIDTH) &&
tiroY >= alienY_ &&
tiroY <= (alienY_ + ALIEN_HEIGHT))
{
ImageIcon explosao_ = new ImageIcon(explosao);
alien.setImagem(explosao_.getImage());
alien.setMorrendo(true);
mortes++;
tiro.morrer();
}
}
}
// PERCORRE O TIRO NO EIXO Y PARA CIMA, CASO O TIRO SAIA DA TELA
// TERMINA O TIRO, CASO CONTRÁRIO CONTINUA SUBINDO
int y = tiro.getY();
y -= 4;
if (y < 0)
{ tiro.morrer(); }
else
{ tiro.setY(y); }
}
// CONTROLE DE ALIENIGINAS
// CRIA UM CURSOR DE POSICIONAMENTO NA ARRAY DOS ALIENS
Iterator itAlien = aliens.iterator();
// ENQUANTO O EXISTIR UM PRÓXIMO CURSOR
while (itAlien.hasNext())
{
// CAPTURA UM ALIEN E OBTEM SEU POSICIONAMENTO EM X
Alieniginas alien1 = (Alieniginas) itAlien.next();
int x = alien1.getX();
// SE A DIREÇÃO FOR PARA DIREITA E ULTRAPASSAR A BORDA
// INVERTE A DIREÇÃO PARA ESQUERDA, CRIA UM NOVO CURSOR,
// E ENQUANTO EXISTIREM ALIENS DESCE A POSIÇÃO EM Y
if (x >= LARGURA - BORDA_DIREITA && direção != -1)
{
direção = -1;
Iterator itAlienAux = aliens.iterator();
while (itAlienAux.hasNext())
{
Alieniginas alien2 = (Alieniginas) itAlienAux.next();
alien2.setY(alien2.getY() + VAI_ABAIXO);
}
}
// SE A DIREÇÃO FOR PARA ESQUERDA E ULTRAPASSAR A BORDA
// INVERTE A DIREÇÃO PARA DIREITA, CRIA UM NOVO CURSOR,
// E ENQUANTO EXISTIREM ALIENS DESCE A POSIÇÃO EM Y
if (x <= BORDA_ESQUERDA && direção != 1)
{
direção = 1;
Iterator itAlienAux = aliens.iterator();
while (itAlienAux.hasNext())
{
Alieniginas alien = (Alieniginas) itAlienAux.next();
alien.setY(alien.getY() + VAI_ABAIXO);
}
}
}
// CRIA UM CURSOR DE POSICIONAMENTO NA ARRAY DOS ALIENS
Iterator itAliensAux2 = aliens.iterator();
// ENQUANTO O EXISTIR UM PRÓXIMO CURSOR, CAPTURA UM ALIEN DA ARRAY
// OBTEM SUA POSIÇÃO EM Y, SE ELE CHEGAR NA BASE, É FIM DE JOGO POR
// INVASÃO DOS ALIENS. E TAMBÉM ATUA O ALIEN NA POSIÇÃO X COM BASE
// NA DIREÇÃO
while (itAliensAux2.hasNext())
{
Alieniginas alien = (Alieniginas) itAliensAux2.next();
if (alien.isVisivel())
{
int y = alien.getY();
if (y > BASE - ALIEN_HEIGHT)
{
estaJogando = false;
mensagem = "INVASÃO!";
}
alien.atuarX(direção);
}
}
// CONTROLE DE BOMBAS
// CRIA UM CURSOR DE POSICIONAMENTO NA ARRAY DOS ALIENS PARA BOMBAS
Iterator itBomba = aliens.iterator();
// CRIA UM RANDOM
Random random = new Random();
// ENQUANTO O EXISTIR UM PRÓXIMO CURSOR
while (itBomba.hasNext())
{
// ESTABELECE UM VALOR ALEATÓRIO PARA ATIRAR A BOMBA
int tiro_ = random.nextInt(15);
// CAPTURA UM ALIEN DA ARRAY
Alieniginas alien = (Alieniginas) itBomba.next();
// CAPTURA A BOMBA PARA ESTE ALIEN
Alieniginas.Bomba bomba_ = alien.getBomba();
// SE O TIRO FOR IGUAL A CHANCES DE ACERTAR, E O ALIEN ESTIVER VISÍVEL
// E A ULTIMA BOMBA ESTIVER DESTRUIDA, ENTÃO DISPARA OUTRA BOMBA, NA
// POSIÇÃO (x,y) DO ALIEN
if (tiro_ == CHANCES && alien.isVisivel() && bomba_.isDestruido())
{
bomba_.setDestruido(false);
bomba_.setX(alien.getX());
bomba_.setY(alien.getY());
}
// OBTEM AS POSIÇÕES DA BOMBA
int bombaX = bomba_.getX();
int bombaY = bomba_.getY();
// OBTEM AS POSIÇÕES DO JOGADOR
int jogadorX = jogador.getX();
int jogadorY = jogador.getY();
// SE O JOGADOR ESTIVER VISÍVEL E A BOMBA NÃO ESTIVER DESTRUIDA,
// ENTÃO VERIFICA SE A BOMBA ATINGIU A POSIÇÃO DO JOGADOR EM ALGUM
// PONTO (x,y), SE FOR VÁLIDO, VERIFICA SE A QUANTIDADE DE VIDAS É 1
// SE FOR, DEFINE COMO MORTE PARA O JOGADOR E DESTRUIÇÃO PARA BOMBA,
// CASO CONTRÁRIO DESTRÓI A BOMBA, E TIRA UMA VIDA DO JOGADOR
if (jogador.isVisivel() && !bomba_.isDestruido())
{
if (bombaX >= (jogadorX) &&
bombaX <= (jogadorX + JOGADOR_WIDTH) &&
bombaY >= (jogadorY) &&
bombaY <= (jogadorY + JOGADOR_HEIGHT))
{
if (vidas == 1)
{
ImageIcon explosao_ = new ImageIcon(explosao);
jogador.setImagem(explosao_.getImage());
jogador.setMorrendo(true);
bomba_.setDestruido(true);
}
else
{
bomba_.setDestruido(true);
vidas--;
}
}
}
// SE A BOMBA NÃO ESTIVER DESTRUIDA DESCE ELA NO PONTO Y
// SE ELA ATINGIR SOMENTE A BASE, SETA ELA COMO DESTRUIDA
if (!bomba_.isDestruido())
{
bomba_.setY(bomba_.getY() + 1);
if (bomba_.getY() >= BASE - BOMBA_HEIGHT)
{ bomba_.setDestruido(true); }
}
}
}
// MÉTODO DE EXECUÇÃO DA CLASSE
public void run ()
{
// DEFINE OS CONTROLES DE TEMPO
long antesTempo, tempoDiferente, sleep;
// OBTEM O TEMPO ATUAL
antesTempo = System.currentTimeMillis();
// ENQUANTO O JOGO ESTIVER EM OPERAÇÃO
while (estaJogando)
{
// REDESENHA A TELA
repaint();
// OPERA AS ANIMAÇÕES
cicloAnimacao();
// DEFINE UM O TEMPO ATUAL EM RELAÇÃO AO ANTERIOR
tempoDiferente = System.currentTimeMillis() - antesTempo;
// APLICA UM DELAY
sleep = DELAY - tempoDiferente;
// NÃO DEIXA ELE SEM DORMIR
if (sleep < 0)
{ sleep = 2; }
try
{
// TENTA FAZER O THREAD DORMIR
Thread.sleep(sleep);
}
catch (InterruptedException e)
{
System.out.println("Erro de interrupção!");
}
// DEFINE O NOVO TEMPO
antesTempo = System.currentTimeMillis();
}
// MOSTRA A MENSAGEM DE FIM DE JOGO
FimDeJogo();
}
// MÉTODO PARA DESENHAR A MENSAGEM NA TELA
public void FimDeJogo ()
{
// OBTEM OS GRÁFICOS DA TELA
Graphics g = this.getGraphics();
// SETA UMA COR
g.setColor(Color.black);
// CRIA UM RETANGULO
g.fillRect(0, 0, LARGURA, ALTURA);
// SETA A COR
g.setColor(new Color(0, 32, 48));
// DEFINE UM NOVO RETANGULO
g.fillRect(50, LARGURA/2 - 30, LARGURA-100, 50);
// SETA A COR
g.setColor(Color.white);
// DEFINE UM NOVO RETANGULO
g.drawRect(50, LARGURA/2 - 30, LARGURA-100, 50);
// DEFINE O ESTILO DA FONTE
Font estilo = new Font("Consolas", Font.BOLD, 14);
// DEFINE A MÉTRICA DA FONTE
FontMetrics metrica = this.getFontMetrics(estilo);
// SETA UMA COR
g.setColor(Color.white);
// SETA UM ESTILO DA FONTE
g.setFont(estilo);
// DESENHA A MENSAGEM NA TELA
g.drawString(mensagem, (LARGURA - metrica.stringWidth(mensagem))/2, LARGURA/2);
}
// CRIA UMA CLASSE DE CAPTURA DO TECLADOR
private class TAdapter extends KeyAdapter {
// DISPARA O MÉTODO DE SOLTA DE TECLA DO JOGADOR
@Override
public void keyReleased(KeyEvent e)
{
jogador.keyReleased(e);
}
// DEFINE O MÉTODO DE TECLA PRESSIONADA DO JOGADOR
@Override
public void keyPressed(KeyEvent e)
{
// O JOGADOR SE MOVIMENTA
jogador.keyPressed(e);
// CAPTURA SUA POSIÇÃO (x,y)
int x = jogador.getX();
int y = jogador.getY();
// SE ESTIVER JOGANDO
if (estaJogando)
{
// OBTEM A TECLA PRESSIONADA
int key = e.getKeyCode();
// SE A TECLA FOR Z E O TIRO DO JOGADOR NÃO ESTIVER VISÍVEL
// DISPARA OUTRO TIRO NA TELA
if (key == KeyEvent.VK_Z)
{
if (!tiro.isVisivel())
{ tiro = new Tiro(x, y); }
}
}
}
}
}
WOOOOOOOOOOOOOOAH BIG! 605 linhas de puro código de funcionamento para um jogo tão simples como esse. UFA, mas agora vem a parte mais suave e final para o jogo funcionar.
CLASSE INVASORES DO ESPAÇO
A classe
InvasoresDoEspaço é a mais básica somente para gerar o JFrame a abrir o JPanel na tela. Observem abaixo.
package INVASORESDOESPACO;
// Importação necessária para a classe
import javax.swing.JFrame;
/*
*
* AUTOR: CAIQUE MONTEIRO ARAUJO
* DATA: 09/06/2013
* CLASSE: INVASORESDOESPACO
* OBJETIVO: TELA DO JOGO
*
*/
public class InvasoresDoEspaco extends JFrame implements Comuns
{
// MÉTODO CONTRUTOR DA CLASSE
public InvasoresDoEspaco ()
{
// CRIA UMA NOVA GRADE NA TELA
add(new Grade());
// DEFINE O TITULO
setTitle("Invasores do Espaço");
// INFORMA O MÉTODO DE SAÍDA
setDefaultCloseOperation(EXIT_ON_CLOSE);
// SETA O TAMANHO
setSize(LARGURA, ALTURA);
// SETA A LOCALIZAÇÃO
setLocationRelativeTo(null);
// SETA A VISIBILIDADE
setVisible(true);
// SETA SE É REDIMENSIONAVEL
setResizable(false);
}
// MÉTODO PRINCIPAL
public static void main(String[] args)
{
// INSTÂNCIA UM NOVO JOGO
new InvasoresDoEspaco();
}
}
FINALMENTE ESSE MINI JOGO TÁ COMPLETO! UHUUUUU, tá, básico mais enfim tenho orgulho desse código. Abaixo eu mostro pra vocês a tela do jogo. ( PS. No jogo Snake eu dei os sprites que fiz, nesse usem a criatividade e criem um para o alien, jogador, bomba, tiro e explosão, as dimensões dessas imagens, estão no código do jogo )