Programando um Jogo (Pong) somente com IA (Gemini)

Douglas Barres
18 Min Leitura

Hoje em dia já existem algumas engines de criação de jogos que nem precisa de programação, mas a grande maioria – como também as mais populares – exigem conhecimento básico para sair alguma coisa. Só que com a popularização das IAs, será que é possível colocar uma IA para cuidar de toda parte de programação da criação de um jogo?

Começando Simples

A ideia nesse experimento é usarmos somente IA para criação dos códigos do zero. Não usamos códigos prontos feito por pessoas que podem ter sido compartilhados na internet, por mais que eles possam agilizar ou deixar o trabalho mais prático.

Para começar, resolvi escolher algo relativamente simples: PONG. Como foi um dos primeiros jogos criados e é relativamente algo simples em escopo, acredito ser uma boa ideia para colocar as IA’s para programar. Curiosamente, usando essa página destinada a gamedevs iniciantes, o Pong é um dos jogos mais simples de se começar no gamedev.

Vou tentar fazer o mesmo jogo com as IAs que eu tenho mãos no momento: ChatGPT e Gemini. Uma de cada vez para testar a eficácia de ambas no quesito programarem sozinhas um jogo. A engine que vou utilizar nesse experimento é a Godot.

Gemini

Já que o Google é generoso e basicamente nos dá tokens infinitos para texto, estarei usando o Gemini 2.5 Pro.

A primeira coisa que eu fiz quando comecei o diálogo com o Gemini foi dizer o que eu queria fazer e pedir um Script de cada vez para ele, ao invés de pedir para ele programar o jogo inteiro de uma vez. Acredito que assim seja mais fácil arrumar quaisquer bugs que aparecerem (se ele conseguir).

Uma das partes fundamentais do Pong é as “raquetes”: uma controlada pelo jogador e outra pela IA. Já que a Raquete do jogador é a mais prática de ser feita, começaremos por ela. Ele retornou o seguinte código para ela:

extends CharacterBody2D

# Define a velocidade de movimento do paddler em pixels por segundo.
# Você pode ajustar este valor no Inspetor para encontrar a velocidade ideal.
@export var speed = 300

func _physics_process(delta):
	# Obtém a direção do input do jogador.
	# "ui_up" e "ui_down" são ações de input pré-definidas na Godot
	# que correspondem às teclas de seta para cima e para baixo e também ao 'W' e 'S'.
	var direction = Input.get_axis("ui_up", "ui_down")

	# Define a velocidade vertical baseada na direção do input.
	# Se 'ui_down' for pressionado, a direção é 1.
	# Se 'ui_up' for pressionado, a direção é -1.
	# Se nada for pressionado, a direção é 0.
	velocity.y = direction * speed

	# move_and_slide() é uma função do CharacterBody2D que move o corpo
	# e lida com colisões automaticamente.
	move_and_slide()

Funcionou como deveria e ele até explicou o que cada coisa do código funciona, é um ponto bem positivo para quem estiver usando a Gemini para aprender programação em si. Para o artigo não ficar muito grande, vou listar somente os códigos que ele criar.

O próximo passo era fazer a “bola”. Já que a raquete IA basicamente segue onde a bola vai, achei melhor fazer a bola primeiro.

extends CharacterBody2D

# A velocidade inicial da bola. Use @export para poder ajustá-la no Inspetor.
@export var speed = 350.0
# Quanto a velocidade aumenta a cada rebatida no paddler.
@export var speed_increase = 25.0

# A direção e velocidade atuais da bola.
var velocity = Vector2.ZERO

# Esta função é chamada uma vez quando o jogo inicia.
func _ready():
	# Espera 1 segundo antes de lançar a bola.
	# Isso dá ao jogador um momento para se preparar.
	await get_tree().create_timer(1.0).timeout
	start_ball()

# Esta função define a direção inicial da bola.
func start_ball():
	# Escolhe uma direção horizontal aleatória (esquerda ou direita).
	var horizontal_direction = [1, -1].pick_random()
	
	# Define a velocidade inicial.
	# A direção vertical é levemente aleatória para que o jogo não seja sempre igual.
	velocity = Vector2(horizontal_direction, randf_range(-0.5, 0.5)).normalized() * speed

# Esta função é chamada a cada quadro de física.
func _physics_process(delta):
	# Move a bola e verifica se houve colisão.
	var collision = move_and_collide(velocity * delta)
	
	# Se a bola colidiu com algo...
	if collision:
		# Pega o objeto com o qual colidiu.
		var collider = collision.get_collider()
		
		# A função "bounce" inverte a velocidade da bola com base na superfície que atingiu.
		# Isso faz a bola quicar de forma realista.
		velocity = velocity.bounce(collision.get_normal())
		
		# Verifica se o objeto com que colidiu está no grupo "paddles".
		# (Vamos configurar os grupos no próximo passo).
		if collider.is_in_group("paddles"):
			# Aumenta a velocidade base da bola.
			speed += speed_increase
			# Aplica a nova velocidade à direção atual da bola.
			velocity = velocity.normalized() * speed

Esse código da bola voltou com um erro que ele me ensinou a corrigir no próximo passo. Notei que esses erros com códigos que ele cria podem vir a acontecer com frequência, mas ao mandar ele corrigir, geralmente ele o faz e não tem mais problemas. Pelo menos quando são coisas simples.

Com o script da bola feita, estava na hora da raquete movimentada por IA (quando digo IA eu me refiro a algo simples, não confundam com algo mais elaborado). Assim ele fez.

extends CharacterBody2D

# A velocidade do paddle da IA. Ajuste este valor no Inspetor para
# mudar a dificuldade do oponente. Valores menores o tornam mais fácil.
@export var speed = 280

# Uma referência ao nó da bola. Precisamos saber onde a bola está para segui-la.
# Usar @export nos permite arrastar e soltar o nó da bola no Inspetor.
@export var ball: CharacterBody2D

# Esta função é chamada a cada quadro de física.
func _physics_process(delta):
	# Etapa 1: Checar se a referência da bola foi definida no Inspetor.
	# Se não, o código para aqui para evitar erros.
	if not ball:
		return

	# Etapa 2: Determinar a direção do movimento.
	var direction = 0
	# Se a bola está abaixo do centro do paddle...
	if ball.global_position.y > self.global_position.y:
		# ...a direção é para baixo (positivo).
		direction = 1
	# Se a bola está acima do centro do paddle...
	elif ball.global_position.y < self.global_position.y:
		# ...a direção é para cima (negativo).
		direction = -1

	# Etapa 3: Definir a velocidade com base na direção.
	velocity.y = direction * speed
	
	# Etapa 4: Mover o paddle.
	move_and_slide()

O script funcionou quase bem. A IA “seguia” a bola como deveria ser, mas estava robótica demais, como se movesse milimetricamente como um crackudo do Pong. Mandei isso pra ele e o Gemini enviou de volta outro script com movimentos mais suaves e algo relacionado a zona morta.

Com a parte das raquetes, movimentação do jogador, movimentação do oponente e a bola, o core do jogo já estava pronto. Precisamos do sistema de pontos. Gemini nos instruiu a criar dois scripts.

Goal.gd:

extends Area2D

# Aqui criamos um "sinal" personalizado.
# Um sinal é como uma transmissão que o nó pode enviar para que outros nós ouçam.
# Neste caso, vamos transmitir que um ponto foi marcado.
signal scored

# A Godot chama esta função automaticamente quando um corpo físico entra na área.
func _on_body_entered(body):
	# Primeiro, verificamos se o corpo que entrou é a bola.
	# A maneira mais fácil é checar se o corpo tem a função "start_ball",
	# que só existe no script da nossa bola.
	if body.has_method("start_ball"):
		# Se for a bola, emitimos nosso sinal personalizado.
		emit_signal("scored")
		# Podemos também desativar a colisão da bola para que ela não marque múltiplos
		# pontos em um único gol enquanto está passando pela área.
		body.get_node("CollisionShape2D").disabled = true

E o main.gd:

extends Node2D

# Referências para os nós que precisamos controlar.
# O @onready garante que a variável seja preenchida assim que a cena estiver pronta.
@onready var ball = $Ball
@onready var player_paddle = $paddlerP
@onready var opponent_paddle = $paddlerP2

# Variáveis para guardar a pontuação.
var player_score = 0
var opponent_score = 0

# Esta função é chamada automaticamente quando o jogo começa.
func _ready():
	print("Jogo iniciado! Placar: ", player_score, " x ", opponent_score)

# Função para reiniciar a rodada após um ponto.
func reset_round():
	# Coloca os paddles de volta na posição inicial (opcional, mas bom).
	# Nota: ajuste os valores de Y se a posição inicial deles não for 324.
	player_paddle.global_position.y = 324
	opponent_paddle.global_position.y = 324
	
	# Coloca a bola no centro da tela.
	# Nota: ajuste os valores se o centro da sua tela for diferente.
	ball.global_position = Vector2(576, 324)
	
	# Reativa a colisão da bola (que desativamos no gol).
	ball.get_node("CollisionShape2D").disabled = false
	
	# Chama a função da própria bola para se lançar novamente.
	ball.start_ball()

# Esta função será conectada ao sinal "scored" do Goal_Right.
func _on_goal_right_scored():
	player_score += 1
	print("Ponto para o Jogador! Placar: ", player_score, " x ", opponent_score)
	reset_round()

# Esta função será conectada ao sinal "scored" do Goal_Left.
func _on_goal_left_scored():
	opponent_score += 1
	print("Ponto para a IA! Placar: ", player_score, " x ", opponent_score)
	reset_round()

Depois de alguns outros passos para “conectar os sinais” (algo da engine da Godot). Tudo correu perfeitamente. Os pontos estavam funcionando. Mas notei que ao rodar o jogo, aconteciam alguns errinhos no console da Godot. Eu só printei quais erros eram e o Gemini prontamente corrigiu eles, me enviando os códigos atualizados. Essa função das IA’s conseguirem analisar elementos de imagens é um mão na roda!

Mas com essa “base” pronta que começou a dor de cabeça…

O loop do BUG eterno

O sistema básico do Pong estava funcionando bem: bola rebatendo nas raquetes, sistema de gols… O problema começou no primeiro bug: Quando a bola batia na parte de BAIXO ou de CIMA da raquete, a bola arrastava a raquete para fora da tela, para a direção do gol. Mesmo explicando várias e várias vezes esse bug pra Gemini, spoiler do artigo: ela nunca conseguiu resolver esse bug.

Um fato interessante que mesmo que eu enviasse um trecho de vídeo para a Gemini, ela “assistia” e identificava o problema. Então a parte do Debug deveria ser bem simples… é, deveria…

Gemini até tentou corrigir esse bug da bola arrastando a raquete corrigindo alguns códigos mas não deu certo. Consegui inclusive capturar outro bug (provavelmente sendo causado pelo mesmo problema):

Uma coisa que comecei a notar a partir daqui é que a Gemini elogia e começaria e pedir desculpas com muita frequência.

Depois dessa mensagem bonita elogiando minha detectação de bugs (como se fosse grandes coisa!), Gemini me mandou uma solução totalmente fora da casinha: mudar as raquetes de “módulo”, transformar elas em “AnimatableBody2D”. Até aí tudo bem, eu mudei, já que não manjo muito da Godot também. O problema é que ao fazer isso, eu perco totalmente a função de poder mover as raquetes!

Ou seja, ela identificou o problema de antes e INVENTOU uma solução que funcionava em partes: poderia provavelmente corrigir o bug mas ao mesmo tempo fez eu perder parte CRUCIAL da mecânica do jogo. Então o diagnóstico dela foi bom, mas sua resolução de problema horrível. E infelizmente daqui pra frente é só pra trás.

Tive que refazer as modificações nas raquetes de antes e Gemini dessa vez sugeriu mudar as configurações da “bola”. E mais uma vez ela começou a “inventar” coisas, pedindo para eu ativar configurações em coisas que nem existem na Godot. O detalhe é que quando eu indago ela sobre isso, ela mesmo entende a cagada que fez. Então imagino que o problema dessa Gemini (e provavelmente da maioria das IA’s) seja na resolução de problemas.

Várias, várias mensagens depois, com diversos pedidos de “desculpas” da Gemini, o problema ainda persistia e inclusive apareceriam outros:

Nessa mensagem acima até estranhei: ela mandou parte da mensagem em inglês e depois voltou pro português. Aquele “FATAL ERROR” não sei se foi algo da mensagem dela mesmo de forma proposital ou uma erro na própria Gemini.

Interessante notar que no final de cada mensagem ela sempre diz algo como “dessa vez vamos corrigir!”. Mas nada acontece e na mensagem seguinte ela tá pedindo desculpas, falando que a culpa é dela…

Foi então que mensagens depois, a Gemini sugeriu novamente algo que já tinha sugerido antes! Foi aí que eu notei que não tinha jeito, ela entraria em um loop de nunca mais conseguir resolver esse bug sozinha.

Quando eu desisti, resolvi procurar na internet algum projeto de Pong na própria Godot e acabei encontrando esse aqui. Um joguinho de Pong maestral que dá pra baixar e abrir os arquivos na própria Godot para estudos. Tentei replicar esse bug que eu estava tendo nele e não consegui, realmente não acontece.

Para me vingar da Gemini, eu enviei esse link do projeto do rapaz para ela tentar corrigir o famoso bug que tinha feito ela tomar um pau. Pensei comigo: se ela for esperta, pelo menos vai usar como base o código pronto do cara para resolver o problema…

NADA! Só o que ela usou de referência no projeto do cara foram alguns módulos, ou como eles eram feitos, mas de resto, me enviou de volta scripts mais simples ainda, que fizeram tudo voltar a estaca zero. Foi aí que eu notei que o loop infernal da Gemini estava confirmado: por algum motivo ela não consegue lidar bem com problemas, ou se consegue identificar a causa, não consegue resolver.

Conclusão

A Gemini peidou na farofa e empacou em um bug que era essencial que fosse corrigido para o nosso “jogo” estar pronto. Um ponto positivo é que ela fez a maioria do core do jogo, mas não dá pra dizer que ela faria tudo do zero, porque ela empacou nesse bug.

Vou deixar alguns pontos abaixo que vocês podem ter depois de ter acompanhado esse artigo:

  • Pode ser que a Gemini tenha chegado nesse bug e isoladamente não conseguiu resolver? Creio que não. Já brinquei com a Godot antes e fazendo um sistema de plataforma, a Gemini também não conseguiu resolver de jeito nenhum um bug que eu tive.
  • A falta de conhecimento em programação do usuário e conhecimento na engine pode ter influenciado nessa travada da Gemini? Com certeza. Alguém com conhecimento em programação da Godot poderia ter resolvido esse bug ou pensado em outra coisa. Mas eu usei como base nesse artigo alguém leigo que estava usando puramente a Gemini para programar.

Vocês também tiveram alguma experiência com criação de códigos para gamedev com IA? Como eu queria ver alguém que tenha conseguido se dar bem nisso, porque eu passei raiva viu.

Compartilhar esse Artigo
Seguir o Autor:
Fundador do Matilha Gamer. Gamer que começou no Super Nintendo com Super Mario World e hoje é viciado em documentar tudo que zera em seu backlog, desde JRPG's até Soulslikes.
Inscrever-se
Notificar de
guest
0 Comentários
mais antigos
mais recentes Mais votado
Feedbacks embutidos
Ver todos os comentários