Seu Primeiro Jogo Em HTML5

Categorias: games

Hoje quero ajudar você a desenvolver seu primeiro jogo, que talvez não fique aquele delírio de diversão, mas é o primeiro passo para você começar a entrar nesse mundo de desenvolvimento de jogos. E para isso vamos usar tecnologias que você tem em mãos no seu navegador. HTML5 e javascript.

Vou passo a passo, tentando deixar claro o porque de algumas coisas primordiais em desenvolvimento de jogos. Cometendo erros para depois corrigi-los. Isso talvez torne o post um pouco extenso, então busque sua garrafa de água para manter seu cérebro hidratado.

Para os sem saco, no final do post você tem um link para o código completo.

O Jogo
Eu havia pensado em desenvolver um Arkanoid para esse post, mas a criação e a lógica subiriam um pouco de nível e a intenção de começar devagar iria se perder. Então temos algo bem mais simples, bolas que caem do topo da tela e que você deve resgatá-las com a plataforma.

HTML
Para montar o desenho da tela, o jogador e a bola, vamos usar a tag canvas. Ela está presente há algum tempo nos navegadores, tendo começado no Safari da Apple, mas ganhou mais popularidade recentemente com o HTML5.

Crie um arquivo HTML com qualquer nome e dentro dele crie um Canvas.

<!DOCTYPE html>
<html>
    <head>
        <title>Seu Primeiro Jogo - HTML5</title>
    </head>   
    <body>
        <canvas id="canvas" width="600" height="480">
            Navegador não suporta HTML5
        </canvas>
    </body>
</html>

Usamos o Doctype do HTML5 junto com um título.
Dentro da tag body vemos o canvas, onde definimos uma largura e uma altura.

Carregando isso no navegador, não vemos nada. Mas não se preocupe, você não fez nada de errado. Vamos com ajuda de um CSS definir uma borda para nosso canvas, assim vamos ver ele na tela. Nosso head vai ficar como abaixo.

<head>
    <title>Seu Primeiro Jogo - HTML5</title>
     <style type="text/css">
    canvas {
        border: 1px solid #000000;
    }
     </style>
</head>  

Fique a vontade para alterar a cor, o tipo da borda ou o que mais quiser nesse CSS.

Nosso trabalho com HTML acaba aqui. Tudo o que vamos usar agora é JavaScript. Para isso vamos então chamar uma função javaScript quando o body estiver totalmente carregado, da seguinte maneira.

<body onload="inicializar()">

JavaScript
Vamos criar nosso JavaScript no mesmo arquivo do HTML. Nosso intuito não é discutir arquitetura aqui. Vamos criar uma tag script logo depois de nosso canvas, e já vamos definir nossa função inicializar que chamamos acima.

<script type="text/javascript">
function inicializar()
{
 // Nosso código aqui
}
</script>

Jogador
Vamos começar desenhando o jogador, que nada mais é do que uma barra.

Vamos iniciar algumas variáveis acima da nossa função. barraAltura e barraLargura.
Em seguida em nossa função inicializar setamos os valores desejados para elas.

Para desenhar com o canvas, precisamos capturar o elemento com javaScript e usar o contexto que no nosso caso vai ser 2D.

O método para desenhar retângulos com canvas é fillRect. Essa função nos pede a posição do elemento (pontos X e Y), sua largura e sua altura.

Como a posição X (horizontal) do jogador vai ficar mudando, vamos criar uma variável para isso também. Já a posição Y (vertical) como não vai mudar, podemos deixá-la fixa usando a altura do canvas menos o tamanho da barra.

Confira o código abaixo com as explicações acima:

var barraAltura, barraLargura, jogadorPosicaoX;
        
function inicializar()
{
    barraAltura = 15;
    barraLargura = 90;

    jogadorPosicaoX = 0;

    canvas = document.getElementById("canvas");
    context = canvas.getContext("2d");

    context.fillRect(jogadorPosicaoX, canvas.height - barraAltura, barraLargura, barraAltura);
}

Resultado na tela:

Nosso jogador não ficou centralizado, porque definimos sua posição X como 0, ou seja, o canto esquerdo do canvas.
Altere os valores de pouco em pouco para você perceber como o jogador irá se mover, isso também irá ajudar na lógica de colisão mais para frente. Entendido isso, vamos deixar nosso jogador centralizado, pegando o tamanho do canvas e dividindo por 2.

Ao atualizar seu browser e conferir o resultado nesse ponto não se preocupe, você não está com problema na vista. É necessário diminuir o tamanho da barra para que o jogador fique exatamente no centro do canvas. Ficando então da seguinte maneira.

jogadorPosicaoX = (canvas.width - barraLargura) / 2;

Use os parênteses para fazer a subtração primeiro.

Comandos do jogador
É hora de dar vida ao jogador.
O que queremos é que quando for pressionado para a direita, para esquerda, a nossa barra obedeça isso.

Vamos deixar nosso javaScript de olho quando o jogador apertar alguma tecla com os Event Listeners do javaScript.

Então definimos a função desse listener. Aqui no meu notebook, o código das teclas para esquerda e para direita são respectivamente 37 e 39. Isso pode variar se você tem um tipo diferente de teclado ou ainda se quiser outras teclas de comando.

