Pong em HTML5 Canvas

Recentemente eu escrevi um turorial de Canvas (HTML5) para iniciantes, que comecei a escrever para aprender Canvas.

Comecei a buscar formas divertidas de aprender canvas e como games em HTML5 me chamaram a atenção desde a primeira vez que li sobre o assunto, porque não aprender canvas desenvolvendo algum game? 🙂

É também uma forma de mostrar o que é possível com HTML5 e expandir as idéias para as aplicações que podem ser feitas com um pouco de criatividade.

Pong em Canvas

Vou explicar o desenvolvimento do Pong em HTML5.

Para facilitar a manutenção procurei desenvolver todo o game com orientação a objetos em quase tudo, então recomendo leitura sobre o assunto caso você não se sinta confortável com esse tipo de código.

O primeiro passo é criar a estrutura do game:

function Pong(){
    _this = this;
}

Todo o game é estruturado em basicamente 2 objetos de dados e algumas funções (métodos) auxiliares que fazem a manipulação dos dados, animação e regras do jogo.

 

Os objetos do game

Players

Cada player é um objeto onde é armazenado as informações do jogador, como pontuação, id, localização do seu "pad" e regra de movimentação (se ele deve ir para a direita ou esquerda).

this.player = function(){

    this.score = 0,
    this.left = false,
    this.right = false;    

    this.paddle = {
        x : 0,
        y : (_this.HEIGHT / 2)-60,
        w : 7,
        h : 120
    };

}

 

 

Bola

A bola é outro objeto do game, nela ficam armazenados sua posição (x,y) e o raio utilizado para o desenho.

this.bola = {
      x : 8,
      y : 50,
      r : 6
}

 

Métodos do Pong

Estruturei uma série de funções para facilitar o desenho e aplicação das regras do game. Acredito que o código ficou bastante legível e você pode ler ele diretamente no game.

 

init()

Chamada no início do jogo esta função define o contexto do canvas, os valores iniciais dos objetos (players, bola) e armazena também o tamanho do canvas, é utilizado para posicionar os pads e a bola. A função também cria os 2 jogadores e posiciona seus "pads" no local correto.

As variáveis "wind" e "gravity" são as duas forças que atuam na bola (horizontal e verticalmente).

 

                this.init = function(selector){

                    _this.ctx = $("#canvas")[0].getContext("2d"),

                    _this.WIDTH = $("#canvas")[0].width,

                    _this.HEIGHT = $("#canvas")[0].height,

                    _this.gravity = 4,

                    _this.wind = 10,

                    _this.p1 = new _this.player(),

                    _this.p1.id = 1,

                    _this.p2 = new _this.player(),

                    _this.p2.id = 2,

                    _this.p2.paddle.x = (_this.WIDTH-7);

                    

                    $(document).keydown(_this.onKeyDown);

                    $(document).keyup(_this.onKeyUp);

                    

                }

 

 

 
_this.ctx = $("#canvas")[0].getContext("2d"),
 
_this.WIDTH = $("#canvas")[0].width,
 
_this.HEIGHT = $("#canvas")[0].height,
 
_this.gravity = 4,
 
_this.wind = 10,
 
_this.p1 = new _this.player(),
 
_this.p1.id = 1,
 
_this.p2 = new _this.player(),
 
_this.p2.id = 2,
 
_this.p2.paddle.x = (_this.WIDTH-7);
 
 
 
$(document).keydown(_this.onKeyDown);
 
