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: Criando as variáveis
O jogo será composto por um dinossauro (T-Rex), um chão, obstáculos e uma mecânica de pontuação. Vamos começar declarando as variáveis que serão utilizadas.
// 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;
// Imagens de Game Over e reinício
var gameOverImg, restartImg;
// Sons do jogo
var jumpSound, checkPointSound, dieSound;
Passo 3: Pré-carregando imagens e sons
Antes de iniciarmos o jogo, precisamos carregar os recursos gráficos e sonoros.
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: Criando o T-Rex
Agora, vamos configurar o dinossauro que será controlado pelo jogador.
function setup() {
// Cria a tela do jogo
createCanvas(600, 200);
// Cria o sprite do T-Rex
trex = createSprite(50, 160, 20, 50);
// Adiciona animações ao T-Rex
trex.addAnimation("running", trex_running);
trex.addAnimation("collided", trex_collided);
// Define o tamanho do T-Rex
trex.scale = 0.5;
}
Passo 5: Criando a pontuação
Agora, o sistema de pontuação do jogador.
function setup() {
// Cria a tela do jogo
createCanvas(600, 200);
// Cria o sprite do T-Rex
trex = createSprite(50, 160, 20, 50);
// Adiciona animações ao T-Rex
trex.addAnimation("running", trex_running);
trex.addAnimation("collided", trex_collided);
// Define o tamanho do T-Rex
trex.scale = 0.5;
// Pontuação
score = 0;
}
function draw() {
// Define o fundo branco
background("white");
// Atualiza a pontuação
text("Pontuação: " + score, 500, 50);
score = score + Math.round(getFrameRate() / 60);
// Desenha os sprites na tela
drawSprites();
}
Passo 6: Criando o cenário
Agora, vamos adicionar o chão e o fundo do jogo.
function setup() {
// Cria a tela
createCanvas(600, 200);
// Cria o T-Rex
trex = createSprite(50, 160, 20, 50);
trex.addAnimation("running", trex_running);
trex.addAnimation("collided", trex_collided);
trex.scale = 0.5;
// Pontuação
score = 0;
// Cria o chão
ground = createSprite(200, 180, 400, 20);
ground.addImage("ground", groundImage);
ground.x = ground.width / 2; // Centraliza o chão
// Cria o chão invisível para colisão
invisibleGround = createSprite(200, 190, 400, 10);
invisibleGround.visible = false;
}
function draw() {
// Define o fundo branco
background("white");
// Atualiza a pontuação
text("Pontuação: " + score, 500, 50);
score = score + Math.round(getFrameRate() / 60);
// Reinicia a posição do chão quando ele sai da tela
if (ground.x < 0) {
ground.x = ground.width / 2;
}
// Move o chão, aumentando a velocidade conforme a pontuação
ground.velocityX = -(4 + 3 * score / 100);
// Impede que o T-Rex atravesse o chão
trex.collide(invisibleGround);
// Desenha os sprites na tela
drawSprites();
}
Passo 7: Criando o pulo do jogador
O T-Rex precisa pular para evitar os obstáculos. Vamos adicionar a funcionalidade de pulo ao jogo.
function draw() {
// Define o fundo branco
background("white");
// Atualiza a pontuação
text("Pontuação: " + score, 500, 50);
score = score + Math.round(getFrameRate() / 60);
// Reinicia a posição do chão quando sai da tela
if (ground.x < 0) {
ground.x = ground.width / 2;
}
// Move o chão e aumenta a velocidade conforme a pontuação
ground.velocityX = -(4 + 3 * score / 100);
// Impede que o T-Rex atravesse o chão
trex.collide(invisibleGround);
// Pula ao pressionar espaço, se estiver acima de 100 no eixo Y
if (keyDown("space") && trex.y >= 100) {
trex.velocityY = -12;
jumpSound.play();
}
// Adiciona gravidade ao T-Rex
trex.velocityY = trex.velocityY + 0.8;
// Desenha os sprites na tela
drawSprites();
}
Passo 8: Criando os obstáculos
Os obstáculos aparecerão na tela e o jogador precisará evitá-los.
function spawnObstacles() {
// Gera um obstáculo a cada 60 quadros
if (frameCount % 60 === 0) {
var obstacle = createSprite(600, 165, 10, 40);
obstacle.velocityX = -(6 + score / 100); // Aumenta a velocidade conforme a pontuação
// Gera obstáculos aleatórios
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;
}
// Define a escala e o tempo de vida do obstáculo
obstacle.scale = 0.5;
obstacle.lifetime = 300;
// Adiciona o obstáculo ao grupo
obstaclesGroup.add(obstacle);
}
}
function setup() {
// Cria a tela
createCanvas(600, 200);
// Cria o T-Rex
trex = createSprite(50, 160, 20, 50);
trex.addAnimation("running", trex_running);
trex.addAnimation("collided", trex_collided);
trex.scale = 0.5;
// Pontuação
score = 0;
// Cria o chão
ground = createSprite(200, 180, 400, 20);
ground.addImage("ground", groundImage);
ground.x = ground.width / 2; // Centraliza o chão
// Cria o chão invisível para colisão
invisibleGround = createSprite(200, 190, 400, 10);
invisibleGround.visible = false;
// Inicializa os grupos de obstáculos
obstaclesGroup = new Group();
}
function draw() {
// Define o fundo branco
background("white");
// Atualiza a pontuação
text("Pontuação: " + score, 500, 50);
score = score + Math.round(getFrameRate() / 60);
// Reinicia a posição do chão quando sai da tela
if (ground.x < 0) {
ground.x = ground.width / 2;
}
// Move o chão e aumenta a velocidade conforme a pontuação
ground.velocityX = -(4 + 3 * score / 100);
// Impede que o T-Rex atravesse o chão
trex.collide(invisibleGround);
// Pula ao pressionar espaço, se estiver acima de 100 no eixo Y
if (keyDown("space") && trex.y >= 100) {
trex.velocityY = -12;
jumpSound.play();
}
// Adiciona gravidade ao T-Rex
trex.velocityY = trex.velocityY + 0.8;
// Chama a função para gerar obstáculos
spawnObstacles();
// Desenha os sprites na tela
drawSprites();
}
Passo 8: Criando as nuvens
As nuvens servem para dar mais profundidade ao cenário.
function spawnClouds() {
// Gera nuvens a cada 60 quadros
if (frameCount % 60 === 0) {
var cloud = createSprite(600, 120, 40, 10);
cloud.y = Math.round(random(80, 120)); // Define a altura aleatória da nuvem
cloud.addImage(cloudImage);
cloud.scale = 0.5;
cloud.velocityX = -3; // A nuvem se move da direita para a esquerda
// Define o tempo de vida da nuvem
cloud.lifetime = 200;
// Ajusta a profundidade das nuvens em relação ao T-Rex
cloud.depth = trex.depth;
trex.depth = trex.depth + 1;
// Adiciona a nuvem ao grupo de nuvens
cloudsGroup.add(cloud);
}
}
function setup() {
// Cria a tela
createCanvas(600, 200);
// Cria o T-Rex
trex = createSprite(50, 160, 20, 50);
trex.addAnimation("running", trex_running);
trex.addAnimation("collided", trex_collided);
trex.scale = 0.5;
// Pontuação
score = 0;
// Cria o chão
ground = createSprite(200, 180, 400, 20);
ground.addImage("ground", groundImage);
ground.x = ground.width / 2; // Centraliza o chão
// Cria o chão invisível para colisão
invisibleGround = createSprite(200, 190, 400, 10);
invisibleGround.visible = false;
// Inicializa os grupos de nuvens e obstáculos
cloudsGroup = new Group();
obstaclesGroup = new Group();
}
function draw() {
// Define o fundo branco
background("white");
// Atualiza a pontuação
text("Pontuação: " + score, 500, 50);
score = score + Math.round(getFrameRate() / 60);
// Reinicia a posição do chão quando sai da tela
if (ground.x < 0) {
ground.x = ground.width / 2;
}
// Move o chão e aumenta a velocidade conforme a pontuação
ground.velocityX = -(4 + 3 * score / 100);
// Impede que o T-Rex atravesse o chão
trex.collide(invisibleGround);
// Pula ao pressionar espaço, se estiver acima de 100 no eixo Y
if (keyDown("space") && trex.y >= 100) {
trex.velocityY = -12;
jumpSound.play();
}
// Adiciona gravidade ao T-Rex
trex.velocityY = trex.velocityY + 0.8;
// Chama a função para gerar obstáculos
spawnObstacles();
// Chama a função para gerar as nuvens
spawnClouds();
// Desenha os sprites na tela
drawSprites();
}
Passo 9: Criando o sprites para os estados de jogo
É necessário criar os sprites do botão de restart e do Game Over, para que possamos criar os estados de jogo.
function setup() {
// Cria a tela
createCanvas(600, 200);
// Cria o T-Rex
trex = createSprite(50, 160, 20, 50);
trex.addAnimation("running", trex_running);
trex.addAnimation("collided", trex_collided);
trex.scale = 0.5;
// Cria o chão
ground = createSprite(200, 180, 400, 20);
ground.addImage("ground", groundImage);
ground.x = ground.width / 2; // Centraliza o chão
// Cria o chão invisível para colisão
invisibleGround = createSprite(200, 190, 400, 10);
invisibleGround.visible = false;
// Inicializa a pontuação
score = 0;
// Inicializa os grupos de nuvens e obstáculos
cloudsGroup = new Group();
obstaclesGroup = new Group();
// Criação do Game Over e Restart
gameOver = createSprite(300, 100);
gameOver.addImage(gameOverImg);
restart = createSprite(300, 140);
restart.addImage(restartImg);
// Ajuste de escala
gameOver.scale = 0.5;
restart.scale = 0.5;
// Inicialmente, escondemos os botões de Game Over e Restart
gameOver.visible = false;
restart.visible = false;
}
Passo 10: Criando o estado de jogo PLAY
O jogo terá dois estados principais: PLAY e END. No estado PLAY, o T-Rex pode pular e o jogo avança continuamente. Se colidir com um obstáculo, o jogo entra no estado END.
function PLAY() {
// Esconde Game Over e Restart
gameOver.visible = false;
restart.visible = false;
// Aumenta a velocidade do chão conforme a pontuação
ground.velocityX = -(4 + 3 * score / 100);
// Atualiza a pontuação
score = score + Math.round(getFrameRate() / 60);
// Reinicia a posição do chão quando sai da tela
if (ground.x < 0) {
ground.x = ground.width / 2;
}
// Pula ao pressionar espaço, se estiver acima de 100 no eixo Y
if (keyDown("space") && trex.y >= 100) {
trex.velocityY = -12;
jumpSound.play();
}
// Adiciona gravidade ao T-Rex
trex.velocityY = trex.velocityY + 0.8;
// Gera as nuvens
spawnClouds();
// Gera obstáculos
spawnObstacles();
// Verifica colisão com obstáculos
if (obstaclesGroup.isTouching(trex)) {
// Reproduz sons e muda o estado do jogo para END
jumpSound.play();
gameState = "END";
dieSound.play();
}
}
function draw() {
// Define o fundo branco
background("white");
text("Pontuação: " + score, 500, 50);
// Chama a função PLAY se o estado do jogo for "PLAY"
if (gameState == "PLAY") {
PLAY();
}
// Impede que o T-Rex atravesse o chão
trex.collide(invisibleGround);
// Desenha os sprites na tela
drawSprites();
}
Passo 11: Criando o estado de jogo END
Após o T-REX colidir com um obstáculo, o jogo entra no estado END. E o jogador tem a opção de recomeçar o jogo.
function END() {
// Exibe Game Over e Restart
gameOver.visible = true;
restart.visible = true;
// Muda a animação do T-Rex para "collided" quando o jogo termina
trex.changeAnimation("collided", trex_collided);
// Para o movimento do chão e do T-Rex
ground.velocityX = 0;
trex.velocityY = 0;
// Define o tempo de vida dos obstáculos e nuvens para que eles não sejam destruídos
obstaclesGroup.setLifetimeEach(-1);
cloudsGroup.setLifetimeEach(-1);
// Para o movimento dos obstáculos e nuvens
obstaclesGroup.setVelocityXEach(0);
cloudsGroup.setVelocityXEach(0);
}
function draw() {
// Define o fundo branco
background("white");
text("Pontuação: " + score, 500, 50);
// Chama a função PLAY se o estado do jogo for "PLAY"
if (gameState == "PLAY") {
PLAY();
}
// Chama a função END se o estado do jogo for "END"
if (gameState == "END") {
END();
}
// Impede que o T-Rex atravesse o chão
trex.collide(invisibleGround);
// Desenha os sprites na tela
drawSprites();
}
Passo 12: Reset do jogo
O jogo terá dois estados principais: PLAY e END. No estado PLAY, o T-Rex pode pular e o jogo avança continuamente. Se colidir com um obstáculo, o jogo entra no estado END.
function reset() {
// Reseta o estado do jogo para "PLAY"
gameState = PLAY;
gameOver.visible = false;
restart.visible = false;
// Destroi todos os obstáculos e nuvens
obstaclesGroup.destroyEach();
cloudsGroup.destroyEach();
// Restaura a animação do T-Rex para "running"
trex.changeAnimation("running", trex_running);
// Reseta a pontuação
score = 0;
}
function END() {
// Exibe Game Over e Restart
gameOver.visible = true;
restart.visible = true;
// Muda a animação do T-Rex para "collided" quando o jogo termina
trex.changeAnimation("collided", trex_collided);
// Para o movimento do chão e do T-Rex
ground.velocityX = 0;
trex.velocityY = 0;
// Define o tempo de vida dos obstáculos e nuvens para que eles não sejam destruídos
obstaclesGroup.setLifetimeEach(-1);
cloudsGroup.setLifetimeEach(-1);
// Para o movimento dos obstáculos e nuvens
obstaclesGroup.setVelocityXEach(0);
cloudsGroup.setVelocityXEach(0);
// Reinicia o jogo quando o botão de restart é pressionado
if (mousePressedOver(restart)) {
reset();
}
}
RESULTADO
// Estado do jogo
var gameState = "PLAY"; // Variável para controlar o estado do jogo
// 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;
// Imagens de Game Over e reinício
var gameOverImg, restartImg;
// Sons do jogo
var jumpSound, checkPointSound, dieSound;
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");
}
function setup() {
// Cria a tela
createCanvas(600, 200);
// Cria o T-Rex
trex = createSprite(50, 160, 20, 50);
trex.addAnimation("running", trex_running);
trex.addAnimation("collided", trex_collided);
trex.scale = 0.5;
// Cria o chão
ground = createSprite(200, 180, 400, 20);
ground.addImage("ground", groundImage);
ground.x = ground.width / 2; // Centraliza o chão
// Cria o chão invisível para colisão
invisibleGround = createSprite(200, 190, 400, 10);
invisibleGround.visible = false;
// Inicializa os grupos de nuvens e obstáculos
cloudsGroup = new Group();
obstaclesGroup = new Group();
// Inicializa a pontuação
score = 0;
gameOver = createSprite(300,100);
gameOver.addImage("gameOverImg", gameOverImg);
restart = createSprite(300,140);
restart.addImage("restartImg",restartImg);
gameOver.scale = 0.5;
restart.scale = 0.5;
gameOver.visible = false;
restart.visible = false;
}
function draw() {
// Define o fundo branco
background("white");
text("Pontuação: " + score, 500, 50);
// Chama a função PLAY se o estado do jogo for "PLAY"
if (gameState == "PLAY") {
PLAY();
}
// Chama a função END se o estado do jogo for "END"
if (gameState == "END") {
END();
}
// Impede que o T-Rex atravesse o chão
trex.collide(invisibleGround);
// Desenha os sprites na tela
drawSprites();
}
function spawnObstacles() {
// Gera um obstáculo a cada 60 quadros
if (frameCount % 60 === 0) {
var obstacle = createSprite(600, 165, 10, 40);
obstacle.velocityX = -(6 + score / 100); // Aumenta a velocidade conforme a pontuação
// Gera obstáculos aleatórios
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;
}
// Define a escala e o tempo de vida do obstáculo
obstacle.scale = 0.5;
obstacle.lifetime = 300;
// Adiciona o obstáculo ao grupo
obstaclesGroup.add(obstacle);
}
}
function spawnClouds() {
// Gera nuvens a cada 60 quadros
if (frameCount % 60 === 0) {
var cloud = createSprite(600, 120, 40, 10);
cloud.y = Math.round(random(80, 120)); // Define a altura aleatória da nuvem
cloud.addImage(cloudImage);
cloud.scale = 0.5;
cloud.velocityX = -3; // A nuvem se move da direita para a esquerda
// Define o tempo de vida da nuvem
cloud.lifetime = 200;
// Ajusta a profundidade das nuvens em relação ao T-Rex
cloud.depth = trex.depth;
trex.depth = trex.depth + 1;
// Adiciona a nuvem ao grupo de nuvens
cloudsGroup.add(cloud);
}
}
function PLAY() {
// Esconde Game Over e Restart
gameOver.visible = false;
restart.visible = false;
// Aumenta a velocidade do chão conforme a pontuação
ground.velocityX = -(4 + 3 * score / 100);
// Atualiza a pontuação
score = score + Math.round(getFrameRate() / 60);
// Reinicia a posição do chão quando sai da tela
if (ground.x < 0) {
ground.x = ground.width / 2;
}
// Pula ao pressionar espaço, se estiver acima de 100 no eixo Y
if (keyDown("space") && trex.y >= 100) {
trex.velocityY = -12;
jumpSound.play();
}
// Adiciona gravidade ao T-Rex
trex.velocityY = trex.velocityY + 0.8;
// Gera as nuvens
spawnClouds();
// Gera obstáculos
spawnObstacles();
// Verifica colisão com obstáculos
if (obstaclesGroup.isTouching(trex)) {
// Reproduz sons e muda o estado do jogo para END
jumpSound.play();
gameState = "END";
dieSound.play();
}
}
function END() {
// Exibe Game Over e Restart
gameOver.visible = true;
restart.visible = true;
// Muda a animação do T-Rex para "collided" quando o jogo termina
trex.changeAnimation("collided", trex_collided);
// Para o movimento do chão e do T-Rex
ground.velocityX = 0;
trex.velocityY = 0;
// Define o tempo de vida dos obstáculos e nuvens para que eles não sejam destruídos
obstaclesGroup.setLifetimeEach(-1);
cloudsGroup.setLifetimeEach(-1);
// Para o movimento dos obstáculos e nuvens
obstaclesGroup.setVelocityXEach(0);
cloudsGroup.setVelocityXEach(0);
// Reinicia o jogo quando o botão de restart é pressionado
if (mousePressedOver(restart)) {
reset();
}
}
function reset() {
// Reseta o estado do jogo para "PLAY"
gameState = "PLAY";
gameOver.visible = false;
restart.visible = false;
// Destroi todos os obstáculos e nuvens
obstaclesGroup.destroyEach();
cloudsGroup.destroyEach();
// Restaura a animação do T-Rex para "running"
trex.changeAnimation("running", trex_running);
// Reseta a pontuação
score = 0;
}