Passive VS Active Rendering – parte 1 de 2

placeholderantigo

RepaintExistem basicamente duas formas de atualizar a tela quando criamos um programa em Java. Podemos fazer isso de forma passiva ou ativa. Cada uma tem suas vantagens e desvantagens e veremos, nesta dupla de artigos, como implementá-las. Iniciaremos pela atualização passiva (passive rendering).

Atualização passiva

Esta é forma padrão de atualização de tela para programas em Java, geralmente utilizada por aplicativos. É comum se ouvir falar que esta forma não serve para jogos, mas isto não é inteiramente verdade. Como tudo nessa vida, isso depende.

A atualização passíva consiste em criar uma rotina que é chamada pelo sistema operacional (S.O.) sempre que ele precisar de uma versão atualizada da nossa tela. Em termos técnicos criamos um manipulador de evento (event handler) para o evento Paint.

Veja que é o S.O. que decide quando a atualização é necessária, baseado em dados que ele possui e não baseado em dados do jogo em sí. Esta é sua maior limitação. Por outro lado, é uma forma muito leve em termos de processamento, já que a tela vai ser atualizada apenas quando for necessário e esta é sua vantagem.

Mas baseado em quê o sistema decide se precisa ou não atualizar a tela? Fundamentalmente em mudanças na área da tela como redimensionamento ou ocultamento/reapresentação.

Por exemplo, se nossa tela é uma janela, o sistema vai requerer uma atualização dela sempre que a mesma for redimensionada ou for ocultada e depois mostrada novamente. Nesses eventos é que precisamos desenhar novamente o conteúdo da tela. Veja a ilutração a seguir.

interface 02

clique para ampliar

Vejamos como implementar isso em Java, usando uma janela do tipo Swing. Criamos uma classe chamada MeuJogo que é derivada da classe JFrame (um frame – moldura – é como chamamos uma janela em Java).

Arquivo: MeuJogo.java

 1 package abrindoojogo.exemplos.atualizacaopassiva;
 2
 3 import java.awt.Color;
 4 import java.awt.Graphics;
 5 import java.awt.event.KeyEvent;
 6 import java.awt.event.KeyListener;
 7 import javax.swing.JFrame;
 8 import javax.swing.WindowConstants;
 9
10 public class MeuJogo extends JFrame implements KeyListener
11 {
12     int tom;
13
14     public MeuJogo()
15     {
16         setTitle("Atualização Passiva");
17         setSize(800, 600);
18         setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
19         addKeyListener(this);
20         tom = 0;
21     }
22
23     @Override
24     public void paint(Graphics g)
25     {
26         g.clearRect(0, 0, getWidth(), getHeight());
27         for (int x = 0; x < 800; x += 10)
28         {
29             for (int y = 0; y < 600; y += 10)
30             {
31                 g.setColor(new Color(x % 256, y % 256, (tom * 50) % 256));
32                 g.fillRect(x, y, 10, 10);
33             }
34         }
35     }
36
37     public void keyPressed(KeyEvent e)
38     {
39         if (e.getKeyCode() == KeyEvent.VK_ENTER)
40         {
41             tom ++;
42         }
43     }
44
45     public void keyReleased(KeyEvent e)
46     {
47
48     }
49
50     public void keyTyped(KeyEvent e)
51     {
52
53     }
54 }

Abaixo o código da classe principal do programa, onde o processamento inicia. Nela criamos um objeto do tipo da classe MeuJogo e fazemos ele ficar visível. Assim a janela aparece na tela.

Arquivo: Main.java

 1 package abrindoojogo.exemplos.atualizacaopassiva;
 2
 3 public class Main
 4 {
 5     public static void main(String[] args)
 6     {
 7         MeuJogo mj = new MeuJogo();
 8         mj.setVisible(true);
 9     }
10 }

