Artigo WebMobile 26 - Desenvolver jogos para celulares virou brincadeira com a ferramenta Game Builder do NetBeans

Aprenda a desenvolver um jogo para celular com a linguagem Java ME através da IDE NetBeans e sua ferramenta Game Builder.

De que se trata o artigo:

O artigo apresenta o Game Builder, uma ferramenta do Netbeans que permite desenvolver jogos complexos para celular utilizando a tecnologia JavaME de forma rápida e fácil. Para um melhor entendimento, esse artigo também apresenta os conceitos básicos sobre criação de jogos para a plataforma MIDP 2.0

Para que serve:

O artigo pode ser utilizado por desenvolvedores de jogos e por programadores JavaME que desejam aprimorar seus conhecimentos sobre desenvolvimento de jogos para celulares.

Em que situação o tema é útil:

Na construção rápida de jogos com Java ME para aparelhos celulares.

Autores: Ricardo Ogliari e Robison Cris Brito

Hoje em dia é comum a utilização de IDEs para agilizar o desenvolvimento de aplicativos utilizando a linguagem de programação Java. Essas ferramentas agilizam a criação de software para várias plataformas, tais como internet, desktop ou mobile, automatizando processos básicos para a construção e distribuição de aplicativos, como a codificação de rotinas básicas, a formatação de códigos, sugestões para correções de erros, etc.

Nesse contexto, duas IDEs livres se destacam, são elas: a IDE Eclipse (www.eclipse.org) e a IDE NetBeans (www.netbeans.org), sendo o Netbeans uma das IDEs preferidas pelos iniciantes no desenvolvimento de aplicativos móveis, pois possui o Mobility Pack, sendo este um conjunto de recursos que auxilia no desenvolvimento de aplicativos para dispositivos móveis.

O Mobility Pack foi inserido na IDE Netbeans a partir da versão 5.0. Com ele, é possível utilizar o Sun Wireless Toolkit (kit de desenvolvimento para celular fornecido pela Sun) no próprio ambiente do Netbeans, sendo possível também clicar e arrastar componentes visuais na tela do celular e realizar a depuração do projeto.

Com o passar das versões do Netbeans, novos recursos foram acoplados ao Mobility Pack, sendo que um dos mais recentes e que chama bastante a atenção é o Game Builder. Este recurso permite a construção da interface gráfica de jogo para dispositivos móveis de forma visual. Dessa maneira, o programador pode interagir em alto nível com seus cenários e personagens, ou seja, graficamente e através de wizards. Assim é possível dedicar mais tempo na lógica do jogo que continua sendo desenvolvida através das linhas de código.

Para o desenvolvimento da interface gráfica, o Game Builder utiliza o MIDP 2.0, o qual possui uma biblioteca específica para o desenvolvimento de jogos, chamada Game API. Para a utilização do Game Builder, é desejável um prévio conhecimento sobre as APIs MIDP/CLDC, bem como alguma experiência no desenvolvimento de aplicativos móveis com o Mobility Pack do Netbeans, lembrando que o Game Builder está disponível a partir da versão 6.0 do Netbeans.

O objetivo desse artigo é apresentar brevemente a Game API e as vantagens de sua utilização. Posteriormente será apresentado o Game Builder, suas telas e recursos e, ao final do texto, será apresentado um exemplo didático de construção de jogo para aparelhos celulares. O game a ser construído apresenta somente duas fases de um jogo fictício, onde um personagem deve circular por dois cenários diferentes. Devido ao objetivo didático sobre a Game Builder, não vamos detalhar o jogo e programar todos os agentes envolvidos. Então, primeiramente o jogador se encontra no primeiro estágio (chamado Forest), quando o personagem atingir o canto inferior direito, será enviado ao segundo estágio, que seria o cenário chamado Scene Desert.

Jogos em JavaME e a Game API

Desde as primeiras versões da plataforma JavaME, era possível utilizar os recursos do MIDP/CLDC para desenvolver jogos para celulares, como por exemplo jogos de arcade, de estratégia, puzzle, RPG, esportes, etc. Porém, a MIDP 1.0, perfil que era utilizado nas primeiras versões do JavaME, possuia muitas deficiências para a manipulação avançada da interface gráfica do celular. Por exemplo, os personagens de um jogo eram desenvolvidos através da codificação pura, não existindo classes que implementassem características comuns, como o tamanho do personagem, a imagem do mesmo, sua posição na tela, dentre outras. Com isso, alguns fabricantes, como a Nokia por exemplo, criaram suas próprias bibliotecas de jogos para suprir estas falhas. Essa ação resultou na perda de portabilidade dos aplicativos, sendo que aplicativos desenvolvidos com as APIS de um fabricante funcionariam apenas em aparelhos compatíveis (da mesma marca).

