JPanel - Movimento com e sem "rastro"

Ola Pessoal,

Sou novo aqui no forum e estou com uma duvida.

Eu tenho um ponto e um retangulo que se movem da direita para a esquerda. O retangulo ele se move sem deixar “rastro”, mas queria que o ponto se movimente deixando o “rastro” na tela. Tem como fazer isso sem precisar armazenar tadas as posicoes do ponto em um vetor??? Se eu armazenar a minha aplicacao fica muito lenta…

Na minha classe paintComponent se eu comentar o super.paintComponent(g2D); os movimentos do ponto e do retangulo sao feitos com rastros…

[code]public void paintComponent(Graphics g)
{
g2D = (Graphics2D)g;

super.paintComponent(g2D);

}[/code]

Alguem tem alguma idea de como resolver isso???

Obrigado.
[]s

A linha do super vai chamar o comportamento padrão do JPanel, que é limpar o background.

Infelizmente, a maneira mais fácil de implementar o rastro é mesmo armazenando um pequeno set ou array das coordenadas anteriores do mouse. Você pode poupar processamento evitando o armazenamento de pontos idênticos ou muito próximos, típicos de um arrasto lento.

Outra coisa. Você não pode manipular o objeto Graphics recebido no parâmetro diretamente. Isso pode alterar o estado desse objeto, o que é proibido no Swing. Desrespeitar essa regra pode gerar um comportamento estranho e difícil de depurar na sua aplicação. Então, use assim:

@Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D)g.create(); //Pintura aqui g2d.dispose(); }

Ola ViniGodoy,

Obrigado pelas dicas… Na realidade estou utilizando a classe Timer para fazer os movimentos do retangulo e do ponto. Pensei que tinha uma forma para resolver esse problema do “rastro” pq armazenando os pontos a aplicacao fica realmente muito lenta cada vez que o vetor vai aumentando… mas vou utilizar a sua ideia de evitar o armazenamento dos pontos muito proximos no vetor.

Estou com uma outra duvida. Quando vc explicou que eu nao posso “Desrespeitar essa regra pode gerar um comportamento estranho e difícil de depurar na sua aplicação”. O que seria esse comportamento estranho?? Pq utilizando o codigo q eu enviei os movimentos do retangulo e do ponto acontecem sem nenhum erro…

Outra coisa no eclipse da erro na linha :

Graphics2D g2D = (Graphics2D).create(); 

O compilador nao “reconhece” o (Graphics2D).create();

[]s

O comportamento estranho é imprevisível. As vezes alguns frames ficam desenhados de maneira errada. O fato é que a Sun diz explicitamente para não alterar o estado do objeto Graphics. Então, você tem 2 alternativas:

  1. A difícil: Grave os atributos que for modificar em variáveis antes de fazer isso, e restaure seu valor original num finally;
  2. A fácil: Tire uma cópia do Graphics, modifique a cópia como quiser, e libere-a ao final.

Quanto ao erro no comando. Bem, é que o que eu postei tinha um errinho mesmo. O certo é:
Graphics2D g2D = (Graphics2D)g.create();

Como eu falei, não acumule pontos demais no seu rastro. Você não precisa guardar absolutamente todos os pontos no seu vetor. Mantenha só os últimos 15 ou 20. Após esse tamanho, para cada ponto adicionado no final, remova o ponto que está no início (talvez uma LinkedList seja melhor indicada do que um ArrayList para usar essa técnica).

O melhor modo de fazer animações não é através da classe Timer. Essa classe compartilha threads com todos os timers da aplicação, o que pode comprometer seriamente o desempenho e gerar efeitos inconvenientes como uma taxa de FPS inconstante, flickering e tearing. O ideal seria você utilizar um thread próprio. Esse material aqui explica certinho a diferença de cada abordagem:
http://fivedots.coe.psu.ac.th/~ad/jg/ch1/index.html

Se você estiver desenhando em toda a área do seu JFrame, então é melhor sobrescrever o próprio JFrame e manipular diretamente os gráficos através da classe BufferStrategy. Isso permite que você ignore os repaints do Swing e use o que chamamos de “pintura direta”, o que dá resultados muitíssimo melhores para animações no geral.

Ola ViniGody,

Muito obrigado pelas dicas. As suas respostas sao sempre muito claras.

