Você pode ganhar um iPad 2 na promoção do Javafree
O Portal javafree.org inicia mais uma promoção para os usuários do fórum. Quem publicar mais posts válidos (perguntas ou respostas) entre 16/4 a 13/7 levará para casa um iPad 2 de 16GB!
Clique aqui e saiba mais.
Home > Artigos > Java ME >
Jogos com MIDP 2.0: Cenários
Publicado por Tutoriais Admin em 17/08/2009 - 19.547 visualizações
O presente artigo visa dar uma continuação ao artigo " Jogos com MIDP 2.0 - Personagens", que também foi publicado aqui no JavaFree. O primeiro artigo desta série tratou dos aspectos básicos da utilização da GameAPI da MIDP 2.0, como por exemplo, o uso da GameCanvas e seu ciclo de iterações. Também, tratou a criação dos personagens de um jogo, através da classe javax.microedition.lcdui.game.Sprite.
Avançando um pouco mais no aprendizado, o próximo passo é a construção dos cenários, pois todo jogo deve ter um. Assim como na primeira parte deste mini-tutorial de jogos, o enfoque será dado em relação a MIDP 2.0, devido a sua massificação no mercado, e sua presença em um grande número de telefones celulares atualmente.
Para cobrir o nosso objetivo, abordaremos as classes LayerManagar e TiledLayer , ambos presentes no pacote da GameAPI. Tendo uma boa compreensão destas duas classes, somando-se ao que foi visto no primeiro artigo, o leitor já terá condições de construir jogos completos. Claro que existem outras questões, como a persistência de dados, que é feita com o RMS ( Record Management System ), mas isso fica para outro artigo.
Boa leitura a todos.
Compilando os exemplos
Para compilar os códigos que seguem ao longo do artigo criamos uma classe chamada ExampleCenario que será a classe que herda de MIDlet, acompanhe o trecho de código abaixo:
01 import....
02
03 public class ExampleLayerManagerMidlet extends MIDlet {
04
05 private Display display;
06
07 public void startApp() {
08 try {
09 display = Display.getDisplay(this);
10
11 GameCanvas gameCanvas = new GameCanvas();
12 gameCanvas.start();
13
14 display.setCurrent(gameCanvas);
15
16 } catch (Exception ex) {}
17 }
18
19 public Display getDisplay() {
20 return display;
21 }
22
23 public void pauseApp() {}
24
25 public void destroyApp(boolean unconditional) {
26 destroyApp(false);
27 notifyDestroyed();
28 }
29 }
A classe ExampleGameCanvas herdará da GameCanvas e será responsável pela exibição da tela do jogo e suas ações. O esqueleto básico da classe é mostrado na listagem abaixo. Para uma melhor compreensão leia o primeiro artigo desta série.
01 import....
02 public class ExampleGameCanvas extends GameCanvas implements Runnable {
03
04 // Constructor and initialization
05 public ExampleGameCanvas() throws Exception {
06 super(true);
07
08 }
09 // Automatically start thread for game loop
10 public void start() {
11 isPlay = true;
12 Thread t = new Thread(this);
13 t.start();
14 }
15 public void stop() { isPlay = false; }
16 // Main Game Loop
17 public void run() {
18 Graphics g = getGraphics();
19 while (isPlay == true) {
20 drawScreen(g);
21 try {
22 Thread.sleep(delay);
23 } catch (InterruptedException ie) {
24 }
25 }
26 }
27 // Method to Display Graphics
28 private void drawScreen(Graphics g) {
29 g.setColor(0xffffff);
30 g.fillRect(0, 0, getWidth(), getHeight());
31 g.setColor(0x0000ff);
32 flushGraphics();
33 }
34 }
LayerManager
Esta classe é de fundamental importância para a criação dos cenários, e ela quem obtém e gerencia todos os componentes visuais do jogo, organizando estes elementos em camadas (layers), utlizando a fórmula do z-order, ou seja, os primeiros elementos gráficos inseridos têm a prioridade maior. Para inserir elementos em um LayerManager deve ser usado o método append(). Levando em conta o gerenciamento utilizado pela classe, e imaginando dois Sprites, um representando o personagem (spriteMonster) e outro o cenário (spriteBackground). As linhas de código abaixo tem um erro grave:
1 layerManager.append(spriteBackground);
2 layerManager.append(spriteMonster);
Já conseguiu reparar o erro? O problema está em inserir primeiro o Sprite que representa o cenário, devido ao algoritmo do z-order, quando o personagem for inserido, o mesmo ficará escondido atrás do cenário, não sendo visualizado na tela do jogo. O correto seria:
1 layerManager.append(spriteMonster);
2 layerManager.append(spriteBackground);
A LayerManager ainda apresenta dois métodos importantes, o remove(Layer l) e o setViewWindow(int x, int y, int width, int height). Este último será explicado na seqüência, para uma compreensão total da classe recomenda-se dar uma olhada na documentação da classe. Além destes, é importante citar seu construtor, que não recebe nenhum parâmetro, sendo assim, para a criação de um novo objeto LayerManagar siga a linha de código abaixo.
1 layerManager = new LayerManager();
Primeiro Exemplo - Usando LayerManager
Agora que já sabemos o essencial sobre a LayerManager, iremos construir um pequeno exemplo, onde teremos dois Sprites, um representa o cenário do jogo propriamente dito (background) e o outro será um personagem. Este exemplo foi retirado do livro ?J2ME & Gaming? de Jason Lam.
Primeiramente, é necessária a criação e inicialização dos dois objetos Sprite, do LayerManager e ligação destes três componentes. No trecho de código abaixo são declarados os três objetos, e posteriormente, dentro do construtor da classe, os mesmos são instanciados. Usando o método append adiciona-se os elementos visuais ao LayerManager.
01 //na declara??o de vari?veis
02 private Sprite spPersonagem;
03 private Sprite spBackground;
04 private LayerManager layerManager;
05 //dentro do construtor
06 Image imgPersonagem = Image.createImage("/personagem.png");
07 spPersonagem = new Sprite (imgPersonagem,32,32);
08 Image imgBack = Image.createImage("/background.png");
09 spBackground = new Sprite(imgBack);
10 layerManager = new LayerManager();
11 layerManager.append(playerSprite);
12 layerManager.append(backgroundSprite);
No início deste artigo mostramos como serão construídos nossos exemplos, o leitor pode ver que existe o método drawScreen(Graphics g), o mesmo é responsável pelo desenho das diretivas gráficas e dos componentes visuais no display do telefone celular, sendo assim, neste método é necessário chamar o método paint(Graphics g, int x, int y) da classe LayerManager. Como parâmetros para este método passamos uma instância de Graphics, que será recebida no próprio método drawScreen, além de sua posição x, y. Veja como o código na listagem abaixo.
1 layerManager.paint(g, 0, 0);
O resultado é mostrado na Figura 1.
|
![]()
Figura 1 - Captura do primeiro exemplo de LayerManager. |
![]()
Figura 2 - LayerManager com posição y alterada. |
Agora, presumindo que o objetivo do programador fosse deixar uma porção de tela na parte superior, para informar a quantidade de vidas e os pontos do personagem. Para isso, basta mudar o método paint() do LayerManager para a forma como é mostrada na listagem de código abaixo. Perceba que a posição y é mudada para 15, fazendo com que o LayerManager seja renderizado um pouco abaixo do topo, veja como fica na Figura 2.
1 layerManager.paint(g, 0, 15);
Segundo Exemplo - Implementando Janelas de Visualização
Geralmente, quando estamos interagindo com um jogo, temos a impressão de que o cenário está se movendo, ou, de que o personagem está avançando no território. Com a GameCanvas, o que realmente acontece é que o cenário formado pelo LayerManager se move, através do método setViewWindow(int x, int y, int largura, int altura), criando o que é chamado de janela de visualização. Para melhor compreensão, acompanhe a Figura 3. Perceba que a partir de uma imagem ampla, a janela de visualização pega apenas parte dela, e devido a possibilidade de mudança em tempo de execução da posição desta janela, obtêm-se a impressão de movimentação do cenário.

