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.

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