Com relacao ao uso da classe Timer… Eu postei no forum o seguinte problema (http://www.guj.com.br/posts/list/101927.java). Sera que eh por isso q estou com esse problema de lentidao?

Eu tinha utilizado threads quando comecei a implementar o sistema… mas estava com problema de desempenho quando executava em uma outra maquina com 256Mb de RAM, por exemplo. Naquela epoca eu andei pesquisando muito sobre isso… e achei em um outro forum que threads nao garante ocuracidade… e que se eu precisar que a minha aplicacao rode igual em qualquer maquina eu tinha que usar a classe Timer (http://www.portaljava.com/forum/posts/list/35674.page). E foi isso q eu fiz…

[]s

É bem provável. Mas o ideal é usar um profiler para identificar os gargalos da sua aplicação.
Você desenvolve em que? Eclipse? Netbeans?

Apesar de desenvolver no Eclipse, eu geralmente uso o profiler do Netbeans para esses casos. Eu monto meu projeto lá, rodo e com isso é fácil identificar rapidamente os gargalos da aplicação. Sugiro que você faça o mesmo.

Ah, sim, como você está fazendo a movimentação no seu programa?

Você chega a calcular quanto tempo se passou entre um ciclo e outro do timer, para então multiplicar esse valor pelo velocidade?

Pq, deslocamento, pela física é dado por:
deslocamento = velocidade * tempo;

O tempo é obtido subtraindo o System.currentTimeMillis() desse loop e do anterior. Com isso vc também pode obter a famosa taxa de frames por segundo.

Logo, se vc quiser deslocar um ponto a 10 pixels/segundo, você deveria ter uma velocidade de 0.001 pixel/ms. Então, essa velocidade seria multiplicada pela quantidade de milis entre uma chamada de timer e outro (essa quantidade pode variar), e o deslocamento final seria constante. Um erro comum de quem está começando em CG é confiar demais no timer, e achar que a velocidade será constante.

Bem, não será. Nem o timer é constante e nem a medição dos millis é precisa. Há mais explicações sobre isso no link do Killer Game Proggraming in Java, que te passei ali em cima.

Esse também é o segredo para que sua aplicação rode igual em qualquer máquina. Máquinas mais rápidas terão um intervalo de tempo menor, enquanto máquinas mais lentas um maior. Como o intervalo é multiplicado pela velocidade, no final das contas, o deslocamento de um pixel na tela acabou sendo o mesmo. Como nos jogos, quem tiver uma máquina mais lenta vai ver tudo mais “saltado”, equando o sujeito de máquina mais rápida vai ver um movimento gradual e suave.

Leia o livro do Killer que ele explica isso direitinho, compara as várias abordagens e dá uma sugestão realmente efetiva de como manter uma taxa de updates constante.

Eu desenvolvo no Eclipse.

Eu nunca usei um profiler… vou dar uma pesquisada sobre isso e rodar no meu projeto.

Estou lendo o material que explica a diferenca de Timer e threads… eh muito interessante e de facil entendimento…

Mais uma vez… muito obrigado pelas dicas…

[]s

Se você baixar o Vikanoid, vai ver que o pacote JGF contém o algoritmo do prof. Davidson implementado. E já possui uma interface fácil para utiliza-lo.

Se eu não me engano, você precisa só implementar a interface GameSteps e usar a classe GameLoop (ou MainLoop, não lembro), que já controla a taxa de atualização para você. Baixe o código, leia o material indicado e então dê uma estudada nessa parte.

E para aplicações de tempo real (computação gráfica, redes, etc), sempre tenha um profiler a mão… É tão importante quanto o debbuger. Ele basicamente vai te listar todos os métodos chamados na sua aplicação, dizendo quanto tempo “acumulado” cada um rodou. Com isso, vc facilmente vê quem é o “campeão de demora” do seu software e otimiza só aquele ponto. O princípio de Paretto também vale software: 80% dos gargalos estão em apenas 20% do código. Por isso, é muito saber onde esses 20% estão. :wink:

[quote]Ah, sim, como você está fazendo a movimentação no seu programa?

Você chega a calcular quanto tempo se passou entre um ciclo e outro do timer, para então multiplicar esse valor pelo velocidade?

Pq, deslocamento, pela física é dado por:
deslocamento = velocidade * tempo;[/quote]

Nao calculo… eu simplesmente defino o tempo na “mao”. Por exemplo, 1000/200.

Realmente a velocidade nao eh constante na minha aplicacao… Infelizmente eh muito facil de perceber isso…

Interessante essa sua explicacao de como “fazer” com que a aplicacao rode igual em qualquer maquina. Quando eu estava com esse problema eu nao achei muitas informacoes sobre isso… era tudo muito vago… mas a sua explicacao foi muito direta e facil de entender.

Obrigado pela referencia… eu vou ler esse livro pq esse assunto realmente eh muito importante para quem desenvolve aplicacoes com “movimento”… Essa eh a minha primeira experiencia com esse tipo de aplicacao…

Mais uma vez… obrigado pelos comentarios e dicas…

[]s

[quote]Se você baixar o Vikanoid, vai ver que o pacote JGF contém o algoritmo do prof. Davison implementado. E já possui uma interface fácil para utiliza-lo.
Se eu não me engano, você precisa só implementar a interface GameSteps e usar a classe GameLoop (ou MainLoop, não lembro), que já controla a taxa de atualização para você. Baixe o código, leia o material indicado e então dê uma estudada nessa parte.
E para aplicações de tempo real (computação gráfica, redes, etc), sempre tenha um profiler a mão… É tão importante quanto o debbuger. Ele basicamente vai te listar todos os métodos chamados na sua aplicação, dizendo quanto tempo “acumulado” cada um rodou. Com isso, vc facilmente vê quem é o “campeão de demora” do seu software e otimiza só aquele ponto. O princípio de Paretto também vale software: 80% dos gargalos estão em apenas 20% do código. Por isso, é muito saber onde esses 20% estão. [/quote]

Parabens…muito legal a sua pagina com os seus jogos… Vou baixar o Vikanoid e estudar o codigo do prof. Davison… e tb utilizar o profiler q tb eh uma otima dica.

Obrigado.

[]s