Programando um Jogo (Pong) somente com IA (ChatGPT-5)

Douglas Barres
11 Min Leitura

No primeiro artigo dessa série, tentamos fazer a Gemini programar o Pong na Godot. Ela conseguiu fazer a base, mas não conseguiu resolver um bug crucial para um bom funcionamento do jogo, então vamos dizer que ela falhou nessa tarefa de programar sozinha. Então dessa vez vamos tentar o amado ChatGPT na mesma tarefa.

Começando Simples

A regra desse desafio com o ChatGPT é o mesmo do Gemini: Só podemos usar códigos criados por eles, nada de outros códigos pegos na internet, mesmo que já estejam prontos.

O jogo de escolha vai continuar sendo PONG para termos um bom parâmetro de comparação. O único problema desse desafio (pelo menos pra mim) com o ChatGPT vai ser o limite diário de mensagens dele.

ChatGPT-5

Eu sempre fico embasbacado com a velocidade das mensagens dessa IA. Mesmo quando mandamos imagem ou vídeo para ela analisar, as mensagens sempre chegam muito rápido. Mas sem mais enrolação, já pedindo para ela criar o script do Paddle. Um fato interessante é que ela disse não saber se eu estava usando Godot 4 ou uma versão mais antiga, mas de qualquer jeito me deu a instrução de fazer um CharacterBody2D com o seguinte script:

extends CharacterBody2D

@export var speed: float = 400.0  # Velocidade de movimento do paddle
@export var is_player_one: bool = true  # Define se este paddle é o da esquerda ou direita

func _physics_process(delta: float) -> void:
    var input_dir := 0.0

    # Controles diferentes para cada paddle
    if is_player_one:
        if Input.is_action_pressed("ui_up"):
            input_dir = -1
        elif Input.is_action_pressed("ui_down"):
            input_dir = 1
    else:
        if Input.is_action_pressed("move_up_p2"):
            input_dir = -1
        elif Input.is_action_pressed("move_down_p2"):
            input_dir = 1

    velocity = Vector2(0, input_dir * speed)
    move_and_slide()

    # Limitar dentro da tela
    var screen_size = get_viewport_rect().size
    var paddle_height = $CollisionShape2D.shape.extents.y
    position.y = clamp(position.y, paddle_height, screen_size.y - paddle_height)

Não sou um expert em códigos e imagino que quem possa dizer qual das IA fez o melhor código é quem manja de programação. De qualquer jeito quem tiver a curiosidade de ir comparando os códigos feito pelo ChatGPT e a Gemini, o artigo que fiz da Gemini está aqui.

Um fato que eu sempre notei no ChatGPT é que ele sempre pergunta no final da mensagem se o usuário quer que ele faça mais alguma coisa, até dá uma sugestão relacionado ao que ele acabou de fazer. E ah, sempre com os malditos emojis! 🤢

Para a Bola, ele escolheu fazer um CharacterBody2D e também fez o script que está abaixo.

extends CharacterBody2D

@export var speed: float = 300.0   # Velocidade base da bola
var direction: Vector2 = Vector2.ZERO

func _ready() -> void:
    # Quando o jogo começa, lança a bola em direção aleatória
    reset_ball()

func _physics_process(delta: float) -> void:
    velocity = direction * speed
    var collision = move_and_collide(velocity * delta)

    if collision:
        # Se colidir com algo, reflete a direção
        direction = direction.bounce(collision.get_normal()).normalized()

func reset_ball() -> void:
    position = get_viewport_rect().size / 2  # Volta pro centro da tela
    
    # Direção inicial aleatória (esquerda/direita + leve variação vertical)
    var x_dir = randf() > 0.5 ? 1 : -1
    var y_dir = randf_range(-0.5, 0.5)
    direction = Vector2(x_dir, y_dir).normalized()

De quebra também me instruiu a como fazer as “paredes” do topo e baixo do cenário.

Foi nesse script da bola que apareceu o primeiro erro e pela primeira vez eu vi o ChatGPT-5 demorar. Ele usou a opção de “Pensar” e ficou nessa por 2 minutos. Aparentemente foi alguma coisa relacionado a ela enviar novamente algo que só funciona em versões antigas da Godot. Com esse problema corrigido, vamos ao Paddle da CPU.

extends CharacterBody2D

@export var speed: float = 350.0          # Velocidade da CPU
@export var ball_path: NodePath           # Caminho para a bola (arrastar no editor)
@export var reaction_delay: float = 0.05  # Atraso de reação para não ser perfeito demais

var ball: Node2D
var target_y: float

func _ready() -> void:
    if ball_path != NodePath():
        ball = get_node(ball_path)

func _process(delta: float) -> void:
    if not ball:
        return

    # CPU "mira" a posição da bola no Y com leve atraso
    target_y = lerp(target_y, ball.position.y, reaction_delay)

func _physics_process(delta: float) -> void:
    if not ball:
        return

    var dir := 0.0
    if position.y < target_y - 10:
        dir = 1
    elif position.y > target_y + 10:
        dir = -1

    velocity = Vector2(0, dir * speed)
    move_and_slide()

    # Limitar dentro da tela
    var screen_size = get_viewport_rect().size
    var paddle_height = $CollisionShape2D.shape.extents.y
    position.y = clamp(position.y, paddle_height, screen_size.y - paddle_height)

Até aqui tudo bem. Agora o último elemento do core do jogo que faltava era o sistema de gols e pontos. Então ela mandou o script do goal.gd

extends Area2D

@export var side: String = "left"  # "left" ou "right"

signal goal_scored(side: String)

func _ready() -> void:
    connect("body_entered", Callable(self, "_on_body_entered"))

func _on_body_entered(body: Node) -> void:
    if body.is_in_group("ball"):  # garante que é a bola
        emit_signal("goal_scored", side)

Ajuste no ball.gd

func _ready() -> void:
    add_to_group("ball")
    rng.randomize()
    reset_ball()

Script de gerenciador de jogo (game.gd)

extends Node
var score_player: int = 0
var score_cpu: int = 0

@onready var ball = $Ball
@onready var goal_left = $Goal_Left
@onready var goal_right = $Goal_Right

func _ready() -> void:
goal_left.connect("goal_scored", Callable(self, "_on_goal_scored"))
goal_right.connect("goal_scored", Callable(self, "_on_goal_scored"))

func _on_goal_scored(side: String) -> void:
if side == "left":
score_cpu += 1
elif side == "right":
score_player += 1

print("Placar: Jogador %d x %d CPU" % [score_player, score_cpu])

ball.reset_ball()

Pronto, toda a nossa base do jogo Pong está pronta! Chegamos a uma conclusão: com as duas IA’s, conseguimos criar toda uma base do Pong sem problemas… mas aconteceu o mesmo bug de quando começou o inferno que a Gemini fez eu passar…

O problema das IAs: Resolução de problemas

Chegamos ao bug que também tinha acontecido com a Gemini programando: a bola arrastando o Paddle se o mesmo atingisse ela com uma das pontas. Mandei a mesma descrição do bug para o ChatGPT-5 e ele mandou a “solução”.

A desgraça da IA fez eu mudar todo o Nó dos Paddles para Area2D, mas depois que não deu certo, ela fez eu mudar pra StaticBody2D. Ou seja, ela se embananou igual a Gemini, dando soluções que realmente resolvem algo, mas acabam quebrando com toda lógica do jogo. A solução definitiva dela foi transformar os Paddles em StaticBody2D e modificar o script deles:

extends CharacterBody2D

@export var speed: float = 300.0
var direction: Vector2 = Vector2.ZERO

signal goal_scored(side: String)

func _ready() -> void:
	reset_ball()

func _physics_process(delta: float) -> void:
	velocity = direction * speed
	velocity = move_and_slide()

	# checa colisões do frame
	for i in range(get_slide_collision_count()):
		var collision = get_slide_collision(i)
		var collider = collision.get_collider()

		if collider and collider.is_in_group("paddle"):
			direction.x = -direction.x

			var paddle_y = collider.position.y
			var hit_pos = position.y - paddle_y
			var paddle_height = collider.get_node("CollisionShape2D").shape.extents.y
			var normalized_hit = hit_pos / paddle_height

			direction.y = normalized_hit
			direction = direction.normalized()

			# Reposiciona a bola fora do paddle
			position += collision.get_normal() * 5

		else:
			direction = direction.bounce(collision.get_normal()).normalized()

func reset_ball() -> void:
	position = get_viewport_rect().size / 2
	direction = Vector2(randf_range(-1, 1), randf_range(-0.5, 0.5)).normalized()

Eu já estava descrente, sabendo que o ChatGPT-5 também tinha peidado na farofa… mas eu estava errado, ou quase errado. O bug da bola arrastar o Paddle que apareceu muito no artigo que fiz com a Gemini, estava corrigido. Mas tinha outro problema agora…

Acredito que em essência o bug continua parecido com o que aconteceu no nosso experimento com a Gemini: o problema é justamente quando a bola bate em uma das pontas do Paddle. Dessa vez, mesmo impedindo da bola levar junto pro limbo o Paddle, é o Paddle que está levando a bola pra cima ou baixo, caso o jogador fique pressionando o botão de movimento com a bola em cima/baixo do Paddle.

Fora mensagens e mais mensagens tentando resolver esse problema e nada. Fiquei pasmo na velocidade que o ChatGPT-5 analisa os vídeos que eu mando (mesmo sendo esses mais curtos como os anexados na postagem).

Conclusão

No fim ficou elas por elas: o Gemini empacou basicamente no mesmo lugar que o ChatGPT-5 empacou. A única vantagem de usar o Gemini é que ele basicamente dá tokens infinitos, então esse projetinho nele eu fiz em 1 tarde. Já o ChatGPT-5 ele dá um limite de uso diário no plano free para usar a versão mais parruda citada nessa postagem, então tive que dividir o desenvolvimento em 2 tardes.

Claro, esses artigos foram feitos com a intenção de que somente as IAs tentassem resolver os pepinos. Se eu for criar algo com IA programando para mim, pode ter certeza que esses pepinos que eles não conseguem resolver, eu vou tentar algumas gambiarras que qualquer gamedev que se preze deve saber.

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