Artigo no estilo: Curso
Por que eu devo ler este artigo:Este artigo abordará a finalização do jogo 2D usando HTML5 e seu recurso nativo Canvas. O jogo segue o estilo shoot em up, um gênero em que o personagem jogador é controlado na horizontal e tem como objetivo destruir os inimigos por meio de combos ou ataques de poder. Abordaremos desde a criação dos inimigos com seus respectivos efeitos, até a detecção de colisões através do uso da estrutura de dados quadtree, muito famosa por herdar conceitos de árvores binárias em algoritmos. Por fim, adicionaremos sons e efeitos randômicos ao jogo, entendendo como as APIs da HTML5 funcionam para cada um dos casos.

Na primeira parte deste artigo nós abordamos conceitos iniciais acerca do desenvolvimento do jogo usando Canvas, atrelado à HTML5, JavaScript e CSS3. Essa introdução foi muito importante para que o leitor consiga se adaptar à forma como o Canvas não só impõe sua arquitetura de camadas, mas também a como ele lida com as funções de script para expor o conteúdo e gerenciar o “desenho” gráfico das diversas formas do jogo.

Até o momento criamos três atores principais: o plano de fundo (e seu efeito de movimento, dando a ideia de cenário infinito), o player e os combos de ataque. Além disso, alguns conceitos físicos foram aplicados, muitos deles abstraídos pela própria linguagem, e continuarão sendo abordados até o fim da implementação, haja vista a necessidade de incutir tais efeitos em quase todos os personagens.

Na segunda parte do artigo focaremos no restante do desenvolvimento, que consiste em criar os inimigos e adicionar os efeitos de animação, identificar a colisão dos combos de ataque entre ambos os atores, adicionar sonorização, implementar as funções que irão randomizar a exibição dos inimigos e players, etc. A partir de agora, faremos referência às figuras do pool de objetos e repositórios de forma mais frequente, uma vez que quanto mais objetos precisarem ser criados, maior o processamento que ocorrerá na instanciação desse tipo de estrutura de dados.

Criando os inimigos

A primeira mudança significativa para criar os inimigos, assim como fizemos para os demais objetos Image, é inserir as referências aos objetos e instanciá-los. Lembre-se que o objeto repositorio funciona como uma espécie de contêiner em memória que pré-carrega as variáveis e objetos antes que eles sejam requisitados pelas respectivas funções e exibidos na tela. Isso alivia o processamento e impede que travamentos e lentidão aconteçam em tempo de jogo. Veja na Listagem 1 o código que usaremos para efetuar essa mudança.

Listagem 1. Adicionando os objetos de inimigo e combo no objeto repositorio.


  01 /**
  02  * Define um objeto para manter todas as nossas imagens do jogo para 
  03  * evitar que elas sejam criadas mais de uma vez.
  04  */
  05 var repositorio = new function() {
  06     // Define os objetos de imagens
  07     this.planofundo = new Image();
  08     this.player = new Image();
  09     this.combo = new Image();
  10     this.inimigo = new Image();
  11     this.comboInimigo = new Image();
  12 
  13     var numImagens = 5;
  14     var numCarregados = 0;
  15     function imgCarregada() {
  16           numCarregados++;
  17           if (numCarregados === numImagens) {
  18                  window.iniciar();
  19           }
  20     }
  21     this.planofundo.onload = function() {
  22           imgCarregada();
  23     }
  24     this.player.onload = function() {
  25           imgCarregada();
  26     }
  27     this.combo.onload = function() {
  28           imgCarregada();
  29     }
  30     this.inimigo.onload = function() {
  31           imgCarregada();
  32     }
  33     this.comboInimigo.onload = function() {
  34           imgCarregada();
  35     }
  36     
  37     // Configura os caminhos (src) das imagens
  38     this.planofundo.src = "imgs/pf.png";
  39     this.player.src = "imgs/player1.png";
  40     this.combo.src = "imgs/combo1.png";
  41     this.inimigo.src = "imgs/enemy1.png";
  42     this.comboInimigo.src = "imgs/combo2.png";
  43 }

As configurações previstas inicialmente para os demais objetos permanecem. Nas linhas 10 e 11 declaramos e instanciamos os objetos inimigo e comboInimigo, respectivamente, que servirão para guardar a referência à imagem física de cada um deles. Posteriormente, estes objetos serão usados para receber o valor randômico que implementaremos. A linha 13 traz a variável numImagens que já havia sido criada e é incrementada de acordo com a quantidade de objetos que foram carregados no pool repositorio. Caso a informação não case exatamente com a quantidade de objetos criados dentro dessa classe, você receberá um erro na tela do console JavaScript.

Da linha 30 à 35 temos a adição das funções que efetuarão o pré-carregamento de fato. Veja como o reaproveitamento de código é feito a partir do uso da função imgCarregada() que irá lidar com quaisquer tipos de objetos desenháveis. Por último temos a configuração dos atributos src dos mesmos objetos que apontam o caminho relativo das imagens. As imagens informadas podem ser encontradas no arquivo de download do código fonte deste artigo, mas o leitor pode ficar à vontade para usar suas próprias imagens de jogadores e inimigos e ver como o jogo se adapta às mesmas. Não esqueça de verificar sempre os tamanhos de cada arquivo para que os personagens não apareçam cortados.

O objeto de pool do nosso código serve não só para pré-inicializar objetos do tipo Image (que são sempre a maioria em jogos), mas quaisquer objetos que se qualifiquem como “desenháveis”, ou seja, que tenham uma representação gráfica, como animações, gráficos, grafos, etc.

Esse código ainda não surtirá nenhum efeito, porque precisamos que as demais configurações dos objetos sejam criadas em suas respectivas funções. Para isso, atualizemos a classe Combo, como mostra a Listagem 2.

Listagem 2. Atualizando as funções na classe Combo.


  01 function Combo(objeto) {       
  02     this.vivo = false; // Será marcado como true se o combo estiver em uso
  03     var self = objeto;
  04     
  05     /*
  06      * Valores dos combos
  07      */
  08     this.configurar = function(x, y, velocidade) {
  09           this.x = x;
  10           this.y = y;
  11           this.velocidade = velocidade;
  12           this.vivo = true;
  13     };
  14 
  15     /*
  16      * Função que desenha os combos
  17      */
  18     this.desenhar = function() {
  19           this.context.clearRect(this.x-1, this.y-1, this.largura+1, this.largura+1);
  20           //this.context.clearRect(this.x, this.y, this.largura, this.altura);
  21           this.x += this.velocidade;
  22           if (self == "combo" && this.x <= 0 - this.largura) {
  23                  return true;
  24           } else if (self === "comboInimigo" && this.y >= this.alturaCanvas) {
  25                  return true;
  26           } else {
  27                  if (self === "combo") {
  28                         this.context.drawImage(repositorio.combo, this.x, this.y);
  29                  } else if (self === "comboInimigo") {
  30                         this.context.drawImage(repositorio.comboInimigo, this.x, this.y);
  31                  }
  32                  return false;
  33           }
  34     };
  35     
  36     /*
  37      * Reinicia as propriedades do combo
  38      */
  39     this.limpar = function() {
  40           this.x = 0;
  41           this.y = 0;
  42           this.velocidade = 0;
  43           this.vivo = false;
  44     };
  45 }
  46 Combo.prototype = new Desenhavel();

Nessa listagem podemos observar dois comportamentos que serão distintos dos combos que já criamos antes:

• Primeiro, os inimigos irão lançar combos diferentes em relação aos do player, e esses mesmos combos também têm comportamentos diferentes ...

Quer ler esse conteúdo completo? Tenha acesso completo