Criando jogos para Android

Esse artigo demonstra como implementar um jogo multiplayer a partir de uma arquitetura cliente-servidor utilizando Android para o Cliente e Java para o Servidor. O software desenvolvido apresenta as vantagens da utilização dos conceitos de Orientação a Objetos, Design Patterns, separação de camadas e desenvolvimento por interfaces no mercado de jogos.

Esse tema é útil para desenvolvedores que tenham interesse em utilizar a linguagem Java no desenvolvimento de jogos. O artigo abordará o uso da linguagem Java em duas situações bem distintas, mas que se integram: no desenvolvimento de um de um servidor multiplayer e na utilização da plataforma Android, para dispositivos móveis. O tema também é útil para desenvolvedores que já tenham experiência no desenvolvimento de jogos e que estejam interessados em projetar uma arquitetura mais flexível e robusta, muito necessária devido às demandas do mercado.

Artigo no estilo: Curso

É impossível ignorar a importância do mercado de jogos eletrônicos (ou simplesmente games) nos dias atuais. O que antes era visto como algo somente para crianças, agora se expandiu para um público amplo de diferentes idades, sexos, gostos, perfis culturais, etc. E a expansão não para. Além das plataformas tradicionais (consoles e computadores), os games se infiltraram nas redes sociais, nos tablets, nos celulares e onde mais conseguirem rodar daqui para frente.

Mas o que em geral passa despercebido é o fato de que por trás das animações e do entretenimento, os jogos são softwares. O desenvolvimento de um jogo segue as mesmas regras para desenvolver um bom software com interface gráfica. Uma arquitetura adequada, um projeto robusto e o uso de padrões são essenciais para fazer um game de qualidade.

As diversas plataformas em que os jogos são distribuídos só tornam maior a importância de uma arquitetura robusta. Se a cada nova plataforma for necessário reescrever ou “remexer” em um código, o projeto pode ser tornar inviável.

Outra situação que exige uma arquitetura robusta são os (cada vez mais comuns) jogos multiplayer, ou seja, os jogos em que vários usuários interagem ao mesmo tempo, se ajudando ou competindo entre si, se comunicando em tempo real.

Do lado do software, um jogo multiplataforma e multiplayer é um sistema cliente-servidor que necessita de diferentes e rápidas formas de comunicação. E quando se deseja um alto número de jogadores (o que sempre se deseja), ele também deve ser um sistema que precisa aguentar uma alta carga de usuários e mensagens trocadas (na ordem de milhares).

Embora existam ferramentas de desenvolvimento de jogos que portem de forma muito eficiente um mesmo código para diferentes plataformas, isso será uma solução apenas para o lado do Cliente (a rica interface gráfica que enche os olhos do jogador). O servidor multiplayer que centralizará a lógica de troca de mensagens e segurança das partidas será apenas um e necessitará se comunicar com diversos clientes que, às vezes, se comunicam de maneiras bem diferentes.

No decorrer desse artigo, será apresentada uma arquitetura cliente-servidor desenvolvida em Java para um jogo multiplayer feito para Android. Embora o Cliente seja, no momento, somente a plataforma Android, a arquitetura focará num protocolo de comunicação flexível que permitirá a comunicação entre Cliente e Servidor de uma forma independente da tecnologia de comunicação escolhida.

O Jogo do Android

O jogo idealizado chama-se SpaceTip e é bem simples (afinal, muitos dos sucessos das redes sociais são jogos simples, certo?). A ideia é uma partida espacial entre dois jogadores, cada um tentando abater as naves do adversário disparando torpedos com simples toques na tela.

