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);
}
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:
- 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)
-
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)
- Em caso positivo, desenhar o pad na nova posição
- 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
- 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.