Figura 3 - ViewWindow na LayerManager. Fonte: J2MEDEV
Para ver estes conceitos apresentados anteriormente, coloque o trecho de código mostrado na listagem abaixo no lugar da linha que continha o método paint() do LayerManager. Perceba que antes do método paint usamos o setViewWindow(int x, int y, int width, int height), nesse momento, configuramos a janela de visualização, como estamos incrementando a cada iteração o valor de aux, a janela irá se mover para a direita, dando a impressão de que o cenário está se movendo para a esquerda.
1 layerManager.setViewWindow(0+aux, 0, getWidth(), getHeight());
2 layerManager.paint(g, 0, 0);
3 aux++;
Evidentemente, neste trecho de código falta um controle para que o cenário não ?fuja? do display, e quando chegar na extremidade direita a janela de visualização pare de se mover, mas isso eu deixo como um desafio para vocês.
Tiled Layer
Antes de explicar esta classe, imaginem um jogo com mais de 20 fases, cada uma delas terá um cenário diferente, agora, fazendo uma imagem para cada cenário a aplicação pode ficar muito grande, afinal serão vários cenários, várias imagens e dependendo do tamanho do display do device, o tamanho do .jar final do jogo ficará inaceitável. Não seria melhor se, a partir de uma única imagem, fosse possível gerar vários cenários distintos? Coincidentemente, esta é uma das principais funções da classe TiledLayer. Para uma melhor compreensão do que acabei de dizer, veja a Figura 4.