O fluxo do jogo é o seguinte:

  • A tela e dividida em dois campos de batalha;
  • Cada jogador posiciona suas naves em seu campo (seu lado da tela);
  • Após os dois posicionarem as naves a partida é iniciada;
  • As jogadas são em turnos alternados para cada jogador, começando pelo que entrou primeiro;
  • Em seu turno, cada jogador deve realizar um toque no seu lado da tela;
  • Após o toque, o torpedo é disparado para o ponto simétrico exato, do lado adversário;
  • Caso o torpedo desfeche no ponto onde existe uma nave adversária, a nave é abatida;
  • Os turnos se alternam até que um jogador consiga abater todas as naves do adversário e seja o ganhador da partida.

A Figura 1 ilustra a tela de uma partida de SpaceTip.

Tela do Jogo SpaceTip

Figura 1. Tela do Jogo SpaceTip.

Projeto Inicial

No projeto do jogo, o primeiro passo é separar as responsabilidades do lado Cliente e do lado Servidor. O primeiro controlará toda a apresentação visual do jogo e a iteração com o jogador, enquanto o segundo controlará as regras do jogo e a comunicação entre os diversos clientes. A partir das responsabilidades, são definidas as primeiras classes de cada lado e como elas se comunicarão entre si. A Figura 2 ilustra o diagrama inicial de classes desse projeto.

Diagrama Inicial de classes do jogo

Figura 2. Diagrama Inicial de classes do jogo.

Na Figura 2 pode-se ver a classe central do Cliente, SpaceTipActivity, responsável pela iteração do usuário com a interface gráfica. Do lado Servidor, a classe Partida é a principal. É ela que controla as regras da partida em si. Essa classe é responsável por armazenar todos os dados da partida, comunicar os jogadores, verificar os acertos dos disparos e identificar o vencedor. Ou seja, ela é o cérebro do jogo.

Para realizar a comunicação entre Cliente e Servidor, o par de classes (na verdade, de interfaces) ProxyCliente e ProxyServidor é utilizado. As duas interfaces seguem o Design Pattern Proxy do GoF (Gang of Four). O objetivo é abstrair a comunicação da lógica do jogo. Nesse momento, tudo que SpaceTipActivity e Partida precisam saber é que elas irão se comunicar entre si através das interfaces Proxy. Como essa comunicação será feita é responsabilidade da implementação de cada Proxy e será visto mais adiante.

Protocolo de Comunicação

Um dos pontos mais importantes da arquitetura de um jogo multiplayer é a forma que os lados Cliente e Servidor se comunicam, ou seja, como eles trocam mensagens entre si. Embora isso seja uma escolha da arquitetura, e pode variar para diferentes tipos de jogos, uma dica importante é tentar sempre fazer a comunicação de forma assíncrona.

Pode não fazer sentido no início, mas a interação com jogos é bem diferente da interação com sistemas que os desenvolvedores estão acostumados a projetar. A maior parte dos sistemas atualmente segue uma arquitetura web, baseada em HTML e HTTP. Nesse tipo de sistema, o Cliente (em geral, um navegador) gera requisições ao acessar determinados endereços. O Servidor apenas responde essas requisições de forma dinâmica. Após inúmeros projetos assim, é natural que a primeira estratégia seja desenvolver a comunicação cliente-servidor em um jogo da mesma forma, ou seja, baseando-se em pares pergunta-resposta.

Em um jogo, no entanto, a pergunta não é uma regra. O Cliente não pode ficar perguntando ao Servidor todas as possibilidades. Tente imaginar um jogo multiplayer de tiro em primeira pessoa. O Cliente não pode perguntar a todo o momento se ele, ou algum outro jogador, levou um tiro. Ele apenas recebe a informação do tiro e altera sua interface gráfica. Do mesmo modo, não pode ficar perguntado sobre a posição de cada elemento na tela. Ele apenas recebe os eventos de posição quando estes ocorrem. São tantas as variantes (o adversário desconectar, o jogador ser banido, etc.) que é inviável criar um protocolo pergunta-resposta para todas as situações. Sendo assim, o Cliente deve estar preparado apenas para receber a qualquer instante uma mensagem assíncrona do Servidor, que pode variar bastante.

