Problema com JPanel, Eventos. [RESOLVIDO]

Olá pessoal, tudo bem.
Estou criando um jogo, no entando cheguei na parte de fazer o movimento da nave (o jogo é uma espécie de space invaders).

O programa está estruturado da seguinte forma (por enquanto):

Classe Main a qual instancia o objeto da classe janela , a Classe Janela que extends JFrame (a qual adiciona o painel) e a Classe PainelGame extends JPanel implements KeyListener a qual possui os components do jogo, invasores e a nave que você controla.

Código:

class Janela extends JFrame
{
    public Janela()
    {
        PainelGame painelGame = new PainelGame();
        add(painelGame);
        // mudar focus para o painel.
    }
}
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.*;

public class PainelGame extends JPanel implements KeyListener
{
   private JLabel barra;
   private JLabel label[] = new JLabel[10];
   private JLabel nave;
   private JLabel tiro;
   private Rectangle invasores[] = new Rectangle[10];
   private int k;
   public PainelGame()
   {

       setBackground(Color.black);
       setLayout(null);
       nave = new JLabel();
       nave.setIcon(new ImageIcon(getClass().getResource("/Imagens/nave.jpg")));
       tiro = new JLabel();
       tiro.setIcon(new ImageIcon(getClass().getResource("/Imagens/tiro.jpg")));
       barra = new JLabel();
       barra.setBounds(0,644,1000,30);
       barra.setIcon(new ImageIcon(getClass().getResource("/Imagens/barraInfo.jpg")));
       add(barra);
       add(nave);
       add(tiro);
       nave.setBounds(0,600,50,50);
        k = 50;
        for(int i=0;i<invasores.length;i++)
        {
            invasores[i] = new Rectangle();
            label[i] = new JLabel();
            invasores[i].setBounds(k,20,50,50);
            label[i].setBounds(k,20,50,50);
            label[i].setIcon(new ImageIcon(getClass().getResource("/Imagens/invader.jpg")));
            k = k + 50;
            add(label[i]);
        }
        addKeyListener(this);
      
   }

    public void keyTyped(KeyEvent e)
    {

    }

    public void keyPressed(KeyEvent e)
    {
     if( e.getKeyCode() == e.VK_ENTER)
     {
         nave.setBounds(0,300,50,50);
     }
    }

    public void keyReleased(KeyEvent e)
    {

    }

}

O problema é o seguinte: após apertar o Enter o JLabel da nave deveria se mover para a cordenada 0,300 mas isso não acontece, acredito que isso seja problema em relação ao Focus, até tentei colocar “requestFocus()”, mas não adiantou. Alguem sabe pq ocorre tal problema?

[]
ArchV.

Bom… primeiramente… não duplique topicos

depois tente apertar ALT + ENTER e responda se funcionou

Quanto ao tratamento de eventos, sem problema com o foco, leia isso.

E esteja ciente que este jogo pode (e é provável que) funcione, mas essa não é a maneira correta de se fazer. Uma ótima leitura para começar aqui. Um SpaceInvaders aqui. Um leitura mais completa aqui.

Ahh… e só para constar: você está carregando a mesma imagem (a dos invasores) invasores.length vezes. Se o vetor for muito grande levará muito tempo antes de iniciar. Carregue apenas uma vez e depois use-a.

Hmm, muito bem observado. Vlw a dica brother!

Agora ficou assim:

...
private ImageIcon img;

img = (new ImageIcon(getClass().getResource("/Imagens/invader.jpg")));
        for(int i=0;i<invasores.length;i++)
        {
            invasores[i] = new Rectangle();
            label[i] = new JLabel();
            invasores[i].setBounds(k,20,50,50);
            label[i].setBounds(k,20,50,50);
            label[i].setIcon(img);
            k = k + 50;
            add(label[i]);
        }
...

Então agora desta forma estou sobrecarregando menos o processamento ?

Vlw.

[quote=dudu_sps]Bom… primeiramente… não duplique topicos

depois tente apertar ALT + ENTER e responda se funcionou[/quote]

Não dupliquei o tópico, o outro é problema em relação a focus/thread e não se encaixa no mesmo contexto.
Então, tentei aqui Alt + Enter e não funcionou.

