Phaser.js #7 – Criando jogo Final!

Estamos chegando ao final de nosso projeto com Phaser pessoal! Lembrando que vocês podem acessar o projeto original em inglês na documentação diretamente no site do Phaser (deixarei o link no final do post).

Pontuação

O que é um jogo sem uma pontuação, não é mesmo? Como saberemos quantas estrelas coletamos?

Para isso, precisamos criar um objeto de jogo de texto. Vamos criar duas variáveis para conter uma pontuação e o objeto de texto:

var score = 0;
var scoreText;

O scoreText será configurado na função create():

scoreText = this.add.text(16, 16, 'score: 0', { fontSize: '32px', fill: '#000' });

A coordenada que exibiremos o texto score: 0 é na 16×16. A fonte padrão de textos do Phaser é a Courier.

Agora, precisamos modificar a função collectStar para que, quando o player coletar uma estrela, o contador seja aumentado, adicionando o nosso score:

function collectStar (player, star)
{
    star.disableBody(true, true);

    score += 10;
    scoreText.setText('Score: ' + score);
}

Deste modo, cada vez que coletamos uma estrela, o nosso contador, que é a variável score, é incrementada em 10, ou seja, a cada estrela coletada, +10 pontos para você 🙂

Você pode verificar isso no seu arquivo part9.html!

Continue lendo “Phaser.js #7 – Criando jogo Final!”

Phaser.js #6 – Criando jogo Parte 5

E aí pessoal, dando sequência ao nosso tutorial de Phaser.js, iremos dar um propósito ao nosso jogo agora.

Já temos as plataformas, já temos a gravidade implantada, e já temos o nosso player com seus comandos especificados (andar para as laterais e pular para as plataformas). Agora, iremos inserir algumas estrelas no nosso cenário, para que o nosso player as colete. Para isso, criaremos um grupo chamado stars e o preencheremos dentro de nossa função create():

stars = this.physics.add.group({
    key: 'star',
    repeat: 11,
    setXY: { x: 12, y: 0, stepX: 70 }
});

stars.children.iterate(function (child) {

    child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8));

});

Este é um processo muito parecido como quando criamos nosso grupo de plataformas, mas como as estrelas precisam se mover e saltar, elas são um grupo dinâmico de física.

Ok, mas o que faremos agora?

Bom, primeiramente precisamos definir a chave de textura (key) para a nossa imagem. Mas o que isso significa?

Significa que todos os objetos filhos criados como resultado do objeto de configuração estrela receberão as texturas de estrela por padrão. 

Ok, já criamos uma estrela, e agora? Agora precisamos definir o repeat. O repeat é o valor de repetição das estrelas. Precisaremos de 12 estrelas saltitando pelo nosso mapa, então precisamos repeti-la 11 vezes.

Feito isso, usaremos o setXY para definir a posição das 12 estrelas criadas pelo nosso grupo star. cada estrela (child) será colocada começando em x:12 e y:0, e com um passo x de 70.

Mas o que isso significa?

Isso significa que a primeira estrela será posicionada em 12 x 0, a segunda terá 70 pixels em 82 x 0, o terceiro em 172 x 0, aumentando 70 pixels proporcionalmente para cada uma das 12 estrelas. Com o valor de 70 pixels, as estrelas vão se espalhar perfeitamente espaçadas na nossa viewport.

Mas e o que a última parte deste código faz?

Bem, esta parte do código itera entre todas as estrelas criadas pelo grupo e fornece a elas um valor aleatório entre 0,4 e 0,8 no eixo Y. O intervalo de rejeição é entre 0 e 1, sendo 0 sem rejeição e 1, rejeição completa. Como as estrelas iniciam sem gravidade, elas são puxadas para o solo, até que ocorra colisão com alguma plataforma.

Continue lendo “Phaser.js #6 – Criando jogo Parte 5”

Conheça o Phaser.JS, uma biblioteca de criação de jogos JavaScript

