J2ME/Lições/GameCanvas

O Canvas foi uma classe criada para o tratamentos de eventos de baixo nível, que durande o Midp1.0 foi muito usada na produção de jogos, a partir da versão Midp2.0, o J2ME apresentou uma nova classe, a GameCanvas com ações mais voltadas para a manipulação de jogos.

Iniciando um GameCanvas editar

Primeiramente vamos importar o pacote que contém a classe GameCanvas.

 import javax.microedition.lcdui.game.*;

Agora vamos criar a classe MeuGameCanvas, estendendo a classe GameCanvas.

 import javax.microedition.lcdui.game.*;
 
 public class MeuGameCanvas extends GameCanvas{
 }

Uma classe que extende o GameCanvas precisa obrigatoriamente de um construtor.

 import javax.microedition.lcdui.game.*;
 
 public class MeuGameCanvas extends GameCanvas{
     public MeuGameCanvas(){
     }
 }

Agora precisamos obrigatoriamente instanciar a classe superior GameCanvas dentro do construtor, usaremos o argumento true (se quisermos herdar os métodos da classe Canvas) ou false (se quisermos usar apenas os métodos da classe GameCanvas.

 import javax.microedition.lcdui.game.*;
 
 public class MeuGameCanvas extends GameCanvas {
     public MeuGameCanvas(){
         super(false);
     }
 }

Utilizando Canvas no GameCanvas editar

Do mesmo modo que a classe Canvas, na classe GameCanvas podemos usar o método paint() (que na GameCanvas é public) para exibir algo na tela. Vamos primeiramente inserir o pacote que contém a classe Canvas.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas {
     public MeuGameCanvas(){
         super(false);
     }
     
     public void paint(Graphics meuGrafico){
     }
 }

Agora vamos criar o método paint().

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas {
     public MeuGameCanvas(){
         super(false);
     }
     
     public void paint(Graphics meuGrafico){
     }
 }

Agora vamos desenhar um retângulo na tela.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas {
     public MeuGameCanvas(){
         super(false);
     }
     
     public void paint(Graphics meuGrafico){
         meuGrafico.drawRect(10, 20, 100, 200);
     }
 }

Também podemos implementar outros métodos da classe Canvas como o keyPressed() (como public).

Estrutura básica do GameCanvas editar

Vimos que podemos fazer tudo que fazíamos no Canvas dentro do GameCanvas, mas vamos ver agora que o GameCanvas não trabalha somente "reinventando a roda", ele possui um tratamento diferente que se assemelha mais as várias APIs de criação de jogos que existem por ai, não vamos trabalhar com um método padrão, e sim com loops que irão fixar a tela, até quando uma condição faça com que o loop pare.

Bom, primeiramente agora que não temos mais o método paint() que era inicializado automaticamente, vamos agora usar outro método que também é chamado automaticamente, ele está dentro da interface Runnable, vamos implementar na classe.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     public MeuGameCanvas(){
         super(false);
     }
 }

Agora devemos obrigatoriamente incluir dentro da nossa classe o método run() da interface Runnable que é o método que é chamado automaticamente, será dentro dele que iremos incluir todas as ações da classe.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     public MeuGameCanvas(){
         super(false);
     }
     public void run(){
     }
 }

Agora vamos instanciar um objeto da classe Thread, ele será o controlador que irá iniciar a nossa classe.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     Thread meuThread;
     
     public MeuGameCanvas(){
         super(false);
     }
     public void run(){
     }
 }

Devemos agora iniciar o objeto thread dentro do construtor da classe, como parâmetro vamos usar o this da classe GameCanvas.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     Thread meuThread;
     
     public MeuGameCanvas(){
         super(false);
         meuThread = new Thread(this);
     }
     public void run(){
     }
 }

Já temos tudo pronto, agora por último vamos iniciar a o objeto Thread que criamos utilizando o método start() dentro do construtor.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     Thread meuThread;
     
     public MeuGameCanvas(){
         super(false);
         meuThread = new Thread(this);
         meuThread.start();
     }
     public void run(){
     }
 }

Exibindo algo no GameCanvas editar

Agora com a estrutura básica do nosso GameCanvas pronta, precisamos criar um objeto do tipo Graphics dentro da nossa classe GameCanvas.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     Thread meuThread;
     Graphics meuGrafico = this.getGraphics();
     
     public MeuGameCanvas(){
         super(false);
         meuThread = new Thread(this);
         meuThread.start();
     }
     public void run(){
     }
 }

Agora dentro criaremos o loop principal dentro do método run(), primeiramente vamos criar uma variavel booleana, para criar a condição do jogo.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     Thread meuThread;
     Graphics meuGrafico = this.getGraphics();
     
     public MeuGameCanvas(){
         super(false);
         meuThread = new Thread(this);
         meuThread.start();
     }
     public void run(){
         boolean fimDeJogo = false;
     }
 }