Vamos agora reposicionar o jogador de acordo com as teclas pressionadas.
Se apertar a tecla código 37, esquerda, diminuimos a posição do jogador. Se apertar a tecla código 39 vamos somar.

Mas vamos somar/diminuir quanto? Vamos definir uma variável para a velocidade do jogador, essa velocidade será a quantidade de pixels que o jogador se movimenta a cada vez que é pressionado as teclas. Voltando então, se pressionar para a direita somamos a velocidade do jogador, se pressionar para a esquerda diminuimos a velocidade do jogador.

Não esqueça de desenhar a barra novamente com a posição nova do jogador.

Confira o código abaixo:

var barraAltura, barraLargura, jogadorPosicaoX, velocidadeJogador;
        
function inicializar() ...
    velocidadeJogador = 20;
    ...
    document.addEventListener('keydown', keyDown);
}
            
function keyDown(e) 
{
    if(e.keyCode == 37)
    {
        jogadorPosicaoX -= velocidadeJogador;
    }
                
    if(e.keyCode == 39)
    {
        jogadorPosicaoX += velocidadeJogador;
    }

    context.fillRect(jogadorPosicaoX, canvas.height - barraAltura, barraLargura, barraAltura);
}

Atualizando nossa tela e apertando as teclas você vê o resultado.

Percebe um problema? Temos dois na verdade, mas vamos um de cada vez.
O primeiro é que não estamos apagando a posição anterior do jogador. Estamos então tendo a sensação de que a barra está aumentando de tamanho.

Para resolver isso temos que entender um conceito no desenvolvimento de jogos que é o GameLoop.
Não vou entrar em detalhes á fundo, mas acompanhe.

Game Loop – Explicação ultra simples
Um jogo não passa de um loop infinito. Se você perde, você volta ao começo.
Então pense em um loop que contêm algumas verificações dentro dele que serão os comandos do jogador, pontuação e etc.

Durante esse loop, os inimigos, o jogador e outras coisas na tela irão mudar de posição o tempo todo.
Mas você deve apagar a posição anterior caso contrário vai ficar um efeito como o que estamos tendo.

Então vamos apagar tudo da tela e fazer aparecer novamente. Ok!
Mas isso não pode ser visível ao jogador, a tela não pode parecer que está piscando.
Então vamos definir um tempo, um certo número de frames que não permita que o jogador perceba isso.

Então vamos re-estruturar nosso código, pensando nesse conceito.

No topo temos a definição das variáveis.

Em seguida na função inicializar, vamos deixar apenas o valor inicial das variável e o listener, e de dentro dela chamar o gameLoop.
Para o loop, vamos usar setInterval e chamar nossa função a cada 30 milisegundos.
Ficando assim:

var barraAltura, barraLargura, jogadorPosicaoX, velocidadeJogador;
        
function inicializar()
{
    barraAltura = 15;
    barraLargura = 90;

    jogadorPosicaoX = (canvas.width - barraLargura) / 2;
    velocidadeJogador = 20;

    canvas = document.getElementById("canvas");
    context = canvas.getContext("2d");              

    document.addEventListener('keydown', keyDown);

    setInterval(gameLoop, 30);
}

Seguimos o código com a função keyDown que fizemos acima, mas já vamos resolver nosso segundo problema que não comentei anteriormente. O jogador não pode continuar indo para a direita ou para esquerda se ele chegar aos limites do canvas. Então adicionamos algumas verificações para isso. Repare também que não vamos mais redesenhar a tela a partir dessa função.

function keyDown(e) 
{
    if(e.keyCode == 37)
    {
        if(jogadorPosicaoX > 0)
        {
            jogadorPosicaoX -= velocidadeJogador;
        }
    }

    if(e.keyCode == 39)
    {
        if(jogadorPosicaoX < (canvas.width - barraLargura))
        {
            jogadorPosicaoX += velocidadeJogador;
        }
    }
}

Por último nosso gameLoop. Ele contém uma função que desenha um retângulo em branco em cima de toda a área do canvas.
Isso limpa a nossa tela. E em seguida redesenhamos nosso jogador.

function gameLoop()
{
    context.clearRect(0, 0, canvas.width, canvas.height);

    context.fillRect(jogadorPosicaoX, canvas.height - barraAltura, barraLargura, barraAltura);
}
Recarregue sua tela e se divirta movimentando a barra.

<strong>Bola</strong>
Vamos primeiro desenhar uma bola parada no topo da tela.
Vamos definir algumas configurações para ela. Diâmetro, posição X, posição Y e sua velocidade.
1
var barraAltura, barraLargura, jogadorPosicaoX, velocidadeJogador, bolaDiametro, bolaPosX, bolaPosY, velocidadeBola;
        
function inicializar()
{
    ...
    bolaDiametro = 10;
    bolaPosX = canvas.width / 2;
    bolaPosY = 0;
    velocidadeBola = 10;
    ...
}

Para desenhar a bola, adicione as seguintes linhas dentro de gameLoop.