É claro que existem situações em que o par pergunta-resposta faz sentido. Por exemplo: ao procurar jogadores, listar campos de batalha, etc. Mas uma boa estratégia é considerá-los como uma exceção e não uma regra, pois em geral essas situações serão minoria e farão parte mais da interface auxiliar do que do jogo em si.

Voltando ao jogo SpaceTip, uma boa forma de manter a comunicação assíncrona é criar somente métodos void. Deste modo, ao enviar uma mensagem, seja no sentido cliente-servidor ou servidor-cliente, não se espera um retorno síncrono imediato. Se for pertinente, o lado oposto enviará outra mensagem informando as atualizações de estado.

As Listagens 1 e 2 mostram essa comunicação assíncrona entre ProxyCliente e ProxyServidor utilizando apenas métodos void.

Listagem 1. Código da interface ProxyCliente.

    public interface ProxyCliente {
   
    public void enviarLogin(String nome); 
    public void enviarNavesPosicionadas(NavesPosicionadas
     navesPosicionadas);      
    public void enviarAtirar(Tiro tiro, Long idJogador);
    public void enviarFimDeJogo(Long idJogador); 
    public void enviarAbandonarJogo(Long idJogador);    
    public void desconectar();
   
  }

Listagem 2. Código da interface ProxyServidor.

    public interface ProxyServidor {
   
    public void enviarPedidoPosicionamento();
    public void enviarLoginEfetuado();
    public void enviarInicioDeJogo(InicioDeJogo inicioDeJogo);
    public void enviarResultadoTiro(ResultadoTiro resultadoTiro);    
    public void enviarJogoAbandonado();   
    public void navesPosicionadas(NavesPosicionadas navesPosicionadas);     
    public void atirar(Tiro tiro);  
    public void abandonarJogo();
   
  }

Cliente Android

O Android é um sistema operacional para dispositivos móveis que já se consagrou no mercado. Baseado no sistema operacional Linux, ele gerencia toda a execução de processos e memória dos dispositivos e também fornece uma plataforma prática para execução e instalação de aplicativos.

Uma característica interessante é que o Android possui uma plataforma de desenvolvimento de aplicativos baseada na linguagem Java. No entanto, embora seja possível desenvolver em Java para Android, ele não possui uma JVM. Ao invés disso, ele possui uma máquina virtual chamada Dalvik. Ao desenvolver uma aplicação para Android, o código Java é compilado normalmente para bytecode, mas na sequência o bytecode é convertido para o formato da Dalvik e enviado para o dispositivo. A conversão para o formato Dalvik é feita de forma transparente. O desenvolvedor apenas precisa desenvolver em Java, utilizando o SDK do Android.

A API fornecida para desenvolvimento no Android é bem semelhante à API da Java SE. No entanto, existem algumas diferenças visíveis entre elas, como alguns pacotes removidos (swing e awt) e outros adicionados (pacote android), mas essencialmente a API é a mesma e um desenvolvedor Java vai se sentir em casa com Android.

Android Developer Tools

O primeiro passo para desenvolver um aplicativo Android é obter o SDK. A forma mais prática de fazer isso é através do ADT (Android Developer Tools), um plugin para a IDE Eclipse disponível no site oficial do Android (veja o endereço na seção Links). Para facilitar ainda mais, com um único download é possível obter o ADT Bundle, que inclui a IDE Eclipse já com o plugin do ADT, o SDK mais recente, as APIs, os emuladores e as ferramentas para gerenciar as diferentes versões de Android. Para utilizá-lo, é necessário apenas fazer o download de um único arquivo, descompactá-lo e executar como uma IDE Eclipse tradicional.

Após iniciar o Eclipse, o primeiro passo é rodar o Android SDK Manager, a partir de um botão no canto esquerdo da barra de ferramentas do Eclipse. Essa ferramenta possibilita ao desenvolvedor instalar e atualizar as plataformas do Android em que ele deseja trabalhar. Através dele é possível baixar as versões mais recentes do Android SDK ou as mais antigas. Basta escolher e ele automaticamente baixa e permite desenvolver para aquela versão. A Figura 3 ilustra o Android SDK Manager.