Agora vamos criar o loop com um comando while que irá comparar a variavel booleana que criamos.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     Thread meuThread;
     Graphics meuGrafico = this.getGraphics();
     
     public MeuGameCanvas(){
         super(false);
         meuThread = new Thread(this);
         meuThread.start();
     }
     public void run(){
         boolean fimDeJogo = false;
         while(fimDeJogo == false){
         }
     }
 }

Por fim dentro do loop vamos chamar o método flushGraphics() que irá desenhar o gráfico na tela do celular.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     Thread meuThread;
     Graphics meuGrafico = this.getGraphics();
     
     public MeuGameCanvas(){
         super(false);
         meuThread = new Thread(this);
         meuThread.start();
     }
     public void run(){
         boolean fimDeJogo = false;
         while(fimDeJogo == false){
             flushGraphics();
         }
     }
 }

Agora como exemplo vamos exibir um retângulo na tela através do objeto do tipo Graphics.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     Thread meuThread;
     Graphics meuGrafico = this.getGraphics();
     
     public MeuGameCanvas(){
         super(false);
         meuThread = new Thread(this);
         meuThread.start();
     }
     public void run(){
         boolean fimDeJogo = false;
         while(fimDeJogo == false){
             meuGrafico.drawRect(10, 20, 100, 200);
             flushGraphics();
         }
     }
 }

Controlando a velocidade do aplicativo editar

Agora que fizemos a exibição precisamos controlar a velocidade de atualização do loop, isso porque do jeito que está o aplicativo irá rodar mais rápido em celulares com melhor hardware e mais lentos em celulares com pior hardware.

Para isso primeiramente precisamos criar um try-catch-finally dentro do loop principal.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     Thread meuThread;
     Graphics meuGrafico = this.getGraphics();
     
     public MeuGameCanvas(){
         super(false);
         meuThread = new Thread(this);
         meuThread.start();
     }
     public void run(){
         boolean fimDeJogo = false;
         while(fimDeJogo == false){
             try{
             }catch (Exception minhaExcessao){
                 minhaExcessao.printStackTrace();
             }finally{
             }
         }
     }
 }

Agora, como devemos sempre pintar a tela a cada loop colocamos o flushGraphics() dentro do finally.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     Thread meuThread;
     Graphics meuGrafico = this.getGraphics();
     
     public MeuGameCanvas(){
         super(false);
         meuThread = new Thread(this);
         meuThread.start();
     }
     public void run(){
         boolean fimDeJogo = false;
         while(fimDeJogo == false){
             try{
             }catch (Exception minhaExcessao){
                 minhaExcessao.printStackTrace();
             }finally{
                 flushGraphics();
             }
         }
     }
 }

Agora dentro do try vamos utilizar o método sleep() da classe Thread e entrar como parâmetro o tempo em milisegundos que a aplicação deve esperar para chamar o próximo loop, nesse caso iremos esperar 50 milisegundos (o que dá 20 quadros por segundo) que é mais ou menos a taxa de atualização da tela da maioria dos celulares.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     Thread meuThread;
     Graphics meuGrafico = this.getGraphics();
     
     public MeuGameCanvas(){
         super(false);
         meuThread = new Thread(this);
         meuThread.start();
     }
     public void run(){
         boolean fimDeJogo = false;
         while(fimDeJogo == false){
             try{
                 meuThread.sleep(50);
             }catch (Exception minhaExcessao){
                 minhaExcessao.printStackTrace();
             }finally{
                 flushGraphics();
             }
         }
     }
 }

Agora por último colocaremos um simples retângulo para andar na tela, para isso criaremos uma variável int auxiliar que irá encrementar-se a cada loop, e depois usaremos essa variável em um parâmetro do método drawRect().

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     Thread meuThread;
     Graphics meuGrafico = this.getGraphics();
     
     public MeuGameCanvas(){
         super(false);
         meuThread = new Thread(this);
         meuThread.start();
     }
     public void run(){
         int auxiliar = 0;
         boolean fimDeJogo = false;
         while(fimDeJogo == false){
             try{
                 meuGrafico.drawRect(auxiliar, 0, 10, 20);
                 meuThread.sleep(50);
             }catch (Exception minhaExcessao){
                 minhaExcessao.printStackTrace();
             }finally{
                 auxiliar++;
                 flushGraphics();
             }
         }
     }
 }

Você pode mudar a taxa de atualização na tela (método sleep()) e verá que o retângulo irá se mover com diferentes velocidades.

Controles no GameCanvas editar

No Canvas tratávamos separadamente a parte dos comandos e a parte dos gráfico, agora no GameCanvas podemos tratar dessas coisas tudo no mesmo loop, vamos ver agora que essa parte será tratada de uma forma bem diferente e muito mais simples.

Primeiramente dentro do run() vamos criar uma variável que tem que ser do tipo int que irá receber os controles.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     Thread meuThread;
     Graphics meuGrafico = this.getGraphics();
     
     public MeuGameCanvas(){
         super(false);
         meuThread = new Thread(this);
         meuThread.start();
     }
     public void run(){
         boolean fimDeJogo = false;
         int tecla;
         
         while(fimDeJogo == false){
             try{
                 meuGrafico.drawRect(10, 20, 100, 200);
             }catch(Exception minhaExcessao){
                 minhaExcessao.printStackTrace();
             }finally{
                 flushGraphics();
             }
         }
     }
 }

