Dificuldades com o Graphics2D

19 respostas
L

Não consegui formular um título mais específico para descrever o problema que estou tendo. E talvez não consiga descrever perfeitamente a minha dúvida. Vou tentar explicar da forma mais simples e resumida possível.

Eu tenho uma classe chamada Sprite com uma propriedade do tipo Image. Essa classe tem o seguinte método para desenhar a imagem na tela:

public void draw(Graphics g) { g.drawImage(image, x, y, null); }
Onde x e y são também propriedades da classe, previamente definidos.

Em uma outra classe, eu faço a chamada de um outro método que, teoricamente deveria desenhar a imagem na tela. O método é o seguinte:

public void loop() {
        while(running) {
            Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
            g.setColor(Color.black);
            g.fillRect(0,0,800,600);

            image.draw(g);

            g.dispose();
            strategy.show();
        }
    }

Onde image é um objeto da classe Sprite e strategy é um objeto BufferStrategy, ambos previamente definidos.

Ok. O problema é que, por algum motivo, quando executo o código, a imagem que teoricamente deveria aparecer na tela (dentro de um JPanel dentro de um JFrame), não aparece. Assim que executado, o programa roda por tempo indeterminado, sem que nada aconteça. Nem mesmo o formulário JFrame aparece.

Ficou claro? Espero que saibam me dizer o que exatamente estou fazendo de errado, e como resolver.

Obrigado.

19 Respostas

J

Você precisa repintar o componente que está sendo usado pelo image( componente que te serviu o “graphics”). Senão não vai ver mudança nenhuma mesmo.

N

olha, eu sei que pode parecer besta, mas vc colocou o setVisible(true) no frame/panel? Pergunto isso pq não encontrei nenhum problema no seu código.

[]s

V

Quem dispara o método loop()?

Você chamou o método para criar o BufferStrategy?

Você já leu o artigo?
http://www.pontov.com.br/site/index.php/java/48-java2d/124-desenho-direto-active-rendering

E seria bom ler esse aqui também:
http://www.pontov.com.br/site/index.php/java/48-java2d/121-o-loop-de-animacao

Outra coisa. Dentro do seu gameloop, seria bom pelo menos uma chamada a Thread.yield() ou Thread.sleep(10).
Assim você evita que ele monopolize o processador.

Melhor ainda seria usar um algoritmo de controle de game loop, como descrito no artigo acima.

L

Vou tentar. Obrigado.

Setei como true sim, mas valeu pela ajuda.

L

O método loop() é disparado pela própria classe na qual ele está contido.

O objeto da classe BufferStrategy, como eu disse, foi previamente definido. Da seguinte forma:

createBufferStrategy(2);
strategy = getBufferStrategy();

Você já leu o artigo?
http://www.pontov.com.br/site/index.php/java/48-ja...esenho-direto-active-rendering

E seria bom ler esse aqui também:
http://www.pontov.com.br/site/index.php/java/48-java2d/121-o-loop-de-animacao


Não li não. Lerei assim que tiver tempo.

Outra coisa. Dentro do seu gameloop, seria bom pelo menos uma chamada a Thread.yield() ou Thread.sleep(10).
Assim você evita que ele monopolize o processador.

Melhor ainda seria usar um algoritmo de controle de game loop, como descrito no artigo acima.


Eu já havia feito isso, da seguinte forma:

try { Thread.sleep(10); } catch (Exception e) {}

Apenas não mostrei no meu post porque queria focar no meu problema, e mostrar isso me pareceu desnecessário.

Eu vou ler o seu artigo sim, mas se for possível descobrir a causa do problema sem que seja necessário ler o artigo seria bom também.

Obrigado pela ajuda.

V

Aparentemente não tem mesmo nada de errado com o seu código. Você poderia posta-lo completo? Basta zipa-lo e usar a opções de attachments, aqui do GUJ mesmo.

L

Ta aí!

V

Esse seu game é mesmo para rodar em desktop, ou você quer migra-lo para um JApplet? Ou a idéia é depois ir para J2ME?
Onde você está baseando para fazer o exemplo? O código tem um estilo muito velho de programação.

V

Bom, em todo caso está aqui o game com as correções.

O seu problema é que você chamava o método startLoop() de dentro do construtor do GameCanvas. Como o game loop vai ficar ativo até que o game termine, o Canvas nunca terminava de ser construído, e nunca seria exibido.
Além disso, você não deu em momento nenhum “setVisible(true)” no seu mainFrame.

Finalmente, outro problema é que o gameLoop não pode ser disparado por uma thread do Swing. Por isso, tive que retirar o EventQueue.invokeAndWait() do inicio do seu programa, o que faz o gameloop ser disparado pela main thread.

Procure ler os tutoriais de Java 2D do Ponto V. O estilo de programação que você está usando é um pouco pré-histórico, parece muito com o do Coke and Code (que ainda usava Java 4).
Tipei corretamente as listas e o map, o que evita aquele monte de casts que você está usando no seu código.

L