$(document).keyup(_this.onKe

Funções de desenho

Os métodos circle, rect e clean são apenas atalhos para os métodos de desenho do Canvas, utilizados para a cada frame redesenhar a bola e os pads em suas novas posições.

 

                this.circle = function(x,y,r){

                    _this.ctx.fillStyle = "rgba(0,0,255,.9)";

                    _this.ctx.beginPath();

                    _this.ctx.arc(x, y, r, 0, Math.PI*2, true);

                    _this.ctx.closePath();

                    _this.ctx.fill();

                }

                

                this.rect = function(x,y,w,h){

                    _this.ctx.fillStyle = "rgba(255,0,0,.6)";

                    _this.ctx.beginPath();

                    _this.ctx.rect(x,y,w,h);

                    _this.ctx.closePath();

                    _this.ctx.fill();

                }

                

                this.clear = function(){

                    _this.ctx.clearRect(0, 0, _this.WIDTH,_this.HEIGHT);

                }

 

 

Interação com teclado

Como fazer um objeto se movimentar no canvas utilizando o teclado? Na função init do game defini que a ação "keyup" e "keydown" do jQuery iriam disparar um método do game. 

A cada ação de Keydown é feito uma verificação de qual tecla foi pressionada. Se a tecla pressionada foi a direcional esquerda ou a direcional direita (veja a tabela de keycodes para javascript) o objeto do player 1 é alterado, modificando a flag "left" ou a flag "right" para true.

O Keydown faz a ação inversa, tornando as flags "false" dependendo de qual tecla foi liberada do teclado.

 

 

_this.onKeyDown = function(evt) {

    switch(evt.keyCode){

        /* seta direita */

        case 39:

            _this.p1.right = true;

            break;

        /* seta esquerda */

        case 37:

            _this.p1.left = true;

            break;

        /* espaco */

        case 32:

            _this.play();

            break;

    }

  

}

 

/* Keyup events */

_this.onKeyUp = function(evt) {

    switch(evt.keyCode){

        /* seta direita */

        case 39:

            _this.p1.right = false;

            break;

        /* seta esquerda */

        case 37:

            _this.p1.left = false;

            break;

    }

}

 

 

 

As regras e animação do jogo

Neste ponto nós já temos os objetos e players, bola, e todas as informações do canvas e forças que vão atuar no nosso jogo. Também temos os controles configurados, ao pressionar as teclas nosso objeto player é alterado.

Agora precisamos "dar vida" ao game, para isso iremos redesenhar o nosso canvas a cada 25 milisegundos. Primeiro vamos a lógica:

A cada 25 milisegundos iremos:

  1. Calcular e desenhar a bola em sua nova posição (de acordo com os valores de wind e gravity, que são as forças que atuam na bolinha)
  2. Verificar se o pad do player 1 deve ir para cima ou para baixo (variáveis left e right indicando que as teclas estão pressionadas)

    1. Em caso positivo, desenhar o pad na nova posição
  3. Se a bolinha bater em cima ou em baixo do canvas, inverter o valor de "gravity" para que a bolinha continue dentro do nosso canvas
  4. Verificar se é gol (se a bola acertou a lateral esquerda ou direita do canvas sem atingir os pads)

 

Estas são as regras do game convertidas para "bom português", agora vamos para o código:

 

this.animate = function (){

    /* limpa e redesenha a bola */

    _this.clear();

    _this.circle(_this.bola.x,_this.bola.y,_this.bola.r);

    

    var speed = 8;

    

    /* player 1 */

    if (_this.p1.right && _this.p1.paddle.y < (_this.HEIGHT – _this.p1.paddle.h)) _this.p1.paddle.y += speed;

    else if (_this.p1.left && _this.p1.paddle.y > 0) _this.p1.paddle.y -= speed;

    _this.rect(_this.p1.paddle.x,_this.p1.paddle.y,_this.p1.paddle.w,_this.p1.paddle.h);

    

    /* saida lateral */

    if (_this.bola.y + _this.gravity > _this.HEIGHT || _this.bola.y + _this.gravity < 0){

        _this.gravity = -_this.gravity;

    }

    

    if(_this.bola.x + _this.wind> _this.WIDTH){

        if(_this.bola.y > _this.p2.paddle.y && _this.bola.y < (_this.p2.paddle.y + _this.p2.paddle.h)){

           _this.wind = -_this.wind;

           _this.gravity = _this.randomize();

        }else{

           _this.p1.score++;

           $('#score-1').html(_this.p1.score);

            _this.bola.x = (_this.WIDTH / 2),

            _this.bola.y = (_this.HEIGHT / 2);

            _this.wind = -_this.wind;

           _this.play();

        }

    }else if(_this.bola.x + _this.wind < 0){

        if(_this.bola.y > _this.p1.paddle.y && _this.bola.y < (_this.p1.paddle.y + _this.p1.paddle.h)){

           _this.wind = -_this.wind;

           _this.gravity = _this.randomize();

        }else{

           _this.p2.score++;

           $('#score-2').html(_this.p2.score);

            _this.bola.x = (_this.WIDTH / 2),

            _this.bola.y = (_this.HEIGHT / 2);

            _this.wind = -_this.wind;

           _this.play();

        }

    }

    

    

    _this.bola.y += _this.gravity;

    _this.bola.x += _this.wind;

}

 

O código acima também já está marcando a pontuação e manipulando o HTML para exibição dos pontos. Retirei a parte do player 2 para facilitar, ele é um bot e está explicado abaixo:

 

Player 2 (NPC)

Ainda não trabalhei muito no bot, estava ansioso para compartilhar e colocar o Pong no ar e não consegui deixar exatamente como eu gostaria. Ele "persegue" a bolinha a partir de certo ponto da nossa quadra (do elemento canvas).

 

    /* bot */

    var delay = 300;

    if(_this.bola.y < (_this.p2.paddle.y + (_this.p2.paddle.h / 3)) & _this.p2.paddle.y > 0 && _this.bola.x > delay){

        _this.p2.paddle.y -= speed;

    }else if(_this.bola.y > (_this.p2.paddle.y – (_this.p2.paddle.h / 3)) && _this.p2.paddle.y < (_this.HEIGHT – _this.p2.paddle.h) && _this.bola.x > delay){

        _this.p2.paddle.y += speed;

    }

    _this.rect(_this.p2.paddle.x,_this.p2.paddle.y,_this.p2.paddle.w,_this.p2.paddle.h);

 

 

Espero que este post desperte curiosidade e idéias! Sugestões, críticas e dúvidas nos comentários.

HTML5 Canvas – Tutorial para iniciantes

O que é Canvas HTML5?

 HTML5 trouxe diversas novas tags, além das novidades nas tags input de formulário e das tags semânticas como article e section, uma das grandes mudanças implementadas foi a tag canvas.

Canvas é uma nova tag que permite você trabalhar e manipular elementos gráficos (raster). A tag canvas é um "board" de desenho no HTML, nele você pode desenhar linhas, elementos, manipular pixel a pixel, carregar e manipular imagens externas (rotacionar, alterar cor, brilho, etc.).

É uma evolução gigante pois permite a manipulação em tempo real do que está sendo impresso como imagem no computador do cliente.

Um exemplo de uso são jogos, gráficos e interfaces ainda mais interativas.

 

Quais navegadores dão suporte ao Canvas?

Praticamente todos os navegadores modernos dão suporte ao Canvas. Confira a tabela abaixo:

 

  IE Firefox Chrome
14 versions back     4.0: Supported
13 versions back     5.0: Supported
12 versions back   2.0: Supported 6.0: Supported
11 versions back   3.0: Supported 7.0: Supported
10 versions back   3.5: Supported 8.0: Supported
9 versions back   3.6: Supported 9.0: Supported
8 versions back   4.0: Supported 10.0: Supported
7 versions back   5.0: Supported 11.0: Supported
6 versions back   6.0: Supported 12.0: Supported
5 versions back   7.0: Supported 13.0: Supported
4 versions back 5.5: Not supported 8.0: Supported 14.0: Supported
3 versions back 6.0: Not supported (but has polyfill available) 9.0: Supported 15.0: Supported
2 versions back 7.0: Not supported (but has polyfill available) 10.0: Supported 16.0: Supported
Previous version 8.0: Not supported (but has polyfill available) 11.0: Supported 17.0: Supported 3.0: Supported
Current 9.0: Supported 12.0: Supported 18.0: Supported
Near future 10.0: Supported 13.0: Supported 19.0: Supported
Farther future   14.0: Supported 20.0: Supported

Veja a tabela completa e atualizada no Can I Use

 

Entendendo melhor o canvas

Neste tutorial inicial vou explicar alguns métodos do canvas e seu funcionamento. O elemento canvas é todo manipulado por javascript, e trabalha com contextos. Pelo javascript você terá acesso a diversos métodos para desenhar nesta tela, no contexto 2D você utiliza um plano cartesiano (x e y) para definir a posição de onde vai trabalhar, e através de métodos específicos pode traçar formas, linhas e definir tamanhos e cores utilizadas.

Para fazer uma animação por exemplo, você faz o seu primeiro desenho e através do javascript define um time que irá limpar o seu contexto e redesenhar os objetos em sua nova posição. Isso também server para jogos em HTML5 ou interação com o usuário: quando acontecer a interaçãovocê limpa o contexto e redesenha sua cena com os objetos manipulados em suas novas posições.

 

Vamos a prática!

Programando para Canvas

Coloque o elemento canvas no seu HTML e use um identificador para facilitar a manipulação do javascript.

<canvas id="myCanvas"></canvas>

 

Vamos agora selecionar o nosso canvas e definir o contexto em que iremos trabalhar, aqui já entra o javascript:

window.onload = function() {
  var canvas = document.getElementById("myCanvas");
  var context = canvas.getContext("2d");
};

 

Isso defina que vamos trabalhar com o contexto 2D do canvas.

 

Desenhando uma linha com Canvas

Agora que você já tem o seu contexto definido, vamos desenhar uma linha no nosso gráfico, no javascript:

var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
context.beginPath();
context.moveTo(100, 150); //define o ponto inicial do desenho
context.lineTo(450, 50); //define a posição de in
context.lineWidth = 5; // define a largura da linha
context.strokeStyle = "#ff0000"; //define a cor da linha
context.stroke();

 

Pronto, temos uma linha que começa no ponto (100,150) e vai até (450,50).

Desenhando um retângulo com canvas

Agora vamos desenhar um retângulo em nosso canvas, para isso no javascript temos o método rect, que funciona da seguinte forma:

context.rect(x, y, width, height);

O exemplo do código completo:

var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.beginPath();
context.rect(188, 50, 200, 100); // desenha o retângulo
context.fillStyle = '#8ED6FF'; // define o preenchimento do retângulo
context.fill(); // Preenche o retângulo
context.lineWidth = 5; // define a largura da linha do contorno
context.strokeStyle = 'black'; // define a cor do contorno
context.stroke(); // desenha o contorno

 

Desenhando um círculo com canvas

Para desenhar círculos (e arcos) temos o método arc, que funciona da seguinte forma:

context.arc(x, y, radius, startAngle, endAngle, antiClockwise);

No exemplo acima podemos desenhar arco com qualquer angulo de início e fim, para desenhar círculos completos podemos usar o seguinte:

context.arc(x, y, radius, 0 , 2 * Math.PI, false);

Fica mais fácil e temos que nos preocupar apenas com o ponto cartesiano (x,y) e com o tamanho do raio do círculo. Abaixo um exemplo completo:

var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var centerX = canvas.width / 2; // pega o centro do canvas (x)
var centerY = canvas.height / 2;  // pega o centro do canvas (y)
var radius = 70; 

context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false); // desenha o círculo no centro do canvas com raio = 70
context.fillStyle = "#8ED6FF"; // define a cor de preenchimento
context.fill(); // desenha o preenchimento
context.lineWidth = 5; // define a expessura da borda
context.strokeStyle = "black"; // define a cor da borda
context.stroke(); // desenha a borda

 

Esse é um tutorial introdutório ao Canvas, ele parece mais complicado do que realmente é. Para entender um pouco melhor o funcionamento do canvas e suas animações criei um Pong em HTML5 Canvas, mas ainda não tive tempo de finalizar. O código está com a leitura fácil, vale a pena a leitura. 😉

 

Recomendo a leitura (em inglês) do html5 canvas tutorials, de onde os exemplos deste post foram retirados.

 

Este post te ajudou de alguma forma? Retribua, divulgue o link deste artigo em seu blog, twitter ou facebook e nos ajude a compartilhar conhecimento.