Agora dentro do loop principal vamos checar se alguma tecla foi pressionada através do método getKeyStates().

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     Thread meuThread;
     Graphics meuGrafico = this.getGraphics();
     
     public MeuGameCanvas(){
         super(false);
         meuThread = new Thread(this);
         meuThread.start();
     }
     public void run(){
         boolean fimDeJogo = false;
         int tecla;
         
         while(fimDeJogo == false){
             try{
                 tecla = getKeyStates();
                 meuGrafico.drawRect(10, 20, 100, 200);
             }catch(Exception minhaExcessao){
                 minhaExcessao.printStackTrace();
             }finally{
                 flushGraphics();
             }
         }
     }
 }

Agora vamos criar uma condição caso uma tecla específica seja apertada, nesse caso vamos usar a tecla FIRE e desenhar outro retângulo na tela caso ela seja apertada, note que agora vamos usar a constante FIRE_PRESSED.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     Thread meuThread;
     Graphics meuGrafico = this.getGraphics();
     
     public MeuGameCanvas(){
         super(false);
         meuThread = new Thread(this);
         meuThread.start();
     }
     public void run(){
         boolean fimDeJogo = false;
         int tecla;
         
         while(fimDeJogo == false){
             try{
                 tecla = getKeyStates();
                 if(tecla == FIRE_PRESSED){
                     meuGrafico.drawRect(20, 40, 80, 100);
                 }
             
                 meuGrafico.drawRect(10, 20, 100, 200);
             }catch(Exception minhaExcessao){
                 minhaExcessao.printStackTrace();
             }finally{
                 flushGraphics();
             }
         }
     }
 }

Imagens no GameCanvas editar

Vamos agora ver como carregar uma imagem no GameCanvas, isso pode se aplicar ao carregamento de outros arquivos também.

Primeiramente dentro do método run() vamos colocar um try-catch.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     Thread meuThread;
     Graphics meuGrafico = this.getGraphics();
     
     public MeuGameCanvas(){
         super(false);
         meuThread = new Thread(this);
         meuThread.start();
     }
     public void run(){
         try{
         }catch(Exception minhaExcessao){
             minhaExcessao.printStackTrace();
         }
         
         boolean fimDeJogo = false;
         while(fimDeJogo == false){
             try{
                 meuThread.sleep(50);
             }catch(Exception minhaExcessao){
                 minhaExcessao.printStackTrace();
             }finally{
                 flushGraphics();
             }
         }
     }
 }

Agora dentro do vamos instanciar o objeto Image na classe MeuGameCanvas.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     Thread meuThread;
     Graphics meuGrafico = this.getGraphics();
     Image minhaImagem;
     
     public MeuGameCanvas(){
         super(false);
         meuThread = new Thread(this);
         meuThread.start();
     }
     public void run(){
         try{
             meuThread.sleep(50);
         }catch(Exception ex){
             ex.printStackTrace();
         }
         
         boolean fimDeJogo = false;
         while(fimDeJogo == false){
             try{
             }catch(Exception minhaExcessao){
                 minhaExcessao.printStackTrace();
             }finally{
                 flushGraphics();
             }
         }
     }
 }

Depois vamos iniciar o objeto Image dentro do try no método run().

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     Thread meuThread;
     Graphics meuGrafico = this.getGraphics();
     Image minhaImagem;
     
     public MeuGameCanvas(){
         super(false);
         meuThread = new Thread(this);
         meuThread.start();
     }
     public void run(){
         try{
             minhaImagem = minhaImagem.createImage("/Minha Imagem.png");
         }catch(Exception ex){
             ex.printStackTrace();
         }
         
         boolean fimDeJogo = false;
         while(fimDeJogo == false){
             try{
                 meuThread.sleep(50);
             }catch(Exception minhaExcessao){
                 minhaExcessao.printStackTrace();
             }finally{
                 flushGraphics();
             }
         }
     }
 }

Agora normalmente dentro do loop do jogo colocamos a imagem para ser exibida.

 import javax.microedition.lcdui.game.*;
 import javax.microedition.lcdui.*;
 
 public class MeuGameCanvas extends GameCanvas implements Runnable {
     Thread meuThread;
     Graphics meuGrafico = this.getGraphics();
     Image minhaImagem;
     
     public MeuGameCanvas(){
         super(false);
         meuThread = new Thread(this);
         meuThread.start();
     }
     public void run(){
         try{
             minhaImagem = minhaImagem.createImage("/Minha Imagem.png");
         }catch(Exception ex){
             ex.printStackTrace();
         }
         
         boolean fimDeJogo = false;
         while(fimDeJogo == false){
             try{
                 meuGrafico.drawImage(minhaImagem, 30, 40, Graphics.LEFT|Graphics.TOP);
                 meuThread.sleep(50);
             }catch(Exception minhaExcessao){
                 minhaExcessao.printStackTrace();
             }finally{
                 flushGraphics();
             }
         }
     }
 }