De que se trata o artigo:

Neste artigo será apresentada a construção de um jogo 2D de rolagem de mapa. Mostraremos de forma prática e passo a passo os conceitos relacionados ao sistema Android para este tipo de jogo.

Em que situação o tema útil:

Este artigo pode ser útil para desenvolvedores iniciantes ou experientes que desejam entrar neste fantástico mundo dos jogos.

Resumo DevMan

De acordo com dados sobre o Market - loja de aplicativos para Android OS da Google, jogos representam entre 30 e 50 por cento do volume de downloads de aplicativos. Sendo assim, torna-se um grande nicho para ser explorado pelos desenvolvedores, e este artigo vem contribuir neste aspecto.

O mercado móvel está em expansão e hoje podemos encontrar vários dispositivos com Android, facilitando assim a sua disseminação. Além disso, a plataforma Android é livre e tem seu código fonte liberado, assim como um SDK que possui um emulador, facilitando o desenvolvimento.

Recentemente, os jogos para celulares ganharam popularidade por fornecer o entretenimento pessoal móvel. Esta popularidade faz com que os jogos desempenhem um papel fundamental na geração de renda para as operadoras de celulares, desenvolvedores de jogos e fabricantes de aparelhos telefônicos.

O desenvolvimento de jogos faz uso de vários conceitos simples mas interessantes de serem conhecidos por aqueles que pretendem entrar neste mercado. Dessa forma, relembrando alguns conceitos apresentados em artigos anteriores, vamos retomar aqui algumas definições que considero muito importantes:

· Curva de aprendizagem: é o tempo que um jogador necessita para assimilar os comandos e as regras do jogo.

· Sprite: é um elemento básico visual que pode ser renderizado a partir de um ou vários frames de uma imagem. Estes vários frames podem ser utilizados para fazer a animação do sprite. Normalmente corresponde a um objeto na tela do jogo, como um inimigo, o personagem principal, ou um tiro.

· FPS (frames per second): significa quadros por segundo. Define quantos quadros são mostrados ao usuário em um segundo de jogo. Para jogos em celulares, o mínimo aceitável são 10 quadros por segundo. Quanto maior a quantidade de quadros por segundo, maior é o processamento requerido.

· Turno de jogo: conjunto de atividades que o jogo faz a cada frame de jogo. Entre atividades típicas, estão repintar a tela, verificar os pontos do jogador ou colisões entre os sprites.

Associado a estes conceitos, é importante também relembrarmos que o desenvolvimento de jogos passa por várias etapas, e algumas delas são:

· Game Design: é a etapa onde o jogo é criado com suas idéias, personagens e estória, ou seja, é a concepção propriamente dita;

· Rascunho dos objetos e cenários: é a etapa do processo que auxilia os modeladores a criarem o mundo do jogo.

· Modelagem: é a etapa onde serão criados modelos e texturas utilizados no jogo.

· Engenharia de Software: etapa onde é feita a modelagem do sistema

· Programação: etapa onde o jogo é de fato codificado.

Este artigo abordará apenas a etapa da programação. Iremos desenvolver um pequeno esboço de framework para o funcionamento e controle do jogo. Neste contexto, neste tutorial desenvolveremos um jogo 2D de rolagem de mapa. Mostraremos de forma prática e passo a passo os conceitos relacionados ao sistema Android para este tipo de jogo.

Entretanto, antes de iniciarmos o desenvolvimento do jogo propriamente dito, devemos configurar o ambiente de desenvolvimento. O Android Development Tools (ADT) trata-se de um plugin para o eclipse que facilita a criação rápida de novos projetos Android. Ele possibilita a criação de interfaces de usuário, adicionar componentes, depurar aplicações utilizando as ferramentas do SDK android e exportar os .apk (assinados ou não).

Em geral, usar o Eclipse com o ADT é a forma mais recomendada de desenvolver aplicações Android e o jeito mais rápido de começar. Se você usa Eclipse, o plugin ADT lhe fornecerá um bom começo no desenvolvimento de aplicações Android.

Sendo assim, nosso primeiro passo é instalar o SDK do Android e o plugin ADT no Eclipse. Iremos precisar deles para construir o nosso jogo e você pode fazer isto baixando o SDK através do site http://developer.android.com/sdk/index.html.

Uma vez que você tenha o SDK e o ADT plugin instalado, é hora de configurar o nosso projeto. Para isso, abra o Eclipse, vá em Window> Android SDK e AVD Manager, clique em virtual devices e verifique se existe ao menos um dispositivo virtual configurado. Se não houver, conforme mostra a Figura 1, dê um clique no botão New, preencha o campo Name com um nome sugestivo, adicione-o selecionando o target Android 2.1 - API level 7, se necessário informe o tamanho do SD Card a ser criado, se for necessário capturar telas do aplicativo, deixe a opção Snapshot selecionada. Pode-se também escolher ou definir as propriedades de resolução do Skin, assim como também acrescentar características de hardware, tais como acelerômetro, gravar áudio, gps e outros. Feito isto, clique no botão Create AVD para criar o dispositivo virtual que irá rodar a aplicação de acordo com a Figura 2.