function gameLoop()
{
    ...
    context.beginPath();
    context.arc(bolaPosX, bolaPosY, bolaDiametro, 0, Math.PI * 2, true);
    context.fill();
    ...
}

Procure pesquisar some a função de desenho do arco que usamos acima. Foge do mérito desse artigo explicá-la.

Como resultado você deve ver metade da bola desenhada no topo do canvas. Como abaixo:

Vemos metade dela pois ela é desenhada a partir do centro, como setamos Y para 0, a outra metade está acima do canvas.
Sendo desenhada do centro, não precisamos diminuir seu diâmetro para centralizar ela na tela, bastou dividir por 2 o eixo X.

Movimentando a bola
Bom, não queremos que a bola inicia com metade aparecendo, melhor que isso queremos que ela venha antes do canvas.
Você pode setar o valor de bolaPosY para -10 para que isso aconteça.

...
    bolaPosY = -10;
    ...

Agora vamos movimentar ela como se estivesse caindo, no eixo Y.
Simples não? Basta a cada gameLoop diminuir a velocidade da bola.

Mas veja que isso só pode acontecer se a posição Y da bola for menor que a altura do canvas, caso contrário a bola vai descer até o infinito.

E já que a bola chegou ao fim do canvas, que tal iniciar outra lá no topo?

Acompanhe isso no trecho abaixo:

...
if(bolaPosY <= canvas.height)
{
    bolaPosY += velocidadeBola;
}
else
{
    bolaPosY = -10;
}
...

Legal! Mas o jogo já não é muito divertido, se a bola ficar caindo sempre no mesmo lugar então.
Vamos adicionar um cálculo para fazer a posição X da bola variar cada vez que é criada uma nova.
Basta reformular o nosso else acima.

...
else
{
    bolaPosX = Math.random() * 600;
    bolaPosY = -10;
}
...

Você já pode ir treinando para o game final agora.

Colisão
Chegou a hora de checar se a bola colide com a barra e com isso marcar pontos para você.

Vamos definir uma variável pontosJogador. Não esqueça de iniciá-la como zero dentro da função inicializar.

var barraAltura, barraLargura, jogadorPosicaoX, velocidadeJogador, bolaDiametro, bolaPosX, bolaPosY, velocidadeBola, pontosJogador;

function inicializar()
{
    ...
    pontosJogador = 0;
    ...
}

Em seguida vamos pensar na matemática da colisão.
Temos que fazer um cálculo que coloque a bola em cima da barra.
Então primeiramente, a posição X da bola tem de ser maior que a posição X da barra.
Mas também essa posição X da bola tem de ser menor que a posição X da barra + o tamanho dela.
Lembrando que na barra, sua posição X é o início dela, no canto esquerdo, por isso somamos a sua largura.

Não podemos bater apenas o ponto X. O eixo Y da bola tem de ser maior ou igual que a altura do canvas menos a altura da barra.

Dentro do IF da colisão incrementamos os pontos do jogador.

Em seguida temos um código para escrever a pontuação na tela.

Confira o código abaixo com a explicação acima:

if((bolaPosX > jogadorPosicaoX && bolaPosX < jogadorPosicaoX + barraWidth) && bolaPosY >= canvas.height - barraHeight)
{
    pontosJogador++;
}

context.font = "32pt Tahoma";
context.fillText(pontosJogador, canvas.width - 70, 50);

Hora de rodar o seu jogo!

Mas espere! Isso não é basquete e a cada bola que pegamos estamos fazendo 3 pontos ou mais.
Porque isso está acontecendo?

Ah, simples! Como refazemos a tela a cada 30 milesegundos, a bola leva muito mais tempo que isso para passar pela barra. Então ela está caindo no IF de colisão mais de uma vez.

Para resolver isso vamos adicionar uma flag dizendo se colidiu ou não.
Vamos primeiro definir essa variável, em seguida inicializar ela como false.

var barraAltura, barraLargura, jogadorPosicaoX, velocidadeJogador, bolaDiametro, bolaPosX, bolaPosY, velocidadeBola, pontosJogador, colisao;

function inicializar()
{
    ...
    colisao = false;
    ...
}

Agora no IF onde checamos a colisão, mudamos o valor para true quando colidir, e antes de checar se colidiu verificamos se esse status ainda é false.

if((bolaPosX > jogadorPosicaoX && bolaPosX < jogadorPosicaoX + barraLargura) && bolaPosY >= canvas.height - barraAltura && colisao == false)
{
    pontosJogador++;
    colisao = true;
}

Por último, se uma bola nova começa a cair ela tem de vir com esse status como falso.
Lembra do nosso else que cria uma nova bola no eixo X?

...
else
{
    bolaPosX = Math.random() * 600;
    bolaPosY = -10;
    colisao = false;
}
...

Feito! Hora de chamar os amigos e iniciar um campeonato.

Conclusão
Estude alterando as variáveis como velocidade do jogador, velocidade da bola e etc.
A partir daqui pode surgir muita coisa. Quem sabe não continuamos desenvolvendo esse jogo em um próximo post?

Código completo aqui: https://github.com/flaviosilveira/primeiro-jogo-html5

Abraço!


Comments