Figura 4 - TiledLayer. Fonte: J2MEDEV
Perceba que a partir de uma imagem, como a mostrada no canto superior esquerdo da Figura 4, pode-se dividir ela em camadas (tiled) e então formar uma outra imagem juntando estes pequenos pedaços, como mostra a imagem do canto inferior esquerdo da Figura 4. Desta maneira, a partir de uma imagem apenas podemos ter vários cenários diferentes, apenas mudando a forma como ?jogamos? os pedaços da imagem de origem para a imagem destino.
Para a criação de um objeto TiledLayer vamos seguir o esquema usado no livro ?J2ME & Gaming? de Jason LAM, citado no início deste artigo. Para continuarmos dê uma olhada na listagem de código abaixo. O construtor da classe recebe a quantidade da matriz de células de tiles, semelhante ao que é mostrado no canto inferior esquerdo da Figura 4, também, recebe uma imagem de origem e a largura e altura dos tiles gerados a partir da imagem original. Esta forma é idêntica ao que o construtor do Sprite faz para a criação de um objeto do seu tipo.
01 private TiledLayer initBackground() throws Exception {
02 Image tileImages = Image.createImage("/tiles.png");
03 TiledLayer tiledLayer = new TiledLayer(10,10,tileImages,32,32);
04 int[] map = {
05 5, 1, 1, 4, 1, 1, 1, 1, 1, 6,
06 5, 1, 3, 1, 1, 3, 1, 1, 1, 6,
07 5, 1, 2, 1, 1, 2, 1, 1, 1, 6,
08 5, 1, 2, 3, 1, 2, 1, 1, 1, 6,
09 5, 1, 4, 2, 1, 2, 1, 1, 1, 6,
10 5, 1, 1, 4, 1, 2, 1, 1, 1, 6,
11 5, 1, 1, 1, 1, 4, 1, 1, 1, 6,
12 5, 1, 1, 1, 1, 1, 1, 1, 1, 6,
13 5, 1, 1, 1, 1, 1, 1, 1, 1, 6,
14 5, 1, 1, 1, 1, 1, 1, 1, 1, 6
15 };
16 for (int i=0; i < map.length; i++) {
17 int column = i % 10;
18 int row = (i - column) / 10;
19 tiledLayer.setCell(column,row,map[i]);
20 }
21 return tiledLayer;
22 } Depois da criação do objeto, é definida uma matriz com os índices dos tiles que cada posição da matriz irá receber, segue-se de um laço for para preencher cada célula, usando para isso o método setCell(int x, int y, int índice). Evidentemente, no construtor da classe ExampleGameCanvas, iremos configurar o objeto TiledLayer da forma que é mostrada no trecho de código abaixo:
1 tiledBackground = initBackground();
Para encerrar a parte de TiledLayer e reforçar a sua importância, veja as Figuras 5 e 6. A primeira mostra a imagem original na parte esquerda, enquanto a outra, ilustra o resultado da construção do cenário através da classe TiledLayer.
|
Figura 5 ? Imagem original. |
Figura 6? Cenário gerado com TiledLayer. |
java ?jar xletview.jar
Ainda existe a possibilidade de configurar algumas células como dinâmicas em um TiledLayer, dando um efeito e uma maior interatividade aos nossos cenários. Para isso, devemos primeiramente configurar em qual índice da célula teremos nosso tiled dinâmico. Veja a listagem de código abaixo. Na primeira linha cria-se um índice, que é passado para o método setCell, juntamente com o endereço de qual índice da matriz será dinâmico.
1 ind = tiledLayer.createAnimatedTile(1);
2 tiledLayer.setCell(2,1,ind);
Completando o código necessário para nosso objetivo, veja a listagem a seguir. O trecho de código mostrado abaixo deve ser colocado dentro do método drawScreen. Nele, a cada iteração é mostrado um tiled da imagem principal, causando com isso, o efeito de cenário dinâmico.
1 if (switchTile) {
2 tiledBackground.setAnimatedTile(ind,1);
3 } else {
4 tiledBackground.setAnimatedTile(ind,5);
5 }
6 switchTile = !switchTile; Conclusão
A construção de jogos com MIDP 2.0 traz muitas facilidades, devido a criação da GameAPI. Tarefas que antes eram de certa forma maçantes com MIDP 1.0 ficaram mais simples, além de trazer uma uniformidade e padronização em certos quesitos importantes de um jogo. O que foi visto nos dois tutoriais ainda é pouco para a construção de um jogo profissional, portanto, esse texto foi só uma primeira leitura para quem quer se tornar fera na criação de games .
Referências Bibliográficas
SUN. Class Tiled Layer. Disponível em & lt; http://java.sun.com/javame/reference/apis/jsr118/javax/microedition/lcdui/game/TiledLayer.html & gt;.
HAMER, Carol. Using the MIDP 2.0 Game API. Disponível em & lt; http://www.javaworld.com/javaworld/jw-08-2004/jw-0809-j2me.html?page=1 & gt;
J2MEDEV. Class LayerManager. Disponível em & lt; http://www.j2medev.com/api/midp/javax/microedition/lcdui/game/LayerManager.html & gt;
- Formatação de textfield
- Erro ao executar aplicacao: "Erro de Aplicacao"
- Motorola 388
- PushRegistry
- A High-Level UI API do MIDP Profile
- Posso por a KVM em um Palm
- ERRO : javax.microedition.lcdui.game does not exist
- High-end PDAs, CDC, PP e MIDP2
- CLDC1.0 e Sockets
- J2ME Serializa objetos?
- MIDP 2.0 ou MIDP 1.0