Figura 1. Verificando o Virtual Device

Figura 2. Criando e configurando o Virtual Device

Criando um jogo baseado em mapas

Em um jogo 2D, o mapa de um nível inteiro, ou o mapa do jogo, é formado por várias telas de largura. Alguns chegam a ter mais de 50 telas de largura, e como o personagem anda pela tela, o mapa faz a rolagem.

Criar uma imagem contendo todo o mapa não é uma boa solução principalmente se levarmos em conta a memória necessária para carregar o mapa. Uma grande imagem não define sozinha onde o jogador pode ou não caminhar, ou mesmo quais partes da mesma possuem movimento ou ações próprias. Assim, em vez de utilizarmos uma grande imagem, iremos criar pequenas células de imagens e exibi-las à medida que o usuário for se deslocando dentro do mapa, como mostrado na Figura 3. Cada célula da grade contém uma imagem.

Figura 3. Deslocamento no mapa do jogo

Utilizar um mapa a base de células é como brincar com blocos de lego. Apenas alguns blocos de cores diferentes são usados, mas você tem uma quantidade ilimitada de cada cor e a imaginação é o limite.

O mapa de células contém as referências de qual imagem pertence a cada célula do gride. Desta forma, você só precisa de algumas imagens pequenas para as células e pode fazer os mapas tão grande quanto você quiser sem se preocupar muito com as restrições de memória.

Na maioria das vezes, o tamanho de cada imagem é derivado de uma potência de 2, como 16, 32 ou 64. Neste jogo, iremos utilizar o tamanho do bloco de 32x32. A Figura 4 apresenta os blocos que serão utilizados.

Figura 4. Blocos utilizados no jogo

Iremos utilizar uma matriz de caracteres para armazenar o mapa de blocos do jogo. Neste artigo, iremos utilizá-lo de forma declarativa, porém podemos fazê-lo de várias formas: xml, texto, binário, etc. A Figura 5 mostra todo o mapa do jogo.

Figura 5. Mapa completo do jogo

Na Listagem 1, podemos ver como o mapa foi declarado e entender melhor o significado da substituição de cada caractere por uma imagem correspondente. Perceba que cada caractere apresentado no mapa corresponde a um tipo de imagem. Perceba também que esta solução nos traz um bom nível de escalabilidade uma vez que não necessitaremos armazenar todo o mapa no celular, mas apenas os diferentes tipos de imagens. Estas serão apresentadas à medida que o usuário se descola pela tela do celular considerando a distribuição definida na Listagem 1.

Listagem 1. Declaração do mapa de caracteres

String[][] mapa = { {                    
                      "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV",
                      "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV",
                      "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV",
                      "VVVQTTTTTTTTTTTTTTTTTTTTTTTTTTTTSVVVVVVVVVVVVV",
                      "VVVG55555555555555555566B6646666MVVQTTTTTSVVVV",
                      "VVVG55U5566665555556666666666666MVVG666P6MVVVV",
                      "VVVG556P666666655566666466666666MVVG66666MVVVV",
                      "VVVG6666664666646666666666666666MVV1003002VVVV",
                      "VVVG66666666666666666666E0C66466MVVVVV3VVVVVVV",
                      "VVVG55555666666666646666MVG66666FTTTTT3TTTSVVV",
                      "VVVG555PP555555555666L66FTD66666L66666666PMVVV",
                      "VVVG555555556646666666666666A6666646666466MVVV",
                      "VVVG55555566666664666466646666646666666666MVVV",
                      "VVVG66555666666666666666666666666555564666MVVV",
                      "VVVG666466646664666666666666666L5555555546MVVV",
                      "VVVG666666666L666666666666P664666665555666MVVV",
                      "VVVG6466664666666666L666666666666665556966MVVV",
                      "VVVG6666666666666L666666666666466555566666MVVV",
                      "VVVG66646666664666646666666646666555666666MVVV",
                      "VVVG6666666666666666666L666666655556666646MVVV",
                      "VVVG664L6666666666666666666666555YX6466666MVVV",
                      "VVVG6666444444444464466666L655555556664666MVVV",
                      "VVVG676646O6666664644666666666655555556666MVVV",
                      "VVVG666646664444466646666666666666666E00002VVV",
                      "VVVG666644X466466646464666666466L6666FTTTTSVVV",
                      "VVVG666646X4444446464646666666666666666466MVVV",
                      "VVVG66664XXXX66666644466646666666666646666MVVV",
                      "VVVG66664444444444644666666646686666666666MVVV",
                      "VVVG66L6666HI666666666666666666666L6666646MVVV",
                      "VVVG66666LLJKLL666666P66666666646666466666MVVV",
                      "VVVG666LLLLL66LL66666666646666666666664666MVVV",
                      "VVVG66666666666666666666666666666666666666MVVV",
                      "VVV1000000000000000000000000000000000000002VVV",
                      "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV",
                      "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV",
                      "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV" 
                    }};

