Criando jogo do T-REX (jogo do google) com JavaScript
O jogo do T-Rex, famoso por aparecer no Google Chrome quando a conexão com a internet cai, foi criado pela Google como um easter egg simples, mas se tornou um dos mini-jogos mais jogados do mundo. No jogo, o objetivo é controlar um dinossauro que corre automaticamente pelo cenário e deve evitar obstáculos como cactos e pássaros pulando no momento certo. Sua recriação em JavaScript usando a biblioteca p5.js é uma excelente forma de aprender sobre desenvolvimento de jogos, lógica de programação e animações interativas!
Passo 1: Configurando o ambiente
Antes de começarmos a programar, certifique-se de ter um ambiente de desenvolvimento configurado:
Um editor de código (VS Code)
- Um navegador (Google Chrome)
A biblioteca p5.js
Passo 2: Declaração de variáveis
Aqui, estamos declarando as variáveis que serão usadas para armazenar os sprites e suas imagens.
// Estado do jogo
var gameState;
// T-Rex e animações
var trex, trex_running, trex_collided;
// Chão e solo invisível
var ground, invisibleGround, groundImage;
// Grupo de nuvens
var cloudsGroup, cloudImage;
// Grupo de obstáculos
var obstaclesGroup, obstacle1, obstacle2, obstacle3, obstacle4, obstacle5, obstacle6;
// Pontuação
var score = 0;
// Imagens de Game Over e reinício
var gameOverImg, restartImg;
// Sons do jogo
var jumpSound, checkPointSound, dieSound;
Passo 3: Função preload()
A função preload() é usada para carregar imagens e animações antes do jogo iniciar.
loadImage("ground2.png")
: Carrega a imagem do chão que se move horizontalmenteloadAnimation("trex1.png", "trex3.png", "trex4.png")
: Cria a animação de corrida do T-Rex com 3 quadrosloadImage("cloud.png")
: Carrega a imagem das nuvens do cenárioloadImage("obstacle1.png")
aloadImage("obstacle6.png")
: Carrega as imagens dos 6 tipos de obstáculosloadImage("gameOver.png")
: Carrega a imagem de Game OverloadImage("restart.png")
: Carrega a imagem do botão de reinícioloadSound("jump.mp3")
: Carrega o efeito sonoro do puloloadSound("die.mp3")
: Carrega o som de colisão/fim de jogoloadSound("checkPoint.mp3")
: Carrega o som de ponto de verificação
function preload() {
// Carrega animações do T-Rex
trex_running = loadAnimation("trex1.png", "trex3.png", "trex4.png");
trex_collided = loadAnimation("trex_collided.png");
// Carrega imagens do chão e nuvem
groundImage = loadImage("ground2.png");
cloudImage = loadImage("cloud.png");
// Carrega imagens dos obstáculos
obstacle1 = loadImage("obstacle1.png");
obstacle2 = loadImage("obstacle2.png");
obstacle3 = loadImage("obstacle3.png");
obstacle4 = loadImage("obstacle4.png");
obstacle5 = loadImage("obstacle5.png");
obstacle6 = loadImage("obstacle6.png");
// Carrega imagens de Game Over e reinício
restartImg = loadImage("restart.png");
gameOverImg = loadImage("gameOver.png");
// Carrega sons do jogo
jumpSound = loadSound("jump.mp3");
dieSound = loadSound("die.mp3");
checkPointSound = loadSound("checkPoint.mp3");
}
Passo 4: Função setup()
A função setup()
é executada apenas uma vez e configura o ambiente do jogo.
createCanvas(600,200)
: Cria um canvas de 400×600 pixels.
function setup(){
createCanvas(400,600);
Agora criamos o sprite do T-Rex (Personagem Principal):
// Criando o sprite do T-Rex
trex = createSprite(50, 160, 20, 50);
trex.addAnimation("running", trex_running);
trex.addAnimation("collided", trex_collided);
trex.scale = 0.5;
createSprite(50,160,20,50)
: Cria o sprite do T-Rex na posição inicial (50,160)addAnimation()
: Adiciona as animações de corrida e colisãoscale = 0.5
: Ajusta o tamanho do sprite
Agora criamos o Chão e Limites:
// Criando o chão
ground = createSprite(200, 180, 400, 20);
ground.addImage("ground", groundImage);
ground.x = ground.width / 2;
ground.velocityX = -4;
// Criando chão invisível para colisão
invisibleGround = createSprite(200, 190, 400, 10);
invisibleGround.visible = false;
createSprite(200,180,400,20)
: Cria o sprite do chão visíveladdImage()
: Adiciona a textura do chãovelocityX = -4
: Faz o chão se mover para esquerdainvisibleGround
: Sprite invisível para detectar colisões com o chão
Criamos também os elementos de UI:
// Configurando tela de Game Over
gameOver = createSprite(300, 100);
gameOver.addImage("gameOverImg", gameOverImg);
gameOver.scale = 0.5;
gameOver.visible = false;
// Configurando botão de Restart
restart = createSprite(300, 140);
restart.addImage("restartImg", restartImg);
restart.scale = 0.5;
restart.visible = false;
gameOver
: Sprite da tela de fim de jogorestart
: Sprite do botão de reiníciovisible = false
: Inicialmente invisíveis
E criamos também os grupos dos sprites:
// Inicializando grupos
cloudsGroup = new Group();
obstaclesGroup = new Group();
}
cloudsGroup
: Grupo para armazenar todas as nuvensobstaclesGroup
: Grupo para armazenar todos os obstáculos
Passo 5: Função draw()
A função draw() é chamada continuamente para atualizar a tela do jogo e manter a animação fluida.
background("white")
: Define o fundo como preto.
function draw() {
// Define o fundo branco
background("white");
Agora, precisamos garantir que o ao iniciar o jogo, quando o estado de jogo for “PLAY”, a função PLAY() seja chamada:
if(gameState === "PLAY"){
PLAY();
}
gameState === "PLAY"
: Executa a lógica principal do jogo
Quando o jogador perder, o estado de jogo será “END”, a função END() será chamada:
if(gameState === "END"){
END();
}
gameState === "END"
: Executa a rotina de fim de jogo
Desenhamos todos os sprites na tela:
drawSprites();
drawSprites()
: Exibe todos os sprites criados na tela do jogo, garantindo que a animação seja atualizada a cada quadro.
Vamos criar a detecção de colisão com o chão invisível:
// Detecção de colisão com o chão invisível
trex.collide(invisibleGround);
trex.collide(invisibleGround)
: Impede o T-Rex de cair infinitamente
Por fim, exibimos a pontuação na tela:
// Exibe a pontuação atual
textSize(20);
fill(0); // Texto preto
text("Pontuação: " + score, 500, 30);
}
textSize(20)
: Define o tamanho da fonte para 20pxfill(0)
: Define a cor do texto como pretotext()
: Exibe a pontuação no canto superior direito
Passo 6: Criando a função para gerar obstáculos
A função spawnObstacles()
é chamada continuamente para criar os obstáculos na tela do jogo.
frameCount % 60 == 0
: Cria um novo obstáculo a cada 60 quadrosrandom(1, 6)
: Seleciona aleatoriamente entre 6 tipos diferentes de obstáculoscreateSprite(600, 165, 10, 40)
: Cria o obstáculo na posição direita da telaaddImage()
: Aplica a imagem correspondente ao tipo de obstáculovelocityX = -(6 + score/100)
: Define velocidade base + aumento progressivo conforme pontuaçãoscale = 0.5
: Ajusta o tamanho do obstáculolifetime = 300
: Remove obstáculos que saíram da tela para otimizaçãoobstaclesGroup.add(obstacle)
: Adiciona ao grupo de gerenciamento
function spawnObstacles() {
if (frameCount % 60 === 0) {
var obstacle = createSprite(600, 165, 10, 40);
obstacle.velocityX = -(6 + score / 100);
// Seleciona aleatoriamente entre 6 tipos de obstáculos
var rand = Math.round(random(1, 6));
switch(rand) {
case 1: obstacle.addImage(obstacle1); break;
case 2: obstacle.addImage(obstacle2); break;
case 3: obstacle.addImage(obstacle3); break;
case 4: obstacle.addImage(obstacle4); break;
case 5: obstacle.addImage(obstacle5); break;
case 6: obstacle.addImage(obstacle6); break;
default: break;
}
obstacle.scale = 0.5;
obstacle.lifetime = 300;
obstaclesGroup.add(obstacle);
}
}
Passo 7: Criando a função para gerar nuvens
A função spawnClouds()
é responsável por criar as nuvens que decoram o cenário do jogo.
frameCount % 60 == 0
: Gera uma nova nuvem a cada 60 quadros (1 segundo)random(80, 120)
: Posiciona as nuvens em alturas aleatóriascreateSprite(600, random(80, 120), 40, 10)
: Cria a nuvem no lado direito da telaaddImage(cloudImage)
: Aplica a imagem da nuvemvelocityX = -3
: Move a nuvem da direita para a esquerdascale = 0.5
: Define o tamanho da nuvemlifetime = 200
: Remove nuvens que saíram da telacloud.depth = trex.depth
: Ajuste de profundidade visualcloudsGroup.add(cloud)
: Adiciona ao grupo de gerenciamento
function spawnClouds() {
if (frameCount % 60 === 0) {
var cloud = createSprite(600, Math.round(random(80, 120)), 40, 10);
cloud.addImage(cloudImage);
cloud.velocityX = -3;
cloud.scale = 0.5;
cloud.lifetime = 200;
// Ajuste de profundidade para ficar atrás do T-Rex
cloud.depth = trex.depth;
trex.depth = trex.depth + 1;
cloudsGroup.add(cloud);
}
}
Passo 8: Função PLAY()
A função PLAY()
contém toda a lógica do jogo quando o estado está ativo.
function PLAY() {
// Esconde elementos de Game Over
gameOver.visible = false;
restart.visible = false;
gameOver.visible = false
: Garante que a imagem de Game Over não seja exibida durante o jogorestart.visible = false
: Mantém o botão de reinício oculto enquanto jogo está ativo
Agora, precisamos garantir o movimento do Cenário:
// Movimentação do chão (efeito infinito)
ground.velocityX = -(4 + 3 * score / 100);
// Reposiciona o chão quando sai da tela
if (ground.x < 0) {
ground.x = ground.width / 2;
}
ground.velocityX
: Velocidade aumenta progressivamente com a pontuaçãoif(ground.x < 0)
: Lógica para criar efeito de chão infinito
Adicionando os controles do jogador:
// Pulo do T-Rex (barra de espaço)
if (keyDown("space") && trex.y >= 100) {
trex.velocityY = -12;
jumpSound.play();
}
// Aplica gravidade
trex.velocityY = trex.velocityY + 0.8;
keyDown("space")
: Controle principal do jogotrex.velocityY
: Física de pulo e gravidade
Chamando as funções que criam os obstáculos e as nuvens:
// Chama funções de criação de elementos
spawnClouds();
spawnObstacles();
spawnClouds()
: Gera as nuvens decorativasspawnObstacles()
: Cria os obstáculos principais
Detectando a colisão com os obstáculos:
// Verifica colisão com obstáculos
if (obstaclesGroup.isTouching(trex)) {
gameState = "END";
dieSound.play();
}
if(obstaclesGroup.isTouching(trex))
: Verifica se o personagem tocou algum cacto (obstaclesGroup
).gameState = "END"
: Encerra o jogo, mudando seu estado para “END”.
Por fim verificamos o sistema de pontuação:
// Atualiza pontuação
score = score + Math.round(getFrameRate() / 60);
// Toca som ao atingir marcos (ex: a cada 100 pontos)
if (score % 100 == 0) {
checkPointSound.play();
}
}
Math.round()
: Arredonda para número inteiro mais próximogetFrameRate()
: Obtém taxa de quadros atual (ex: 60 FPS)score % 100 == 0
: Verifica se a pontuação é múltiplo exato de 100.play()
: Executa reprodução do áudio
Passo 9: Função END()
A função END()
contém toda a lógica do jogo quando está no fim.
function END() {
// Exibe elementos de Game Over
gameOver.visible = true;
restart.visible = true;
gameOver.visible = true
: Garante que a imagem de Game Over seja exibida no fim do jogorestart.visible = true
: Mantém o botão de reinício visível enquanto jogo está no fim
Agora, precisamos criar a animação e nova posição do jogador quando perder o jogo:
// Muda animação do T-Rex para estado de colisão
trex.changeAnimation("collided", trex_collided);
trex.changeAnimation()
: Altera a animação do T-Rex para a versão de colisão
Também vamos parar os objetos:
// Para o movimento do chão e do T-Rex
ground.velocityX = 0;
trex.velocityY = 0;
ground.velocityX = 0
: Interrompe o movimento horizontal do chãotrex.velocityY = 0
: Cancela qualquer movimento vertical do T-Rex
Agora, precisamos definir o tempo de vida dos objetos da tela:
// Define o tempo de vida dos obstáculos e nuvens para que eles não sejam destruídos
obstaclesGroup.setLifetimeEach(-1);
cloudsGroup.setLifetimeEach(-1);
obstaclesGroup.setLifetimeEach(-1)
: Impede que obstáculos sejam removidos automaticamentecloudsGroup.setLifetimeEach(-1)
: Faz o mesmo para as nuvens
Depois, retiramos a velocidade dos objetos:
// Para o movimento dos obstáculos e nuvens
obstaclesGroup.setVelocityXEach(0);
cloudsGroup.setVelocityXEach(0);
obstaclesGroup.setVelocityXEach(0)
: Para todos os obstáculos em movimentocloudsGroup.setVelocityXEach(0)
: Para todas as nuvens em movimento
Por fim, reiniciamos o jogo quando o botão de restart é pressionado:
// Reinicia o jogo quando o botão de restart é pressionado
if (mousePressedOver(restart)) {
reset();
}
}
mousePressedOver(restart)
: Detecta quando o jogador clica no botão de reiniciarreset()
: Chama a função que reinicializa o jogo
Passo 10: Função reset()
A função reset()
é uma rotina de reinicialização que prepara o jogo para uma nova partida, restaurando todos os elementos ao seu estado original.
gameState = "PLAY"
: Volta o jogo para estado de jogo ativogameOver.visible = false
: Oculta a mensagem de Game Overrestart.visible = false
: Esconde o botão de reinícioobstaclesGroup.destroyEach()
: Limpa todos os obstáculos da telacloudsGroup.destroyEach()
: Remove todas as nuvens do cenáriotrex.changeAnimation()
: Volta para animação de corridatrex.x = 50
: Posição horizontal inicialtrex.y = 160
: Posição vertical inicialscore = 0
: Zera a pontuação do jogadorground.velocityX
: Reinicia velocidade base do chãoground.x
: Reposiciona o chão na posição inicial
function reset() {
gameState = "PLAY";
gameOver.visible = false;
restart.visible = false;
obstaclesGroup.destroyEach();
cloudsGroup.destroyEach();
trex.changeAnimation("running", trex_running);
trex.x = 50;
trex.y = 160;
score = 0;
ground.velocityX = -(4 + 3 * score / 100);
ground.x = ground.width / 2;
}