Fala galera.
Sou novo por aqui, tenho alguma experiência em programação. Trabalho a mais ou menos 8 anos na área e gostaria de dar uma pequena contribuição para esse fórum JAVA.
O exemplo que darei é sobre algoritmos de detecção e algoritmos de movimentação. Tenham em mente que esse exemplo serve para qualquer jogo, mas quando o desenvolvi, tinha em mente um jogo de estratégia.
Imaginem um jogo igual Starcraft ou Warcraft, onde eu tenha unidades de combate, e elas devem ter consciência de presença, ou seja, se um inimigo se aproximar, ela tem que saber disso. Além disso, ela deve poder se movimentar.
Fiz um pequeno projeto com 5 classes. Ficou bem simples o código e usei OO para que fique melhor ainda de entender. Abaixo, uma breve descrição das classes do projeto:
clsUnidade.java => Representa a unidade de combate. Nessa classe, ficará toda a lógica de estratégia, combate, defesa, ataque, DETECÇÃO DE INIMIGOS, MOVIMENTAÇÃO e etc. Enfim, tudo que for pertinente a "auto-suficiência", será implementado nessa classe.
clsDesenhaUnidade => Essa classe ficará responsável por desenhar na tela a unidade e possivelmente tudo que acontece com ela. Se ela se movimentar, esta classe deve fazer isso acontecer na tela, se ela explodir, mais uma vez essa classe deverá entrar em ação e desenhar ela explodindo. Enfim, ela vai ficar responsável por APENAS desenhar na tela o que está acontecendo na classe da unidade.
clsLocaisUnidade => Guardará todas as informações que forem "globais", ou seja, localização de unidades, depósitos de minérios, enfim. Tudo para orientar as unidades durante o jogo.
clsFrame => Desenha o "form" na tela.
Main => Dispara todo o processo.
Na lógica OO, as classes se comunicam da seguinte maneira:
Código:
[clsUnidade]
|
| } ENVIA DADOS (COORDENADAS X,Y).
|
[clsDesenhaUnidade]
|
| } PEGA OS DADOS ENVIADOS E DESENHA NO JPANEL.
|
[clsFrame] } ADICIONA A clsDesenhaUnidade E CONSEQUENTEMENTE DESENHA O JPG DA UNIDADE.
Como podem ver, as classes se comunicam em 3 camadas, deixando cada uma com sua tarefa, facilitando bastante a compreensão do código. Legal tb é que esse projeto usa Threads, ou seja, para aqueles que não sabem implementar threads em java (imprecindível para desenvolvimento de jogos), poderão aprender aqui. O resto das explicações estão em comentários no próprio código. Espero que consigam entender.
Vamos ao código:
Classe Unidade:
Código:
package radar;
public class clsUnidade
{
private int X; // Coordenada X.
private int Y; // Coordenada Y.
private int X2; // Largura.
private int Y2; // Altura.
private String ArquivoImagem; // Local onde se encontra o arquivo de imagem (JPG).
private int RaioRadar; //Raio de ação do radar da unidade.
private String Nome; // Uma identificação qualquer.
private int NovoX; // Destino de movimentação em X.
private int NovoY; // Destino de movimentação em Y.
// Faz com que a unidade se mova até os pontos indicados em NovoX,NovoY.
public void Mover()
{
// Movimento em X
if ((this.getNovoX()>0) && (this.getNovoX()>this.getX()))
{
// O novo ponto está a direita da unidade. Executa movimento --->
this.setX(this.getX()+1);
}
if ((this.getNovoX()>0) && (this.getNovoX()<this.getX()))
{
// O novo ponto está a esquerda da unidade. Executa movimento <---
this.setX(this.getX()-1);
}
if ((this.getNovoX()>0) && (this.getNovoX()==this.getX()))
{
// O novo ponto em X foi alcançado. Pode parar de andar em X.
this.setNovoX(0);
}
// Movimento em Y.
if ((this.getNovoY()>0) && (this.getNovoY()>this.getY()))
{ //
// O novo ponto está abaixo da unidade. Executa movimento para baixo.
this.setY(this.getY()+1);
}
if ((this.getNovoY()>0) && (this.getNovoY()<this.getY()))
{
// O novo ponto está acima da unidade. Executa movimento para cima.
this.setY(this.getY()-1);
}
if ((this.getNovoY()>0) && (this.getNovoY()==this.getY()))
{
// O novo ponto em Y foi alcançado. Pode parar de andar em Y.
this.setNovoY(0);
}
}
// Sobreposição de toString, para poder "exibir" o objeto.
public String toString()
{
return this.getNome()+" em " + this.getX()+ ","+this.getY();
}
// Retorna TRUE, se a unidade passada em pUnidade estiver no raio de ação deste objeto (this).
public boolean EstahNoRaio(clsUnidade pUnidade)
{
// Verifica se a coordenada X está dentro do raio de ação.
boolean XDentro = ((pUnidade.getX() >= this.getX()-this.getRaioRadar()) && (pUnidade.getX() <= this.getX()+this.getRaioRadar()));
boolean YDentro = ((pUnidade.getY() >= this.getY()-this.getRaioRadar()) && (pUnidade.getY() <= this.getY()+this.getRaioRadar()));
return (XDentro && YDentro);
}
// Faz loop no array de objetos registrados e verifica se há algum no raio de ação.
public void Detectar()
{
for(clsUnidade u: clsLocaisUnidades.Unidades)
{
if (!u.equals(this))
{
if (this.EstahNoRaio(u))
{
System.out.println(this+" detectou objeto "+u+".");
}
}
}
}
public clsUnidade(int pX,int pY,int pX2,int pY2, String pArquivoImagem)
{
this.setX(pX);
this.setY(pY);
this.setX2(pX2);
this.setY2(pY2);
this.setArquivoImagem(pArquivoImagem);
clsLocaisUnidades.RegistrarUnidade(this);
// Não adiciono esta unidada no painel agora pq eu estaria indo de encontro
// a orientação a objeto. Essa classe deve apenas cuidar de cálculos,
// estratégias de combate, deteção de inimigos e etc. Colocar algo sobre
// imagens aqui seria quebrar esta regra.
}
// Métodos Get e Set das variáveis da classe. Nada especial.
public int getX()
{
return X;
}
public void setX(int X)
{
this.X = X;
}
public int getY()
{
return Y;
}
public void setY(int Y)
{
this.Y = Y;
}
public String getArquivoImagem()
{
return ArquivoImagem;
}
public void setArquivoImagem(String ArquivoImagem)
{
this.ArquivoImagem = ArquivoImagem;
}
public int getX2()
{
return X2;
}
public void setX2(int X2)
{
this.X2 = X2;
}
public int getY2()
{
return Y2;
}
public void setY2(int Y2)
{
this.Y2 = Y2;
}
public int getRaioRadar()
{
return RaioRadar;
}
public void setRaioRadar(int RaioRadar)
{
this.RaioRadar = RaioRadar;
}
public String getNome()
{
return Nome;
}
public void setNome(String Nome)
{
this.Nome = Nome;
}
public int getNovoX()
{
return NovoX;
}
public void setNovoX(int NovoX)
{
this.NovoX = NovoX;
}
public int getNovoY()
{
return NovoY;
}
public void setNovoY(int NovoY)
{
this.NovoY = NovoY;
}
Agora, a classe clsDesenhaUnidade:
Código:
package radar;
import javax.swing.*;
import java.awt.*;
public class clsDesenhaUnidade extends JPanel implements Runnable
{
private clsUnidade Unidade; // Unidade que o painel usa para desenhar.
private ImageIcon Img; // Objeto que desenha o JPG da unidade.
private Thread threadRadar; // Thread responsável por dar "vida" a unidade.
// Faz uma pausa em pN segundos, na execução do loop de "vida" da unidade.
private void Pausa(int pN)
{
try
{
Thread.sleep(pN);
}
catch(Exception e)
{
}
}
// Atualiza as dimensões e localização da unidade.
private void AtualizarDimensoes()
{
this.setBounds(Unidade.getX(),Unidade.getY(),Unidade.getX()+Unidade.getX2(),Unidade.getY2()+Unidade.getY());
}
public clsDesenhaUnidade(clsUnidade pUnidade)
{
this.setUnidade(pUnidade);
this.AtualizarDimensoes(); // Aqui sim, eu posso "pintar" na tela a vontade.
threadRadar = new Thread(this);
threadRadar.start();
}
// Override necessário para que a unidade seja "pintada" com seu JPG.
public void paintComponent(Graphics g)
{
this.AtualizarDimensoes();
Img = new ImageIcon(Unidade.getArquivoImagem());
((Graphics2D) g).drawImage(Img.getImage(),Unidade.getX(),Unidade.getY(),this);
}
// Tem que ser obrigatoriamente sobrescrito, para classes que implementam RUNNABLE.
// Aqui fica o loop de vida da unidade. Todos os "estímulos" que ela deve responder,
// devem estar aqui. Essa é a principal parte do código.
public void run()
{
while (true)
{
Unidade.Detectar();
if ((Unidade.getNovoX()>0) || (Unidade.getNovoY()>0))
{
Unidade.Mover();
this.repaint();
}
this.Pausa(100); // Preciso dessa pausa, para que o gerenciador de segmentos da
// JVM possa executar outros segmentos. Este número pode ser reduzido.
}
}
// Métodos get e set das variáveis da classe. Nada especial.
public clsUnidade getUnidade()
{
return Unidade;
}
public void setUnidade(clsUnidade Unidade)
{
this.Unidade = Unidade;
}
}
Agora, a classe que sabe tudo no jogo:
Código:
package radar;
import java.util.*;
// Esta classe armazena as informações que devem ser "globais", para todo o jogo,
// como por exemplo: posicionamento de unidades.
public class clsLocaisUnidades
{
// Esta variável armazena todas as unidades do jogo, para que o algortimo de
// radar possa detectar quando uma unidade entra no radar de outra unidade.
static public ArrayList<clsUnidade> Unidades = new ArrayList<clsUnidade>();
// Deve ser atualizada toda vez que uma nova unidade for criada.
static public void RegistrarUnidade(clsUnidade pUnidade)
{
Unidades.add(pUnidade);
}
public clsLocaisUnidades()
{
}
}
A classe do JFrame:
Código:
package radar;
import javax.swing.*;
import java.awt.*;
public class clsFrame
{
// Classe responsável por criar e exibir o frame.
private JFrame Frame;
public clsFrame()
{
Frame = new JFrame("Teste de Radar");
Frame.setLayout(null);
Frame.setBounds(10,10,600,600);
Frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Frame.setVisible(true);
}
public void Adicionar(clsDesenhaUnidade pDu)
{
Frame.add(pDu);
}
}
E por fim, a classe Main, responsável por disparar tudo isso:
Código:
package radar;
public class Main
{
public Main()
{
}
public static void main(String[] args)
{
// Cria e exibe o form.
clsFrame frm = new clsFrame();
//Cria unidade na memória. Indique um arquivo que exista!
clsUnidade u = new clsUnidade(60,100,30,30,"c:\\img.jpg");
u.setNome("Azul");
u.setRaioRadar(80);
clsDesenhaUnidade du = new clsDesenhaUnidade(u);
// Adiciona unidade no form.
frm.Adicionar(du);
// Envia comando para pintar a unidade na tela.
du.repaint();
// Cria unidade 2 na memória. Indique um arquivo que exista!
clsUnidade u2 = new clsUnidade(60,0,30,30,"c:\\img2.jpg");
u2.setNome("Vermelho");
u2.setRaioRadar(0);
// Atrela unidade 2 no painel desenhador.
clsDesenhaUnidade du2 = new clsDesenhaUnidade(u2);
// Adiciona unidade 2 no form.
frm.Adicionar(du2);
// Envia comando para pintar a unidade na tela.
du2.repaint();
// Depois de 2 segundos...
try {Thread.sleep(2000);}catch(Exception e){}
// ...anda com a unidade até o ponto X=70,Y=200.
u2.setNovoX(70);
u2.setNovoY(200);
}
}
OBS: na classe Main, não esqueçam de colocar arquivos JPG que existam!
Quando vc executar o projeto, ele vai exibir um form e em seguida, os dois arquivos JPG que vc indicou. Dois segundos depois, a unidade de cima irá se aproximar da unidade que está mais abaixo. Assim que a unidade que está se movimentado entrar no radar da unidade que esta parada, mensagens vão aparecer no seu console, indicando que a unidade azul X,Y detectou unidade vermelha em X,Y.
Com isso, temos dois exemplos aqui: um algoritmo de movimentação e outro de detecção.
Qualquer dúvida, não exitem em perguntar!
Então é isso, até a próxima!