Para implementarmos esta arquitetura, nosso jogo será composto de três módulos:

· Lógica do jogo: este módulo é responsável pela mudança de estado dos objetos “atores” do jogo. Atores são os objetos que possuem estado, por exemplo, para o herói do jogo podemos calcular a posição, verificar possíveis colisões, etc;

· Som: este módulo irá produzir a música de fundo;

· Gráficos: este é o módulo responsável pela apresentação visual do jogo. Isso pode ser tão simples como desenhar diretamente na tela, através de um buffer, ou mesmo algo mais elaborado utilizando OpenGL (ler Nota DevMan 1).

Nota DevMan 1. OpenGL

O Android inclui suporte para gráficos de alto desempenho em 2D e 3D com o Open Graphics Library (OpenGL), mais especificamente, a API OpenGL ES. OpenGL é uma multiplataforma API gráfica que especifica uma interface de software padrão para hardware de gráficos 3D de processamento. OpenGL ES é uma versão da especificação OpenGL destinada para dispositivos embarcados. O OpenGL ES 1.0 e 1.1 têm sido apoiados desde o Android 1.0. Começando com o Android 2.2 (API Nível 8), a estrutura suporta a especificação OpenGL ES 2.0.

Implementando o mapa

Como mencionado antes, os mapas de rolagem são muito maiores do que a tela, portanto, apenas uma parte do mapa é mostrada na tela em um determinado momento. Com o movimento do jogador, iremos rolar a tela e não propriamente o jogador, mantendo o mesmo no centro da tela. Observe o código da Listagem 2 para entender como o mapa é desenhado.

Listagem 2. Desenhando o mapa na tela

int curRow,curColumn,destX,destY;
   
  // Desenha o mapa
  for (int y=-4;y<=4;y++){
         for (int x=-4;x<=4;x++){
               curRow              = y + char_Y + 1;
               curColumn    = x + char_X + 1;
               destX = (x+4) * this.blockWidth;
               destY = (y+4) * this.blockHeight; 
               
               try {
                      canvas.drawBitmap(this.map.getBitmapForBlock(curColumn, curRow), 
                                    null, 
                                    new Rect ( destX, destY, destX + this.blockWidth, destY + this.blockHeight), 
                                    paint);
               } catch (Exception e) {
                      Log.i("Log",String.format("curRow %d curColumn %d destX %d destY %d %c", curRow,curColumn,destX,destY,this.map.getCharForBlock(curColumn,curRow)));
               }
                                          
         }
  }            
  // Desenha o heroi no centro do mapa, ou seja quem realmente deslocou foi o mapa
  destX = 4 * this.blockWidth;
  destY = 4 * this.blockHeight;
  canvas.drawBitmap(this.map.getBitmapForCharacter(char_Face), 
               null, 
               new Rect (destX, destY, destX + this.blockWidth, destY + this.blockHeight), 
               paint); 

Rolagem paralaxe

Deslocamento de paralaxe é uma técnica especial de rolagem onde as imagens de fundo se movem de forma mais lenta do que as imagens em primeiro plano, criando uma ilusão de profundidade em um jogo 2D e aumentando a imersão. Esta técnica é utilizada desde os anos 1940, e foi popularizada em 1982 no jogo Moon Patrol. Esta técnica não será utilizada neste jogo, e sim no próximo artigo onde teremos um jogo com rolagem vertical como os clássicos Mario Bros e Zelda. Mas com fins de aprendizado, vamos mostrar um exemplo do efeito paralaxe.

Criando o efeito paralaxe

Nas Listagens 3 e 4 temos um exemplo de uma pequena engine para criar o efeito paralaxe na tela no eixo x. Na Listagem 3 criamos uma nova View. A View é uma classe simples que nos proporciona o tratamento de eventos (como onTouch) e um container para podermos desenhar. A maneira mais simples de criá-la é estender da própria classe View. Podemos também estender da SurfaceView, que é uma classe que nos possibilita um acesso de mais baixo nível. Entretanto, para este exemplo utilizaremos a View por ser mais simples de ser manipulada e satisfazer às nossas necessidades.

Listagem 3. Main.java

package br.com.parallax;
   
  import android.app.Activity;
  import android.os.Bundle;
   
  public class main extends Activity {   
        private GameView view;
      @Override
      public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          view = new GameView(this);
              setContentView(view);
      }
  }
...

Quer ler esse conteúdo completo? Seja um assinante e descubra as vantagens.
  • 473 Cursos
  • 10K Artigos
  • 100 DevCasts
  • 30 Projetos
  • 80 Guias
Tenha acesso completo