Sim, o game é pra rodar em desktop. E eu realmente estou me baseando num tutorial do Coke and Code (http://www.cokeandcode.com/node/6). Talvez por isso o estilo de programação pré-histórico.

Quanto ao MainFrame, eu dei setVisible(true) sim. Talvez não corretamente, mas foi chamado dentro do método main(). Tanto é que antes de tentar mostrar as imagens, o frame aparecia na tela normalmente.

Sobre as correções, eu vou dar uma olhada aqui e tentar entender. Muito obrigado mesmo, cara. :smiley:

L

Só uma dúvida: após as alterações que você fez, as imagens apareceram dentro do frame? Porque aqui, apesar do frame voltar a aparecer, as imagens não estão sendo desenhadas.

V

Então retire fora essa classe Canvas, que é da AWT, e faça o jogo diretamente sobre o JFrame.

Dê uma olhada nos tutoriais do meu site, o Ponto V, que explica também uma forma melhor de organizar o seu game loop.
Simplesmente dar um Sleep(10) é um problema, pois não garante uma taxa de FPS estável.

L

Vou ler os artigos. Qual é a ordem recomendada de leitura? Por qual começar? Lembrando que a minha principal dificuldade no momento, antes de fazer o loop ou qualquer outra coisa, é desenhar as imagens na tela. Até mesmo porque eu não vou fazer um loop. O método gameLoop() do código atual foi apenas uma tentativa de mostrar as imagens na tela, como um teste mesmo. Para a versão verdadeira eu usarei multi-threads.

V

No Ponto V! há uma série de tutoriais sobre Java 2D:
Escolhendo onde desenhar
Conhecendo o JFrame
Uma visão rápida sobre o Java 2D
Pintando no java 2D
O fantasma do Pacman (aqui tem um exemplo funcional)
Trabalhando com imagens

E como vai precisar fazer algo animado, veja também:
O loop de animação
A primeira animação
Desenho direto - Active Rendering

Outra boa fonte é o livro Killer Game Programming in Java, disponível online em:
http://fivedots.coe.psu.ac.th/~ad/jg/

Só cuidado que o livro também é em Java 4. O Java 5 foi um marco no Java, e esse material deveria ter sido atualizado depois dele. O livro do Brackeen, o tutorial do Coke and Code e o Killer tão precisando urgentemente de uma segunda edição.

L

Li todos os artigos, mas por causa do prazo apertado e da ânsia que isso gera, não consegui compreender ainda o mesmo que venho tentando compreender desde que criei esse tópico: como desenhar a imagem na tela? Talvez “desenhar” não seja o termo correto. “Desenhar”, “pintar”, “mostrar”… sei lá! O que eu quero saber é como fazer com que a imagem apareça na tela. Se eu entendi bem, os métodos draw(), drawImage(), paint() e semelhantes, por si só, não fazem com que a imagem seja pintada dentro do componente (no caso, o JFrame), fazem? Como fazer então?

V

Quando o Java desenha a janela, ele chama o método paint() da janela. Esse método é um pedido do java para a janela se pintar.

A janela, por sua vez, chama o método paintComponent de todos os componentes que estiverem dentro dela.

Quando você faz um jogo, você sobrescreve o método paint do JFrame para, ao invés de pintar componentes, pintar a tela do seu jogo.

Dentro do método paint, você recebe um objeto do tipo Graphics. Esse objeto é como se fosse um lápis (muito poderoso) capaz de pintar coisas na área de pintura.

O objeto Graphics possui métodos para pintar linhas, esferas, retângulos e imagens. Simplesmente use o drawImage dele.

L

Cara, muito obrigado. Consegui fazer o que pretendia.

Vou aproveitar o tópico pra tirar uma outra dúvida. Essa dúvida não está necessariamente relacionada com o tópico, mas como é meio urgente (e eu estou desesperado :smiley: ), vou perguntar aqui mesmo.

Qual o método mais eficiente de detectar colisões? O problema que eu estou tendo no meu jogo é que muitas vezes uma imagem atravessa a outra antes que dê tempo de detectar a colisão.

V

Geralmente basta usar BoundingBox usando a classe Rectangle2D.

Crie um Rectangle2D do tamanho das imagens dos sprites, e use o método intersects. Se isso não for suficiente, aí vai exigir mais matemática, pois você terá que levar em consideração a movimentação dos sprites entre dois frames.

L

É, mas foi justamente assim que eu fiz.

Enfim, deixa pra lá. Gostaria de ter tido mais tempo pra fazer. Não deu pra fazer todos os recursos que eu queria, e ficou cheio de bugs. Mas quando funciona até que é bem legal. :smiley:

Se quiser dar uma olhada, tá ali embaixo. Com certeza tem vários erros, e não tive tempo pra organizar do jeito que queria. E se não der problema na primeira vez, execute mais algumas vezes que certamente vai dar algum erro.

E mais uma vez, obrigado pela ajuda.

Criado 4 de novembro de 2010
Ultima resposta 8 de nov. de 2010
Respostas 19
Participantes 4