[quote=marcobiscaro2112]Quanto ao tratamento de eventos, sem problema com o foco, leia isso.

E esteja ciente que este jogo pode (e é provável que) funcione, mas essa não é a maneira correta de se fazer. Uma ótima leitura para começar aqui. Um SpaceInvaders aqui. Um leitura mais completa aqui.[/quote]

Vlw brother, vou ler isso e depois posto se consegui.

[]
ArchV.

Olá pessoal, seguindo o código-tutorial do ViniGodoy url=http://www.guj.com.br/posts/list/140986.java[/url]

Editei a classe PainelGame tratando os eventos tal como o tópico do ViniGodoy (link acima) sugere.
Mesmo assim, o evento não esta funcionando.


import java.awt.Color;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import javax.swing.*;

public class PainelGame extends JPanel 
{
   private JLabel barra;
   private JLabel label[] = new JLabel[10];
   private JLabel nave;
   private JLabel tiro;
   private Rectangle invasores[] = new Rectangle[10];
   private int k;
   private JLabelEvento labelEvento = new JLabelEvento(nave);
   private ImageIcon imgIcon;
   public PainelGame()
   {

       setBackground(Color.black);
       setLayout(null);
       nave = new JLabel();
       nave.setIcon(new ImageIcon(getClass().getResource("/Imagens/nave.jpg")));
       tiro = new JLabel();
       tiro.setIcon(new ImageIcon(getClass().getResource("/Imagens/tiro.jpg")));
       barra = new JLabel();
       barra.setBounds(0,644,1000,30);
       barra.setIcon(new ImageIcon(getClass().getResource("/Imagens/barraInfo.jpg")));
       add(barra);
       add(nave);
       add(tiro);
       nave.setBounds(0,600,50,50);
        k = 50;
        imgIcon = (new ImageIcon(getClass().getResource("/Imagens/invader.jpg")));
        for(int i=0;i<invasores.length;i++)
        {
            invasores[i] = new Rectangle();
            label[i] = new JLabel();
            invasores[i].setBounds(k,20,50,50);
            label[i].setBounds(k,20,50,50);
            label[i].setIcon(imgIcon);
            k += 50;
            add(label[i]);
        }
        registrarAcoesTeclado(this);
   }
   private void registrarAcoesTeclado(JPanel painel)
   {
    ActionMap actionMap = painel.getActionMap();
    actionMap.put("nave", labelEvento);
    painel.setActionMap(actionMap);

    InputMap iMap = painel.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
    iMap.put(KeyStroke.getKeyStroke("ENTER"), "nave");
   }
   private class JLabelEvento extends AbstractAction
   {

    private JLabel label;

    public JLabelEvento(JLabel label)
    {
        this.label = label;
    }

    public void actionPerformed(ActionEvent e)
    {
       label.setBounds(0,0,50,50);
    }

   }
}

Aparecem vários erros ao precionar o ENTER para ativar o evento, sendo que o primeiro a aparecer é:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
        at PainelGame$JLabelEvento.actionPerformed(PainelGame.java:68)

Alguém poderia me ajudar?

[]

Sim, quando vc cria o labelEvento, você passa o valor de “nave”. Porém naquela linha “nave” ainda é null.

Troque a declaração para:

private JLabelEvento labelEvento;

E logo depois da linha:

nave = new JLabel();  

Faça:

labelEvento = new LabelEvento(nave);

[quote=ViniGodoy]Sim, quando vc cria o labelEvento, você passa o valor de “nave”. Porém naquela linha “nave” ainda é null.

Troque a declaração para:

private JLabelEvento labelEvento;

E logo depois da linha:

nave = new JLabel();  

Faça:

labelEvento = new LabelEvento(nave);

Muito obrigado ViniGodoy, Funcionou perfeitamente.
:smiley:

Agora, o ideal não é você trabalhar com Labels e Panels, e sim com Java2D diretamente. Dá uma olhada nos links colocados pelo marco ali em cima. Vão te facilitar muito a vida.

Ok ViniGodoy, agradeço novamente a sua dica e irei estudar os tópicos/livros e implementar em Java2D diretamente.


