HTML5 - Como criar Asteroids com o mecanismo de jogo HTML5 Impact

Rob Hawkes

Evangelista Técnico

Março 2013

Nos últimos anos houve um aumento expressivo no número de jogos HTML5 na Web, graças, em boa parte, aos mecanismos HTML5 que facilitam muito seu desenvolvimento.

Neste tutorial, vou mostrar como criar um jogo de asteróides usando o Impact, um dos mecanismos de jogo mais robustos do momento.

O Impact é um mecanismo comercial, então você precisará comprar uma licença para usá-lo, mas isso não o impede de, pelo menos, seguir o tutorial para ver como ele funciona.

Você também precisará destes ativos de jogo dentro do tutorial, então sugiro que os baixe agora se planeja seguir adiante.

Às vez, gastar dinheiro é bom

Vamos reforçar isso: o Impact não é grátis. Se você for um pouco parecido comigo, provavelmente está um pouco decepcionado com aquela afirmação. Afinal, estamos usando tecnologias de Web abertas aqui, então porque cobrar por algo que é criado com tecnologia grátis? Certo? Bem, em termos simples: o desenvolvedor precisa ganhar a vida. E mais, e provavelmente mais importante: o Impact é um mecanismo imensamente confiável e bem documentado. Eu iria mais longe dizendo que é o melhor mecanismo de jogo HTML5 com que já tive oportunidade de lidar até hoje, apesar de não ser 100% perfeito (mas o que é?).

Tenho certeza de que o preço do mecanismo é um reflexo to tempo e do esforço consumidos para criar e suportá-lo. Resumindo, se você não puder se dar ao luxo de pagar US$ 99 por uma licença, sugiro que dê uma olhada nos mecanismos de jogo HTML5 gratuitos.

Visão geral do mecanismo de jogo Impact

Como já mencionei, o Impact é um mecanismo de jogo confiável cujo desenvolvimento obviamente demandou muito tempo e esforço. Ele é executado em todos os grandes navegadores que suportam tela HTML5, assim como no iPhone, iPod Touch e no iPad. A meu ver, o que coloca o Impact bem à frente da concorrência é a documentação detalhada, os tutoriais e o suporte poderoso que pode ser acessado através dos fóruns. O Impact certamente transmite uma sensação geral de qualidade que alguns dos outros mecanismos não têm.

Dn151489.23127E241272ABDFAB395DA0B3083E8A(pt-br,MSDN.10).png

O Impact tem uma grande quantidade de funcionalidades embutidas, como entidades (objetos dentro do jogo), física, animações, input do usuário, detecção de colisão, áudio e até mesmo um editor de níveis completo (mais sobre isso em um momento). Essa não é uma lista completa de tudo que o mecanismo pode fazer, mas espero que ela mostre um pouco do quanto do trabalho tedioso ele pode tirar de suas mãos. O que ajuda nisso é a maneira como o mecanismo foi construído, usando uma abordagem orientada a objeto que permite a utilização direta de novos recursos em seu jogo através da extensão do que já está embutido. Ele é rápido e fácil e exatamente o que você quer como um desenvolvedor de jogos ocupado.

O editor de níveis Weltmeister é um dos pontos altos do Impact, e talvez seja um dos recursos mais impressionantes visualmente do mecanismo. Esse editor de níveis é efetivamente uma interface gráfica de usuário que permite construir o mundo de seu jogo, do desenho dos níveis à disposição de objetos e definição de como eles interagem uns com os outros. É bastante impressionante!

Dn151489.38E6A45ECFBD085808457BE1AE573376(pt-br,MSDN.10).png

É importante destacar que você não usará o editor de níveis Weltmeister neste tutorial, já que quero ensinar o código bruto, mas recomendo que assista a este tutorial em vídeo sobre o editor para ter uma ideia do que ele pode fazer.

O que você vai fazer

Neste tutorial, você fará um jogo espacial simples no qual precisa evitar os asteróides que flutuam ao redor. Deixará de lado recursos como áudio e tiros por enquanto, já que, agora, eles apenas complicariam as coisas, mas fique tranquilo de que são recursos que você certamente incluirá sozinho mais tarde e sem complicações.

