Criando um jogo em Javascript [5/5] – Pontuação

Que tal finalizar nossa versão do jogo da galinha do Atari?

Fizemos o pano de fundo do jogo, no primeiro post.

A movimentação do personagem principal a partir do teclado foi feita no segundo post.

Alguns carros movimentados automaticamente foram inseridos no terceiro post.

No quarto post, o GAME OVER foi detectado ao ocorrer a colisão dos carros e nosso personagem principal.

Iniciando a pontuação

Vamos criar uma variável pontos para armazenar o número de pontos e também uma função desenhaPontos que, quando invocada, desenha os pontos no canto superior esquerdo da página.

var pontos = 0;
function desenhaPontos(){
    contexto.fillStyle = "black";
    contexto.font="12pt Monospace";
    contexto.fillText(pontos, 5, 20);
}

Na função do setTimeout, precisamos desenhar os pontos atuais. Devemos invocar a função que acabamos de criar depois de desenhar o fundo:

setInterval(function(){
    desenhaFundo();
    desenhaPontos();
    dilminha.desenhaImagem();
    carrinhoAmarelo.desenhaImagem();
    carrinhoAzul.desenhaImagem();
    carrinhoPolicia.desenhaImagem();

    //restante do código...
},50);

O resultado do código acima está disponível em:
http://a-dilminha.appspot.com/passo-a-passo/pontuacao/15-zerado.html

Pontuação zerada

Contando os pontos

No jogo do Atari, sempre que a galinha atravessa o lado de cima da rua ganhamos um ponto. Faremos o mesmo.

Quanto mais pra cima tiver o personagem principal, menor é o valor de y. Um valor menor ou igual a zero indica que o personagem atravessou a rua totalmente.

Vamos criar uma função passou que detecta se o personagem principal ultrapassou o limite superior da tela. Já que essa função não valerá pra os carros, vamos colocá-la diretamente como uma propriedade do objeto dilminha.

var dilminha = new Sprite("../../dilminha.png", 320, 400);
dilminha.passou = function(){
    if(this.y <= 0) {
        return true;
    }
    return false;
};

Sempre que houver alguma teclada pressionada no teclado, vamos verificar se a rua foi totalmente atravessada, invocando o método passou que acabamos de definir. Caso o personagem principal tenha ultrapassado o topo da tela, contaremos mais um ponto:

document.onkeydown = function(event) {
    //verificação de game over...
    //tratamento da tecla pressionada...
    if(dilminha.passou()){
        pontos++;
    }
}

O resultado do código acima está disponível em:
http://a-dilminha.appspot.com/passo-a-passo/pontuacao/16-pontos.html

Contagem de pontos

Bug na pontuação

Há um defeito na contagem dos pontos: se o personagem principal deslizar horizontalmente no topo da tela, são contados múltiplos pontos.

Veja:

Defeito na pontuação

Existem várias maneiras de resolver esse problema. Uma das mais simples é movermos o personagem principal lá pra baixo da tela assim que detectarmos que o topo da tela foi ultrapassado. Podemos implementar isso alterando o método passou:

dilminha.passou = function(){
    if(this.y <= 0) {
        this.y = canvas.height - this.altura;
        return true;
    }
    return false;
}

O resultado do código acima está disponível em:
http://a-dilminha.appspot.com/passo-a-passo/pontuacao/17-final.html

Defeito corrigido

Então é isso, pessoal

Poderíamos melhorar muito o joguinho: implementar uma colisão decente, salvar as pontuações em um servidor, fazer funcionar em dispositivos móveis…

Mas vamos parar por aqui! Já deu pra fazer algo razoável. Divertido, não?

Nosso código final ficou assim:

<html>
<body>
  <canvas id="canvas" width="640" height="480" style="border: solid 1px black; margin: 0px auto; display: block;"></canvas>
  <script>
    var gameOver = false;

    var canvas = document.getElementById("canvas");
    var contexto = canvas.getContext("2d");

    function desenhaFundo() {
      //preenche o fundo com cinza escuro
      contexto.fillStyle = "dimgray";
      contexto.fillRect(0, 0, canvas.width, canvas.height);

      //calcada superior
      contexto.fillStyle = "lightgray";
      contexto.fillRect(0, 0, canvas.width, 80);

      //calcada inferior
      contexto.fillStyle = "lightgray";
      contexto.fillRect(0, 380, canvas.width, 100);

      //faixas
      contexto.fillStyle = "white";
      for (var i = 0; i < 25; i++) {
        contexto.fillRect(i * 30 - 5, 185, 20, 4);
        contexto.fillRect(i * 30 - 5, 280, 20, 4);
      }
    }

    var pontos = 0;
    function desenhaPontos() {
      contexto.fillStyle = "black";
      contexto.font = "12pt Monospace";
      contexto.fillText(pontos, 5, 20);
    }

    function Sprite(caminhoDaImagem, xInicial, yInicial) {
      this.x = xInicial;
      this.y = yInicial;

      this.imagem = new Image();
      this.imagem.src = caminhoDaImagem;

      var that = this;
      this.imagem.onload = function () {
        that.largura = that.imagem.width;
        that.altura = that.imagem.height;
        that.desenhaImagem();
      }

      this.desenhaImagem = function () {
        contexto.drawImage(this.imagem, this.x, this.y, this.largura, this.altura);
        contexto.strokeStyle = "darkred";
        contexto.lineWidth = 0.2;
        contexto.strokeRect(this.x, this.y, this.largura, this.altura);
      }

      this.move = function (dx, dy) {
        this.x += dx;
        this.y += dy;

        //limites
        if (this.x > canvas.width) {
          this.x = -this.largura;
        } else if (this.x < -this.largura) {
          this.x = canvas.width;
        }
        if (this.y > canvas.height - this.altura + 5) {
          this.y -= dy;
        } else if (this.y <= -5) {
          this.y = canvas.height - this.altura;
        }
      }

      this.colidiu = function (outro) {
        var colidiuNoXTopo = outro.x >= this.x && outro.x <= (this.x + this.largura);
        var colidiuNoYTopo = outro.y >= this.y && outro.y <= (this.y + this.altura);
        var colidiuNoXBase = (outro.x + outro.largura) >= this.x && (outro.x + +outro.largura) <= (this.x + this.largura);
        var colidiuNoYBase = (outro.y + outro.altura) >= this.y && (outro.y + outro.altura) <= (this.y + this.altura);
        return (colidiuNoXTopo && colidiuNoYTopo) || (colidiuNoXBase && colidiuNoYBase);
      }

    }

    var dilminha = new Sprite("../../dilminha.png", 320, 400);
    dilminha.passou = function () {
      if (this.y <= 0) {
        this.y = canvas.height - this.altura;
        return true;
      }
      return false;
    }

    var carrinhoAmarelo = new Sprite("../../carrinho-amarelo.png", -10, 300);
    var carrinhoAzul = new Sprite("../../carrinho-azul.png", 560, 200);
    var carrinhoPolicia = new Sprite("../../carrinho-policia.png", 10, 100);

    document.onkeydown = function (event) {
      if (gameOver) {
        return;
      }

      switch (event.which) {
      case 37: //pra esquerda
        dilminha.move(-10, 0);
        break;
      case 38: //pra cima
        dilminha.move(0, -10);
        break;
      case 39: //pra direita
        dilminha.move(10, 0);
        break;
      case 40: //pra baixo
        dilminha.move(0, 10);
        break;
      }

      if (dilminha.passou()) {
        pontos++;
      }
    }

    setInterval(function () {
      desenhaFundo();
      desenhaPontos();

      dilminha.desenhaImagem();
      carrinhoAmarelo.desenhaImagem();
      carrinhoAzul.desenhaImagem();
      carrinhoPolicia.desenhaImagem();

      if (gameOver) {
        contexto.fillStyle = "red";
        contexto.font = "Bold 80px Sans";
        contexto.fillText("GAME OVER", canvas.width / 16, canvas.height / 2 + 20);
        return;
      }

      carrinhoAmarelo.move(7, 0);
      carrinhoAzul.move(-5, 0);
      carrinhoPolicia.move(10, 0);

      if (carrinhoAmarelo.colidiu(dilminha) || carrinhoAzul.colidiu(dilminha) || carrinhoPolicia.colidiu(dilminha)) {
        gameOver = true;
      }

    }, 50);
  </script>
</body>
</html>

O resultado do código acima está disponível em:
http://a-dilminha.appspot.com/index.html