t-rex

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 horizontalmente

  • loadAnimation("trex1.png", "trex3.png", "trex4.png"): Cria a animação de corrida do T-Rex com 3 quadros

  • loadImage("cloud.png"): Carrega a imagem das nuvens do cenário

  • loadImage("obstacle1.png") a loadImage("obstacle6.png"): Carrega as imagens dos 6 tipos de obstáculos

  • loadImage("gameOver.png"): Carrega a imagem de Game Over

  • loadImage("restart.png"): Carrega a imagem do botão de reinício

  • loadSound("jump.mp3"): Carrega o efeito sonoro do pulo

  • loadSound("die.mp3"): Carrega o som de colisão/fim de jogo

  • loadSound("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ão

  • scale = 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ível

  • addImage(): Adiciona a textura do chão

  • velocityX = -4: Faz o chão se mover para esquerda

  • invisibleGround: 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 jogo

  • restart: Sprite do botão de reinício

  • visible = 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 nuvens

  • obstaclesGroup: 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 20px

  • fill(0): Define a cor do texto como preto

  • text(): 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 quadros

  • random(1, 6): Seleciona aleatoriamente entre 6 tipos diferentes de obstáculos

  • createSprite(600, 165, 10, 40): Cria o obstáculo na posição direita da tela

  • addImage(): Aplica a imagem correspondente ao tipo de obstáculo

  • velocityX = -(6 + score/100): Define velocidade base + aumento progressivo conforme pontuação

  • scale = 0.5: Ajusta o tamanho do obstáculo

  • lifetime = 300: Remove obstáculos que saíram da tela para otimização

  • obstaclesGroup.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órias

  • createSprite(600, random(80, 120), 40, 10): Cria a nuvem no lado direito da tela

  • addImage(cloudImage): Aplica a imagem da nuvem

  • velocityX = -3: Move a nuvem da direita para a esquerda

  • scale = 0.5: Define o tamanho da nuvem

  • lifetime = 200: Remove nuvens que saíram da tela

  • cloud.depth = trex.depth: Ajuste de profundidade visual

  • cloudsGroup.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 jogo

  • restart.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ção

  • if(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 jogo

  • trex.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 decorativas

  • spawnObstacles(): 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óximo

  • getFrameRate(): 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 jogo

  • restart.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ão

  • trex.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 automaticamente

  • cloudsGroup.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 movimento

  • cloudsGroup.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 reiniciar

  • reset(): 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 ativo

  • gameOver.visible = false: Oculta a mensagem de Game Over

  • restart.visible = false: Esconde o botão de reinício

  • obstaclesGroup.destroyEach(): Limpa todos os obstáculos da tela

  • cloudsGroup.destroyEach(): Remove todas as nuvens do cenário

  • trex.changeAnimation(): Volta para animação de corrida

  • trex.x = 50: Posição horizontal inicial

  • trex.y = 160: Posição vertical inicial

  • score = 0: Zera a pontuação do jogador

  • ground.velocityX: Reinicia velocidade base do chão

  • ground.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;
}
				
			

Posts Similares

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *