|
No artigo passado, vimos como fazer uma animação usando os já conhecidos paint() e repaint(). Isto é chamado de desenho passivo (passive rendering). Isto porque você pede um repaint, mas é o Swing quem efetivamente decide quando fazê-lo. De fato, algumas vezes o repaint é solenemente ignorado, o que é bom para aplicações janelas, mas não tão bom para jogos. Neste artigo, veremos como desenhar diretamente na tela, modificando o programa anterior para fazer o desenho direto (active rendering).
Estratégias de vídeo
No cinema, temos um rolo de filme fotográfico com diversas imagens, cada uma mostrando um instante de tempo diferente. Exibir uma imagem por vez nos dá a sensação de animação, pois nosso olho as interpola, dando a ilusão de movimento. No computador, usamos o mesmo princípio, mas podemos pintar uma imagem, enquanto exibimos outra.
No início da computação, os cientistas tentavam desenhar enquanto a imagem era exibida, na esperança do olho ser lento o suficiente para não perceber a pintura. Mas, nossos olhos não foram tão lentos assim, e enxergávamos uma imagem piscante, de baixa qualidade. Esse problema ficou conhecido como flickering.
Isso foi corrigido com o uso de duas áreas de memória. Uma delas é a área de vídeo, onde tudo que está desenhado é exibido na tela (front buffer). A outra, a área de desenho, que não é exibida. O Java trabalha com essa técnica, e todas as suas estratégias de vídeo se baseiam nela.
As estratégias de vídeo variam então a forma que o Java faz a troca dos dois buffers. Na verdade, quem faz essa troca é a placa de vídeo, e o Java tentará obter a melhor estratégia possível, dada as capacidades do hardware. As estratégias são:
- Blitting: Quando é hora de exibir a tela, o conteúdo do buffer de pintura é copiado para o buffer de vídeo.
- Page flipping: Quando é hora de exibir a tela, a placa de vídeo simplesmente atualiza dois ponteiros. Isso faz com que a área de pintura se torne a área de desenho, e vice-versa.
A figura abaixo demonstra o page flipping:
A segunda estratégia é certamente mais eficiente que a primeira. Por mais que a cópia da imagem seja feita de maneira eficiente, atualizar somente quatro bytes (referentes aos ponteiros de vídeo e da área de desenho), será muito mais rápido. Entretanto, o page flipping só é possível se exibirmos a nossa aplicação em tela cheia, tema que veremos em artigos futuros.
A classe BufferStrategy
No java, temos uma classe chamada BufferStrategy. Ela é o tipo base de qualquer estratégia de pintura que iremos utilizar. Afinal, as operações básicas sobre o buffer serão sempre as mesmas: obtê-lo, pedir a troca do buffer, testar se o contexto de vídeo se perdeu, etc.
O método createBufferStrategy(), presente na classe Window, da qual JFrame deriva, permite a obtenção de uma estratégia de pintura concreta. Qual estratégia? A mais eficiente possível, para aquela configuração de vídeo. Como o método precisa determinar as configurações da janela ativa para ser chamado, só podemos chamá-lo com a janela visível. Portanto, um bom momento para criarmos nosso BufferStategy é a fase de setup() do nosso game loop. Criá-lo é bastante fácil. Basta chamar o método indicando como parâmetro o número de buffers. Esse valor geralmente será 2, pois são raríssimos os casos onde mais de 2 buffers são necessários. Nosso novo método de setup ficará:
public void setup() {
//Criamos a estratégia de double buffering
createBufferStrategy(2);
//Subtrai a decoração da janela da largura e altura máximas
//percorridas pela bola.
ball = new Ball(getWidth() - getInsets().left - getInsets().right,
getHeight() - getInsets().top - getInsets().bottom);
}
Simples não? Podemos obter a BufferStrategy criada chamando o método getBufferStrategy().
Como iremos desenhar diretamente, podemos também pedir para o Swing ignorar todo e qualquer evento de repaint() na tela. Eventos de repaint ocorrem de maneira espontânea quando, por exemplo, algo que cobre a janela deixa de cobri-la, ou quando a janela é minimizada e restaurada. Para isso, basta adicionarmos ao construtor da janela o comando:
setIgnoreRepaint(true);
Vamos agora repensar no nosso método renderGraphics. No exemplo anterior ele ficou vazio, já que era o paint() que fazia seu papel. Nesse exemplo, nós iremos usá-lo para desenhar no buffer. Podemos obter o contexto gráfico associado a um BufferStrategy chamando o método getDrawGraphics(). Esse contexto já será uma cópia, portanto, precisamos chamar dispose() sobre ele.
Ele representará o buffer de pintura, assim, tudo o que fizermos nele ainda não será exibido. A exibição só ocorrerá efetivamente quando o método show() for chamado. Chamaremos o método show() na etapa paintGraphics() do nosso game loop. Isso facilita a instrumentação do nosso jogo, pois permite-nos saber quanto tempo gastamos em nossa lógica de pintura, e quanto tempo o Java gastou atualizando efetivamente o buffer.
@Override
public void renderGraphics() {
Graphics g = getBufferStrategy().getDrawGraphics();
//Criamos um contexto gráfico que não leva em conta as bordas
Graphics g2 = g.create(getInsets().right,
getInsets().top,
getWidth() - getInsets().left,
getHeight() - getInsets().bottom);
//Limpamos a tela
g2.setColor(Color.BLACK);
g2.fillRect(0, 0, getWidth(), getHeight());
ball.draw((Graphics2D) g2); //Desenhamos a bola
//Liberamos os contextos criados.
g.dispose();
g2.dispose();
}
@Override
public void paintScreen() {
if (!getBufferStrategy().contentsLost())
getBufferStrategy().show();
}
Note que no método paintScreen() tivemos que testar se o conteúdo do BufferStrategy foi ou não perdido antes de pintar a tela. Embora isso seja raro, esse teste se faz necessário pois é possível que a placa de vídeo limpe totalmente a sua área de vídeo, por exemplo, quando a resolução de tela muda (se o usuário pressionar ALT+TAB na sua aplicação de tela cheia, por exemplo). Nesse caso, não faremos a troca dos buffers, e deixaremos que o próprio loop principal pinte novamente o quadro.
Considerações finais
A classe JApplet não possui os métodos relacionados ao BufferStategy. Podemos contornar esse problema criando dentro do JApplet uma instância de JWIndow, que os possui, e fazendo-a ocupar toda a área do Applet.
Você pode baixar o código fonte da solução aqui. Nesses fontes, você encontrará também a classe BallFrame1, que é a classe do artigo anterior, para que você possa comparar as duas soluções.
-
24/02/2010 11:05:52 |200.186.180.xxx| Vinícius - Outras bibliotecas