Você conhece a biblioteca Phaser.JS para criação de jogos em JavaScript?

Phaser é uma biblioteca para criação de jogos que foi idealizada pela Photon Storm. Com ela, podemos criar jogos que rodam tanto em ambiente mobile quanto desktops. Ela roda nos principais navegadores atuais de desktop. Em dispositivos móveis, é suportado no Chrome mobile, no navegador Safari (no IOS5 para cima). Acesse a biblioteca: phaser.io

Principais recursos:

O Phaser não utiliza nenhuma prática interna de orientação a objeto, não possui heranças de código e componentes. Mas você pode alterar as funções do Phaser.

Para renderização de gráficos, o Phaser utiliza o Pixi.js. Nos jogos, se o navegador suportar WebGL, o usuário terá uma melhor experiência. Os jogos em HTML5 caminham para o futuro de WebGL disponível em dispositivos móveis. Para isso acontecer, é apenas uma questão de tempo.

O carregamento do Phaser suporta:

  1. Imagens;

  2. Sprite Sheets;

  3. Texture Atlases;

  4. Arquivos de áudio;

  5. Arquivos de dados;

  6. Arquivos JavaScript;

  7. Tilemaps;

  8. Fontes Bitmap.

Áudio:

Com a WebAudio API, podemos ter uma integração apropriada de áudio, com múltiplas rotas, canais e os mais variados tipos de efeitos.

Inputs:

Multi-Touch, Keyboard, Pointer e Mouse: O Phaser suporta os mais variados tipos de inputs, como toques na tela, cliques de mouse, teclas pressionadas, entre outros.

Continue lendo “Conheça o Phaser.JS, uma biblioteca de criação de jogos JavaScript”

Amazon GameLift, o seu servidor JavaScript na AWS

Conheça o Amazon GameLift, o servidor ideal para seus jogos.

A Amazon tornou disponíveis servidores em tempo real do GameLift. Você pode, com poucas linhas de JavaScript, criar um servidor de jogos e escalar para seus jogos por uma fração de centavo por jogador por mês.

Sendo uma hospedagem de servidores de jogos dedicados na nuvem, o GameLift consegue maximizar a economia, diminuindo também a latência e o tempo de espera. Veja:

Latência mediana global: 45 ms;

Economia de custo em comparação com soluções co-location: até 70% de economia;

