Criar um jogo de tiro no espaço simples em HTML5 com EaselJS

0
37
HTML5

Com o aumento da popularidade (e capacidades) do HTML5, vamos dar uma olhada em como fazer um jogo de tiro com HTML5, JavaScript e EaselJS.

Pré-visualização do Resultado Final

Vamos dar uma olhada no resultado final em que vamos trabalhar:

Clique para jogar

Passo 1: Breve visão geral

Usando sprites pré-fabricados, vamos codificar um divertido jogo de tiro espacial em HTML5 usando a biblioteca EaselJS.

O jogador será capaz de controlar uma nave espacial e atirar em vários inimigos enquanto viaja no espaço.

Passo 2: Interface

Uma interface simples e futurista será usada, isso envolve bitmaps e muito mais. Eu usei uma grande biblioteca de sprite na demo deste tutorial, estes são parte do Sinistar Clone Graphics gratuito.

Os recursos de interface necessários para este tutorial podem ser encontrados no download em anexo.

Passo 3: Obtenha EaselJS

A biblioteca EaselJS será usada para construir nosso jogo, certifique-se de ler o tutorial Começando começado se você é novo nesta biblioteca.

Você pode baixar o EaselJS de seu site oficial.

Passo 4: Estrutura HTML

Vamos preparar o nosso documento HTML, é uma estrutura HTML simples para começar a escrever a nossa aplicação. Guarde isto como Shooter.html.

<!DOCTYPE html> 
<html> 
    <head> 
        <title>Shooter</title> 
    </head> 
    <body> 
    </body> 
</html>

Passo 5: Ocultar luz de iluminação móvel


Vamos adicionar um pouco de CSS também, esta linha irá remover o destaque padrão quando você toca em um elemento usando um navegador móvel; sem isso, a experiência móvel diminuiria drasticamente.

<!DOCTYPE html> 
<html> 
    <head> 
        <title>Shooter</title> 
          
        <style>*{-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}</style> 
      
    </head> 
    <body> 
    </body> 
</html>

Passo 6: Bibliotecas JavaScript

O código a seguir adiciona as bibliotecas JavaScript necessárias para que nosso aplicativo funcione.

<!DOCTYPE html> 
<html> 
    <head> 
        <title>Shooter</title> 
          
        <style>*{-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}</style> 
  
        <script src="easel.js"></script> 
        <script src="Tween.js"></script> 
        <script src="sound.js"></script> 
        <script src="Main.js"></script> 
    </head> 
    <body> 
    </body> 
</html>

Main.js é o arquivo que vamos usar para armazenar todas as nossas próprias funções para o jogo. Crie agora e salve-o na mesma pasta do Shooter.html. Você também precisará baixar as bibliotecas EaselJS listadas.

Passo 7: Chamar a função principal


Nas próximas linhas chamaremos a nossa função principal; esta é a função que irá iniciar a nossa aplicação, será criada mais tarde no nosso código JavaScript.

<!DOCTYPE html> 
<html> 
    <head> 
        <title>Shooter</title> 
          
        <style>*{-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}</style> 
  
        <script src="easel.js"></script> 
        <script src="Tween.js"></script> 
        <script src="sound.js"></script> 
        <script src="Main.js"></script> 
  
    </head> 
    <body onload="Main();"> 
    </body> 
</html>

Passo 8: Elemento de lona


A Tela é adicionada nesta linha. Nós atribuímos um ID para referenciá-lo mais tarde e também definimos sua largura e altura.

<!DOCTYPE html> 
<html> 
    <head> 
        <title>Shooter</title> 
          
        <style>*{-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}</style> 
  
        <script src="easel.js"></script> 
        <script src="Tween.js"></script> 
        <script src="sound.js"></script> 
        <script src="Main.js"></script> 
  
    </head> 
    <body onload="Main();"> 
        <canvas id="Shooter" width="320" height="480"></canvas> 
    </body> 
</html>

Passo 9: Iniciando o JavaScript


Vamos começar a nossa criação de jogos!

Abra seu editor JavaScript preferido (qualquer editor de texto funcionará, mas você não terá realce de sintaxe) e prepare-se para escrever seu incrível jogo. Abra o arquivo Main.js que você criou anteriormente.