A Game API surgiu para suprir essa deficiência. Ela foi disponibilizada a partir da versão 2.0 do MIDP e possui uma biblioteca de classes poderosa para desenvolvimento de jogos. Além disso, com as padronizações advindas com a Game API, até o tamanho dos jogos gerados pelo JavaME ficaram menores, pois alguns códigos, como por exemplo os que gerenciam os personagens e cenários, estão incluso na biblioteca de classe, que são implementadas nos dispositivos.

Dentre os novos recursos oferecidos pela Game API, destacam-se:

GameCanvas

É uma evolução da classe Canvas, sendo projetada especificamente para o desenvolvimento de jogos. Dentre as melhorias, podemos destacar que com GameCanvas é possível controlar todo o jogo com apenas uma Thread, e existem algumas facilidades para captura de evento de teclado.

Outra vantagem da GameCanvas está na atualização da tela do jogo, que só irá acontecer após a chamada do método flushGraphics(). Essa técnica faz com que toda a tela do jogo seja atualizada de uma só vez, e não a cada alteração da tela (ex. primeiro se altera a posição de todos os personagens do jogo e depois executa o método flushGraphics(), que apresentará a nova tela). Dessa maneira, as imagens são apresentadas de forma mais suave para o usuário.

Outra mudança significativa é a captura de eventos de teclado. Na MIDP 1.0 isso era feito em uma Thread separada. Na Game API isso é feito através do método getKeyStates. Além disso, este método permite a captura de teclas simultâneas (mais de uma tecla pressionada ao mesmo tempo) e de teclas que são mantidas pressionadas.

Sprite

A classe Sprite é um elemento gráfico do jogo, geralmente utilizada para os personagens. Essa classe possui vários métodos que facilitam o trabalho do desenvolvedor, como para verificar colisões, métodos para transformação de uma imagem e para visualização de uma seqüência de imagens (idéia de animação). A Figura 1 apresenta os dois Sprites que serão utilizados pelo programa de exemplo desse artigo.

Figura 1. Sprites Karel e Thomas, personagens do aplicativo a ser desenvolvido nesse artigo.

LayerManager

Essa classe é de fundamental importância para a criação dos cenários. É ela quem obtém e gerencia todos os componentes visuais do jogo, organizando estes elementos em camadas (layers). Ao ser inserido no LayerManager, o componente visual recebe uma prioridade (representada pela letra z), assim os elementos gráficos com maior prioridade poderão sobrescrever os com menor prioridade se necessário. A Figura 2 apresenta a organização dos elementos do jogo no LayerManager. Nessa figura (extraída do NetBeans) são apresentados os tipos dos componentes (Sprite ou Tiled Layer), a prioridade (z), se o mesmo será apresentado na tela, se está bloqueado para movimentação (evitar movimentações acidentais do componente na tela), o nome e as posições x e y.

Figura 2. Representação gráfica do LayerManager, apresentando os personagens e algumas características deles.

TiledLayer

A classe TiledLayer é usada para construção de cenários de jogos. Basicamente é utilizada para montar imagens a partir de sub-imagens, ou seja, através de uma repetição de pequenas imagens é possível criar um cenário maior e mais complexo. A Figura 3 apresenta o TiledLayer referente ao fundo do primeiro estágio do jogo.

Figura 3. TiledLayer da primeira fase do jogo desenvolvido no artigo.

Utilizando a Game Builder

Para iniciar na utilização da Game Buider, será utilizado um exemplo pronto oferecido pela própria IDE Netbeans. Para isso, deve-se selecionar o menu File à New Project... Deve-se selecionar a categoria Java ME (MIDP), que é uma subcategoria de Samples. Em seguida, em Projects devemos selecionar Simple Game Created With Game Builder. Ao pressionar o botão Next, será solicitado o nome do projeto (para o exemplo foi utilizado FirstGameWebMobile) e o local onde o mesmo será armazenado. Novamente, ao se pressionar o botão Next será apresentada a última tela do Wizard, onde será solicitada informações como a versão do MIDP/CLDC desejada. Para finalizar o projeto, deve-se clicar no botão de Finish. Uma tela semelhante à Figura 4 é apresentada.

Figura 4. Tela principal de um projeto criado com o Game Builder.

À esquerda (1), é apresentada a paleta Projects, onde estão disponíveis os arquivos do projeto. Como pode ser visualizado, o projeto é composto por dois arquivos de imagens (plataform_tiles.png e topview_tiles.png) e quatro classes que se encontram dentro do pacote demo.

