terça-feira, 24 de junho de 2014

EaselJs: Biblioteca javascript para criação de animações em Canvas

Porque todo o que é nascido de Deus vence o mundo; e esta é a vitória que vence o mundo, a nossa fé. (1 João 5:4)

Segue agora uma breve introdução á uma importante biblioteca feita em javascript, para criação de animações e jogos em CANVAS. Antes de prosseguir, é necessário que tenha uma noção de HTML5, principamente da utlização da TAG CANVAS. Para tanto, recomento a leitura do seguinte artigo: Animações com canvas e Criando jogos em canvas.

Esta biblioteca, do qual estamos falando, chama-se EaselJs, cuja documentação e código-fonte está disponível em EaselJs. Esta biblioteca é utilizada para facilitar na criação de gráficos e animações com CANVAS. No site da desenvolvedora, há muitos exemplos e links para jogos que implementam esta ferramenta. Se voçê já criou algo com CANVAS, irá reparar que esta ferramenta agiliza bastante para voçê atingir o objetivo, sem se preocupar, por exemplo, com a instanciação do elemento CANVAS para fazer desenhos simples, ou ficar executando a função setTImeout, para chamar a função de atualização da animação.

O jogo consiste no seguinte: Há duas bolas no cenário (Uma azul e outra vermelha). Quando voçê clica em algum ponto do cenário, ou toca em algum ponto do cenário (Caso utilize algum dispositivo com touchScreen), aparece um quadrado laranja, que é o destino do ponto azul. A bola vermelha persegue a bola azul, e se a alcança, é o fim do jogo. Depois de um tempo, aparece uma bola preta, que é onde a bola azul irá escapar, encerrando a animação.

O funcionamento do easeljs, de forma resumida, se resume no seguinte: Quando se carrega uma página, é necessária a execução de inicialização da animação, que é acionada pelo evento onLoad da página. Ou seja, a animação é inicializada quando a página é carregada, ou quando alguém inicia a animação, clicando em INICIAR. Logo em seguida, é necessária uma função que dá continuidade á animação. Á cada intervalo de tempo, é executado o evento tick pelo objeto estático Ticker, que é responsável por todo o controle da animação. Segue abaixo a estrutura codificada:


< !DOCTYPE html >
< html lang=”en-US” >
< head >


TESTES
 
< /head >
< body >
< /body >
< /html >

 

Agora que voçê tem uma noção de como o easeljs trabalha, vamos ver passo-a-passo a construção da animação:

Quando se carrega a página, é executada a função init(), que inicializa a animação.

 function init() {
  w = document.getElementById("demoCanvas").width;
  l = document.getElementById("demoCanvas").height;
  stage = new createjs.Stage("demoCanvas");
  r = 10;
  bola = new createjs.Shape();
  red = new createjs.Shape();
  es = new createjs.Shape();
  saida = new createjs.Shape();
  saida.ex = false;
        var is_touch_device = 'ontouchstart' in document.documentElement;
 
  constroe();
  
  desenha();

  createjs.Ticker.init();

  
  createjs.Ticker.on("tick", tick);
  
  
  createjs.Ticker.setFPS(20);
  
  if(is_touch_device === true){
   document.getElementById("demoCanvas").addEventListener("touchstart",handleTouch,false);
  }else{
   document.getElementById("demoCanvas").onclick = function(e){
    handleClique(e);
   }
  }
 }

 function tick(event) {
  
  if(checaColisao(es,bola) === true && bola.x == es.x && bola.y == es.y){
   // Quadrado laranja some, e bola azul fica no lugar
   
  }else{
   // Bola azul segue bola laranja
   perseguicao(bola,es);

  }
  
  if(checaColisao(bola,red) === true || ( (bola.x > red.x - r ) && (bola.x < red.x + r ) && (bola.y > red.y - r) && (bola.y < red.y + r) ) ){
   // Quadrado laranja some, e bola azul fica no lugar
   alert("GAME OVER");

   createjs.Ticker.reset();
   

   // Rever como reexecutar o ticker
   init();
  }else{
   // Bola vermelha segue bola azul
   perseguicao(red, bola);

  }
  
  // Aumentando velocidade do vilão
  
  if(createjs.Ticker.getTime() > 10000){
   console.log("INCREMENTANDO");
   red.v += 0.02;
   
   // Fazendo aparecer bola preta
   saida.ex = true;
   
   if(checaColisao(saida,bola) || ( (bola.x > saida.x - 20 ) && (bola.x < saida.x + 20 ) && (bola.y > saida.y - 20) && (bola.y < saida.y + 20) )){
    alert("VOCE ESCAPOU");
    createjs.Ticker.reset();
    // Rever como reexecutar o ticker
    init();
   }
  }
  
  stage.removeAllChildren();

  desenha();

  stage.update(event);
 }



Note que, na função init(), as variáveis são inicializadas, os objetos são instanciados (Função constroe()), depois, são desenhados na tela (Função desenha()), e é verificado se a interface aceita toque de tela (7 últimas linhas da função). Além disso, é inicializado o Ticker, além de ser associada a função tick() ao evento tick do Ticker. A função setFps define velocidade de animação para 20 milisegundos por frame - Cada frame é uma execução da função tick.

A função constroe() é responsável por definir o posicionamento inicial dos objetos, de forma randômica. Ou seja, são definidas, de forma aleatória, coordenadas x,y de cada objeto.