Passo 10: Definir Tela


Vamos começar por definir todas as variáveis gráficas e lógicas.

As próximas variáveis representam o elemento canvas HTML e o estágio que será ligado a ele. (A variável de estágio se comportará de forma similar ao estágio em um projeto AS3 Flash.)

/* Define Canvas */
  
var canvas; 
var stage;

Passo 11: Fundo

As variáveis seguintes armazenam as imagens de fundo. Duas imagens em mosaico são usadas para criar um fundo com rolagem infinita.

/* Background */
  
var bgImg = new Image(); 
var bg; 
var bg2Img = new Image(); 
var bg2;

Passo 12: Nave

Esta é a nave que será usada como personagem ou herói do jogador.

/* Ship */
  
var sImg = new Image(); 
var ship;

Passo 13: Inimigo

Vários inimigos estarão no palco; eles vão usar isso como o gráfico de origem.

/* Enemy */
  
var eImg = new Image();

Passo 14: Chefão

Um chefe estará presente no jogo, maior e com mais saúde que os outros inimigos. Estas variáveis são usadas para instanciá-lo.

/* Boss */
  
var bImg = new Image(); 
var boss;

Passo 15: Vidas

O ícone da “vida”. Três vidas são dadas no início, e você perde uma quando atingido por um inimigo.

/* Lives */
  
var lImg = new Image();

Passo 16: Munição

Esta é a tua arma: dispara balas contra os inimigos para os matar. Esta variável armazena a imagem de origem.

/* Bullets */
  
var bltImg = new Image();

Passo 17: Gráficos de alerta

Dois alertas são usados no jogo, um para quando você ganha e outro para quando você perde. Veremos como determinar uma vitória ou uma derrota mais tarde neste tutorial.

/* Alert */
  
var winImg = new Image(); 
var loseImg = new Image(); 
var win; 
var lose;

Passo 16: Variáveis


Estas são as variáveis que vamos usar, leia os comentários no código para saber mais sobre elas. Alguns dos seus nomes são auto-explicativos e por isso não têm comentários.

var lives = new Container(); //stores the lives gfx 
var bullets = new Container(); //stores the bullets gfx 
var enemies = new Container(); //stores the enemies gfx 
var bossHealth = 20; 
var score; 
var gfxLoaded = 0; //used as a preloader, counts the already loaded items 
var centerX = 160; 
var centerY = 240; 
var tkr = new Object(); //used as a Ticker listener 
var timerSource; //references a setInterval method

Passo 17: Sons

Vamos usar efeitos sonoros para melhorar a sensação do jogo. Você pode encontrar os sons usados neste exemplo no Soungle.com usando as palavras-chave espaço, explosão e laser.

Passo 18: Função principal


A função Main() será a primeira a executar quando a página web é carregada, pois é referida no atributo onload do documento HTML (ver Passo 7).

Chama as funções necessárias para iniciar o jogo. Vamos criar essas funções nos próximos passos – tudo do Passo 19 ao Passo 23 deve ir dentro desta função.

function Main() 
{ 
    //code... 
}

Passo 19: Tela de ligação


Esse código obtém o HTML canvas ID e o vincula à classe EaselJS Stage. Isso fará com que a variável de estágio se comporte como a classe de estágio no AS3.

/* Link Canvas */
      
canvas = document.getElementById('Shooter'); 
stage = new Stage(canvas);

Passo 20: Ativar eventos do mouse


Eventos do Mouse são desativados por padrão no EaselJS para melhorar o desempenho; como precisamos daqueles no jogo, adicionamos a seguinte linha.

stage.mouseEventsEnabled = true;

Passo 21: Sons de carga


Vamos usar o SoundJS para adicionar sons ao nosso jogo. O método addBatch do SoundJS usa uma matriz de três parâmetros para cada chamada:

name: O nome da instância que você quer que o som tenha – este será usado para reproduzir o som mais tarde.
src: O URL do ficheiro de som.
instâncias: O número de instâncias que podem ser reproduzidas ao mesmo tempo.

/* Sound */
  
