Esse artigo faz parte da revista WebMobile edição 17. Clique aqui para ler todos os artigos desta edição

imagem.JPG

/STRONG>

Criando jogos de ação em 2D com a API de jogos em JavaME

 

O MIDP 2.0 inclui uma API de jogos que simplifica o desenvolvimento de games em 2D. Esta API é compacta, sendo composta apenas por cinco classes no pacote javax.microedition.lcdui.game. Essas cinco classes provêm duas importantes capacidades:

·         A nova classe GameCanvas possibilita desenhar (paint) uma tela e responder a uma entrada (input) no corpo de um loop do jogo, ao invés de depender apenas das threads de input e paint do sistema;

·         Uma flexível API de camada (layer) que torna fácil a construção de cenários complexos de forma eficiente.

 

Ao longo deste artigo iremos descrever a construção de jogos em 2D utilizando esta API.

Construindo um Loop de Jogo com GameCanvas

GameCanvas é um Canvas com capacidades adicionais; ele prove métodos para desenhos imediatos e para detecção do estado das teclas do aparelho. Esses novos métodos tornam possível integrar todas as funcionalidades de um jogo em um simples loop, sob controle de uma simples thread. Para entender porque isso é atrativo, pense sobre como você implementaria um jogo tipo (por exemplo, o jogo muTank – Figura 1, que desenvolveremos ao longo do artigo) usando Canvas. A Listagem 1 apresenta esta estrutura.

 

Figura 1. Tela de exemplo do Jogo muTank

Listagem 1. Estrutura do código fonte do jogo utilizando a classe Canvas

public class MicroTankCanvas

    extends Canvas

    implements Runnable {

  public void run() {

    while (true) {

      // Update the game state.

      repaint();

      // Delay one time step.

    }

  }

 

  public void paint(Graphics g) {

    // Painting code goes here.

  }

 

  protected void keyPressed(int keyCode) {

    // Respond to key presses here.

  }

}

 

O método run(), que executa em uma thread da aplicação, atualiza o jogo uma vez a cada passo (de acordo com o parâmetro de atualização que foi definido). Tarefas típicas de um jogo 2D seriam a atualização da posição da bola ou da nave e animar caracteres ou veículos, por exemplo. A cada tempo ao longo do loop, o método repaint() é chamado para atualizar a tela do jogo. O sistema associa eventos das teclas ao método keyPressed(), que é responsável pela atualização do estado do jogo apropriadamente.

O problema é que tudo isso está sendo executado em diferentes threads, e o código do jogo é, de forma confusa, construído e executado através de três métodos diferentes. Quando o loop de animação principal no método run() chama o método repaint(), não existe uma forma de saber exatamente quando o sistema irá chamar o método paint(). Quando o sistema chama o método keyPressed(), não existe forma de saber o que está acontecendo com as outras partes da aplicação. Se o seu código em keyPressed() está fazendo atualização para o estado do jogo no momento em que a tela está sendo renderizada no método paint(), a tela pode terminar ficando com uma aparência estranha, diferente daquela realmente desejada. Se a thread leva mais tempo para renderizar a tela do que um tempo de um passo simples no método run(), a animação pode ter uma aparência incompleta ou estranha.

GameCanvas permite que você contorne os mecanismos normais de desenho (paint) e de eventos de tecla de forma que todo o jogo possa estar contido em um simples loop. Primeiramente, GameCanvas permite que você acesse seu objeto Graphics diretamente usando o método getGraphics(). Qualquer renderização sobre o objeto Graphics retornado é feita em um buffer offscreen (por fora da tela real do jogo). Você pode então copiar o buffer para a tela real do jogo usando o método flushGraphics(), que não retorna até que a tela tenha sido atualizada. Esta abordagem dá ao desenvolvedor um controle mais preciso que se fosse utilizado o método repaint(). O método repaint() retorna imediatamente e sua aplicação não possui garantia alguma sobre o momento exato quando o sistema irá chamar o método paint() para atualizar a tela do jogo.

GameCanvas contém ainda um método para obtenção do estado atual das teclas do aparelho, uma técnica chamada polling. Ao invés de aguardar que o sistema chame o método keyPressed(), você pode determinar imediatamente quais teclas são pressionadas através da invocação do método getKeyStates() do GameCanvas.

Um loop de jogo típico utilizando GameCanvas é algo similar ao apresentado na Listagem 2.

Listagem 2. Estrutura do loop de um jogo utilizando a classe GameCanvas

public class MicroTankCanvas

    extends GameCanvas

    implements Runnable {

  public void run() {

    Graphics g = getGraphics();

    while (true) {

      // Update the game state.

      int keyState = getKeyStates();

      // Respond to key presses here.

      // Painting code goes here.

      flushGraphics();

      // Delay one time step.

    }

  }

}

 

O exemplo descrito na Listagem 3 demonstra um loop de um jogo básico. Ele mostra uma rotação X na qual você pode mover ao longo da tela usando as setas do teclado. O método    run() é extremamente claro e customizado graças ao GameCanvas.

 

Listagem 3. Exemplo do loop de um jogo utilizando a classe GameCanvas

import javax.microedition.lcdui.*;

import javax.microedition.lcdui.game.*;

 

public class SimpleGameCanvas

    extends GameCanvas

    implements Runnable {

  private volatile boolean mTrucking;

  private long mFrameDelay;

 

  private int mX, mY;

...

Quer ler esse conteúdo completo? Tenha acesso completo