A classe responsável pelo desenvolvimento da interface visual do jogo é a GameDesign. Dando dois cliques sobre esse arquivo, é exibido o ambiente de desenvolvimento visual (6). Para alternar entre o desenvolvimento visual e o código gerado pelo Game Builder, é utilizado os botões Source e GameBuilder (3).

Também é apresentada uma janela de navegação (2), na qual é possível alternar entre os elementos visuais do jogo. Esse exemplo de jogo gerado pelo Netbeans possui dois personagens (Sprites) com nomes Karel e Thomas, e quatro TiledLayers, os quais representam partes dos cenários do jogo. O Base é o cenário propriamente dito, Things são os objetos fixos existentes no cenário, Threes representam as árvores que serão adicionadas ao cenário e Water representa um lago. Basicamente, os TiledLayesr, com exceção da base, são os obstáculos que não poderão ser ultrapassados pelos personagens.

Nesse exemplo, todos os TiledLayers fazem parte de um cenário, que pode ser considerado um estágio do jogo.

Para facilitar a navegação, pode também ser utilizado o ComboBox da parte superior (4), possuindo ao seu lado botões que permitem adicionar novos cenários, TiledLayers e Sprites (5).

A janela central (6) também apresenta uma lista com todos os componentes do jogo. Ao clicar em cima de um componente, é apresentada a tela de edição, podendo retornar à tela inicial escolhendo a opção GameDesign no ComboBox (4).

Ao selecionar a cena Forest (primeira fase do jogo – Figura 5), temos uma instância do Layer Manager (1) com todos os componentes visuais da tela (conforme exibido na Figura 2).

Figura 5. Layout Manager do cenário Forest.

Ao centro, visualiza-se a posição inicial dos Sprites (2), bem como dos TiledLayers (3) (4) (5). Como existem réguas horizontal e vertical, visualiza-se o posição inicial e final em pixel de cada componente, bem como o tamanho do cenário. Como a tela é maior que a resolução da maioria dos celulares (384x368), é evidenciada a utilização de paginação no cenário.

É possível também, na tela de edição de cenários, adicionar novos elementos visuais (TiledLayers e Sprites) clicando com o botão direito sobre a tela e escolhendo a opção addSprite ou addTiledLayer. É possível adicionar apenas uma instância de cada componente visual na tela (por exemplo, um Sprite do Karel e um Sprite do Thomas).

Ao retornar à tela principal (Game Design) e clicar sobre o Sprite Karel, é apresentada a tela de edição de Sprites (Figura 6).

Figura 6. Tela de configuração do Sprite Karel.

O Sprite é criado a partir de uma imagem ou um conjunto de imagens. Nesse exemplo, as imagens de Karel são recuperadas do arquivo topviw-tiles.png, sendo que este personagem é criado a partir de uma seqüência de imagens, as quais formarão uma animação. Para essa animação dá-se o nome de seqüência.

Um Sprite pode possuir uma ou mais seqüências. No exemplo do Sprite Karel, existe uma que representa a movimentação do personagem quando ele sobe no cenário, outra que representa sua movimentação para baixo e outras duas que representam as movimentações laterais. As listas de seqüência disponíveis são apresentadas no Navigator (1), sendo apresentada a seqüência padrão atribuída para o Sprite, o nome das seqüências, a quantidade de frames (imagens) e o delay (tempo de espera) entre um frame e outro. O tempo do delay é atribuído via interface visual (4), sendo possível visualizar a animação (5). Caso seja necessário, novas imagens podem ser adicionadas à seqüência, clicando na imagem desejada (3) e arrastando até a seqüência desejada (2).

Para criar uma nova seqüência, devemos clicar com o botão direito sobre a área central (2) e escolher a opção Create Sequence (ver Nota 1).

Nota 1. Dica sobre Sprite

Ao ser iniciada, a lista com as imagens - Figura 6 (3) – possui altura zero, logo deve ser redimensionada para apresentar as imagens disponíveis.

Voltando à tela principal (GameDesign), pode-se editar os TiledLayers do aplicativo clicando sobre ele. Será exibida uma tela de edição, conforme apresentado na Figura 7.

Figura 7. Tiled Layer Base.

Um TiledLayer é formado a partir de uma matriz de “sub-imagens”, as quais são agrupadas com o objetivo de formar uma imagem maior. No exemplo da Figura 7, temos um Tiled Layer com 23 linhas e 24 colunas, sendo que cada célula da matriz possui 16x16 pixels.