A função desenha() é responsável por desenhar os objetos na tela, com base nas propriedades já definidas (Coordenadas x e y). Observe que são desenhados dois círculos e um quadrado; e que, ambos são adicionados como nós do objeto Stage, que gerencia a disposição dos desenhos na tela.

 function constroe(){
  pos = definePosicaoRandomica(w,l);
  bola.x = pos.x;
  bola.y = pos.y;
  bola.v = 7;

  pos = definePosicaoRandomica(w,l);
  red.x = pos.x;
  red.y = pos.y;
  red.v = 2;
  
  pos = definePosicaoRandomica(w,l);
  saida.x = pos.x;
  saida.y = pos.y;
  
 }
 
 function desenha(){

  bola.graphics.beginFill("blue").drawCircle(0,0,r);

  red.graphics.beginFill("red").drawCircle(0,0,r);
  
  es.graphics.beginFill('#FF7F50').drawRect(es.x,es.y,10,10);
  
  stage.addChild(bola);
  
  stage.addChild(red);
  
  stage.addChild(es);
  
  if(saida.ex == true){
   saida.graphics.beginFill("#000000").drawCircle(0,0,20,20);
   stage.addChild(saida);
  }
  
 }

 Note que, quando há toque na tela, é executada a função handleTouch. E quando há algum clique no CANVAS, é executada a função handleClique. Porém, ambos fazem a mesma coisa: Setar as coordenadas do quadrado laranja, que é o alvo da bola azul.

 function handleTouch(e){
  
  es.x = 0;
  es.y = 0;
  var coords = getCoordsTouch(e);
  es.x = coords.x;
  es.y = coords.y;

 }
 
 function getCoordsTouch(event){
  var saida = {};
  saida.x = event.targetTouches[0].pageX - document.getElementById("demoCanvas").offsetLeft;
  saida.y = event.targetTouches[0].pageY - document.getElementById("demoCanvas").offsetTop;
  
  return saida;
 }
 
 function handleClique(e){
  es.x = 0;
  es.y = 0;
  
  var pX;
  var pY;
  pX = e.pageX - document.getElementById("demoCanvas").offsetLeft;
  pY = e.pageY - document.getElementById("demoCanvas").offsetTop;
  es.x = pX;
  es.y = pY;

 }

 

Na função tick(), que executo de tempos em tempos, eu verifico se há colisão entre as bolinhas, ou se a bolinha azul chegou no ponto indicado pelo quadrado laranja. Se a bolinha azul chegou no quadrado laranja, a mesma para. Se, porventura, a bolinha vermelha alcançou a bolinha azul, é exibida a mensagem "GAME OVER", e a animação é encerrada. Caso não ocorra nenhuma coisa nem outra, é executada a função perseguicao(), que é responsável por direcionar, a cada frame, um objeto á seu destino.

 function tick(event) {
  
  if(checaColisao(es,bola) === true && bola.x == es.x && bola.y == es.y){
   // Quadrado laranja some, e bola azul fica no lugar
   
  }else{
   // Bola azul segue bola laranja
   perseguicao(bola,es);

  }
  
  if(checaColisao(bola,red) === true || ( (bola.x > red.x - r ) && (bola.x < red.x + r ) && (bola.y > red.y - r) && (bola.y < red.y + r) ) ){
   // Quadrado laranja some, e bola azul fica no lugar
   alert("GAME OVER");

   createjs.Ticker.reset();
   

   // Rever como reexecutar o ticker
   init();
  }else{
   // Bola vermelha segue bola azul
   perseguicao(red, bola);

  }
  
  // Aumentando velocidade do vilão
  
  if(createjs.Ticker.getTime() > 10000){
   console.log("INCREMENTANDO");
   red.v += 0.02;
   
   // Fazendo aparecer bola preta
   saida.ex = true;
   
   if(checaColisao(saida,bola) || ( (bola.x > saida.x - 20 ) && (bola.x < saida.x + 20 ) && (bola.y > saida.y - 20) && (bola.y < saida.y + 20) )){
    alert("VOCE ESCAPOU");
    createjs.Ticker.reset();
    // Rever como reexecutar o ticker
    init();
   }
  }
  
  stage.removeAllChildren();

  desenha();

  stage.update(event);
 }
 function perseguicao(obj1,obj2){
  // Animação de perseguição
  if(obj1.x < obj2.x){
   obj1.x += obj1.v;
  }else if(obj1.x > obj2.x){
   obj1.x -= obj1.v;
  }
  
  if(obj1.y < obj2.y){
   obj1.y += obj1.v;
  }else if(obj1.y > obj2.y){
   obj1.y -= obj1.v;
  }
  
  if(obj1.x >= w){
   obj1.x -= obj1.v; 
  }else if(obj1.y >= l){
   obj1.y -= obj1.v;
  } 
 }

 

No final da função tick, é verificado se o tempo decorrido desde o início da animação é maior do que 10 segundos. Se sim, aparece um círculo preto, que é por onde o herói deve escapar, e, a cada frame, a velocidade da bolinha vermelha aumenta 0,2.

Como se vê, o que dá a sensação de movimento no jogo, é que, a cada frame, é executada a função desenha(), que printa os elementos na tela. Quando voçê faz um clique, voçê redefine propriedades dos objetos que estão sendo manipulados, porém, o que dispôe os objetos na tela com base em seus atributos, é a função desenha(), chamada através do evento tick().

 

  stage.removeAllChildren();

  desenha();

  stage.update(event);

 

Exemplo disponível para visualização:  Exemplo

Para baixar clique no link ao lado: BAIXAR

 

 

Nenhum comentário:

Postar um comentário