ViniGodoy ou alguém que possa responder.

sobre o seguinte Fragmento.


 private class JLabelEvento extends AbstractAction
   {

    private JLabel label;
 //   private KeyStroke keyStroke; // tentei usar com e.getSource().
    private int pixelsX = 0;
    public JLabelEvento(JLabel label)
    {
        this.label = label;
    }


    public void actionPerformed(ActionEvent e)
    {
      
        String s;
        s = e.getActionCommand();
       if (s.equals("RIGHT"))
      {
       pixelsX+=15;
       label.setBounds(pixelsX,600,50,50);
      }
    }

No método ActionPerformed tentei estabelecer aquela comparação para saber se o usuário precionou RIGHT e consequentemente mover para a direita. No entanto, não é possível estabelecer a comparação desta forma, tentei também usando o cast (KeyStroke) no e.getSource(). para obter o getKeyStroke(“String”). Mas, não obtive sucesso.
Qual seria a maneira correta de estabelecer esta relação?

Atenciosamente,
ArchV.

Aproveitando a ocasião, há como controlar o pressionamento e o “soltamento” de teclas dessa maneira (assim como o keyPressed e keyReleased)?

Se alguém puder sanar esta dúvida que está aí a dias ficaria agradecido, e muito!

Att.
ArchV.

Galera, preciso resolver este problema o quanto antes, por favor, tem como alguem esclarecer essa dúvida? Vlw, abraço!

Up!

Não tem como fazer no ActionPerformed. Pq vc está usando ele no lugar do KeyPressed/KeyReleased?

ViniGodoy, primeiramente fiz com o KeyListener, mas ele não respondia ao comando (não mudava o label de posição) duvidei que isso seria problema em relação ao Focus (tentei “painel.requestFocus()” mas falhou), postei aqui daí você sujeriu o esquema do implements AbstractAction da calculadora pra resolver esse problema do focus, até resolveu mas, tal como eu disse acima não consigo “manipular(tratar)” e saber qual tecla foi precionada.

Se isso não for possível, irei voltar ao KeyListener. Obrigado pela atenção!

Agradeço.

Sim, mas pq vc não cria um action diferente para cada botão, como fiz na calculadora?

Olá ViniGodoy.
Fiz somente com um action pq a única coisa que deve ser alterada é o setBounds do JLabel “nave” (entendi o teu exemplo da calculadora, mas a lógica não irá ser análoga neste caso). No entanto, pensei , repensei e não consegui pensar na lógica para “tratar” estes eventos.

Até pensei em fazer os actions da seguinte forma:

  • Um (Action) para mover a nave para direita, outro para esquerda, outro para cima e o outro para baixo. No entanto, da forma que eu pensei isso iria precisar de 4 JLabels, até iria funcionar (o tratamento do evento) mas, como cada JLabel teria a imagem, após alguns movimentos iria ficar a imagem de 4 naves na tela e isso seria um problema não tão agradável de se tratar, pois as colisões que irei fazer no jogo será através da intersecção dos retangulos das imagens.

Preciso de um esboço de como ficaria esse tratamento que você sugeriu.

Atenciosamente, ArchV.

Por que você teria que criar 4 JLabels?

Você cria 4 classes Action, uma cada direção. Faz como a que vc fez ali atrás.
Essa classe recebe um JLabel como parâmetro.

Passe o mesmo JLabel para as 4 actions.

[quote=ViniGodoy]Por que você teria que criar 4 JLabels?

Você cria 4 classes Action, uma cada direção. Faz como a que vc fez ali atrás.
Essa classe recebe um JLabel como parâmetro.

Passe o mesmo JLabel para as 4 actions.[/quote]

Ola ViniGodoy, agora que intendi o raciocinio dessa forma de trabalhar com evento.
Agradeço ae por tudo, consegui fazer o que eu queria.

[b]ViniGodoy, ultima pergunta:

  • Sempre que eu tiver um programa e se em um certo ponto ele ter que trocar o painel e trabalhar com evento do teclado seria então mais prático trabalhar do modo que você ensinou por AbstractAction, InputMap e ActionMap? (para evitar o possível problema de focus)[/b]

Tópico Resolvido.

Vlw, abraço!