Para iniciar um jogo (incluindo grupos de jogadores em sessões de jogos rapidamente: 100 ms.

Com o GameLift, você poderá focar no seu jogo, dispondo de total atenção para o desenvolvimento, e dispondo menos do seu tempo a se dedicar ao servidor.


Acelerando o desenvolvimento dos jogos:

Com o GameLift da Amazon, é possível enviar o jogo com maior agilidade e oferecendo aos jogadores a experiência que eles esperam. Como a Amazon faz isso?

Escala automaticamente a capacidade do servidor de acordo com o tráfego dos players: Você deixa de pagar por capacidade que não está sendo utilizada quando o tráfego de jogadores está baixo;


Matchmaking flexível:

Você pode usar a solução personalizada da Amazon, chamada FlexMatch para criar jogos competitivos, ou pode usar seu próprio serviço de matchmaking.


Sem inatividade para atualizações:

Precisa lançar atualizações ou novos conteúdos? Com o GameLift ps servidores ficarão ativos 24 horas por dia, 7 dias por semana.


Suporte ao jogo em plataformas cruzadas:

A comunidade fica conectada a todos os tipos de dispositivos, ampliando o grupo de jogadores.


 

Funciona com mecanismos e fluxos de trabalho existentes:

Você pode colocar os servidores em operação na nuvem, integrando facilmente o SDK do Amazon GameLift com mecanismos AAA como Unity, Unreal, Lumberyard ou soluções internas desenvolvidas em C++.


Como usar o Amazon GameLift?

Você pode colocar seus servidores dedicados de jogos em operação com três etapas fáceis:

Cadastre-se em uma conta da AWS (leva menos de cinco minutos e a inscrição será totalmente gratuita no nível gratuito da AWS).

Faça o upload da compilação: Comece a usar o Amazon GameLift.

Implante os servidores de jogo: aprenda a implantar um servidor de jogo com a amostra de servidor da Amazon e veja os dados de jogadores e sessões criadas no painel ao vivo da Amazon GameLift.

Confira mais detalhes no link: https://aws.amazon.com/pt/gamelift/


Definições de preço:

Utilizando o Amazon GameLift, você paga apenas pela capacidade utilizada, não sendo necessário desenbolsar por espaço ocioso. O GameLift cobra as instâncias por tempo de uso e quantidade de dados transferidos.

Veja um exemplo de definição de preço em: https://aws.amazon.com/pt/gamelift/pricing/#gamelift-example


Reduza os gastos usando Auto Scaling:

O Autoscaling do Amazon GameLift pode desativar automaticamente servidores de jogos quando não forem necessários e adicionar novos quando surgirem mais jogadores. O GameLift disponibiliza instâncias spot e sob demanda. Com instâncias spot, você pode ter uma economia de até 90%, mas obtendo o mesmo hardware de alta performance que com as sob demanda. Com o recurso FleetIQ, novas sessões serão colocadas em servidores de jogos com base nas latências do jogador, nos preços das instâncias e nas taxas de interrupção do spot. Saiba mais em: https://aws.amazon.com/pt/blogs/gametech/reduce-cost-by-up-to-90-with-amazon-gamelift-fleetiq-and-spot-instances/


Instâncias do Amazon GameLift:

Com as instâncias spot do Amazon GameLift, você paga o preço spot vigente no início de cada hora da instância em execução. Os preços são definidos pela AWS e são ajustados de acordo com tendências de oferta e demanda de instâncias spot no longo prazo, nunca excedendo os preços sob demanda. Se o preço spot for alterado após a execução da instância, o novo preço será cobrado para uso da instância na hora subsequente.

Veja as tabelas dos valores por hora das instâncias spot e sob demanda separadas por tipo de instância e região em: https://aws.amazon.com/pt/gamelift/pricing/


EXPLORANDO O JAVASCRIPT NA AWS:

AWS SDK para JavaScript no Node.js:

Comece a usar rapidamente a AWS com o AWS SDK para JavaScript em Node.js. Para mais informações: https://aws.amazon.com/pt/sdk-for-node-js/

AWS SDK para JavaScript no navegador:

O AWS SDK para JavaScript permite que você acesse diretamente os serviços da AWS por meio do código JavaScript em execução no navegador. Mais informações em:

https://aws.amazon.com/pt/sdk-for-browser/


Biblioteca do AWS Amplify para web e React Native:

Escolha seus serviços de nuvem e conecte-os facilmente ao seu aplicativo com poucas linhas de código. Acesse:https://aws-amplify.github.io/


Implante Node.js no Elastic Beanstalk:

Com o Elastic Beanstalk, é fácil implantar, escalar e gerenciar seus aplicativos web Node.js usando a AWS, estando disponível para qualquer pessoa que esteja hospedando ou desenvolvendo um aplicativo web usando Node.js. Acesse:

https://docs.aws.amazon.com/pt_br/elasticbeanstalk/latest/dg/create_deploy_nodejs.html


Implante Node.js no AWS OpsWorks:

o AWS OpsWorks permite:

Criação de servidores Puppet Master gerenciados pelo AWS para Puppet Enterprise;

Criação de servidores gerenciados pelo Chef na AWS, incluindo recursos especiais do Chef Automate e usando o Chef DK e outras ferramentas de gerenciamento do Chef.

AWS OpsWorks Stacks: implantar e monitorar aplicativos nas suas pilhas. O Stacks não requer nem cria servidores do Chef, mas executa algumas das tarefas de um servidor do Chef por você.

Acesse mais informações em:

https://docs.aws.amazon.com/pt_br/opsworks/latest/userguide/welcome.html

Projeto Diablo.JS

Após navegar no mundo do código fonte para jogos, encontrei um projeto já abandonado a alguns anos chamado Diablo.js. Ele é um projeto simples que utiliza 1 arquivo HTML, 1 arquivo JS (não que isso seja bom) e diversasimagens para simular o jogo Diablo 2.

Como esse jogo consumiu muitas horas da minha vida, pensei em usa-lo como uma ferramenta para praticar programação e reviver o passado.Então fiz um fork dele e comecei a trabalhar em diversas alterações.

O objetivo do projeto é o seguinte:

  • “Atualizar” o código JavaScript para ES6.
  • Manter o projeto em um formato fácil de executar para outros que estão aprendendo a programar ou queira brincar com o código.
  • Melhorar o posicionamento e quantidade monstros e itens aleatórios.
  • Aumentar o mapa.
  • Permitir que o personagem morra.
  • Corrigir bug de colisão e barreiras físicas (paredes e objetos).
  • Permitir escolher outros personagens.
  • Adicionar ao menos 1 habilidade com o botão direito.

Não sei se tudo será possível, mas um objetivo fica difícil chegar a algum lugar.

Tendo dito isso, aqui vão algumas observações sobre o jogo para que você que está interessado em contribuir possa começar a fazer. Se você quiser fazer alterações, comentar cada classe e função trabalhada é muito importante!

Converter funções em classes

Já fiz isso para praticamente todas as classes, mas algumas ainda precisam ser ajustadas, as propriedades precisam ter nomes mais uteis que x, y e z.

Separar arquivos

Não é opção mais performática, mas para manter o código mais fácil de ler, seria interessante separar as classes e funcionalidades em arquivos diferentes.

Imortal

Esse aqui é o Diablo Imortal, o bárbaro não morre mesmo chegando a zero de vida.

Paredes

Além de ser possível “pisar” nas paredes, é possível atacar e ser atacado através delas

Piso e Limite do Mapa

Consegui adicionar paredes, objetos e piso indo para a diagonal esquerda, mas para o outro lado não tem força que renderize o chão.

Talvez seja possível resolver esse problema com paredes, mas tanto o jogador quanto os inimigos podem sair do limite do mapa. Dependendo, os inimigos começaram de fora do mapa.

Contador de ouro

Não que ele tenha utilidade na versão atual, mas seria legal saber quanto de ouro o personagem já coletou

Codificando um jogo de carrinho

[download id=”467″]

Olá galera do MundoJs

Vamos a mais um tutorial sobre javascript e games !
Desta vez algo um pouco mais avançado.
Se você está começando agora é melhor tentar antes esse tutorial e esse aqui também.

Este artigo mostra uma maneira de fazer um jogo de carrinho em javascript sem nenhuma imagem.

Para começar vamos desenhar o carrinho com HTML5 e Css:

<div style='position:relative; z-index:2; top:254px; left:254px; width:20px; height:40px; background-color:#000000; border-radius:4px;'>
  <div style='width:10px; height:4px; background-color:#000000; border:5px solid #ff4444; border-top:none; border-bottom:none;'></div>
  <div style='width:14px; height:20px; background-color:#999999; border:3px solid #000000;'></div>
</div>

Este carrinho ficará no meio da tela.
A pista irá se mover sob o carrinho.

Vamos desenhar a pista num <canvas>

<canvas id=canvas width=460 height=460 style='background-color:#ffffff; border:4px solid #497894; border-radius:10px;'></canvas>
<script>
  ctx = document.getElementById("canvas").getContext("2d");
  ctx.fillStyle = "#385383";
  ctx.fillRect(200, 200, 200, 200);
</script>

Vamos fazer esta pista se mover sob o carrinho com a função setTimeout atualizando a posição:

<div style='position:relative; z-index:2; top:254px; left:254px; width:20px; height:40px; background-color:#000000; border-radius:4px;'>
  <div style='width:10px; height:4px; background-color:#000000; border:5px solid #ff4444; border-top:none; border-bottom:none;'></div>
  <div style='width:14px; height:20px; background-color:#999999; border:3px solid #000000;'></div>
</div>
<canvas id=canvas width=460 height=460 style='background-color:#ffffff; border:4px solid #497894; border-radius:10px;'></canvas>
<script>
  ctx = document.getElementById("canvas").getContext("2d");
  x = 10;
  function move() {
    ctx.fillStyle = "#9aba9a";
    ctx.fillRect(0, 0, 460, 460);
    ctx.fillStyle = "#385383";
    x = x + 10;
    if (x > 300) x = -400;
    ctx.fillRect(200, 200 + x, 200, 200);
    setTimeout('move()', 100);
  }
  move();
</script>

O próximo passo é centralizar tudo usando a tag <center>

Depois disto interceptar o pressionamento das teclas com o atributo onKeydown

<!doctype html>
<html>
<title>Jogo de Corrida</title>

<body onKeyDown="vira(event.keyCode);" bgcolor=#e1e1e1>
  <br>
  <center>
    <div id=carro>
      <div style='position:relative; z-index:2; top:254px; width:20px; height:40px; background-color:#000000; border-radius:4px;'>
        <div style='width:10px; height:4px; background-color:#000000; border:5px solid #ff4444; border-top:none; border-bottom:none;'></div>
        <div style='width:14px; height:20px; background-color:#999999; border:3px solid #000000;'></div>
      </div>
    </div>
    <canvas id=canvas width=460 height=460 style='background-color:#ffffff; border:4px solid #497894; border-radius:10px;'></canvas>
  </center>
</body>
<script>
  ctx = document.getElementById("canvas").getContext("2d");
  x = 10;
  function move() {
    ctx.fillStyle = "#9aba9a";
    ctx.fillRect(0, 0, 460, 460);
    ctx.fillStyle = "#385383";
    x = x + 10;
    if (x > 300) x = -400;
    ctx.fillRect(200, 200 + x, 200, 200);
    setTimeout('move()', 100);
  }
  move();
  function vira(k) {
    alert(k);
  }
</script>
</html>

A função ‘vira()’ é executada quando o usuário pressiona qualquer tecla.
Com isso podemos descobrir o código das teclas direcionais.

O próximo passo é fazer o carro rotacionar com o comando <body onKeyDown>

<!doctype html>
<html>
<title>Jogo de Corrida</title>

<body onKeyDown="vira(event.keyCode);" bgcolor=#e1e1e1>
  <br>
  <center>
    <div id=carro>
      <div style='position:relative; z-index:2; top:254px; width:20px; height:40px; background-color:#000000; border-radius:4px; transform:rotate(180deg);'>
        <div style='width:10px; height:4px; background-color:#000000; border:5px solid #ff4444; border-top:none; border-bottom:none;'></div>
        <div style='width:14px; height:20px; background-color:#999999; border:3px solid #000000;'></div>
      </div>
    </div>
    <canvas id=canvas width=460 height=460 style='background-color:#ffffff; border:4px solid #497894; border-radius:10px;'></canvas>
  </center>
</body>
<script>
  ctx = document.getElementById("canvas").getContext("2d");
  x = 10;
  function move() {
    ctx.fillStyle = "#9aba9a";
    ctx.fillRect(0, 0, 460, 460);
    ctx.fillStyle = "#385383";
    x = x + 10;
    if (x > 300) x = -400;
    ctx.fillRect(200, 200 + x, 200, 200);
    setTimeout('move()', 100);
  }
  move();
  angulo = 18;
  function vira(k) {
    //alert(k);
    if (k == 37) angulo = (angulo + 35) % 36;
    if (k == 39) angulo = (angulo + 1) % 36;
    document.getElementById('carro').innerHTML = "<div style='position:relative; z-index:2; top:254px; width:20px; height:40px; background-color:#000000; border-radius:4px; transform:rotate(" + angulo + "0deg);'><div style='width:10px; height:4px; background-color:#000000; border:5px solid #ff4444; border-top:none; border-bottom:none;'></div><div style='width:14px; height:20px; background-color:#999999; border:3px solid #000000;'></div></div>";
  }
</script>
</html>

Repare que o angulo começa em 18.
O carro rotacionado 180 graus.
Apontado pra baixo.

’37’ e ’39’ são os códigos das teclas ‘esq’ e ‘dir’ do teclado.

Quando ‘esq’ é apertado o valor do angulo aumenta 35.
Como é sempre o resto da divisão por 36… nunca fica maior que 35.
Praticamente (angulo+35)%36 significa angulo-1.

Esse é o movimento básico do carrinho!

Mas tem uns detalhes a mais.

Enquanto o usuário manter a tecla pressionada o carro tem que ficar girando.
Isso complica um pouco porque o sistema operacional dá uma margem… um intervalo… um delay… quando user pressiona.

Para contornar essa situação vamos usar ‘onkeydown’ e ‘onkeyup’.
Mais duas variáveis ‘esq’ e ‘dir’.

E vamos colocar a atualização da variável angulo, e o desenho do carro, dentro da função ‘move()’

<!doctype html>
<html>
<title>Jogo de Corrida</title>

<body onKeyDown="vira(event.keyCode);" onKeyUp="para(event.keyCode);" bgcolor=#e1e1e1>
  <br>
  <center>
    <div id=carro></div>
    <canvas id=canvas width=460 height=460 style='background-color:#ffffff; border:4px solid #497894; border-radius:10px;'></canvas>
  </center>
</body>
<script>
  ctx = document.getElementById("canvas").getContext("2d");
  x = 10;
  angulo = 18;
  esq = false;
  dir = false;
  function vira(k) {
    if (k == 37) esq = true;
    if (k == 39) dir = true;
  }
  function para(k) {
    esq = false;
    dir = false;
  }
  function move() {
    if (esq) angulo = (angulo + 35) % 36;
    if (dir) angulo = (angulo + 1) % 36;
    document.getElementById('carro').innerHTML = "<div style='position:relative; z-index:2; top:254px; width:20px; height:40px; background-color:#000000; border-radius:4px; transform:rotate(" + angulo + "0deg);'><div style='width:10px; height:4px; background-color:#000000; border:5px solid #ff4444; border-top:none; border-bottom:none;'></div><div style='width:14px; height:20px; background-color:#999999; border:3px solid #000000;'></div></div>";
    ctx.fillStyle = "#9aba9a";
    ctx.fillRect(0, 0, 460, 460);
    ctx.fillStyle = "#385383";
    x = x - 10;
    if (x < -400) x = 460;
    ctx.fillRect(200, 200 + x, 200, 200);
    setTimeout('move()', 50);
  }
  move();
</script>
</html>

Agora começa a parte complicada!
Fazer o carro se mover em relação à pista.

Para isso vamos usar umas variáveis ‘fatorx’ e ‘fatory’.
Elas vão definir o deslocamento do carro em relação a pista… no eixo x e no eixo y.

Por exemplo… quando o carro estiver inclinado 30 graus ele irá se deslocar 6 pixels no eixo x e -3 pixels no eixo y…

fatorx=[9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8];
fatory=[0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,8,7,6,5,4,3,2,1];
<!doctype html>
<html>
<title>Jogo de Corrida</title>

<body onKeyDown="vira(event.keyCode);" onKeyUp="para(event.keyCode);" bgcolor=#e1e1e1>
  <br>
  <center>
    <div id=carro></div>
    <canvas id=canvas width=460 height=460 style='background-color:#ffffff; border:4px solid #497894; border-radius:10px;'></canvas>
  </center>
</body>
<script>
  ctx = document.getElementById("canvas").getContext("2d");
  fatorx = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8];
  fatory = [0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2, 1];
  px = 10;
  py = 10;
  angulo = 18;
  esq = false;
  dir = false;
  function vira(k) {
    if (k == 37) esq = true;
    if (k == 39) dir = true;
  }
  function para(k) {
    esq = false;
    dir = false;
  }
  function move() {
    if (esq) angulo = (angulo + 35) % 36;
    if (dir) angulo = (angulo + 1) % 36;
    document.getElementById('carro').innerHTML = "<div style='position:relative; z-index:2; top:254px; width:20px; height:40px; background-color:#000000; border-radius:4px; transform:rotate(" + angulo + "0deg);'><div style='width:10px; height:4px; background-color:#000000; border:5px solid #ff4444; border-top:none; border-bottom:none;'></div><div style='width:14px; height:20px; background-color:#999999; border:3px solid #000000;'></div></div>";
    ctx.fillStyle = "#9aba9a";
    ctx.fillRect(0, 0, 460, 460);

    px += fatorx[angulo] * 3.7;
    py += fatory[angulo] * 3.7;

    ctx.fillStyle = "#385383";
    ctx.fillRect((200) + py + 38, (200) + px + 28, 200, 200);

    setTimeout('move()', 75);
  }
  move();
</script>
</html>

Agora já temos um carro completamente dirigível !!!
Uhu!!!

Agora vamos à pista.

Vamos criar um ‘array de arrays’ com zeros representando grama e um representando asfalto.

<!doctype html>
<html>
<title>Jogo de Corrida</title>

<body onKeyDown="vira(event.keyCode);" onKeyUp="para(event.keyCode);" bgcolor=#e1e1e1>
  <br>
  <center>
    <div id=carro></div>
    <canvas id=canvas width=460 height=460 style='background-color:#ffffff; border:4px solid #497894; border-radius:10px;'></canvas>
  </center>
</body>
<script>
  ctx = document.getElementById("canvas").getContext("2d");
  fatorx = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8];
  fatory = [0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2, 1];
  pista = [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1],
    [1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1],
    [1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1],
    [1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1],
    [1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1],
  ];
  px = 10;
  py = 10;
  angulo = 18;
  esq = false;
  dir = false;
  function vira(k) {
    if (k == 37) esq = true;
    if (k == 39) dir = true;
  }
  function para(k) {
    esq = false;
    dir = false;
  }
  function move() {
    if (esq) angulo = (angulo + 35) % 36;
    if (dir) angulo = (angulo + 1) % 36;
    document.getElementById('carro').innerHTML = "<div style='position:relative; z-index:2; top:254px; width:20px; height:40px; background-color:#000000; border-radius:4px; transform:rotate(" + angulo + "0deg);'><div style='width:10px; height:4px; background-color:#000000; border:5px solid #ff4444; border-top:none; border-bottom:none;'></div><div style='width:14px; height:20px; background-color:#999999; border:3px solid #000000;'></div></div>";
    ctx.fillStyle = "#9aba9a";
    ctx.fillRect(0, 0, 460, 460);
    px += fatorx[angulo] * 3.7;
    py += fatory[angulo] * 3.7;
    ctx.fillStyle = "#385383";
    for (x = 0; x < pista.length; x++)for (y = 0; y < pista[x].length; y++)if (pista[x][y] != 0) ctx.fillRect((200 * y) + py + 38, (200 * x) + px + 28, 200, 200);
    setTimeout('move()', 75);
  }
  move();
</script>
</html>

A linha 48 desenha a pista inteira com os ‘for’ e os parâmetros em ‘fillrect’…

Um próximo passo pode ser fazer o carro diminuir a velocidade quando está sobre a grama!

Fazer outras fases…

Fazer a velocidade ir aumentado.

Usar imagens para o carro. Imagens para a pista. pngs transparentes.

‘O céu é o limite.’

Dá para melhorar bem.

Essa é só uma simples versão de 53 linhas