SoundJS.addBatch([ 
        {name:'boss', src:'boss.mp3', instances:1}, 
        {name:'explo', src:'explo.mp3', instances:10}, 
        {name:'shot', src:'shot.mp3', instances:10}]);

Passo 22: Carregar gráficos


Este código é usado para pré-carregar os gráficos com a ajuda de uma função que iremos escrever mais tarde. Aponta cada objecto Image que criamos antes para o ficheiro PNG de origem na nossa pasta de documentos. É dado um nome para detectar qual a imagem que está carregada, e por último a função que manuseia as imagens carregadas é chamada.

/* Load GFX */
  
bgImg.src = 'bg.png'; 
bgImg.name = 'bg'; 
bgImg.onload = loadGfx; 
      
bg2Img.src = 'bg2.png'; 
bg2Img.name = 'bg2'; 
bg2Img.onload = loadGfx; 
      
sImg.src = 'ship.png'; 
sImg.name = 'ship'; 
sImg.onload = loadGfx; 
  
eImg.src = 'enemy1.png'; 
eImg.name = 'enemy'; 
eImg.onload = loadGfx; 
  
bImg.src = 'boss.png'; 
bImg.name = 'boss'; 
bImg.onload = loadGfx; 
  
lImg.src = 'live.png'; 
lImg.name = 'live'; 
lImg.onload = loadGfx; 
  
bltImg.src = 'bullet.png'; 
bltImg.name = 'bullet'; 
bltImg.onload = loadGfx; 
  
winImg.src = 'win.png'; 
winImg.name = 'win'; 
winImg.onload = loadGfx; 
  
loseImg.src = 'lose.png'; 
loseImg.name = 'lose'; 
loseImg.onload = loadGfx;

Passo 23: Set Ticker


A classe Ticker fornece um “tick” centralizado, transmitido em um intervalo definido. Podemos usar sua função tick() para executar um certo código em uma frequência regular.

O seguinte código define a taxa de quadros para 30 e define o estágio como o ouvinte para os ticks.

A classe TweenJS ouvirá este tiquetaque para executar as animações.

/* Ticker */
  
Ticker.setFPS(30); 
Ticker.addListener(stage);

Passo 24: Função de pré-carga


Cada vez que se carrega um gráfico, esta função será executada. Ela irá atribuir cada imagem a um objeto bitmap e verificar se todos os elementos estão carregados antes de continuar a chamar o addGameView.

function loadGfx(e) 
{ 
    if(e.target.name = 'bg'){bg = new Bitmap(bgImg);} 
    if(e.target.name = 'bg2'){bg2 = new Bitmap(bg2Img);} 
    if(e.target.name = 'ship'){ship = new Bitmap(sImg);} 
      
    gfxLoaded++; 
      
    if(gfxLoaded == 9) 
    { 
        addGameView(); 
    } 
}

Passo 25: Adicionar vista do jogo


Quando todos os gráficos são carregados, a função addGameView é chamada. Esta função adicionará o ship, lives counter, score e backgrounds ao estágio.

function addGameView() 
{ 
    ship.x = centerX - 18.5; 
    ship.y = 480 + 34; 
      
    /* Add Lives */
      
    for(var i = 0; i < 3; i++) 
    { 
        var l = new Bitmap(lImg); 
          
        l.x = 248 + (25 * i); 
        l.y = 463; 
          
        lives.addChild(l); 
        stage.update(); 
    } 
      
    /* Score Text */
      
    score = new Text('0', 'bold 14px Courier New', '#FFFFFF'); 
    score.maxWidth = 1000;  //fix for Chrome 17 
    score.x = 2; 
    score.y = 476; 
      
    /* Second Background */
      
    bg2.y = -480; 
      
    /* Add gfx to stage and Tween Ship */
      
    stage.addChild(bg, bg2, ship, enemies, bullets, lives, score); 
    Tween.get(ship).to({y:425}, 1000).call(startGame); 
}

Passo 26: Mover navio


A nave do jogador será controlada pelo rato, e usamos esta função para lidar com isso:

function moveShip(e) 
{ 
    ship.x = e.stageX - 18.5; 
}

e.stageX refere-se à coordenada x do rato, e esta função é chamada sempre que o rato se moveu.

Passo 27: Atirar


A nossa nave será capaz de disparar balas para destruir e proteger-se dos inimigos. Esta função será executada sempre que o usuário clicar no palco e colocar uma bala na frente da nave que será posteriormente movida pela função update(). Ele também toca um som de tiro.

function shoot() 
{ 
    var b = new Bitmap(bltImg); 
      
    b.x = ship.x + 13; 
    b.y = ship.y - 20; 
      
    bullets.addChild(b); 
    stage.update(); 
      
    SoundJS.play('shot'); 
}

Passo 28: Adicionar Função Inimiga


Não seria um atirador sem algo para atirar. Aqui, um setInterval() é usado para criar um inimigo a cada 1000 milissegundos (você pode mudar esse valor no próximo passo) que é posteriormente movido pela função update().

function addEnemy() 
{ 
    var e = new Bitmap(eImg); 
      
    e.x = Math.floor(Math.random() * (320 - 50)) 
    e.y = -50 
      
    enemies.addChild(e); 
    stage.update(); 
}

Passo 29: Iniciar Jogo


Estas linhas irão adicionar os ouvintes necessários ao palco e ao temporizador; isto inclui eventos do rato, eventos cronometrados (via setInterval) e eventos de ticker que irão actualizar o jogo a cada frame.

function startGame() 
{ 
    stage.onMouseMove = moveShip; 
    bg.onPress = shoot; 
    bg2.onPress = shoot; 
      
    Ticker.addListener(tkr, false); 
    tkr.tick = update; 
      
    timerSource = setInterval('addEnemy()', 1000); 
}

Passo 30: Mover fundo


O fundo é movido a cada quadro para simular uma viagem espacial; quando o sprite de fundo inferior atinge o limite do estágio, ele é movido de volta para o topo, criando um loop infinito.

