Alura > Cursos de Inteligência Artificial > Cursos de Engenharia de LLMs & Agentes > Conteúdos de Engenharia de LLMs & Agentes > Primeiras aulas do curso ChatGPT e JavaScript: construa o jogo Pong

ChatGPT e JavaScript: construa o jogo Pong

Interagindo com o ChatGPT - Apresentação

Guilherme: Olá! Meu nome é Guilherme Silveira e eu serei um de seus instrutores no curso de ChatGPT e JavaScritpt: Inteligência artificial aplicada a programação.

Eu sou uma pessoa não binária, de cabelos castanhos e encaracolados. Tenho uma "covinha" artificial de um piercing que usei na bochecha. Ao meu fundo, há alguns objetos de decoração, em especial, um quadro de arte interativa.

Monica: Olá! Eu sou a Moni Hillman!

Sou uma mulher de cabelos castanhos, com mexas roxas na frente e franja. Estou usando um fone de ouvido rosa. Ao meu fundo, há alguns livros em cima de uma mesa e objetos decorativos.

Guilherme: Juntos, neste curso, vamos trabalhar em um projeto muito bacana, em dois sentidos:

Estamos visualizando a página do jogo sem o áudio. Ele acontece no espaço sideral. Há uma bola que se movimenta aleatoriamente pela tela e entra em choque com uma barra lateral esquerda. Podemos mover essa barra para cima e para baixo, movimentando, consequentemente, a bola.

No ChatGPT, isso tudo será feito com muita conversa. A maior parte do código será gerado em diversas conversas: vamos, analisar a resposta e repetir a pergunta, escrita de outra forma, quando a resposta não é exatamente o que esperávamos.

Encontraremos formas e táticas de conversar com a inteligência artificial, assim, obteremos melhores resultados.

Monica: O único pré-requisito para este curso é saber um pouco de JavaScript, assim, você entenderá as funcionalidades do p5.js que utilizamos e não explicamos com profundidade.

Nos encontramos durante as aulas! Não deixe de se matricular!

Interagindo com o ChatGPT - Construindo o Pong

Guilherme: Tudo bem, pessoal? Vamos começar?

Acessaremos o site do ChatGpt, que é a versão do Generative Pre-Trained Transformer ou Transformador pré-treinado generativo (GPT) via chat. Existem outras ferramentas que fazem o mesmo tipo de operação com chat, você pode escolher a de sua preferência.

Estamos usando o modelo GPT-4, versão paga, mas você pode usar a versão 5 Default (GPT-3.5) que é gratuita. Vamos escrever nosso primeiro pedido, lembrando de indicar a linguagem:

Eu quero um jogo de pong em JavaScript

Moni: Mesmo que o ChatGpt esteja nos respondendo, ele ainda não é uma pessoa, por isso, não toma iniciativas com base no que acha que queremos, mas, sim, a partir do que foi ensinado.

Guilherme: Ele vai responder sobre o pong considerando o que teve contato. Por exemplo, se existir muitos pongs em Python, a resposta provavelmente será em Python.

Continuando, também precisaremos de uma biblioteca. Uma boa opção para esse tipo de simulador/jogo é a p5js.

Eu quero um jogo de pong em Javascript. Eu quero usar a biblioteca p5js.

O nosso jogo é single player, ou seja, de apenas um jogador contra o computador.

Eu quero um jogo de pong em Javascript. Eu quero usar a biblioteca p5js. É um jogo de um jogador versus o computador.

Vamos usar uma inteligência de computador simples, assim evitamos problemas.

Eu quero um jogo de pong em Javascript. Eu quero usar a biblioteca p5js. É um jogo de um jogador versus o computador. A inteligência do computador é bem simples: quando o jogador toca na bola, a raquete do computador escolhe uma posição aleatória e vai para aquela posição.

Não queremos marcar pontos, nem nada complexo. A ideia é construir algo simples, inicial.

Eu quero um jogo de pong em Javascript. Eu quero usar a biblioteca p5js. É um jogo de um jogador versus o computador. A inteligência do computador é bem simples: quando o jogador toca na bola, a raquete do computador escolhe uma posição aleatória e vai para aquela posição. Não quero pontos. Quando tem gol, a bola volta ao centro com um novo movimento aleatório.