A área central (1) é utilizada para montar a tela, sendo possível clicar e arrastar novas imagens de (2). Na parte superior (3) é possível alternar entre as opções Paint Mode, o qual permite adicionar a imagem selecionada em (2) quando se seleciona uma célula em (1), e a opção Selection Mode, que permite selecionar células em (1), aplicando sobre elas operações de duplicação, deleção, etc. (essas operações são obtidas através do clique com o botão direito sobre a célula selecionada) (Ver Nota 2).

Nota 2. Dica sobre Tiled Layer

Para preencher uma grande área com a mesma imagem, basta selecionar a área que se deseja preencher (a seleção pode ser feita clicando sobre os cabeçalhos de linhas e colunas em (1)) e após clicar/arrastar a imagem que se deseja no preenchimento (2), assim toda a área selecionada será preenchida com a mesma imagem.

Outro recurso interessante no TiledLayer são os AnimatedTiles (4). Estes são animações que podem ser adicionadas à tela. Para criar um novo AnimatedTile, devemos clicar no botão New Animated Tiled (5). Será apresentada uma tela semelhante à criação de Sprites, como pode ser observado na Figura 8.

Figura 8. Edição de um AnimatedTiled.

Assim como os Sprites, um AnimatedTiles possui uma seqüência, a qual pode ser formada por uma ou mais imagem. Para se criar um novo elemento com o Game Builder (Scene, TiledLayer ou Sprite), devemos escolher a opção Create correspondente ao recurso na tela representada pela Figura 4. Como exemplo, será iniciado o desenvolvimento de uma nova TiledLayer. Portanto, devemos selecionar Create Tiled Layer. A tela da Figura 9 é apresentada ao programador.

Figura 9. Criação de um Tiled Layer.

Para o exemplo, foi dado o nome de Sand para o novo TiledLayer (1), sendo o mesmo criado a partir da imagem topview_tiles.png. Essa imagem é formada por um conjunto de sub-imagens, as quais podem ser visualizadas em (3). Se for necessário utilizar uma imagem que não está no projeto, podemos escolher a opção Import sample images (4). É possível escolher a opção zoom (5) para aumentar as imagens em (3), e também definir um novo tamanho para as sub-imagens deslizando os sliders (6) e (7). Após clicar em OK será exibida uma tela em branco para o desenvolvimento no novo Tiled Layer. Como sugestão, segue a tela apresentada na Figura 10.

Figura 10. Edição do novo TiledLayer.

Com o novo TiledLayer, será desenvolvido um novo estágio para o jogo. Então na tela principal de Game Design devemos escolher a opção Create Scene... e na tela seguinte será solicitado um nome para a fase. Para esse exemplo será utilizado o nome Desert.

Será apresentada a tela para desenvolvimento da Scene (Figura 11). Nessa tela podemos adicionar Tiled Layers clicando com o botão direito e escolhendo a opção Add TiledLayer (1). Também é possível adicionar Sprites, escolhendo a opção Add Sprite.

Figura 11. Tela de construção de um novo estágio para o jogo.

Conforme se adiciona elementos visuais ao estágio do jogo, os mesmos são apresentados em Navigator (3), sendo possível mudar a prioridade dos mesmos (z) e visualizar a posição inicial na tela (x e y).

Agora para finalizar o exemplo, a Scene Desert será apresentada quando o personagem Karel, esse controlado pelo usuário, atingir o canto inferior direito do primeiro estágio (Forest). Para isso, devemos adicionar os primeiros códigos ao jogo desenvolvido. Como já foi observado, o projeto possui quatro classes, sendo elas:

GameMidlet.java – Esse é o MIDlet do jogo. Essa classe é responsável por controlar o ciclo de vida do aplicativo e também por apresentar a tela inicial do jogo.

GameDesign.java – Classe que possui o código de todos os elementos visuais do jogo (TiledLayer, Sprite, etc.). Essa classe é criada e mantida automaticamente pelo ambiente visual do Game Builder.

SpriteRandomMoviment – Essa classe é responsável pelo movimento dos personagem que são controlados pela máquina (celular). Nesse exemplo, o personagem Thomas, que é uma aranha, se move constantemente pelo cenário e ao encontrar um obstáculo (nesses exemplos os únicos obstáculos são as bordas do cenário), ele muda de direção automaticamente.

DemoGameCanvas – Essa é a classe responsável pela lógica do jogo. É a classe principal e seu código está dividido em:

o Método Construtor - public DemoGameCanvas(): Esse método é utilizado para iniciar o jogo. Algumas configurações, como deixar o jogo ocupando toda a tela do celular, podem ser realizadas nesse método.

o Método private void init(): Nesse método são instanciados os componentes visuais da tela, é iniciada as animações (Sprites e AnimatedTile). Esse método também pode ser utilizado para iniciar os Scores do jogo, por exemplo.

