J2ME/Lições/GameCanvas

Origem: Wikilivros, livros abertos por um mundo aberto.

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 | editar código-fonte]

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 | editar código-fonte]

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 | editar código-fonte]

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 | editar código-fonte]

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 | editar código-fonte]

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 | editar código-fonte]

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 | editar código-fonte]

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();
             }
         }
     }
 }