Android SDK Manager

Figura 3. Android SDK Manager

Versão do Android

Um passo importante antes de começar a desenvolver um aplicativo para Android é escolher a versão mínima do Android para a qual esse aplicativo deverá rodar. Escolher uma versão antiga (como o Android 2.2) significará um número maior de dispositivos podendo executar o aplicativo. Por outro lado, escolher uma versão mais recente (como a 4.3) significará mais recursos para o desenvolvimento. O Android evolui rápido, e cada nova API traz boas novidades tanto para o desenvolvedor quanto para o usuário final.

Ao se tratar de um jogo, a escolha da versão mínima fica ainda mais importante. Mudanças de recursos de interface visual e animação são comuns entre as APIs. Deste modo, a decisão de como implementar determinadas rotinas do jogo pode impactar na versão da API que precisa ser utilizada. É possível verificar as mudanças na API no site oficial do Android (veja a seção Links).

A versão da API utilizada tem impacto direto no número de dispositivos em que o jogo funcionará. É possível analisar esse número no site oficial que fornece os dados mais recentes medidos através do Google Play Store App. A Tabela 1 apresenta as diferentes versões, com seus codinomes, números da API e respectiva distribuição percentual nos aparelhos no mercado, medidos até o dia 4 de setembro de 2013. A Figura 4 ilustra o percentual de distribuição. É importante notar que embora as versões do Android sigam a numeração mais conhecida e divulgada, como 2.2 (Froyo) ou 4.3 (Jelly Bean), as APIs seguem uma numeração sequencial, de 1 a 17 (até o momento da elaboração desse artigo).

Distribuição no mercado das diferentes versões do Android, em 4 de setembro de
2013

Tabela 1. Distribuição no mercado das diferentes versões do Android, em 4 de setembro de 2013.
Gráfico da distribuição do Android, em 4 de setembro de
2013. Fonte: Site oficial do Android

Figura 4. Gráfico da distribuição do Android, em 4 de setembro de 2013. Fonte: Site oficial do Android.

Como o jogo desenvolvido nesse artigo é para fins didáticos, a versão escolhida foi a mais recente, 4.2.2 (API 17). Essa escolha foi feita porque o objetivo aqui não é ter uma boa abrangência nos dispositivos do mercado, mas sim demonstrar o máximo dos recursos disponíveis na API.

penGL ES

Quando se trata de jogos, um dos pontos mais avaliados pelos usuários é a qualidade dos gráficos exibidos, em muitos casos, em 3D. Apesar de o Android possuir uma rica API para interface gráfica, ela é destinada a aplicativos e não a gráficos de alta performance 2D ou 3D. Felizmente o Android já possui suporte a API OpenGL ES, uma versão para dispositivos embarcados da conhecida API Gráfica OpenGL, que permite utilizar o máximo dos recursos gráficos do hardware para obter a melhor qualidade possível quando o assunto é interface gráfica.

Embora o resultado obtido ao se utilizar a OpenGL ES seja indiscutivelmente de uma melhor qualidade e performance do que utilizando a API padrão do Android, desenvolver em OpenGLé uma situação muito específica. A OpenGLé uma complexa API que manipula coordenadas, matrizes e vetores para uma melhor aproveitamento do driver gráfico. Sendo assim, dificilmente o desenvolvedor irá manipulá-la diretamente. Para o desenvolvimento de jogos, existem no mercado diversos engines que fazem o trabalho intermediário de utilizar a OpenGL.

No jogo proposto para esse artigo serão utilizadas apenas as classes da API padrão do Android, que já é bem rica para o desenvolvimento de aplicativos e jogos comuns para dispositivos móveis. Desenvolver um jogo 3D com gráficos de alta performance, seja utilizando a OpenGL ES ou algum dos diversos engines de jogos disponível no mercado, está fora das finalidades desse artigo.