function update() 
{ 
    /* Move Background */
      
    bg.y += 5; 
    bg2.y += 5; 
      
    if(bg.y >= 480) 
    { 
        bg.y = -480; 
    } 
    else if(bg2.y >= 480) 
    { 
        bg2.y = -480; 
    }

Passo 31: Mover balas


As próximas linhas de código verificam se há balas no palco; se assim for, as balas são movidas para cima.

/* Move Bullets */
      
    for(var i = 0; i < bullets.children.length; i++) 
    { 
        bullets.children[i].y -= 10; 
    }

Passo 32: Remova as balas de fora do palco


Vamos adicionar algumas linhas para detectar a posição da bala, e usar isso para destruir uma bala quando ela não estiver mais visível.

/* Move Bullets */
      
    for(var i = 0; i < bullets.children.length; i++) 
    { 
        bullets.children[i].y -= 10; 
          
        /* Remove Offstage Bullets */
          
        if(bullets.children[i].y < - 20) 
        { 
            bullets.removeChildAt(i); 
        } 
    }

Passo 33: Mostrar chefe


Vamos adicionar um grande chefe mau ao jogo. Quando o utilizador atinge uma determinada pontuação, o chefe aparece:

/* Show Boss */
      
    if(parseInt(score.text) >= 500 && boss == null) 
    { 
        boss = new Bitmap(bImg); 
                  
        SoundJS.play('boss'); 
                  
        boss.x = centerX - 90; 
        boss.y = -183; 
                  
        stage.addChild(boss); 
        Tween.get(boss).to({y:40}, 2000)   //tween the boss onto the play area 
    }

Passo 34: Mover inimigos


Os inimigos, como as balas, também são movidos a cada frame. Este código encontra todos os inimigos no palco usando o contentor de inimigos, e move-os a cada 5px para baixo.

/* Move Enemies */
      
    for(var j = 0; j < enemies.children.length; j++) 
    { 
        enemies.children[j].y += 5;

Passo 35: Remova inimigos fora do palco


Também verificamos as posições dos inimigos para destruí-los quando já não forem visíveis.

/* Move Enemies */
      
    for(var j = 0; j < enemies.children.length; j++) 
    { 
        enemies.children[j].y += 5; 
          
        /* Remove Offstage Enemies */
          
        if(enemies.children[j].y > 480 + 50) 
        { 
            enemies.removeChildAt(j); 
        }

Passo 36: Bala – Colisão Inimiga

As balas no contentor são testadas para colisão com os inimigos; quando isso acontece, ambos são removidos do palco, um som é tocado e a pontuação é atualizada.

for(var k = 0; k < bullets.children.length; k++) 
{ 
    /* Bullet - Enemy Collision */
      
    if(bullets.children[k].x >= enemies.children[j].x && bullets.children[k].x + 11 < enemies.children[j].x + 49 && bullets.children[k].y < enemies.children[j].y + 40) 
    { 
        bullets.removeChildAt(k); 
        enemies.removeChildAt(j); 
        stage.update(); 
        SoundJS.play('explo'); 
        score.text = parseFloat(score.text + 50); 
    }

Passo 37: Colisão entre bala e chefe

O seguinte código lida com as colisões de bosses, ele usa o mesmo método usado no loop de colisão entre balas e inimigos. Aqui usamos a variável bossHealth para determinar quando o boss é derrotado.

/* Bullet - Boss Collision */
              
    if(boss != null && bullets.children[k].x >= boss.x && bullets.children[k].x + 11 < boss.x + 183 && bullets.children[k].y < boss.y + 162) 
    { 
        bullets.removeChildAt(k); 
        bossHealth--; 
        stage.update(); 
        SoundJS.play('explo'); 
        score.text = parseInt(score.text + 50); 
    } 
}

Passo 38: Nave – Colisão inimiga

Aqui verificamos se um inimigo colide com a nave do jogador; se colide, um som é tocado, uma vida é removida e a nave é animada.

/* Ship - Enemy Collision */
          
    if(enemies.hitTest(ship.x, ship.y) || enemies.hitTest(ship.x + 37, ship.y)) 
    { 
        enemies.removeChildAt(j); 
        lives.removeChildAt(lives.length); 
        ship.y = 480 + 34; 
        Tween.get(ship).to({y:425}, 500) 
        SoundJS.play('explo'); 
    } 
}

Passo 39: Verificar se há vitória ou perda

O jogador ganha quando o patrão perde toda a sua saúde e perde se todas as suas vidas forem perdidas. As próximas linhas detectam essas situações e chamam uma função de alerta usando o parâmetro correto.

/* Check for win */
      
    if(boss != null && bossHealth <= 0) 
    { 
        alert('win'); 
    } 
      
    /* Check for lose */
      
    if(lives.children.length <= 0) 
    { 
        alert('lose'); 
    } 
}

Passo 40: Alerta

O Alerta mostra ao jogador informações sobre o estado do jogo; é mostrado quando um evento de jogo é alcançado. Remove os ouvintes do jogo e mostra a mensagem apropriada.

function alert(e) 
{ 
    /* Remove Listeners */
          
    stage.onMouseMove = null; 
    bg.onPress = null; 
    bg2.onPress = null; 
      
    Ticker.removeListener(tkr); 
    tkr = null; 
      
    timerSource = null; 
      
    /* Display Correct Message */
      
    if(e == 'win') 
    { 
        win = new Bitmap(winImg); 
        win.x = centerX - 64; 
        win.y = centerY - 23; 
        stage.addChild(win); 
        stage.removeChild(enemies, boss); 
    } 
    else
    { 
        lose = new Bitmap(loseImg); 
        lose.x = centerX - 64; 
        lose.y = centerY - 23; 
        stage.addChild(lose); 
        stage.removeChild(enemies, ship); 
    } 
      
    bg.onPress = function(){window.location.reload();}; 
    bg2.onPress = function(){window.location.reload();}; 
    stage.update(); 
}

Passo 41: Teste

Salve seu trabalho (se você não tiver) e abra o arquivo HTML no navegador para ver seu jogo HTML5 funcionando!

Conclusão

Você aprendeu a criar um jogo de tiro no espaço com todas as suas características básicas, tente expandi-lo usando o que você já sabe. Um bom começo seria fazer com que os inimigos ou o boss disparem contra o jogador.

Espero que tenham gostado deste tutorial, obrigado pela leitura!

Deixe uma resposta