o Método public boolean spriteCollides(Sprite sprite): É responsabilidade desse método detectar a colisão entre os Sprites passados por parâmetro e os componentes visuais da tela. Inicialmente são detectadas apenas as colisões do Sprite com as bordas da interface, porém outras colisões podem ser implementas no código fonte.

o Método private void adjustViewport(int x, int y): Esse método é utilizado para a paginação na interface visual. Quando o Sprite controlado pelo usuário sai da área de visão (tamanho do display) esse método faz o deslocamento da tela.

o Método public void run(): Método mais importante do jogo. É responsável pela lógica do aplicativo. Dentro desse método que é chamado o spriteCollides para identificar colisões e é atualizada a posição da tela com o método adjustViewport.

o Método public void stop(): Método responsável por parar a lógica do jogo. Chamado quando se recebe uma ligação, por exemplo.

Além dos métodos citados anteriormente, a classe DemoGameCanvas possui duas classes privadas, sendo elas:

o private class TileAnimationTask extends TimerTask: Classe responsável pelo efeito de animação dos Animated Tiles.

o private class SpriteAnimationTask extends TimerTask: Classe responsável pelo efeito de animação dos Sprite.

A lógica para mudar de fase é apresentada na Listagem 1, onde é apresentado parte da lógica do método run.

Listagem 1. Lógica para mudar de fase no DemoGameCanvas.java

01. public void run() { 02. Graphics g = getGraphics(); 03. 04. while (!this.interrupted) { 05. 06. if (this.spriteKarel.getX() >= 340 && 07. this.spriteKarel.getY() >= 340) { 08. 09. try { 10. 11. while (this.lm.getSize() > 0) { 12. javax.microedition.lcdui.game.Layer l = 13. this.lm.getLayerAt(0); 14. 15. this.lm.remove(l); 16. } 17. 18. this.gameDesign.updateLayerManagerForDesert(this.lm); 19. } catch (IOException e) { 20. e.printStackTrace(); 21. } 22. 23. this.viewPortX = 0; 24. this.viewPortY = 0; 25. this.lm.setViewWindow( this.viewPortX, this.viewPortY, 26. this.getWidth(), this.getHeight()); 27. 28. this.lastDirection = -1; 29. } 30. 31. //check for user input 32. int keyState = getKeyStates(); 33. 34. //if user is pressing the left button 35. if ((keyState & LEFT_PRESSED) != 0) { 36. ... 37. ... 38. }

Na listagem anterior, o método run() inicialmente recupera a interface gráfica do celular através do comando getGraphics() (linha 02) para então iniciar o looping de renderização da tela.

Dentro do looping será verificada a posição do Sprite Karel através dos comandos getX() e get(Y), verificando se as coordenadas são maiores que 340 (linhas 06 e 07), sendo este o tamanho em pixel aproximado da Scene Forest. Caso positivo, é realizado um looping pelo objeto lm, o qual representa o LayerManager, sendo cada elemento do objeto recuperado (linhas 12 e 13) e excluído (linha 15), afim de limpar da tela os componentes visuais do primeiro estágio.

Na linha 18 é utilizado o comando updateLayerManagerForDesert(), o qual possui a função de adicionar à tela o segundo cenário. Para finalizar a lógica, são reiniciadas as variáveis para o controle de paginação na tela (linhas 23 e 24), definida a área visível do segundo cenário (linhas 25 e 26) e reinicializada a variável que representa a última movimentação do Sprite Karel (linha 28). Em seguida, a lógica continua conforme o arquivo original, recuperando a tecla pressionada pelo usuário (linha 34) e fazendo a verificação da mesma (linha 37).

As telas do jogo apresentadas no emulador são apresentadas na Figura 12.

Figura 12. Telas do jogo. À esquerda o primeiro estágio e à direita o segundo estágio desenvolvido.

Conclusão

A criação de jogos é um dos principais ramos de atuação da plataforma JavaME, e a última versão de seu perfil MIDP traz melhorias significativas na construção deste tipo de aplicativo. Para ajudar ainda mais o desenvolvedor, a IDE NetBeans traz a ferramenta Game Builder, que faz aumentar significativamente a produtividade no desenvolvimento de jogos. Agora é soltar a imaginação e utilizar todos os recursos dessa ferramenta.

Links

Tutorial Básico sobre a utilização do GameBuilder
http://www.netbeans.org/kb/samples/mobile-game-builder.html

Tutoriais, artigos e vídeo aulas sobre JME e Jogos
www.devmedia.com.br

Artigos relacionados