Criando o Projeto

Após configurar o ambiente com a versão 4.2.2 (API 17), o próximo passo é criar um projeto Android Application Project no Eclipse. Para isso, acesse New > Android Application Project. A primeira tela de configuração apresentada está ilustrada na Figura 5. O projeto criado foi chamado de “SpaceTip”.

Criando um novo Android Application Project

Figura 5. Criando um novo Android Application Project.

Na criação do projeto, além das informações habituais de nome e pacote, é necessário informar os parâmetros das versões da Android API que o projeto irá suportar e utilizar (como poder ser visto na Figura 5). Os parâmetros de API são:

  • Minimum Required SDK: A mínima versão da API que o aplicativo deverá suportar. Esse parâmetro serve apenas para avisar ao sistema Android para que ele bloqueie a instalação em dispositivos com API menores que essa. Ele não garante que o aplicativo irá funcionar correntemente nessa versão. Por exemplo, se o desenvolvedor definir a API 8 como Minimum Required SDK e utilizar métodos que só estão presentes a partir da API 10, o aplicativo poderá ser instalado mas não funcionar corretamente nas versões 8 e 9. Sendo assim, é uma boa prática fazer testes para se certificar que o aplicativo é realmente compatível com a API definida;
  • Target SDK: A API para a qual o aplicativo será desenvolvido, ou seja, a versão para a qual ele será definitivamente testado. Em geral, será a API mais recente, com mais recursos;
  • Compile With: O SDK que será de fato utilizado para compilar o projeto (é como escolher uma plataforma do JDK). Ela é muito importante, pois embora os outros dois parâmetros indiquem o intervalo de versão para o qual o aplicativo deve funcionar, será essa API que o projeto irá realmente utilizar, com seus pacotes e classes. Se o aplicativo for executado em um aparelho cuja API não seja compatível com essa versão (mesmo que sua API esteja entre os valores Minimum e Target), ele não irá funcionar corretamente.

Classes do Cliente

Na arquitetura do Cliente, SpaceTipActivityé a classe principal. Ela controla a lógica de apresentação do jogo utilizando objetos da classe NaveClientepara armazenar informações das naves da partida e a interface ProxyClientepara se comunicar com o Servidor.

O Cliente funciona da seguinte forma:

  1. SpaceTipActivity envia Login ao Servidor;
  2. SpaceTipActivity recebe LoginEfetuado do Servidor;
  3. SpaceTipActivity recebe PedirPosicionamento do Servidor;
  4. Jogador clica na tela e SpaceTipActivity posiciona a imagem das naves em cada ponto clicado. Com os pontos, SpaceTipActivity envia NavesPosiciondas ao Servidor;
  5. SpaceTipActivity recebe InicioDeJogo do Servidor e posiciona a imagem das naves do adversário de acordo com os dados recebidos;
  6. Jogador clica na tela e SpaceTipActivity envia Atirar ao Servidor;
  7. SpaceTipActivity recebe ResultadoTiro do Servidor e exiba as animações do tiro realizado.

A Figura 6 ilustra o diagrama de sequência do funcionamento do Cliente.

Diagrama de sequência do funcionamento do Cliente
Figura 6. Diagrama de sequência do funcionamento do Cliente.

A classe SpaceTipActivity

No sistema Android, as classes principais são as chamadas Activity, como é o caso da classe SpaceTipActivity. Uma Activity pode ser definida como uma classe que representa uma tela do aplicativo. Sua utilização tem semelhanças com uma classe Applet da Java SE ou uma classe MIDlet da Java ME. A Activity controla atributos visuais, tratamentos de eventos e possui seu ciclo de vida. Mas as semelhanças param por aí, pois sua implementação segue a API bem específica do Android.

O layout de uma Activity pode ser montado diretamente via código, agrupando os objetos das diversas subclasses da classe View, que é a classe básica para montagem de componentes de interface gráfica, como campos de texto, imagens, gerenciadores de layout (que agrupam outras views) ou mesmo classes novas que possam ser criadas herdando de View.

Embora montar o layout via código não seja muito complicado, para facilitar o processo, o layout pode ser definido via arquivos XML colocados na pasta /res/layout. A estrutura do arquivo permite montar um layout utilizando tags correspondentes aos componentes de interface gráfica, como Button, TextView e muitos outros. É possível também adicionar tags que referenciam classes, que herdam da classe View, criadas pelo próprio desenvolvedor.

Para a classe SpaceTipActivity, o layout é bem simples e foi definido no arquivo activity_space_tip.xml, que pode ser visto na Listagem 3. Na implementação realizada, ao invés de utilizar os componentes de interface gráfica disponibilizados pela própria API, foi criada a classe Fundo, herdando de View. A lógica da nova View Fundo inclui somente uma imagem de fundo, para o campo da batalha, e traça a linha central, que divide o campo de cada jogador, como mostrado na Listagem 4.

Listagem 3. Conteúdo do arquivo activity_space_tip.xml.

  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:tools="http://schemas.android.com/tools"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      tools:context=".SpaceTipActivity" 
      android:id="@+id/layout_space_tip">
         
      <com.summercrow.spacetip.cliente.Fundo android:id="@+id/fundo" 
          android:layout_width="wrap_content" 
          android:layout_height="wrap_content"/>
   
  </RelativeLayout>

Listagem 4. Código da classe Fundo.

  public class Fundo extends View{
        
        private Paint vermelho;
   
        public Fundo(Context context) {
              super(context);
              init(context);
        }      
   
        public Fundo(Context context, AttributeSet attributeSet) {
              super(context, attributeSet);
              init(context);
        }      
   
        private void init(Context context) {           
              vermelho = new Paint();
              vermelho.setARGB(255, 255, 0, 0);       
              setBackgroundResource(R.drawable.espaco);
        }
        
        @Override
        protected void onDraw(Canvas canvas) {
              super.onDraw(canvas);
              float y = getHeight() / 2;
              float x = getWidth();
              canvas.drawLine(0, y, x, y, vermelho);
        }
  }

Uma vez definido o layout no arquivo activity_space_tip.xml, é necessário associá-lo à classe SpaceTipActivity. Para isso, basta chamar o método setContentView() como mostra a Listagem 5. Este método recebe a referência para o arquivo activity_space_tip.xml presente na classe R. A classe R é gerada automaticamente pela compilação do Android SDK e possui referências para os recursos adicionados nas subpastas da pasta res, como figuras, layouts, menus, valores e outros.

Listagem 5. Definindo o layout da activity, utilizando a classe R.

  @Override
  protected void onCreate(Bundle savedInstanceState) {
        ...    
        setContentView(R.layout.activity_space_tip);
        ...    
  }

Evento de Toque

O Android possui um rico sistema de tratamento de eventos gerados pela interação do usuário. Sua utilização é simples, em geral, sobrescrevendo métodos ou adicionando Listeners, de modo semelhante à forma de tratar eventos fornecida, no Swing ou Java ME.

No jogo desenvolvido, precisamos tratar o evento de toque do usuário na tela. Para isso, basta sobrescrever o método onTouchEvent(MotionEvent event) na classe SpaceTipActivity, como indicado na Listagem 6. Dentro do método, o estado do jogo é verificado para realizar uma ação de posicionamento de alguma nave, ou do tiro.

Listagem 6. Captura de eventos de toque na tela.

  @Override
  public boolean onTouchEvent(MotionEvent event) {
              
        switch (event.getAction()) {
              case MotionEvent.ACTION_UP:
                     tratarEstado(event);
                     break; 
              default:
                     break;
        }            
        return true;
  }
   
  private void tratarEstado(MotionEvent event) {
        switch (estado) {
              case POSICIONANDO:
                     posicionarNaveMinha(event);
                     break;              
              case EM_JOGO:
                     atirar(event);
              default:
                     break;
        }
  }