Na verdade, ninguém perde tempo implementando outras bibliotecas sendo que a API padrão é muito completa, e de boa performance.
A alternativa clássica ao Swing é o SWT:
http://www.eclipse.org/articles/Article-SWT-graphics/SWT_graphics.htmlNão conheço a performance dele para gráficos 2D. Para dispositivos móveis, outra alternativa é a API do Android.
Você também pode usar uma API 3D, como a JOGL, e fazer jogos 2D nela, especialmente os vetoriais.
-
02/11/2010 13:27:33 |187.10.106.xxx| Mateus PL - Muito bom.

Estou na espera do próximo tutorial.
Só para acrescentar o que você disse sobre o JOGL, me parece que o exemplo do Space Invader do site http://www.cokeandcode.com/spaceinvaderstutorial usa esse esquema de fazer jogos 2D na API 3D.
-
04/08/2010 21:01:09 |189.34.52.xxx| raghy - applets, ou web mesmo

é possível usar em applets.
hoje também existe o webstart (?)
launch (?)
-
29/09/2010 14:52:10 |189.47.25.xxx| Moises

Primeiramente, Quero Parabenizá-lo pelos artigos. São bem didáticos e com conteúdo interessante. Estou começando a desenvolver alguma coisa na parte de animação, mas estou tendo um problema que não estou conseguindo resolver.
Já consultei a API do java e seu código, mas mesmo assim a dúvida persiste.Estou "setando" true para 'setIgnoredRepaint()' no construtor de um JFrame.
Mas ainda assim o formulário continua sendo repintado.
Criei um metodo paint( Graphics ), com uma mensagem de saída pra confirmar.
E ele realmente executa o metodo. No seu exemplo Ball testei a mesma coisa e não executou o método.Gostaria que pudesse me ajudar a contornar o problema. Grato pela atenção.
(Abaixo o link com um simples exemplo) - toda vez que a janela é redimensionada paint() é chamado.
http://pastebin.com/iUcmQx8R
-
29/09/2010 15:04:07 | Vinícius Godoy de Mendonça