Não vamos pedir o código de uma vez só. Ele poderia vir cortado na resposta, por conta do tamanho, e no processo de copiar e colar, provavelmente teríamos um erro. A ideia é pedir só o HMTL.

Eu quero um jogo de pong em Javascript. Eu quero usar a biblioteca p5js. É um jogo de um jogador versus o computador. A inteligência do computador é bem simples: quando o jogador toca na bola, a raquete do computador escolhe uma posição aleatória e vai para aquela posição. Não quero pontos. Quando tem gol, a bola volta ao centro com um novo movimento aleatório. Me mostre primeiro somente o arquivo hmtl. Sem css, sem javascript.

Monica: O ChatGpt é bem instável. Pode ser que no futuro ele fique super rápido. Por enquanto, ele tem travado no meio do processo de formulação da resposta. Não é exatamente um corte, aparecem duas linhas de resposta e ele trava.

Guilherme: Agora o ChatGpt abriu uma janela de código e está gerando o HTML, segundo o nosso pedido. Podemos copiar e colar o resultado em um arquivo HTML. Como ele não disse o nome do arquivo, vamos nomeá-lo como index.hmtl. Vamos conferir!

<!DOCTYPE html>
<html lang="pt">
<head>
  <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Pong Game</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script> src="sketch.js"></script>
</head>
<body>
</html>

Como estou acostumado a usar o p5js, sei que esse arquivo é o de sempre. Todos os arquivos do p5js são um script HTML, em que há um script que inclui o p5js e outro que inclui o nosso script: sketch ou rascunho.

<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script> src="sketch.js"></script>

Ele entendeu o que queremos. É um passo importante. Agora vamos acessar o site do Editor p5js. Nele, encontraremos o básico de um arquivo p5js.

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(220);
}

Apertando o botão de "Play", ele rodará o sketch.js e desenhará um quadrado cinza, 400x400 (400 por 400), na função setup(). Um Canvas está sendo criado e roda uma única vez. Depois, com a função draw() (desenha) ele "printa" o fundo, background(), com a cor 220, que é um tom de cinza-claro.

Vamos adicionar outro comando após o background() para que um retângulo branco, na posição 50 e tamanho 100x200 seja criado: rect(50, 50, 200).

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(220);
    rect(50, 50, 200);
}

Agora, quando rodamos o comando, aparece um retângulo branco dentro do quadrado cinza, posicionado à esquerda. É no scketch.js que escreveremos o JavaScript. Na parte superior do campo de edição do comando, há uma seta posicionada para a direita. Apertando essa seta, ela mostrará, à esquerda, os "Arquivos do esboço": index.html; sketch.js; style.css.

Abrindo o index.html, será possível conferir que é o script quem adiciona o p5.js em uma versão mais atualizada, o css, que está usando apenas como reset mínimo, e o sketch.js. É no sketch.js que vamos trabalhar.

Moni: O p5 até importa as bibliotecas de sons, p5.sound.min.js, mas, não precisamos usá-las necessariamente. Além disso, ele também realiza o trabalho de configuração do ambiente. Mas, se você quiser usar o VSCode, Sublime ou outro editor, basta copiar o código que foi gerado pelo ChatGpt e colar e colocar no index.html.

Guilherme: Estamos usando o editor do p5 para não precisarmos instalar o VSCode ou outro editor. Mas poderia rodar também no CodePen, por exemplo.

Prosseguindo, fecharemos o sketch.js e retornaremos à página do chat. Vamos pedir o arquivo sketch.js.

Agora por favor me de o arquivo sketch.js

Moni: O resultado será tão grande quanto uma bíblia.

Guilherme: O ChatGpt está criando as variáveis para as raquetes e para a bola. Isso faz sentido, porque temos duas raquetes e elas estão em determinada posição da tela. A bola também fica em alguma posição.

// Variaveis para as raquetes e bola 
let playerPadle, computerPadle, ball;

function setup() {
  createCanvas(800, 400);
    playerPadle = new Padle(30, height / 2, 10, 60);
    computerPadle = new Padle(width - 40, heigth / 2, 10, 60);
    ball = new Ball(width / 2, height / 2, 10);

}

function draw() {
  background(0);

Há um objeto chamado Padle(), que é "raquete" em português. Ele está definindo a posição dessa raquete, por exemplo, "30" no X, isto é, à direita, bem no meio da tela e na vertical. Com o computerPadle ele está criando também o lado da direita, a grossura é 10 e a altura é 60.

A classe bola, ball(), na posição do meio, com valor 10, que deve se referir ao raio da esfera. A função draw() atualiza as raquetes e a bola. Depois, verifica colisões e mostra.

function draw() {
  background(0);

    // Atualiza as posições das raquetes e da bola
    playerPaddle.update();
    computerPadlle.update();
    ball.update();

    // Vericia colisões entre a bola e raquetes
    ball.checkPaddleCollision(playerPaddle);
    ball.checkPaddleCollision(computerPadle);

    // Desenha as raquetes e a bola
    playerPaddle.display();
    computerPaddle.display();
    ball.display();
}

A classe da raquete "seta" muitas variáveis e outras tarefas. A classe da bola também. O código ainda está sendo gerado.

class Raquete {
  constructor(x, y, w, h) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
  }

Um detalhe interessante é que a classe da raquete está usando o mouse. Significa que se mexermos o mouse para cima e para baixo, a raquete segue a posição do mouse.

atualizar() {
    if (this === raqueteJogador) {
      this.y = mouseY;
    } else {
      if (bola.y > this.y + this.h / 2) {
        this.y += 3;
      } else if (bola.y < this.y - this.h / 2) {
        this.y -= 3;
      }
    }
    this.y = constrain(this.y, this.h / 2, height - this.h / 2);
  }

    display() {
    fill(255);
    rectMode(CENTER);
    rect(this.x, this.y, this.w, this.h);
  }
}

Sobre a bola, uma inteligência está sendo criada para movimentar a bola para cima e para baixo. O constrain() serve para limitar a tela e mostrar "desenhe um retângulo":

rectMode(CENTER);
    rect(this.x, this.y, this.w, this.h);

Na classe da bola, Ball, ele chama um constructor(), reset(), o update() acontece de acordo com a velocidade. Se foi gol, ela "reseta" o jogo. Lembrando que, no comando, está especificado gol para a direita e para a esquerda:

if (this.x + this.r / 2 >= width) {
      this.reiniciar();
    } else if (this.x - this.r / 2 <= 0) {
      raqueteComputador.y = random(height - raqueteComputador.h);
      this.reiniciar();
    }
  }

Seguindo, ele verifica colisão e mostra.

checkPadleCollision(paddle) {
    if (
      this.x - this.r / 2 <= paddle.x + paddle.w / 2 &&
      this.x + this.r / 2 >= paddle.x - paddle.w / 2 &&
      this.y + this.r / 2 >= paddle.y - paddle.h / 2 &&
      this.y - this.r / 2 <= paddle.y + paddle.h / 2
    ) {
      this.velocidadeX *= -1;
    }
  }

  display() {
    fill(255);
    ellipseMode(CENTER);
    ellipse(this.x, this.y, this.r);
  }
}

O ChatGpt parou no meio da resposta e, fora da janela de código, exibiu a mensagem: "Este código cria um jogo de Pong simples, onde o usuário joga".

Monica: Aconteceu o que eu tinha avisado: ele travou no meio da resposta.

Guilherme: Tudo bem, o código gerado até agora é suficiente. Vamos apertar "Coy code" na parte superior da janela para copiar, colar na área de edição do p5. arquivo scketch.js, salvar com o comando "Ctrl + S" e rodar.

Na lateral direita, visualizaremos uma prévia: temos um Pong funcionando. O fundo é preto, há duas raquetes brancas, uma do lado direito e outra do lado esquerdo.

