Criando o Space Invaders – Tutorial em JavaScript
Space Invaders é um clássico jogo de arcade lançado em 1978, desenvolvido por Tomohiro Nishikado. No jogo, o objetivo é controlar uma nave espacial que se move horizontalmente na parte inferior da tela, atirando em uma formação de invasores alienígenas que descem gradualmente. É um marco da indústria dos videogames, e sua recriação em JavaScript é uma maneira perfeita de praticar e aprender a programação de jogos e reviver essa lenda dos arcades!
Acesse o code.org, ao entrar na sua conta, selecione a opção criar novo projeto e escolha “game lab”.

Passo 1: Configurando as Variáveis
Aqui, vamos criar o nosso jogador, os grupos de inimigos e balas, além de variáveis que vão acompanhar as vidas e pontos do jogador.
// variável jogador e animação
var jogador = createSprite(169, 359, 20, 20);
jogador.setAnimation("nave");
jogador.scale = 0.5;
//variáveis grupos inimigos
var grupoinimigo = createGroup();
var grupoinimigo2 = createGroup();
var grupoinimigo3 = createGroup();
var grupobullet = createGroup();
//variáveis hud
var life = 3;
var Points = 0;
//variável estado de jogo
var gamestate = "iniciar";
Dica Pro: Lembre-se de criar as animações para deixar seu jogo mais atrativo!


Passo 2: Controles do jogador
Damos ao jogador controle total para mover a nave para os lados, atirar balas e, claro, manter-se vivo enquanto enfrenta ondas de inimigos!
//movimento do jogador
if (keyDown("left")) {
jogador.x -= 5;
}
if (keyDown("right")) {
jogador.x += 5;
}
//impedindo que o jogador saia da tela
if(jogador.x>363){
jogador.x=363;
}
if(jogador.x<37){
jogador.x=37;
}
//disparando as bullets
if (keyWentDown("space")) {
criarbullet();
}
Passo 3: Criando as bullets
Vamos dar ao jogador a habilidade de atirar balas! Toda vez que o jogador pressionar a barra de espaço, uma nova bala será criada e adicionada ao grupo de balas.
// Função para criar uma bala
function criarbullet() {
var bullet = createSprite(jogador.x, jogador.y, 10, 20);
bullet.shapeColor = 'blue';
//atribuindo velocidade a bala
bullet.velocityY = -10;
grupobullet.add(bullet); // Adiciona a bala ao grupo
}
Solução Inteligente: Notou como o grupobullet.add(bullet)
facilita a gestão das balas? Assim, fica super fácil manipular todas as balas como um único conjunto.

Passo 4: Criando os inimigos
A cada intervalo de tempo, novos inimigos aparecerão na tela!
// Função para criar inimigos
function criarinimigo1() {
if (frameCount % 60 == 0) {
var inimigo = createSprite(random(50, 370), 28, 20, 20);
inimigo.setAnimation("inimigo1");
inimigo.scale = 0.10;
inimigo.velocityY = 4;
inimigo.lifetime = 100;
grupoinimigo.add(inimigo);
}
}

Passo 5: Implementando a Colisão entre o Jogador e os Inimigos
Vamos implementar as colisões, ou seja, quando o jogador colidir com um inimigo, algo épico vai acontecer: a vida do jogador será reduzida e o inimigo será eliminado da tela!
// Verificando a colisão entre o jogador e os inimigos
for (var x = 0; x < grupoinimigo3.length; x++) {
if (jogador.isTouching(grupoinimigo3[x])) {
// Controla a vida do jogador ao ser atingido
lifecontrol();
// Destroi o inimigo que colidiu
grupoinimigo3[x].destroy();
}
}

Passo 6: Gerenciando vidas do jogador
A função lifecontrol() é bem simples, mas super poderosa! Ela faz o controle das vidas do jogador e define se ele ainda pode continuar jogando ou se perdeu o jogo.
function lifecontrol (){
life = life - 1; // Reduzimos a vida do jogador
if(life >= 1){ // Verifica se o jogador ainda tem vidas
gamestate = "iniciar"; // Se sim, reiniciamos o jogo
}
else {
gamestate = "perdeu"; // Caso contrário, o jogador perdeu
}
}
Passo 7: Destruindo os Inimigos com as bullets do Jogador
No código, utilizamos um laço duplo para verificar se cada bala está colidindo com um dos inimigos. Quando essa colisão acontece, tanto a bala quanto o inimigo são destruídos, e o jogador ganha pontos!
// Laço externo: percorre todos os inimigos no grupo inimigo3
for (var x = 0; x < grupoinimigo3.length; x = x + 1) {
// Laço interno: percorre todas as balas no grupo de balas (grupobullet)
for (var y = 0; y < grupobullet.length; y = y + 1) {
// Verifica se o inimigo e a bala existem e se estão se tocando
if (grupoinimigo3[x] && grupobullet[y] && grupobullet[y].isTouching(grupoinimigo3[x])) {
// Se a colisão for detectada, destrói o inimigo
grupoinimigo3[x].destroy();
// Também destrói a bala
grupobullet[y].destroy();
// Incrementa os pontos do jogador ao destruir um inimigo
Points = Points + 10;
}
}
}
Passo 8: Configurando o Estado Inicial do Jogo com a Função iniciar()
No desenvolvimento de jogos, um dos conceitos mais importantes é o estado do jogo. Ele define o que está acontecendo no jogo em cada momento: se estamos jogando, na tela de pausa, ou aguardando o início da partida. Neste passo do tutorial, vamos falar sobre a função iniciar()
e como ela organiza o jogo antes de tudo começar.
function iniciar() {
// Pausa os inimigos, impedindo que eles se movam
grupoinimigo.setVelocityYEach(0);
grupoinimigo2.setVelocityYEach(0);
grupoinimigo3.setVelocityYEach(0);
// Define a posição inicial da nave do jogador no centro da tela
jogador.x = 200;
jogador.y = 350;
// Exibe uma mensagem para o jogador saber como iniciar o jogo
textSize(30);
fill("red");
textFont("Aptos Black");
text("clique UP para iniciar", 90, 200);
// Remove todos os inimigos da tela (se houver algum restante)
grupoinimigo.destroyEach();
grupoinimigo2.destroyEach();
grupoinimigo3.destroyEach();
// Verifica se a tecla "UP" foi pressionada para mudar o estado do jogo para "jogar"
if (keyDown("up")) {
gamestate = "jogar";
}
}
Passo 9: Implementando a Função Principal do Jogo com jogar()
Agora que já configuramos o início do jogo e o comportamento dos inimigos, vamos falar sobre a função que comanda a ação enquanto o jogo está em andamento: a função jogar()
. É aqui que a verdadeira batalha espacial acontece!
A função jogar()
controla o movimento da nave do jogador, a criação de inimigos, o disparo de balas e a detecção de colisões entre balas e inimigos.
function jogar() {
// Limita o movimento da nave do jogador para que não saia da tela
if(jogador.x > 363) {
jogador.x = 363;
}
if(jogador.x < 37) {
jogador.x = 37;
}
// Cria os inimigos regularmente
criarinimigo1();
criarinimigo2();
criarinimigo3();
// Controles do jogador
if (keyDown("left")) {
jogador.x -= 5; // Move a nave para a esquerda
}
if (keyDown("right")) {
jogador.x += 5; // Move a nave para a direita
}
// Cria uma bala quando a barra de espaço é pressionada
if (keyWentDown("space")) {
criarbullet();
}
// Verifica se as balas atingem os inimigos do grupo inimigo1 e os destroem
for (var x = 0; x < grupoinimigo.length; x = x + 1) {
for (var y = 0; y < grupobullet.length; y = y + 1) {
if (grupoinimigo[x] && grupobullet[y] && grupobullet[y].isTouching(grupoinimigo[x])) {
grupoinimigo[x].destroy();
grupobullet[y].destroy();
Points += 10; // Adiciona 10 pontos ao destruir um inimigo
}
}
}
// Verifica se as balas atingem os inimigos do grupo inimigo2 e os destroem
for (var x = 0; x < grupoinimigo2.length; x = x + 1) {
for (var y = 0; y < grupobullet.length; y = y + 1) {
if (grupoinimigo2[x] && grupobullet[y] && grupobullet[y].isTouching(grupoinimigo2[x])) {
grupoinimigo2[x].destroy();
grupobullet[y].destroy();
Points += 10; // Adiciona 10 pontos ao destruir um inimigo
}
}
}
// Verifica se as balas atingem os inimigos do grupo inimigo3 e os destroem
for (var x = 0; x < grupoinimigo3.length; x = x + 1) {
for (var y = 0; y < grupobullet.length; y = y + 1) {
if (grupoinimigo3[x] && grupobullet[y] && grupobullet[y].isTouching(grupoinimigo3[x])) {
grupoinimigo3[x].destroy();
grupobullet[y].destroy();
Points += 10; // Adiciona 10 pontos ao destruir um inimigo
}
}
}
// Verifica se o jogador foi atingido por algum inimigo do grupo inimigo1
for (var x = 0; x < grupoinimigo.length; x = x + 1) {
if (jogador.isTouching(grupoinimigo[x])) {
lifecontrol(); // Reduz a vida do jogador
grupoinimigo[x].destroy(); // Destroi o inimigo ao tocar o jogador
}
}
// Verifica se o jogador foi atingido por algum inimigo do grupo inimigo2
for (var x = 0; x < grupoinimigo2.length; x = x + 1) {
if (jogador.isTouching(grupoinimigo2[x])) {
lifecontrol(); // Reduz a vida do jogador
grupoinimigo2[x].destroy(); // Destroi o inimigo ao tocar o jogador
}
}
// Verifica se o jogador foi atingido por algum inimigo do grupo inimigo3
for (var x = 0; x < grupoinimigo3.length; x = x + 1) {
if (jogador.isTouching(grupoinimigo3[x])) {
lifecontrol(); // Reduz a vida do jogador
grupoinimigo3[x].destroy(); // Destroi o inimigo ao tocar o jogador
}
}
}
Passo 10: Implementando o Fim de Jogo com a Função perdeu()
Agora vamos falar sobre uma parte inevitável de qualquer jogo: o fim de jogo. Em nosso Space Invaders, quando o jogador perde todas as suas vidas, entramos no estado “perdeu”. É aqui que a função perdeu()
entra em ação!
Essa função exibe uma mensagem para o jogador informando que ele perdeu e oferece a opção de reiniciar o jogo pressionando a tecla “R”. Além disso, ela garante que todos os inimigos sejam removidos da tela, criando uma pausa antes de reiniciar.
function perdeu() {
// Define o tamanho e a cor do texto
textSize(20);
fill("red");
textFont("Aptos Black");
// Exibe a mensagem de fim de jogo
text("Você perdeu, clique em R para reiniciar", 50, 200);
// Remove todos os inimigos da tela
grupoinimigo.destroyEach();
grupoinimigo2.destroyEach();
grupoinimigo3.destroyEach();
// Verifica se a tecla "R" foi pressionada para reiniciar o jogo
if (keyDown("r")) {
gamestate = "restart"; // Muda o estado do jogo para "restart"
}
}

Passo 11: Implementando a Função de Reiniciar o Jogo com reiniciar()
Agora que já configuramos o fim de jogo com a função perdeu()
, vamos criar a função responsável por reiniciar o jogo!
Quando o jogador perde e pressiona a tecla “R”, a função reiniciar()
é chamada. Ela reseta as principais variáveis do jogo, como as vidas e os pontos, e coloca o jogo de volta no estado “iniciar”, pronto para um novo começo.
function reiniciar() {
// Reseta a quantidade de vidas para o valor inicial
life = 3;
// Reseta a pontuação do jogador para 0
Points = 0;
// Muda o estado do jogo para "iniciar", preparando o jogo para um novo começo
gamestate = "iniciar";
}
Passo 12: Desenhando e Controlando o Fluxo do Jogo com a Função draw()
A função draw()
é chamada repetidamente a cada quadro do jogo e é responsável por desenhar todos os elementos na tela, como a pontuação, as vidas do jogador e os sprites, além de controlar o estado do jogo. Dependendo do estado atual, ela chama as funções correspondentes para iniciar, jogar, perder ou reiniciar o jogo.
function draw() {
// Define o fundo preto para o jogo
background("black");
// Atualiza o desenho dos sprites na tela
drawSprites();
// Exibe a quantidade de vidas do jogador na tela
textSize(40);
fill("red");
textFont("Aptos Black");
text("vida: " + life, 20, 368);
// Exibe a pontuação do jogador na tela
fill("white");
text("Points: " + Points, 116, 32);
// Verifica o estado do jogo e chama a função correspondente
if (gamestate == "iniciar") {
iniciar(); // Chama a função que prepara o jogo para iniciar
}
if (gamestate == "jogar") {
jogar(); // Chama a função que controla o jogo durante a ação
}
if (gamestate == "perdeu") {
perdeu(); // Chama a função que trata o fim de jogo
}
if (gamestate == "reiniciar") {
reiniciar(); // Chama a função que reinicia o jogo
}
}
RESULTADO

// Variáveis
var jogador = createSprite(169, 359, 20, 20);
jogador.setAnimation("nave");
jogador.scale = 0.5;
var grupoinimigo = createGroup();
var grupoinimigo2 = createGroup();
var grupoinimigo3 = createGroup();
var grupobullet = createGroup();
var life = 3;
var Points = 0;
var gamestate = "iniciar";
var inimigo, inimigo2, inimigo3;
// Função desenhar - ocorre a todo momento
function draw() {
background("black");
// Atualiza o desenho dos sprites
drawSprites();
// Exibe vidas e pontuação
textSize(40);
fill("red");
textFont("Aptos Black");
text("vida: " + life, 20, 368);
fill("white");
text("Points: " + Points, 116, 32);
// Controle do fluxo do jogo baseado no estado
if (gamestate == "iniciar") {
iniciar();
}
if (gamestate == "jogar") {
jogar();
}
if (gamestate == "perdeu") {
perdeu();
}
if (gamestate == "reiniciar") {
reiniciar();
}
}
// Função para criar uma bala
function criarbullet() {
var bullet = createSprite(jogador.x, jogador.y, 10, 20);
bullet.shapeColor = 'blue';
bullet.velocityY = -10;
bullet.lifetime = 100;
grupobullet.add(bullet);
}
// Funções para criar inimigos
function criarinimigo1() {
if (frameCount % 60 === 0) {
inimigo = createSprite(random(50, 370), 28, 20, 20);
inimigo.setAnimation("inimigo1");
inimigo.scale = 0.10;
inimigo.velocityY = 4;
inimigo.lifetime = 100;
grupoinimigo.add(inimigo);
}
}
function criarinimigo2() {
if (frameCount % 90 === 0) {
inimigo2 = createSprite(random(50, 370), 28, 20, 20);
inimigo2.setAnimation("inimigo2");
inimigo2.scale = 0.10;
inimigo2.velocityY = 4;
inimigo2.lifetime = 100;
grupoinimigo2.add(inimigo2);
}
}
function criarinimigo3() {
if (frameCount % 90 === 0) {
inimigo3 = createSprite(random(50, 370), 28, 20, 20);
inimigo3.setAnimation("inimigo3");
inimigo3.scale = 0.10;
inimigo3.velocityY = 4;
inimigo3.lifetime = 100;
grupoinimigo3.add(inimigo3);
}
}
// Função para controle de vida
function lifecontrol() {
life -= 1;
if (life >= 1) {
gamestate = "iniciar";
} else {
gamestate = "perdeu";
}
}
// Função para iniciar o jogo
function iniciar() {
grupoinimigo.setVelocityYEach(0);
grupoinimigo2.setVelocityYEach(0);
grupoinimigo3.setVelocityYEach(0);
jogador.x = 200;
jogador.y = 350;
textSize(30);
fill("red");
textFont("Aptos Black");
text("clique UP para iniciar", 90, 200);
grupoinimigo.destroyEach();
grupoinimigo2.destroyEach();
grupoinimigo3.destroyEach();
// Transição para estado "jogar" ao pressionar "UP"
if (keyDown("up")) {
gamestate = "jogar";
}
}
// Função para jogar
function jogar() {
if (jogador.x > 363) {
jogador.x = 363;
}
if (jogador.x < 37) {
jogador.x = 37;
}
criarinimigo1();
criarinimigo2();
criarinimigo3();
if (keyDown("left")) {
jogador.x -= 5;
}
if (keyDown("right")) {
jogador.x += 5;
}
if (keyWentDown("space")) {
criarbullet();
}
// Verifica se as balas atingem os inimigos e os destroem
for (var x = 0; x < grupoinimigo.length; x++) {
for (var y = 0; y < grupobullet.length; y++) {
if (grupoinimigo[x] && grupobullet[y] && grupobullet[y].isTouching(grupoinimigo[x])) {
grupoinimigo[x].destroy();
grupobullet[y].destroy();
Points += 10;
}
}
}
for (var x = 0; x < grupoinimigo2.length; x++) {
for (var y = 0; y < grupobullet.length; y++) {
if (grupoinimigo2[x] && grupobullet[y] && grupobullet[y].isTouching(grupoinimigo2[x])) {
grupoinimigo2[x].destroy();
grupobullet[y].destroy();
Points += 10;
}
}
}
for (var x = 0; x < grupoinimigo3.length; x++) {
for (var y = 0; y < grupobullet.length; y++) {
if (grupoinimigo3[x] && grupobullet[y] && grupobullet[y].isTouching(grupoinimigo3[x])) {
grupoinimigo3[x].destroy();
grupobullet[y].destroy();
Points += 10;
}
}
}
// Verifica se os inimigos tocam o jogador e ajusta a vida
for (var x = 0; x < grupoinimigo.length; x++) {
if (jogador.isTouching(grupoinimigo[x])) {
lifecontrol();
grupoinimigo[x].destroy();
}
}
for (var x = 0; x < grupoinimigo2.length; x++) {
if (jogador.isTouching(grupoinimigo2[x])) {
lifecontrol();
grupoinimigo2[x].destroy();
}
}
for (var x = 0; x < grupoinimigo3.length; x++) {
if (jogador.isTouching(grupoinimigo3[x])) {
lifecontrol();
grupoinimigo3[x].destroy();
}
}
}
// Função para fim de jogo
function perdeu() {
textSize(20);
fill("red");
textFont("Aptos Black");
text("Você perdeu, clique em R para reiniciar", 50, 200);
grupoinimigo.destroyEach();
grupoinimigo2.destroyEach();
grupoinimigo3.destroyEach();
if (keyDown("r")) {
gamestate = "restart";
}
}
// Função para reiniciar o jogo
function reiniciar() {
life = 3;
Points = 0;
gamestate = "iniciar";
}
https://studio.code.org/projects/gamelab/9ebb7IIAckYqDwVNyFalDy-9AasNPjETlRlRo9draq8/edit