O repaint irá ignorar apenas as mensagens de repaint que vierem do sistema operacional. Ou seja, se sua janela for parcialmente encoberta e depois exibida novamente.
O redimensionamento provoca um repaint do próprio Swing, aí o método será chamado mesmo.
Para evitar totalmente, use o BufferStrategy como explicado no artigo. 100% vc não evitará, mas seu método paint também não terá quase nada e isso deixa de ser um problema.
As chamadas ao paint também reduzem se você usar a tela em full screen exclusive mode (FSEM).
-
26/11/2010 15:10:52 |150.161.2.xxx| Gilles Paiva - Dúvidas iniciais...

Boa tarde,
Gostaria de saber se os seus tutorias podem ser baixados com o código completo. Tipo... Estou vendo teus métodos, mas onde consigo a classe completa para poder entender tudo o que está acontecendo?
-
26/11/2010 18:56:15 | Vinícius Godoy de Mendonça

Olá, sempre olhe no final dos artigos. Ali, em "considerações finais" geralmente coloco um link para os fontes. Nesse artigo, está na última frase, bem na palavra aqui.
-
19/03/2011 12:54:18 |201.29.231.xxx| Clovis Rocha - Duvidas sobre eliminar efeito de cintilamento

Sempre pensei comigo mesmo, porque as pessoas escrevem tão rebuscado quando tentam passar alguma coisa a outros? Lendo seus artigos reforçou ainda mais minha pergunta. Seus artigos são claros como uma folha de papel e extremamente objetivos (dá impressão que vc perguntou a cada um de nós o que gostaríamos de saber. Apesar de tudo isso estou com um problema. Só tenho ~ 1 mês de Java, embora já programe a vários anos em ASPNET. NÃO SOU PROGRAMADOR, sou um engenheiro que usa programação como ferramenta de trabalho, e procuro disponibilizar meu trabalho a todos interessados na Intranet da Empresa (ex cálculos elétricos, entre outros). Em contato com o java deparei com um ambiente de programação muito mais estimulante e poderoso. No entanto, o Java é para valentes que necessariamente têm que dominar perfeitamente os conceitos desta linguagem OO. No ASPNET o IDE do Visual Studio já entrega muita coisa pronta.
Estou fazendo um programa, para o qual escolhi que fosse um Applet ou JApplet, para disponibilizar no browser de cada computador interessado na nossa intranet. Em resumo a tela tem dezenas de retas em coordenadas. Em cima deste gráfico estático, o movimento do mouse leva com ele uma reta vertical e uma horizontal, que o acompanham conforme a posição do mouse. Já fiz o código e está funcionando. Porém estou com o maldito “flickering” (no ASPNET a gente conta com a ajuda do AJAX). Li seu ótimo artigo (e todos os outros) sobre as estratégias de vídeo(BufferStrategy). Devido a minha (ainda) falta de conceitos me enrolei para implementação em Applet. (???JWindow...JApplet...etc).
Se não for abuso, poderia me fornecer um cógigo fonte bem simples (só com 1 reta e o mouse passeando por cima dela)? Grato antecipadamente.... e desculpe a cara de pau!
Clovis Rocha
-
23/03/2011 13:43:57 |187.33.21.xxx| Clovis Rocha - Eliminasr Flickering

Achei exemplos ótimos para minha dúvida.
http://www.realapplets.com/tutorial/DoubleBuffering.htmlNever mind...
-
24/03/2011 08:46:35 | Vinícius Godoy de Mendonça

Você chegou a usar o Double Buffering através da BufferStrategy como propus no artigo? Achei estranho vc perguntar de flickering, pois os artigos do gameloop e esse justamente eliminam esse problema.
-
28/07/2011 16:55:27 |177.19.89.xxx| DAVI LIMA - GAMES

Prezados Senhores,
Parabéns pela iniciativa, sou desenvolvedor experiente em java(SE,ME e EE) e sempre vi que faltava algo na minha profissão - A PARTE GRÁFICA.
Com os posts do site Pontov estou melhorando a construção de telas amigáveis e dinâmicas.Grato
Davi Lima
-
15/10/2011 02:12:03 |189.69.52.xxx| Luiz Paulo de Vasoncellos - Ótimo tutorial!

Um ótimo tutorial, de grande ajuda. Estou desenvolvendo um jogo 2D como passa-tempo e esse site me salvou de muita dor de cabeça
Só uma dúvida, as vezes quando eu rodo o jogo usando o ActiveRendergin(BufferStrategy), ele simplesmente falha ao iniciar, diz que não foi possível criar os buffers e trava a aplicação.
Testei várias vezes, e de umas 50 vezes que rodei deu 2 duas vezes esse erro. Se puder me ajudar com isso, muito obrigado!
-
15/10/2011 15:03:02 | Vinícius Godoy de Mendonça

Geralmente ele faz isso se você tenta criar os buffers antes do JFrame estar visível.
Quando ocorrer a próxima vez, mande-nos o texto da exception.
-
17/10/2011 08:48:47 |200.186.180.xxx| Luiz Paulo

Entendi. Realmente, se eu tento criar o Buffer antes de setVisible ele dá esse erro. Só que ele ocorre ocasionalmente mesmo depois do setVisible (tive que rodar umas 30 vezes pra conseguir que ele aparecesse
).----
Exception in thread "main" java.lang.IllegalStateException: Buffers have not been created
...
-
16/10/2011 13:45:10 | Vinícius Godoy de Mendonça

É um bug do Java mesmo:
http://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=520b5db156ed822 48f98f175d90fe?bug_id=6933331Existe um workaround que é desabilitar o Direct 3D, adicionando a opção -Dsun.java2d.d3d=false na linha de comando do java:
java -Dsun.java2d.d3d=false -jar seuGame.jarNão tem previsão para ser corrigido. É por essas e outras que tenho recomendado o uso de C# e XNA no lugar do Java.
Não só é muito mais poderoso, como é oficialmente suportado e você vai poder rodar seus jogos no X-Box (ou até publica-los na Live).
-
16/10/2011 16:34:46 |187.57.174.xxx| Luiz Paulo de Vasconcellos

Vish, um bug do java?
Que pena, mas acho que vou continuar meu projeto mesmo assim.Quanto a usar C# e XNA, não sei... Não gosto do fato de rodar só em Windows (embora eu saiba que mesmo em java a grande maioria vai rodar no Windows).
Eu tava pesquisando, conhece a biblioteca LWJGL? Parece ser muito boa e estável, foi usada em alguns projetos importantes, parece. Estava pensando em aprender ela em seguida, o que acha?
-
17/10/2011 08:47:08 | Vinícius Godoy de Mendonça

O Java Desktop hoje é suportado em duas plataformas, Windows e Linux. Sendo que na segunda, o número de jogadores e extremamente pequeno, insignificante. Se você quer mesmo apostar em games em Java, use o Android.
Adicionalmente, o Java não dá suporte a diversas coisas necessárias em jogos:
1. Não tem suporte para controles: Você deverá recorrer a bindings, como a JInput, que estão um tanto abandonados;
2. Não tem boas classes para reproduzir sons: Você terá que recorrer a Java Zoom ou a classes feitas por indies;
3. Não tem suporte a APIs gráficas 3D. Novamente, somente através de projetos terceiros.Parte desses bindings não são totalmente multiplataforma. É o caso do JInput, que hoje não roda bem no Linux e não tem suporte para o Mac (bom, nem o Java tem).
Agora, em desktop, o XNA é oficialmente suportado também em duas plataformas. Windows e X-Box (além do Windows Phone 7, que não é representativo no Brasil). O Windows domina 96% do mercado de games para Desktop, enquanto o X-Box domina em torno de 30% do mercado para consoles. Se você quer atingir um público maior, não use Java.Também é possível rodar o XNA em Linux e Mac através do projeto Mono. Sem falar que a MS tem interesse e incentiva o mercado de games, do contrário da Sun e Oracle, que se fecham para esse mercado.
Quanto à LWJGL. Ela é apenas um binding entre o Java, a OpenGL, a OpenAL e o JInput. Mas não vai muito mais longe que isso. Temos tutoriais de OpenGL aqui no portal (são em C++, mas vc vai ver que é praticamente a mesma coisa, já que OpenGL é OpenGL em qualquer lugar).
Se quer usar uma API para desenvolver em 2D, e quer insistir com o Java, dê uma olhada na Slick2D, que roda sobre LWJGL e é portável para Android.Usar a LWJGL direto é uma boa se você quiser apenas estudar OpenGL, usando o Java, que é uma linguagem que você já conhece. Para games 3D, há APIs com maior suporte como a Unity3D e a JMonkeyEngine.
-
17/10/2011 13:55:34 |187.57.180.xxx| Luiz Paulo

...
...
Conseguiu me convencer
Estou indo para o segundo ano de Sistemas de Informação e "pretendo" trabalhar com programação para jogos. Melhor me adequar para uma ferramenta melhor.Depois de terminar esse projeto, pretendo aprender OpenGL sim, embora o que eu tenha visto até agora tenha me desanimado um pouco. Código de openGL parece mandarim
-
17/10/2011 14:03:49 | Vinícius Godoy de Mendonça

O que pega em computação gráfica 3D não é tanto as APIs gráficas. A OpenGL é bem fácil de mexer. Mas sim, a matemática.
Você precisa necessariamente saber como funcionam os vetores matemáticos na ponta da língua, assim como as matrizes de transformação. Felizmente, temos artigos sobre todos esses assuntos aqui no Portal.
Quanto ao Java, como vc deve saber, sou moderador do GUJ e sempre fui um grande fã do Java. Quando comecei os artigos, o Java ainda se apresentava uma promessa, mas tecnologias melhores foram surgindo ou se consolidando ao longo dos anos (XNA, Unity, etc.), ao mesmo tempo que o Java deixava de lado o suporte ao Mac, o J2ME morria e o JavaFX nunca se tornou uma alternativa realmente viável.
Se quer apostar em Java para jogos hoje, a única alternativa industrialmente viável é o Android.
-
17/10/2011 10:40:01 | Vinícius Godoy de Mendonça

Esqueci de dizer no post anterior. A Sony também anunciou que o C# será a linguagem de desenvolvimento do Playstation Suite.











Existe outra maneira de desenhar graficos 2D com java?
Todos os exemplos que eu vejo são utilizando swing. Gostaria de saber se existe outra maneira/biblioteca que seja melhor/pior, mais rapida ou lenta.
Grato