Faça o seguinte teste: execute esse exemplo e arraste o canto inferior direito da janela, redimensionando-a. Você vai notar que a imagem pisca: isso é porque está sendo redesenhada a cada alteração de tamanho. Faça isso bem rapidamente e você poderá notar que a imagem deixa de piscar por alguns instantes. Isso ocorre porque o sistema fica sobrecarregado com a mudança de tamanho e, para poupar CPU, deixa de atualziar a imagem, aguardando você parar de mover o mouse para, só então, gerar o evento de redesenho.

Experimente també ficar pressionando a tecla ENTER. Nada ocorre, embora tenhamos um manipulador para o evento KeyPressed que incrementa a variável “tom”, que é utilizada para determinar a cor do gráfico. Porque a cor do gráfico não muda?

O que ocorre é que nosso modelo, digamos assim, ou seja, a representação interna do gráfico mudou (a variável foi incrementada), mas como o S.O não sabe disso, ele não enviou o evento para atualziar a tela e por isso a imagem permanece igual. Para contornar isso basta chamar o método repaint() dentro do evento keyPressed. Veja abaixo.

37     public void keyPressed(KeyEvent e)
38     {
39         if (e.getKeyCode() == KeyEvent.VK_ENTER)
40         {
41             tom ++;
42             repaint();
43         }
44     }

Esse método informa ao S.O. que desejamos uma atualização da tela. Rode o programa agora a fique pressionando ENTER. Agora sim, a cor do gráfico muda. Pronto, temos uma máquina simples que serve como base para um jogo de pouca ação. Por exemplo, jogos de tabuleiro como batalha naval.

Porque não serve para jogos de ação intensa e animações constantes? Porque não temos controle sobre quando a tela será atualizada e porque tudo pisca (você notou que o gráfico piscar ao ficar pressionando ENTER?).

Colocar a chamada ao método repaint não faz a tela ser atualizada na hora. Apenas informa ao S.O. que queremos uma atualização, mas o S.O. pode demorar até alguns segundos para atualizar. Se for um jogo de ação, digamos de luta, o modelo interno do jogo vai ser atualizado várias vezes até que a tela seja redesenhada. Ou seja, seu oponente pode lhe dar um chute, você perder energia e o oponente voltar a posição normal, tudo isso antes da próxima atualização da tela. Quando a tela for redesenhada, você vai ver que sua energia diminuiu, mas não viu o que aconteceu.

E, é claro, tem o problema de piscar. Como estamos atualizando diretamente a tela, ocorre o efeito de tearing (rasgo), onde parece que a imagem fica rasgada. Explicarei porque isso ocorre em um próximo post.

Mas para jogos lentos, quase parando, essa é uma boa opção porque consome muito pouco da CPU. Por exemplo, se for um jogo de damas. Você só precisa chamar o repaint() quando um dos jogadores mover uma peça, o que ocorre com pouca frequência. Não terá o efeito de ficar piscando.

Durante muito tempo isso foi tudo que podia ser feito em Java. Mas já alguns anos que a linguagem permite o que chamamos de active rendering. Esse recurso possibilita atualização instantânea da tela, a cada frame e de quebra nos oferece double buffer (o fim das piscadas), tela-cheia e até mudança de modo de vídeo. Veremos tudo isso amanhã, nesse mesmo site, nesse mesmo horário.

Baixe o código fonte deste artigo.

Ilustrações: Inara Cavichioli
Confira a segunda parte>>
Autor: Luiz Nörnberg Ver todos os posts de
Sou Bacharel em Ciência da Computação pela Universidade Católica de Pelotas (UCPel), onde também atuei como professor. Desde a época da faculdade (mais de quinze anos atrás) a paixão por jogos tem sido importante no meu direcionamento profissional. Sou sócio-fundador do Izyplay Game Studio, onde exerço o cargo de Diretor de Tecnologia. Sempre tive grande foco em desenvolvimento em Java, embora tenha migrando para a tecnologia Adobe AIR em função de sua portabilidade. Ah, e é claro, dou meus palpites no game design.

Um comentário em "Passive VS Active Rendering – parte 1 de 2"

Deixar um Comentário