Posicionando uma nova imagem

Durante o decorrer da partida, é necessário inserir imagens dinamicamente na tela, de acordo com o posicionamento das naves escolhido pelo próprio jogador. Uma imagem (no caso, de uma nave) é representada no Android por um objeto da classe ImageView (que herda de View). Para posicionar a imagem da nave dinamicamente, basta criar uma ImageView e adicioná-la ao layout de SpaceTipActivity, seguindo as coordenadas recebidas pelo evento de toque. A Listagem 7, que apresenta o código do método posicionarNaveMinha() – chamado a partir da Listagem 6 – indica como é feito o posicionamento.

Listagem 7. Posicionamento dinâmico de uma nova imagem.

  private void posicionarNaveMinha(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
         
        ... 
        
        ImageView naveImg = new ImageView(this);
        naveImg.setImageResource(R.drawable.nave);
        naveImg.setX(x -(larguraNave/2));
        naveImg.setY(y - (alturaNave/2));
        meuLayout.addView(naveImg);
        ...
  }

Animando imagens

Um dos recursos fundamentais em um jogo eletrônico é sem dúvida os efeitos de animação. Embora não seja impossível, conseguir desenvolver um jogo sem nenhum tipo de animação é bastante improvável.

O jogo SpaceTip faz uso de animações no momento em que o jogador realiza seu tiro, ao dar um simples toque na tela. Como já foi explicado na Figura 6, no momento do tiro o Cliente envia a mensagem Atirar ao Servidor. Após processar as coordenadas do tiro, o Servidor envia a mensagem ResultadoTiro ao Cliente. Nesse momento, a classe SpaceTipActivityprocessa a mensagem e exibe a animação do tiro. A animação é feita utilizando recursos de exibir, movimentar e esconder imagens da tela, obedecendo aos seguintes passos:

1. Exibir a imagem de um torpedo no ponto em que o jogador tocou na tela;

2. Movimentar a imagem do torpedo até o campo simétrico do adversário;

3. Ao finalizar o movimento do torpedo no ponto desejado, esconder a imagem deste;

4. No ponto alvo do torpedo, exibir a imagem da explosão;

5. Esconder a imagem da explosão.

A API do Android possui duas formas de criar animações dinamicamente. A primeira é através da classe Animation, disponível desde a versão 1 da API, e a segunda é através da classe Animator, disponível apenas a partir da versão 11 (Android 3.0), possuindo uma estrutura mais simples e flexível. No final do artigo, há um link para o site oficial que ilustra as principais diferenças entre essas duas opções.

Focando na segunda opção, para animar dinamicamente uma determinada propriedade de um objeto, utiliza-se a classe ObjectAnimator (que herda de Animator). Seu método estático ofFloat() cria a animação de uma propriedade cujo valor é expresso em ponto flutuante (float). A utilização desse método é simples, recebendo o objeto que se deseja animar, o nome da propriedade a ser animada e seus valores inicial e final na animação (ou somente o valor final). O retorno é uma instância de ObjectAnimator que pode receber ajustes, como o tempo de duração da animação.

Para trabalhar com várias animações simultaneamente, utilizamos a coleção AnimatorSet. Esta agrupa instâncias de ObjectAnimator de forma ordenada, permitindo estabelecer uma precedência ou um paralelismo entre elas. Após serem agrupadas, uma simples chamada ao método start() de AnimatorSet inicia toda a sequência de animações configuradas. A Listagem 8 indica como foi feito esse processo para a animação de tiro.

