em Uncategorized

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.

Escreva um comentário

Comentário