Dn151489.69C205EA5C70FA67F0EE3608A8D0CB15(pt-br,MSDN.10).png

Por que não dá uma olhada neste vídeo do jogo em ação?

Configuração do Impact

Antes de poder colocar mãos à obra no jogo, você precisa configurar o mecanismo Impact. É relativamente tranquilo, mas vou cobrir isso em detalhes, porque é importante saber o que está acontecendo. Eu certamente veria a página da documentação sobre a configuração do Impact para um guia passo a passo mais detalhado.

Uso de um domínio para desenvolvimento

Uma das coisas principais para se ter em mente no desenvolvimento com Impact é que você precisa trabalhar em um servidor Web, e precisa acessar o jogo com um nome de domínio. Você pode usar um servidor online ou um servidor de desenvolvimento local através de algo como MAMP (Mac), WAMP (Windows), ou XAMPP (multiplataforma). Esse requisito se dá devido a questões de segurança em JavaScript e não tem nada a ver com o mecanismo Impact.

Download e desempacotamento

Depois de comprar o Impact você receberá um email com seu link de download e chave de licença particular. Espero que você o guarde em local seguro! Esse link de download é associado à sua conta, então se você o perder, terá de entrar em contato com o desenvolvedor para reenvio. Os primeiros passão são baixar sua cópia pessoal do mecanismo, descompactar o arquivo zip, abrir a pasta impact e mover os arquivos e pastas para o seu servidor Web. Você saberá se tudo correu bem porque verá uma mensagem quando carregar o arquivo index.html em seu navegador.

Dn151489.AC2C1C27CFB42D77962484FAE63EECB8(pt-br,MSDN.10).png

Os arquivos que acabou de copiar são os arquivos centrais do mecanismo (a pasta lib/impact) e alguns arquivos de exemplo que usará para criar o jogo (a pasta lib/game). Os outros arquivos que você vê são relacionados ao editor de níveis Weltmeister (a pasta lib/weltmeister) e algumas ferramentas para ajudá-lo a empacotar o jogo para distribuição pública (a pasta tools).

Durante este tutorial você precisa se preocupar apenas com a pasta lib/game e com o arquivo index.html.

Configurando o HTML e a CSS

O arquivo HTML padrão usa CSS alinhada, então vamos fazer alguns ajustes e mover a CSS para um arquivo separado.

Abra index.html, remova tudo e adicione o seguinte código:

<!DOCTYPE html>
<html>
    <head>
        <title>Impact Asteroids Game</title>
        <meta charset="utf-8">
        <link href="game.css" rel="stylesheet" type="text/css">
        <script type="text/javascript" src="lib/impact/impact.js"></script>
        <script type="text/javascript" src="lib/game/main.js"></script>
    </head>
    <body>
        <canvas id="gameCanvas"></canvas>
    </body>
<html>

Em seguida, crie um novo arquivo chamado game.css e coloque no mesmo diretório que index.html. E o código a seguir dentro do arquivo CSS:

* { margin: 0; padding: 0; }
html, body { height: 100%; width: 100%; }
canvas { display: block; }
body {
    background: #000;
}
 
#gameCanvas {
    height: 512px;
    left: 50%;
    margin: -256px 0 0 -384px;
    position: relative;
    top: 50%;
    width: 768px;
}

Tudo que isso faz é fornecer uma redefinição básica de CSS e posicionar o jogo no centro no navegador. Note como você definiu a largura e a altura do jogo dentro da CSS em um tamanho bem específico. Explicarei em um momento porque você escolheu aquelas dimensões.

Configuração do arquivo principal do jogo

Se você atualizar o navegador notará que tudo que aparece é um fundo preto. Isso acontece porque você acabou de quebrar o jogo mexendo com o HTML e mudando a identificação do elemento canvas. Opa! Vamos resolver isso.

Primeiro, abra o arquivo lib/game/main.js, que é o arquivo principal para o seu jogo. Explicarei em um momento o que as várias seções desse arquivo fazem, mas por enquanto queremos apenas fazer algumas alterações para colocar aquela mensagem de volta na sua tela.

Encontre a chamada a ig.main no final do arquivo e a substitua pelo seguinte:

ig.main( "#gameCanvas", MyGame, 60, 768, 512, 1 );

Tudo que você fez aqui foi alterar o primeiro argumento para apontar para a nova identificação do elemento canvas, aquela que mudou antes. Outras alterações foram feitas nos quarto e quinto argumentos para refletir as novas dimensões do jogo, e também no último argumento para impedir que o jogo seja dimensionado para o dobro do tamanho. Você está usando dimensões específicas porque o mecanismo é baseado em blocos, e os blocos que você vai usar são principalmente de 64x64 pixels - 768 e 512 são perfeitamente divisíveis por 64, só isso.

Veja a documentação da função ig.main se quiser saber mais sobre como ela funciona.

Se atualizar a janela do navegador, deve obter a mensagem anterior novamente, embora, desta vez, ela deva estar um pouco menor, porque você impediu que o jogo se expandisse.

Dn151489.5C32D2F1153A1CC34AB4B0C98F6F0232(pt-br,MSDN.10).png

Módulos

Vale a pena gastar alguns momentos para explicar como módulos trabalham, já que eles são o que você usa para criar seu jogo. Aqui está o código do módulo do arquivo main.js que você acabou de ver, com algumas partes removidas, porque ainda não são importantes para abordar:

ig.module( 
    "game.main" 
)
.requires(
    "impact.game",
    "impact.font"
)
.defines(function(){
    // Main module code
});

A primeira seção, ig.module, define um módulo novo, cujo nome é declarado na segunda linha - neste caso, "game.main". O nome é representativo da estrutura de pastas, então "game.main" se refere a um arquivo chamado main.js que existe na pasta lib/game. É bastante direto. A segunda seção, requires, lista módulos que precisam ser carregados antes que este módulo seja executado. Cada módulo segue a mesma diretriz de nomenclatura de antes, então "impact.game" se refere ao arquivo game.js na pasta lib/impact. Finalmente, a seção defines é onde o código para o módulo é colocado. Essa seção não é executada até que todos os módulos necessários tenham sido carregados.

Criação das entidades do jogo

Agora que o arquivo principal está pronto, é hora de criar as entidades para o seu jogo. Entidades são tudo no jogo que seja interativo, como o jogador ou inimigos.

Entidade asteroid

Você vai configurar suas entidades como módulos, então a primeira coisa a fazer é criar um arquivo chamado asteroid.js dentro da pasta lib/game/entities. Abra o arquivo e adicione o código a seguir para definir módulo entity:

ig.module(
    "game.entities.asteroid"
).requires(
    "impact.entity"
).defines(function() {
    // Subclassed from ig.Enitity
    EntityAsteroid = ig.Entity.extend({
        // Set some of the properties
        size: {x: 64, y: 64},
 
        // Entity type
        type: ig.Entity.TYPE.B,
 
        init: function(x, y, settings) {
            // Call the parent constructor
            this.parent(x, y, settings);
        },
 
        // This method is called for every frame on each entity.
        update: function() {
            // Call the parent update() method to move the entity according to its physics
            this.parent();
        }
    });
});

Este é o código principal por trás da maioria das entidades, cuja maior parte você deve reconhecer do arquivo main.js que você olhou há pouco - e é um módulo.

Você nomeia o módulo entity no alto em ig.module, chamando-o de "game.entities.asteroid" e depois declara em requires que o módulo "impact.entity" precisa ser carregado. Esse módulo fornece toda a funcionalidade básica para a criação de entidades.

Dentro da seção defines você configura sua entidade terminando o módulo entity, assim:

EntityAsteroid = ig.Entity.extend({

A primeira parte é o nome da entidade, e a segunda está apenas dizendo que você quer estender a classe base entity a partir do Impact.

Dentro da chamada a ig.Entity.extend você coloca todo o código para definir a entidade asteroid, começando com as propriedades size e type. A propriedade size se refere às dimensões da entidade e não corresponde ao tamanho da imagem do sprite que é usado para exibi-lo (você notará isso quando criar a entidade player). Definir type permite que você agrupe entidades em um grupo A ou B. Por exemplo, isso pode ser usado para separar entidades perigosas (B) de amistosas (A).

A função init é chamada quando a entidade é criada, e update é chamado em cada loop do jogo, antes que qualquer coisa seja desenhada.

Neste momento, a entidade asteroid não faz nada, então vamos configurá-la e deixá-la visível dentro do jogo.

Inclua o código a seguir abaixo de onde você definiu a propriedade type:

  1. // Load an animation sheet
  2. animSheet: new ig.AnimationSheet("media/asteroid.png", 64, 64),

Isso configura o sprite para o asteroide e permite que você o anime se desejar. No seu caso, você está usando sprite asteroid dos ativos do jogo fornecidos com este tutorial, mas sinta-se à vontade para criar o seu próprio, se quiser.

O próximo passo é definir uma animação para o sprite, que é simples neste caso, pois você não quer animar o sprite. Adicione o código a seguir abaixo de this.parent na função init:

  1. // Add animations for the animation sheet
  2. this.addAnim("idle", 1, [0]);

O primeiro argumento nomeia a animação e pode ser o que você quiser, o segundo é o tempo em segundos para cada quadro e o terceiro é uma matriz de número de quadros. No seu caso há apenas um quadro, que é 0 (o primeiro elemento de uma matriz é sempre 0).

Você não poderá ver nada ainda, então passe para o arquivo main.js e adicione o código a seguir dentro da seção requires no alto (lembre-se de colocar uma vírgula após o arquivo anterior):

"game.entities.asteroid"

E acrescente o seguinte dentro da função init:

var asteroidSettings;
for (var i = 0; i < 8; i++) {
    asteroidSettings = {vel: {x: 100-Math.random()*200, y: 100-Math.random()*200}};
    this.spawnEntity(EntityAsteroid, ig.system.width/2, ig.system.height/2, asteroidSettings);
};

Assim você define oito asteróides dentro do jogo no meio da tela, cada qual com uma velocidade aleatória. Isso se tiver acrescentado a imagem do sprite asteroide ao jogo. Para tal, mova asteroid.png dos ativos do jogo fornecidos e coloque-o dentro da pasta media.

Se tudo correu bem, você deve ver um conjunto de asteróides se dispersando a partir do centro da tela. Resultado!

Dn151489.825DFDE2BCE9F513E5264828E2A9A8B1(pt-br,MSDN.10).png

Enquanto está em main.js, remova todo o código dentro da função draw exceto this.parent. Isso removerá a mensagem da tela, já não mais necessária.

Verificações de limites

O problema no momento é que os asteróides desaparecem nas bordas da tela e nunca mais voltam. Isso é ruim. Volte para o arquivo da entidade asteroids.js e acrescente o código a seguir abaixo de this.parent na função update:

// Boundary checks
if (this.pos.x > ig.system.width) {
    this.pos.x = -64;
} else if(this.pos.x < -64) {
    this.pos.x = ig.system.width;
};
 
if (this.pos.y > ig.system.height) {
    this.pos.y = -64;
} else if (this.pos.y < -64) {
    this.pos.y = ig.system.height;
};

O que isso faz é verificar a posição do asteroide ao final de cada atualização e, se ele estiver fora da área do jogo, move o asteroide para o outro lado do jogo. Isso dá o efeito de um mundo infinito em que entidades não se perdem nas bordas.

Entidade player

Agora que a entidade asteroid básica foi criada, é hora de definir a entidade player. Felizmente, a maior parte da lógica básica é a mesma.

Crie um arquivo novo chamado player.js e coloque-o dentro da pasta lib/game/entities. E acrescente o código:

ig.module(
    "game.entities.player"
).requires(
    "impact.entity"
).defines(function() {
    // Subclassed from ig.Enitity
    EntityPlayer = ig.Entity.extend({
        // Set the dimensions and offset for collision
        size: {x: 28, y: 50},
        offset: {x: 18, y: 7},
 
        // Entity type
        type: ig.Entity.TYPE.A,
 
        // Load an animation sheet
        animSheet: new ig.AnimationSheet("media/player.png", 64, 64),
 
        init: function(x, y, settings) {
            // Call the parent constructor
            this.parent(x, y, settings);
 
            // Add animations for the animation sheet
            this.addAnim("idle", 0,05, [0]);
            this.addAnim("thrust", 0.05, [1,2]);
        },
 
        // This method is called for every frame on each entity.
        update: function() {            
 
            // Call the parent update() method to move the entity according to its physics
            this.parent();
 
            // Boundary checks
            if (this.pos.x > ig.system.width) {
                this.pos.x = -64;
            } else if(this.pos.x < -64) {
                this.pos.x = ig.system.width;
            };
 
            if (this.pos.y > ig.system.height) {
                this.pos.y = -64;
            } else if (this.pos.y < -64) {
                this.pos.y = ig.system.height;
            };
        }
    });
});

Não nenhuma grande novidade aqui. Uma diferença é que a entidade tem um nome diferente nas seções ig.module e defines. Outra é que a propriedade size não é mais 64x64 pixels; agora é 28x50 e a folha de animação está procurando o sprite do jogador (que você pode encontrar nos ativos do jogo com este tutorial). A propriedade size menor representa o tamanho do foguete dentro da imagem do sprite do jogador, que você pode ver nesta imagem:

Dn151489.847322856DD8CAB34E42F36497CCDD4F(pt-br,MSDN.10).png

A área azul é a definida pela propriedade size, e sua posição é definida pela propriedade offset. Isso é importante para garantir que a área certa do sprite seja usada quando do cálculo de colisões.

Finalmente, existem duas animações sendo definidas desta vez: uma para quando o jogador está parado (idle) e outra para quando ele está se movendo (thrust):

  1. this.addAnim("idle", 1, [0]);
  2. this.addAnim("thrust", 0.05, [1,2]);

Há dois quadros na animação thrust, o que significa que o sprite alternará do quadro 1 para o 2 e voltará para o 1 outra vez. Cada quadro será exibido durante 0.05 segundo. Você verá isso em um momento, mas o efeito é basicamente o de uma chama pulsante.

O último passo é adicionar a entidade player no arquivo principal do jogo, exatamente como você fez antes com os asteróides. Abra main.js e acrescente a linha a seguir na seção requires no alto (lembre-se de adicionar uma vírgula depois do arquivo anterior):

"game.entities.player"

E acrescente o seguinte acima da função init:

player: null,

E o seguinte abaixo de onde acrescentou os asteróides na função init:

  1. // Load player entitiy
  2. var playerSettings = {};
  3. this.player = this.spawnEntity(EntityPlayer, 150, 150, playerSettings);

Nada aparecerá ainda, então, como aconteceu com os asteróides, você precisará mover o arquivo player.png dos ativos do jogo fornecidos com este tutoria para a pasta media.

Atualize o navegador e deve ver um pequeno foguete no canto superior esquerdo. Legal!

Dn151489.F95D7614159DA3D5AB15184ADE9B5CA1(pt-br,MSDN.10).png

Fundo do jogo

Embora não seja uma entidade verdadeira, é algo que ainda precisamos definir. Enquanto ainda está em main.js, adicione o seguinte acima da função init:

background: new ig.Image("media/background.png"),

Isso define a imagem de fundo, mas ela ainda não será desenhada. Para desenhá-la, você precisará substituir a linha this.parent na função draw pelo seguinte:

  1. // Draw the background
  2. this.background.draw(0, 0);
  3.  
  4. // Draw all entities
  5. for(var i = 0; i < this.entities.length; i++) {
  6.     this.entities[i].draw();
  7. };

Isso substitui o método embutido para desenhar entidades do jogo por um sobre o qual você tem um pouco mais de controle.

Ainda assim, antes que possa vê-lo, você precisará mover o arquivo background.png dos ativos do jogo para a pasta media. O resultado deve ser um belo fundo estrelado para o seu jogo.

Dn151489.54AED7C65B699B1EA90EC89802A8691F(pt-br,MSDN.10).png

Inclua controles de teclado

Embora tenha adicionado a entidade player, você não pode movimentá-la Isso não é bom, então vamos resolver como adicionar controles de teclado ao jogo.

Ouvintes de evento

O primeiro passo é adicionar os ouvintes de evento que detectarão quando uma tecla é pressionada. Acrescente o código a seguir em main.js e coloque-o no alto da função init:

  1. ig.input.bind(ig.KEY.LEFT_ARROW, "left");
  2. ig.input.bind(ig.KEY.RIGHT_ARROW, "right");
  3. ig.input.bind(ig.KEY.UP_ARROW, "up");
  4. ig.input.bind(ig.KEY.ENTER, "play");

As teclas ouvidas aqui devem ser bastante óbvias; esquerda, direita, para cima para mover o jogador e Enter para reiniciar o jogo quando terminar. Os nomes dados a cada ouvinte de jogo (left, right, up e play) podem ser alterados para qualquer cadeia de caracteres que você quiser. Entretanto, sugiro que os deixe como estão, já que os usará novamente em alguns instantes.

Movimentação do jogador

O objetivo de se acrescentarem controles de teclado é mover o jogador pelo jogo. Fazer isso é surpreendentemente direto, e segue a facilidade com que o Impact pode lidar com essas coisas.

Entre no arquivo da entidade player.js e adicione o código a seguir abaixo da propriedade offset no alto:

  1. // Angle, in degrees for more rotation granularity
  2. angle: 0,
  3.  
  4. // Thrust, dictating how much to accelerate
  5. thrust: 0,

E Adicione o código a seguir acima de this.parent na função update:

// "input.pressed" is called once for every key press
// "input.state" is called on every frame that the key is held down for
if (ig.input.state("left")) {
    this.angle -= 3;
};
 
if (ig.input.state("right")) {
    this.angle += 3;    
};
 
if (ig.input.state("up")) {
    // Accelerate the player in the right direction
    this.accel.x = Math.sin(this.angle*Math.PI/180)*this.thrust;
    this.accel.y = -(Math.cos(this.angle*Math.PI/180)*this.thrust);
    this.currentAnim = this.anims.thrust;
} else {
    this.accel.x = 0;
    this.accel.y = 0;
    this.currentAnim = this.anims.idle;
};
 
// Set the angle for the current animation
this.currentAnim.angle = this.angle*(Math.PI/180);

Pode parecer muito, mas é surpreendentemente pouco para se acrescentar controle completo sobre o movimento do jogador. Deixe-me explicar o que está acontecendo.

A primeira instrução condicional considera ig.input.state("left"), o que significa que está solicitando o estado atual do evento keyboard que você chamou de "left" na seção anterior (por isso era importante não alterar aqueles nomes). Se aquela tecla for pressionada, o método ig.input.state retornará true, exceto false.

No caso das teclas direita e esquerda, você aumenta ou diminui a propriedade angle do jogador dependendo de qual tecla está sendo pressionada. Depois, no caso da tecla para cima, está usando aquele ângulo, junto com a propriedade thrust, para calcular a aceleração do jogador no ângulo em que se move. Isso se baseia em trigonometria básica, e sugiro que você aprenda para entender completamente.

Além disso, com a tecla para cima você alterna a animação do player para a animação "thrust" se a tecla estiver pressionada (o jogador está se movendo) e depois para a animação "idle" se a tecla for liberada (o jogador está parado).

Finalmente, você usa a propriedade angle do player para definir o ângulo da imagem do sprite. Essa linha é importante, já que a imagem não mudará de ângulo de outra maneira. A fórmula maluca no final é apenas a conversão do ângulo de graus para radianos, e é nisso que o JavaScript trabalha.

Se tudo correu bem, agora você deve ser capaz de girar o player com as teclas esquerda e direita e disparar a animação da chama com a tecla para cima.

Dn151489.28C1CE22197FF49FD67D2B1CB861951E(pt-br,MSDN.10).png

Mas por que o jogador não está se movendo para frente? Porque você não definiu a propriedade thrust como algo diferente de zero. Volte ao arquivo main.js e altere a linha playerSettings para o seguinte:

var playerSettings = {thrust: 250, maxVel: {x: 300, y: 300}};

A propriedade maxVel é usada para limitar com que velocidade a entidade pode se mover, o que é muito útil para impedir que o jogador acelere até próximo da velocidade da luz.

Experimente o jogo no navegador; você deve poder mover o foguete pela tela com física bastante realista. Muito bem!

Dn151489.EB90404F2B3C56D98C5D9310807BAB9A(pt-br,MSDN.10).png

Término do jogo

Então agora você tem um jogo operacional, mas ainda não pode morrer, o que o torna um tanto sem sentido. A boa notícia é que adicionar lógica de fim de jogo é muito simples com o Impact.

Abra o arquivo da entidade player.js (pela última vez, prometo) e adicione o código a seguir abaixo da propriedade type no alto:

checkAgainst: ig.Entity.TYPE.B,

Isso deve fazer sentido; está definindo que a entidade player (que é type A) se compare objetos que são type B (os asteróides). Para usar isso, você deve definir mais uma função, então adicione o código a seguir acima da função update:

check: function(other) {
    // Game over
    this.health = 0;
},}

A função check é chamada sempre que a entidade atual (o jogador) se sobrepuser a outra entidade do tipo declarado na propriedade checkAgainst. Se as duas entidades se sobrepuserem, é hora de matar o jogador, então defina a propriedade health como zero. Isso não encerrará o jogo, mas você adicionará essa lógica em seguida.

Abra o arquivo main.js (pela última vez) e adicione o código a seguir abaixo da propriedade background no alto:

gameOver: false,

Você usará essa propriedade para deixar o jogo saber quando terminar Adicione o código a seguir acima de this.parent na função update:

// Run if the game is over
if (this.gameOver) {
    if(ig.input.pressed("play") ) {
        ig.system.setGame(MyGame);
    };
 
    // Return to stop anything else updating
    return;
};

Isso impedirá que o jogo se atualize caso tenha terminado e reiniciará o joogo usando o método ig.system.setGame quando a tecla Enter for pressionada. Umas poucas linhas de código podem fazer tanto!

Para definir a propriedade gameOver você precisa adicionar o código a seguir abaixo de this.parent na função update:

// Check for game over condition
if (this.player.health == 0) {
    this.gameOver = true;
};

Se o jogador morrer, o jogo estará definido para terminar no loop seguinte. Simples.

Esse passo final no jogo (quase terminado) é para exibir uma mensagem quando o jogo acabar. Adicione o código a seguir abaixo de this.background.draw na função draw:

// Game over screen
if (this.gameOver) {
    this.font.draw("Game Over!", ig.system.width/2, 132, ig.Font.ALIGN.CENTER);
    this.font.draw("Press Enter", ig.system.width/2, 232, ig.Font.ALIGN.CENTER);
    this.font.draw("to Restart", ig.system.width/2, 272, ig.Font.ALIGN.CENTER);
 
    // Return to stop anything else drawing
    return;
};

Isso usa o mesmo código incluído com o código do Impact no início para exibir aquela pequena mensagem de demonstração, só que, desta vez, você o está usando para exibir uma mensagem em três linhas separadas. Entretanto, se você o executar em seu navegador, notará que a fonte é minúscula!

Dn151489.B60FE2A36D090CF514A6CB18D8E1AA85(pt-br,MSDN.10).png

Para corrigir isso, você precisa substituir o arquivo 04b03.font.png na pasta media pelo fornecido nos ativos do jogo que vieram com este tutorial, ou criar sua própria imagem de fonte usando a ferramenta Impact Font Tool. O resultado deve ser uma fonte muito maior e mais legível.

Dn151489.B15BD096032C5287A8F931604F841F85(pt-br,MSDN.10).png

Resumo

O jogo que você criou é simples, mas mostra a maior parte da funcionalidade básica que o mecanismo Impact fornece pronta. Sugiro levar o jogo adiante adicionando áudio, balas para destruir os asteróides e melhores elementos gráficos para estes e talvez até mesmo controles de toque para dispositivos móveis.

Assim, você chegou ao final desta fornada com o mecanismo de jogo Impact. Espero que o tenha achado tão interessante e empolgante quando eu na primeira vez em que o usei. Em minha opinião, o Impact é um mecanismo muito poderoso que você deve considerar quando desenvolver jogos em HTML5 no futuro.

Se quiser ver o jogo em ação, baixe o código-fonte e o execute você mesmo. Note que para executar a demonstração, você precisará obter uma cópia licenciada do mecanismo de jogo HTML5 Impact.

Mostrar: