Jump to content

Bem vindo à Unidev
Registre para ter acesso a todos os recursos do site. Uma vez registrado e logado, você poderá criar tópicos, postar em tópicos já existentes, gerenciar seu perfil e muito mais. Se você já tem uma conta, faça login aqui - ou então crie aqui uma conta agora mesmo!
- - - - -
Photo

Iniciando em HTML5 (com Javascript)


por Maksim Mikityanskiy

O seguinte artigo será um guia bem minimalista e direto para se iniciar em HTML5. Mesmo sendo direcionado a
iniciantes, alguma experiencia em programação é preferível já que não vou me preocupar em explicar o básico de Javascript. Deixarei isso para outros tutoriais online ou uma introdução de um curso de ciência da computação. Também não é especifico de certa plataforma, de maneira nenhuma. Deve rodar igualmente bem em Linux, Windows ou MacOS.

O HTML

Já que este será um web game, um arquivo HTML é necessário. Esta não é uma pagina web apropriada, mas apenas o suficiente para fazer o game rodar.

<!DOCTYPE html>


<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="game.js"></script>


<body bgcolor=#808080>


<canvas id="gameCanvas" width="800" height="600"
onContextMenu="javascript: return false;"></canvas>


</body>

A primeira linha especifica que versão do HTML usar, especificamente a 5.

Nota: Algumas versões mais antigas do Internet Explorer forçam o modo de compatibilidade em certos domínios onde é impossível fazer override da web page. Isto pode ter sido corrigido desde a última vez que usei o Internet Explorer. Não foi um problema enquanto estava testando, mas se você tentar colocar seu código em um blogspot por exemplo, características do HTML5 como o canvas podem não funcionar por essa razão.



A segunda linha é a dependência. O JQuery faz muitas coisas uteis, mas somente o usarei para inputs do mouse neste artigo. A terceira linha é um link para o script contendo a lógica do jogo.

A cor de fundo não é necessária, mas eu a acho útil.

Finalmente, você tem o canvas de HTML5. A única coisa não muito comum aqui é o onContextMenu; que retorna false quando o usuário clica no canvas com o botão direito e como resultado, nenhum menu aparece.

A LÓGICA DO JOGO

DECLARAÇÕES

Agora a lógica do jogo que via no game.js. Primeiro vem as declarações:

var canvasID, context;
var cursorX = 0, cursorY = 0;
var canvasX = 0, canvasY = 0, canvasW = 800, canvasH = 600;
var playerScore=0, aiScore=0;

canvasID é o próprio canvas. O context é um objeto especifico para o canvas que contém todas as funcionalidades de desenho.

A localização do cursor é auto explicativa. A posição do canvas é necessária para calcular a posição do cursor relativa ao canvas. As dimensões do canvas como variáveis também permitem grande flexibilidade enquanto codificando.

INICIALIZAÇÃO E CÓDIGO DO MOUSE

Estas duas coisas precisam ser feitas primeiro, que é a única razão do porque que estão juntas:

function OnMouseMove(e){
cursorX = e.pageX-canvasX;
cursorY = e.pageY-canvasY;
}


$(document).ready(function(){
canvasID = document.getElementById("gameCanvas");
canvasID.addEventListener("mousemove", OnMouseMove, false);
context = canvasID.getContext('2d');
var rect = canvasID.getBoundingClientRect();
canvasX = rect.left; canvasY = rect.top;
canvasW = rect.right-canvasX; canvasH = rect.bottom-canvasY;
})

A função OnMouseMove recebe um Event Handler como argumento. O event handler tem a posição do cursor relativa ao canto superior esquerdo da página, não do canvas. É ai que entra o canvasX e canvasY.

O próximo pedaço é mágica do JQuery. Quando o documento esta pronto (ready, tudo carregado) a função é executada.

Buscar o canvasID é bem direto.

A próxima linha é a última do código de input do mouse. Ela adiciona um event listener que chama OnMouseMove quando o mouse é movido. Há também mousedown e mouseup para quando se pressiona e solta o botão do mouse. O e.which no event handler especifica que botão foi pressionado.

Buscando Contexto é simples.

A seguir é onde nós achamos a localização e dimensões reais do canvas. A função getBoundingClientRect() retorna o espaço que é ocupado pelo cliente. Pegar a localização é trivial. O rect.right e rect.bottom são a soma da posição e altura de y e posição e altura de x.

O LOOP DE UPDATE

function Run(){
context.fillStyle="black";


context.clearRect(0,0, canvasW, canvasH);
context.fillRect(0,0, canvasW, canvasH);
}


setInterval(Run,20);

A função Run será o loop de update. É onde tudo que precisa ser executado repetidamente será colocado. A função setInterval diz ao browser que isto precisar ser executado a cada 20ms (ou o que você especificar).

Agora para o código de desenho. As funções fillStyle e strokeStyle especificam a cor com que você estará trabalhando. A primeira é para preencher as formas, e a última é para desenhar linhas.

A função clearRect esvazia os dados do canvas. Não é muito importante quando você está começando, mas realmente importa quando você lida com transparências e composições. Os argumentos são x, y, largura e altura. A função fillRect deve ser auto explicativa; recebe os mesmos argumentos.

DESENHANDO OS PONTOS

function DrawScores(){
context.fillStyle = "white";
context.font = "16px sans-serif";
context.fillText(aiScore, 30,30);
context.fillText(playerScore, canvasW-50,30);
}

Nós já trabalhamos com fillStyle.

A fonte é especificada como uma string contendo o tamanho e o typeface. Alguns outros tipos válidos de typeface são serif e monotype.

A função fillText recebe uma string, a coordenada de x e a coordenada de y como argumento. A função strokeText irá desenhar um contorno no texto; apenas certifique-se de especificar o strokeStyle em vez disso.

Finalmente, chame-a na função Run depois que você limpa o canvas.

function Run(){
context.fillStyle="black";


context.clearRect(0,0, canvasW, canvasH);
context.fillRect(0,0, canvasW, canvasH);
    
DrawScores();
}

AS CLASSES

Javascript não tem classes como a maioria das outras linguagens de programação, mas elas funcionam. As únicas duas coisas necessárias para um jogo de ping-pong são as raquetes e a bola.

A Bola

function Ball(){
this.radius = 8;
this.x = canvasW/2;
this.y = canvasH/2;
this.vx = 5;
this.vy = 0;


this.Draw = function(){
//...\\
}


this.Update = function(){
        //...\\
}
}

O código acima inclue tudo que a bola precisa; raio, posição e velocidade. Ela também precisa de uma função Draw e uma função Update. A função Draw é a mais simples:

this.Draw = function(){
    context.fillStyle = "white";
    
    context.beginPath();
    context.arc(this.x, this.y, this.radius, 0, 3.1416*2);
    context.closePath();
    
    context.fill();
}

A única coisa levemente incomum sobre desenhar circulos no HTML5 é o fato de que o context não tem uma função para explicitamente desenha-los. Em vez disso, pode-se criar um arc path. Primeiro chame a função beginPath, então crie um arco de angulo 2pi, e finalmente feche o path. Os argumentos são a posição em x, posição em y, raio, começo do angulo e fim do angulo. Paths mais complicados são possiveis, mas isso é além do escopo deste artigo.

Agora para a próxima função:

this.Update = function(){
    this.x += this.vx;
    this.y += this.vy;
    //move the ball
    
    if(this.x > playerPaddle.x - playerPaddle.w/2 - this.radius){//check if the ball has traveled 
         //far enough to the right to possibly interact with the right paddle.
        if(this.y >= playerPaddle.y - playerPaddle.h/2 && this.y <= playerPaddle.y + playerPaddle.h/2){
             //check if it actually hit the paddle
            this.vy = (playerPaddle.y-this.y)*-0.4; //change the y velocity depending on which part 
             //of the paddle the ball hit.
            this.x = playerPaddle.x - playerPaddle.w/2 - this.radius; //moves the ball out of the paddle
            this.vx*=-1; //make the ball bounce off
        }else{//if the player misses the ball, incriment the ai score and reset the ball.
            aiScore++;
            this.vy=0;
            this.x = canvasW/2;
            this.y = canvasH/2;
        }
        aiPaddle.AIChangeOffset();//AI thing, will make sense later.
    }
    if(this.x < aiPaddle.x + aiPaddle.w/2 + this.radius){//same thing
        if(this.y >= aiPaddle.y - aiPaddle.h/2 && this.y <= aiPaddle.y + aiPaddle.h/2){
            this.vy = (aiPaddle.y-this.y)*-0.2;
            this.x = aiPaddle.x + aiPaddle.w/2 + this.radius;
            this.vx*=-1;
        }else{
            playerScore++;
            this.vy=0;
            this.x = canvasW/2;
            this.y = canvasH/2;
        }
    }
    
    if(this.y<this.radius){
        this.vy*=-1; this.y=this.radius;}
    else if(this.y>canvasH-this.radius){
        this.vy*=-1; this.y=canvasH-this.radius;}
    //have the ball bounce off the top and bottom of the screen.
}

Não há nada de especifico do HTML5 aqui, somente a álgebra e aritmética de sempre. Eu não comentei o código de aiPaddle porque é mais ou menos a mesma coisa.

A Raquete

Começando com as coisas mais simples; declaração de variáveis e a função Draw:

function Paddle(){
this.x=0;
this.y = canvasH/2;
this.w = 32;
this.h=64;


this.aiv=9; //the maximum velocity with which the ai paddle can move
this.aiOffset = 0; //how far off center the paddle will be when the ball reaches the end


this.Draw = function(){
context.fillStyle="white";
context.fillRect(this.x - this.w/2, this.y - this.h/2, this.w, this.h);
}

Isto incluí todas as propriedades físicas relevantes, e um par de propriedades relacionadas a IA.

this.FloorAndCeiling = function(){
if(this.y < this.h/2) this.y = this.h/2;
if(this.y > canvasH-this.h/2) this.y = canvasH-this.h/2;
}

O código acima se certifica que a raquete não esta fora dos limites da tela. Ele põe um teto e um chão na posição y. A sintaxe pode parecer incomum, mas funciona do mesmo jeito.

this.PlayerUpdate = function(){
this.y = cursorY;
this.FloorAndCeiling();
}

O código acima configura a posição y da raquete igual a posição y do cursor.

this.AIUpdate = function(){
if(ball.vx < 0){//0.1
if(ball.y + this.h*this.aiOffset > this.y -this.aiv){//1.0
if(this.y+this.aiv<ball.y + this.h*this.aiOffset)//1.1
this.y+=this.aiv;
else
this.y=ball.y + this.h*this.aiOffset;//1.2
}
if(ball.y + this.h*this.aiOffset < this.y + this.aiv){//2.0
if(this.y-this.aiv>ball.y + this.h*this.aiOffset)
this.y-=this.aiv;
else
this.y=ball.y + this.h*this.aiOffset;
}


this.FloorAndCeiling(); //3.0
}
}

O código do IA é um pouco mais difícil de entender conceitualmente, então as linhas estão rotuladas.

0.1 diz a IA para somente fazer alguma coisa se a bola estiver se movendo em sua direção. Isto não é absolutamente necessário e o jogo funciona bem sem essa linha.

1.0 checa onde esta a bola em relação a raquete. Retorna true especificamente quando a bola esta em cima da raquete. + this.h*this.aiOffset simula erro mudando onde o centro da raquete realmente é. aiOffset é um numero entre -0,45 e 0,45 para que o novo "centro" sempre esteja na raquete.

1.1 checa se a distancia y entre o "centro" da raquete e a bola é maior que a velocidade máxima que a IA pode mover a raquete. Se é, ela move a raquete àquela distancia. Senão,

1.2 a bola está "dentro do alcance" então em vez de fazer um overshooting (mover mais do que deveria), somente a move para a posição y da bola

2.0 basicamente a mesma coisa, mas na direção oposta

3.0 certifica-se de que a IA não mova a raquete pra fora da tela.

this.AIChangeOffset = function(){
this.aiOffset = (Math.random()-0.5)*0.9;
}
}

Finalmente, somente declare a ultima função que muda o offset. Se você se lembra, isso é chamado toda vez que a bola pula pra fora da raquete do jogador. Lembre-se de fechar todas as chaves.

INICIALIZAÇÃO DE VARIÁVEIS

var ball = new Ball();
var aiPaddle = new Paddle(), playerPaddle = new Paddle();
playerPaddle.x = canvasW; //move the paddle to the other side of the screen

Aqui nós criamos a bola e as raquetes.

O LOOP DO JOGO

Finalmente, atualize a função Run:

function Run(){
    //NEW CODE PT 1
aiPaddle.AIUpdate();  
playerPaddle.PlayerUpdate();
ball.Update(); //Update all the game objects
//END NEW CODE PT 1
    
context.fillStyle="black";


context.clearRect(0,0, canvasW, canvasH);
context.fillRect(0,0, canvasW, canvasH);//clear the context


    //NEW CODE PT 2
ball.Draw();
aiPaddle.Draw();
playerPaddle.Draw();
DrawScores();//Draw it all
    //END NEW CODE PT 2
}

Tudo aqui é bem direto. Somente chame todas as funções Update e Draw nos objetos, e desenhe o placar (pontos). A única coisa importante aqui é chamar Update antes do Draw, e limpar o context antes do Draw também.

CONCLUSÃO

Este provavelmente é o jogo funcional mais simples que alguém pode fazer. Tem um pouco de tudo: gráficos, inputs, física e IA. Não usa nenhuma função avançada, mas é um bom ponto de partida.

Fontes: http://uploads.gamed...74526eef77c.zip

Este artigo foi originalmente postado em Gamedev.net
http://www.gamedev.n...ith-html5-r3352
  • MM_lol likes this



2 Comments

Olá, muito bom o artigo.

Implementei e dei uma refatorada no código adicionando alguns conceitos, como por exemplo, herança.

Quem quiser baixar o código que fiz, fazer um fork, pull request, etc, coloquei ele no GitHub:

https://github.com/l...Html5CanvasPong

 

[]'s

Photo
RodrigoSchio
Set 15 2014 01:04

É um artigo interessante.

Baixei os fontes do jogo (gamedev) e experimentei também a versão do Ivendrame.

Tive bastante interesse nesse artigo porque também já desenvolvi uma versão do mesmo jogo em html5:
http://www.bolaelasticajs.appspot.com

A minha versão funciona no teclado ao invés do mouse... e tem várias outras diferenças... mas na essência tem muito em comum

A minha versão tá publicada em um servidor na net... e armazena os recordes em uma base de dados.