Listagem 8. Código da animação de tiro.

  ObjectAnimator aparecer = 
  ObjectAnimator.ofFloat(imageTorpedo, "alpha", 0, 1);
  aparecer.setDuration(100);
  
  ObjectAnimator movimento = 
        ObjectAnimator.ofFloat
        (imageTorpedo, "y", imageTorpedo.getY() + distancia);
  movimento.setDuration(2000);
  
  ObjectAnimator desaparecer = 
        ObjectAnimator.ofFloat(imageTorpedo, "alpha", 1, 0);
  desaparecer.setDuration(1000);
   
  ...
   
  long tempoExplosao = 1000;
  ObjectAnimator explosao1 = ObjectAnimator.ofFloat
    (fogo, "alpha", 0, 1);
  explosao1.setDuration(tempoExplosao);
  ObjectAnimator explosao2 = ObjectAnimator.ofFloat
    (fogo, "alpha", 1, 0);
  explosao2.setDuration(tempoExplosao);
  AnimatorSet animacao = new AnimatorSet();
   
  animacao.play(aparecer).before(movimento);
  animacao.play(movimento).before(desaparecer);
  animacao.play(desaparecer).with(explosao1);
  animacao.play(explosao1).before(explosao2);
   
  ...
   
  animacao.start();

O Android também fornece uma forma de configurar animações via arquivos XML, colocados na pasta /res/animator. A estrutura do XML possui tags com nomes análogos aos nomes das subclasses de Animator, como set (para AnimatorSet) e objectAnimator (para a classe de mesmo nome). O link no final do artigo ilustra como animações podem ser implementadas via XML.

Conclusão

Do ponto de vista da criatividade e originalidade, a criação de um jogo não precisa e nem deve ter limites. Porém, do ponto de vista da implementação, jogos eletrônicos são softwares e, portanto, estão sujeitos aos mesmos desafios que o desenvolvimento de qualquer software pode enfrentar.

Dito isso, nessa primeira parte do artigo, abordamos a utilização da plataforma Android no desenvolvimento do lado Cliente de um jogo multiplayer constituído por uma arquitetura Cliente-Servidor. O artigo demonstrou a importância de ser feita uma análise, antes da fase de desenvolvimento, de pontos essenciais como versão da plataforma e protocolos de comunicação.

Na próxima parte do artigo, abordaremos o desenvolvimento do lado Servidor e suas diferentes formas de se comunicar com o Cliente. Tecnologias distintas de comunicação serão utilizadas para demonstrar que conceitos de Orientação a Objetos podem ser utilizados no desenvolvimento de jogos, garantindo assim um resultado final mais robusto e confiável.

Para encerrar, gostaria de agradecer a Guilherme Mota, Diretor de Arte, por ter feito a composição da arte para o jogo SpaceTip.

Links Úteis

  • Android InputMask e Saripaar 2:
    Aprenda a inserir máscaras nos campos do formulário com a biblioteca InputMask e validação através da Android Saripaar 2.
  • O que é Grunt?:
    Aprenda neste curso o que é o Grunt e como utilizá-lo em seus projetos para automatizar tarefas comuns do desenvolvimento front-end.
  • Já ouviu falar em Single Page Applications?:
    Neste DevCast falaremos sobre esse modelo de aplicação que vem ganhando espaço no mercado e que deve ser conhecido pelos programadores web.

Saiba mais sobre PHP ;)

  • Guias Java:
    Encontre aqui os Guias de estudo que vão ajudar você a aprofundar seu conhecimento na linguagem Java. Desde o básico ao mais avançado. Escolha o seu!
  • Carreira Programador Java:
    Nesse Guia de Referência você encontrará o conteúdo que precisa para iniciar seus estudos sobre a tecnologia Java, base para o desenvolvimento de aplicações desktop, web e mobile/embarcadas.
  • Android:
    O Android é a plataforma oficial para desenvolvimento de aplicativos mobile do Google. Neste guia você encontrará artigos, vídeos e cursos para aprender tudo sobre esse SDK, que domina o mundo dos dispositivos móveis.

Referência:

Site do Android Developer