Elas são representadas por barras verticais que se movimentam para cima e para baixo, segundo a movimentação do mouse. Há também uma pequena bola branca que se movimenta aleatoriamente e entra em choque com as raquetes.

Funcionou mesmo! Estou muito feliz que está funcionando muito bem, porque quando preparei o curso, não estava tão bom.

Monica: O ChatGpt está mais treinado!

Guilherme: Alguma sugestão de feature (recurso) que podemos adicionar?

Monica: Poderíamos substituir o que está escrito em inglês para português no comando.

Guilherme: Vamos trocar para português. Basta pedir ao ChatGpt:

Por favor, o nome das classes e o nome das variáveis deveriam estar em português

Ele estava gerando a mudança para português e travou. O ChatGpt gera uma quantidade limite de caracteres. Se ele parar, é porque, provavelmente, atingiu esse limite e temos que pedir que continue.

Mas, se pedimos que ele continue, pode acontecer um erro e ele não saber o que continuar ou gerar uma resposta que não tem nada a ver com o que estamos fazendo.

Temos um contexto e é importante considerá-lo. Precisamos pensar nas melhoras formas de comunicação com o chat para que ele entenda o que queremos. Se simplesmente dissermos "continue", provavelmente não receberemos a continuação correta.

Ele já gerou a raquete, o setup() e o draw(). Vamos copiar esse trecho, colar no editor do p5, retornar ao chat e pedir o que está faltando: a função da bola. Então, vamos escrever:

Me mostre a classe Bola completa, somente ela

Agora ele está gerando o código, mantendo o texto em português e mantendo a coerência com o texto de código anterior. Vamos copiá-lo e testar se vai funcionar.

A ideia não é aprender a copiar o código, mas, entender como podemos nos comunicar com o ChatGpt, isto é, como devemos escrever as mensagens para alcançarmos os resultados desejados.

O código está pronto. Vamos copiá-lo e colá-lo no p5, logo após o comando da raquete. Depois de salvá-lo, podemos rodar e testar se o gol funciona. Ao lado direito da tela, há uma pré-visualização do jogo de Pong. Quando acontece o gol, o jogo continua. Está funcionando!

Nos encontramos no próximo vídeo para resolver outros problemas do nosso jogo de Pong.

Monica: Vamos enfrentar os novos desafios!!

Interagindo com o ChatGPT - Solicitando mudanças

Monica: O que podemos implementar no nosso jogo de Pong para que ele fique cada vez mais bonito?

Guilherme: Uma das funcionalidades clássicas, é: quando a bolinha bate nas extremidades da tela, tanto embaixo quanto em cima, não faz sentido ela quicar no "nada". Se ela bate nas raquetes (laterais), é necessário que existam barras em cima e embaixo para justificar que a bola bata e volte.

Monica: É super importante dar um feedback visual para os usuários. Se a bola é lançada pela raquete para cima e não existe nenhuma barreira que pare seu movimento, em tese, ela deveria continuar a se movimentar infinitamente.

Sabemos que existe um limite, mas se existir uma representação visual dessa barreira, o que está acontecendo ficará mais compreensível.

Guilherme: Então, vamos pedir que o ChatGpt adicione barras ao jogo de Pong:

Queremos duas barras horizontais, uma no topo e uma embaixo da tela para mostrar que quando a bola chega nessas bordas, ela quica. Isso significa que ao invés de quicar no limite do Y do canvas, vai quicar agora nessas bordas finas.

Podemos especificar a espessura. Talvez, a mesma da barra da raquete. O que você acha, Moni?

Monica: Acho que poderia ser um pouco mais fina.

Guilherme: Vamos retornar ao p5 e verificar a espessura da raquete.

  createCanvas(800, 400);
  raquete(30, height, 2, 10, 60);
Raquete(30, height / 2, 10, 60);

A espessura da raque é 10. Vamos pedir 5 pixels. Continuando a escrever no ChatGpt:

Queremos duas barras horizontais, uma no topo e uma embaixo da tela para mostrar que quando a bola chega nessas bordas, ela quica. Isso significa que ao invés de quicar no limite do Y do canvas, vai quicar agora nessas bordas finas. A espessura delas é de 5.

Monica: É interessante analisar os prompts (comandos) que outras pessoas criam para pesquisar no ChatGpt, porque eu não saberia ser tão específica.

Quem me conhece a partir de grupos de estudos e cursos sabe que tenho dificuldade com números, computação gráfica, dentre outros. Neste sentido, especificar y, x e demais valores para criar a raquete e outros elementos do jogo acaba sendo uma dificuldade.

Guilherme: Na próxima eu vou parar de fazer o prompt e você assume. Assim, não influenciarei, com a minha experiência, a construção do p5js.

Monica: Vamos saber como é criar um prompt para quem tem dificuldade em geometria.

Guilherme: Perfeito! Eu posso ajudar, sem problemas!

Vamos continuar. O ChatGpt está o comando para adicionar duas barras horizontais, uma no topo e outra na parte inferior da tela do jogo de Pong. Ele criou mais duas variáveis: barraSuperior e barraInferior.

// Variáveis para as raquetes, bola e barras horizontais
let raqueteJogador, raqueteComputador, bola, barraSuperior, barraInferior;

function setup() {
  createCanvas(800, 400);
  raqueteJogador = new Raquete(30, height / 2, 10, 60);
  raqueteComputador = new Raquete(width - 40, height / 2, 10, 60);
  bola = new Bola(width / 2, height / 2, 10);
  barraSuperior = new Barra(0, 5, width, 5);
  barraInferior = new Barra(0, height - 5, width, 5);
}

Após as variáveis, temos o setup(), a função draw() para desenhar as duas barras. Em seguida, ele sinaliza código omitido com reticências para a classe da bola.

class Raquete {
   // ...
}

class Bola {
   // ...

Monica: Sinal de que ele não está escrevendo nada que não seja necessário.

Guilherme: Vamos copiar esse trecho das variáveis até a classe Raquete() e colar no editor do p5. Se a classe Raquete() está como antes, por que a classe da bola está diferente? Você sabe o motivo?

Monica: Acho que é para calcular por onde a bola vai voltar ou quando vai bater.

Guilherme: É a colisão!

if (
      this.y - this.r / 2 <= barraSuperior.y + barraSuperior.h ||
      this.y + this.r / 2 >= barraInferior.y - barraInferior.h
    ) {
      this.velocidadeY *= -1;
    }

Antes a bola colidia com o limite do canvas. Agora, ela precisa colidir com a barra, ou seja, 5 pixels antes. Para calcular essa distância de colisão 5 pixels antes, o chatGpt pegou o y da barra, barraSuperior.y, que é o ponto vertical e calculou menos ou mais (a depender de qual das barras) a espessura dessa barra.

Ele construiu o comando de forma dinâmica. Assim, se mudarmos a espessura da barra para 13, por exemplo, ele funcionará. Antes, a bola colidia com o this.y, sendo o tamanho zero e height. Ou seja, o y menor era zero e o y maior era a altura do canvas.

     if (this.y - this.r / 2 <= 0 || this.r /2 
>= height) {
      this.velocidadeY *= -1;
      }

Agora, isso não acontece mais. Agora a bola bate na barra.

Monica: Pensei que ele colocaria apenas visualmente a barra, porque não pedimos uma barra tão grossa. Mas, ele fez até uma verificação de colisão com ela.

Guilherme: Mas esse é o meu truque: no meu prompt, eu pedi que ele fizesse isso. Vamos retornar ao pedido:

Queremos duas barras horizontais, uma no topo e uma embaixo da tela para mostrar que quando a bola chega nessas bordas, ela quica. Isso significa que ao invés de quicar no limite do Y do canvas, vai quicar agora nessas bordas finas. A espessura delas é de 5.

Foi proposital a escolha pela frase: "Isso significa que ao invés de quicar no limite do Y do canvas, vai quicar agora nessas bordas finas". Se não especificarmos o que queremos, o resultado dificilmente será o esperado.

Neste caso, a bola passaria pela barra e bateria na borda. Nós perceberíamos esse movimento de entrada e saída, o que não é tão legal.

Monica: Isso está bastante interligado ao que estava pensando. Disse que no meu prompt eu não especificaria esse detalhe da barra, pois não saberia da necessidade de especificar.

O resultado seria o que estava esperando: apenas uma mudança visual, mas não no impacto da barra.

Guilherme: Estamos no p5. Vamos rodar o código e conferir o resultado com as duas barras. Na lateral direita da tela, temos a janela de prévia do jogo de Pong. Está funcionando: temos duas barras horizontais, uma no topo e outra na parte inferior da tela, e a bola bate nelas e volta.

Ele acabou gerando uma margem também, isto é, uma faixa preta entre a barra branca e o final da tela.

Monica: Só acho que a bola está muito devagar. Assim, o jogo fica um pouco entediante. Podemos pensar nisso agora.

Guilherme: Ótimo! Alguma sugestão de prompt?

Monica: Será dinâmica ou fixa a velocidade?

Guilherme: Acho que poderia ser dinâmica, isto é, ir aumentando aos poucos.

Monica: Minha sugestão de prompt é:

Altere o código para a velocidade da bola mudar a cada vez que ocorrer um impacto com a raquete.

Guilherme: Durante o processo de geração do código, tenho uma sugestão a fazer. Considerando que o tempo para gerar os códigos é longo, seria útil saber antecipadamente qual impacto esse código deve ter.

Por exemplo, o código deve afetar a classe "bola", mais precisamente a função de impacto dessa bola, ou talvez a classe Raquete(), onde ocorre a colisão (mesmo que seja na bola).

Com isso, evitamos gerar comandos desnecessários que não produzem alterações significativas. Podemos especificar explicitamente: "quero o código da classe bola" e indicar qual trecho do código precisa ser atualizado.

No nosso caso, fica mais fácil identificar onde serão as alterações, porque a velocidade é usada em apenas um trecho. Mas, em códigos maiores, talvez o design fique muito ruim, incompreensível, embora funcione. Então, se você já tem uma sugestão de onde colocar o trecho de código, indique para o chat.

Após a classe bola, ele criou uma função aumentarVelocidade(). O fator de aumento é 10% (dez por cento) no x e no y. Vamos copiar essa função, colar na classe bola, editor do p5.

class Bola {
   // ...

aumentarVelocidade() {
    const fatorAumento = 1.1;
    this.velocidadeX *= fatorAumento;
    this.velocidadeY *= fatorAumento;
  }

Também vamos copiar a função verificarColisaoRaquete(raquete). Agora ela está verificando e aumentando a velocidade.

 verificarColisaoRaquete(raquete) {
    if (
      this.x - this.r / 2 <= raquete.x + raquete.w / 2 &&
      this.x + this.r / 2 >= raquete.x - raquete.w / 2 &&
      this.y + this.r / 2 >= raquete.y - raquete.h / 2 &&
      this.y - this.r / 2 <= raquete.y + raquete.h / 2
    ) {
      this.velocidadeX *= -1;
      this.aumentarVelocidade();
    }
  }

No p5, vamos salvar, rodar e testar. É possível notar, na janela de visualização prévia, que a velocidade está aumentando de 10 em 10%, cada vez que a bola bate nas raquetes. Se deixarmos o fator mais rápido, conseguiremos acompanhar melhor esse aumento.

Monica: Acho que está suficiente. Mais que isso, ficará impossível ganhar.

Guilherme: Assim já está bem difícil ganhar. Está ótimo!

Monica: Podemos deixar o Pong ainda melhor: mais dinâmico, complexo, bonito e jogável. No próximo vídeo, continuaremos nos desafiando no jogo de Pong, usando o ChatGpt. Até lá!!

Sobre o curso ChatGPT e JavaScript: construa o jogo Pong

O curso ChatGPT e JavaScript: construa o jogo Pong possui 102 minutos de vídeos, em um total de 35 atividades. Gostou? Conheça nossos outros cursos de Engenharia de LLMs & Agentes em Inteligência Artificial, ou leia nossos artigos de Inteligência Artificial.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Engenharia de LLMs & Agentes acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas