Voltar para o Blog
Quest Log

enum no GDScript: adeus numeros magicos e strings soltas

Tokens de estado rotulados como parado, andando e pulando conectados num diagrama limpo de constantes nomeadas

Aprenda enum no GDScript da Godot 4: sintaxe, valores customizados, uso com match e @export no Inspector. Deixe estados e tipos legiveis e seguros.

Ferramenta:Godot

Você abre um script que escreveu semana passada e encontra isto: if estado == 2:. O que era 2 mesmo? Pulando? Caindo? Você para, rola o arquivo, procura onde definiu, e perde dois minutos só pra lembrar. Multiplique isso por cada número solto e cada "andando" digitado à mão no seu projeto e você tem a receita do código que ninguém, nem você, consegue ler depois. O enum existe exatamente pra matar esse problema: ele dá nome aos números, agrupa constantes relacionadas e deixa o GDScript te avisar quando você escreve besteira.

Neste post eu mostro o que é um enum na Godot 4, por que ele vence tanto os números mágicos quanto as strings de estado, a sintaxe completa (nomeado, anônimo, valores customizados), como combinar enum com match pra montar uma máquina de estados simples e como exportar tudo pro Inspector. Código sempre tipado e sempre Godot 4 real.

O problema: numeros magicos e strings soltas

Antes de mostrar o enum, vale entender o que ele substitui. Tem dois padrões ruins que quase todo iniciante escreve.

O primeiro é o número mágico. Você decide que 0 é "parado", 1 é "andando", 2 é "pulando", e espalha esses números pelo código:

var estado: int = 0

func _physics_process(delta: float) -> void:
    if estado == 1:
        mover()
    elif estado == 2:
        pular()

Funciona, mas é ilegível. Ninguém sabe o que 1 e 2 significam sem um comentário. E se você resolver inserir um estado novo no meio, todos os números mudam e o código quebra em silêncio.

O segundo padrão é a string de estado. Parece melhor porque é legível:

var estado: String = "parado"

func _physics_process(delta: float) -> void:
    if estado == "andando":
        mover()
    elif estado == "pulando":
        pular()

O problema aqui é sutil e traiçoeiro. Se você digitar "andandi" num lugar, o GDScript não reclama. Ele compara, dá false, e o bug fica escondido até você perceber que o personagem "às vezes" não anda. String não tem autocomplete confiável e não protege contra erro de digitação. Você troca clareza por fragilidade.

O que e enum e a sintaxe basica

Um enum (de "enumeração") é uma forma de dar nome a um conjunto de constantes inteiras relacionadas. Você declara uma vez, no topo do script:

enum Estado { PARADO, ANDANDO, PULANDO }

Com essa linha, a Godot cria três constantes: Estado.PARADO vale 0, Estado.ANDANDO vale 1 e Estado.PULANDO vale 2. A contagem começa em 0 e sobe de 1 em 1, automaticamente. Você nunca mais precisa lembrar qual número é qual, porque usa o nome:

extends CharacterBody2D

enum Estado { PARADO, ANDANDO, PULANDO }

var estado: Estado = Estado.PARADO

func _physics_process(delta: float) -> void:
    if estado == Estado.ANDANDO:
        mover()
    elif estado == Estado.PULANDO:
        pular()

Repare em dois detalhes. Primeiro, var estado: Estado usa o próprio enum como tipo da variável, o que ativa o autocomplete: ao digitar Estado. o editor lista PARADO, ANDANDO e PULANDO pra você. Segundo, se você tentar estado = Estado.PULANDU, o GDScript acusa erro na hora, porque essa constante não existe. É a diferença exata pra string solta: o erro aparece antes de rodar, não depois.

A honestidade sobre o que acontece por baixo: enum no GDScript é açúcar sintático para constantes inteiras. Estado.PULANDO é literalmente o número 2 em tempo de execução. Se você imprimir print(estado), sai 2, não "PULANDO". O tipo Estado documenta a intenção e melhora o autocomplete, mas a Godot não impede em tempo de execução que você jogue um int fora da faixa numa variável tipada como enum. O ganho real é legibilidade e proteção contra erro de digitação, não uma trava rígida. Sabendo disso, você usa o enum pelo que ele é bom, sem esperar milagre.

enum nomeado vs enum anonimo

O exemplo acima é um enum nomeado: tem o nome Estado. Esse nome vira um tipo que você pode usar em variáveis, parâmetros e retornos de função:

func mudar_estado(novo: Estado) -> void:
    estado = novo
    print("Novo estado: ", novo)

Aqui o parâmetro novo: Estado deixa claro que essa função espera um valor do enum Estado, não um int qualquer. Quem chamar a função vê no autocomplete o que passar.

Existe também o enum anônimo, sem nome:

enum { NORTE, SUL, LESTE, OESTE }

var direcao: int = NORTE

Aqui a Godot cria as constantes NORTE, SUL, LESTE e OESTE soltas no script (você usa NORTE, não Direcao.NORTE), mas não existe um tipo agrupador. Por isso a variável fica tipada como int, não como um enum. Use o anônimo pra constantes rápidas de uso interno, quando você não precisa passar o conjunto como tipo. Na maioria dos casos de gameplay, o nomeado é melhor: dá tipo pras variáveis, agrupa tudo sob um nome e evita colisão de nomes entre enums diferentes.

Próximo nível
Quer aprender isso na prática?

No CursoGame.Dev você sai dos tutoriais soltos e constrói jogos publicáveis, com trilha progressiva, quests práticas e feedback real.

Conhecer a plataforma
+500 alunos4.9/5Garantia 7 dias

Valores customizados

Por padrão o enum numera de 0 pra cima, mas você pode definir valores explícitos. Isso é útil quando os números precisam casar com algo externo: IDs de rede, códigos de save, faixas de dano.

enum CodigoDano {
    FOGO = 10,
    GELO = 20,
    VENENO = 30,
    ELETRICO = 40,
}

Você também pode misturar: membros sem valor continuam a contagem a partir do último definido.

enum Fase {
    TUTORIAL,        # 0
    FLORESTA = 5,    # 5
    CAVERNA,         # 6
    CASTELO,         # 7
}

TUTORIAL vale 0, FLORESTA pula pra 5, e a partir daí CAVERNA é 6 e CASTELO é 7. Deixar espaço nos números (0, 5, 6...) permite inserir fases novas no meio depois sem renumerar tudo. Só use valores customizados quando tiver motivo; se você só quer nomes legíveis, deixe a Godot numerar sozinha.

enum com match: maquina de estados simples

O enum brilha de verdade quando você combina com match, o equivalente do GDScript ao switch de outras linguagens. Aquele encadeado de if/elif do começo vira algo muito mais limpo. Uma máquina de estados finitos (FSM, na sigla em inglês) é só isto: o objeto está sempre em exatamente um estado, e o comportamento por frame depende de qual é.

extends CharacterBody2D

enum Estado { PARADO, ANDANDO, PULANDO }

const VELOCIDADE: float = 200.0
const FORCA_PULO: float = -400.0
const GRAVIDADE: float = 980.0

var estado: Estado = Estado.PARADO

func _physics_process(delta: float) -> void:
    velocity.y += GRAVIDADE * delta
    var direcao: float = Input.get_axis("ui_left", "ui_right")

    match estado:
        Estado.PARADO:
            velocity.x = 0.0
            if direcao != 0.0:
                estado = Estado.ANDANDO
            elif Input.is_action_just_pressed("ui_accept") and is_on_floor():
                estado = Estado.PULANDO
        Estado.ANDANDO:
            velocity.x = direcao * VELOCIDADE
            if direcao == 0.0:
                estado = Estado.PARADO
            elif Input.is_action_just_pressed("ui_accept") and is_on_floor():
                estado = Estado.PULANDO
        Estado.PULANDO:
            velocity.x = direcao * VELOCIDADE
            if is_on_floor():
                estado = Estado.PARADO if direcao == 0.0 else Estado.ANDANDO

    if estado == Estado.PULANDO and is_on_floor() and Input.is_action_just_pressed("ui_accept"):
        velocity.y = FORCA_PULO

    move_and_slide()

Cada bloco do match cuida de um estado e das transições possíveis a partir dele. Ler esse código é ler as regras do personagem em português quase direto: "quando parado, se apertar direção, começa a andar". Adicionar um estado AGACHADO é adicionar um valor no enum e um bloco no match, sem mexer no resto.

Uma dica que evita bug bobo: quando seus estados dependem de tempo, como um dash com duração fixa ou um cooldown antes de poder atacar de novo, não conte frames na mão. Use um Timer. Eu detalho esse padrão no post sobre Timer e cooldown na Godot, que combina muito bem com a FSM de enum daqui.

Exportar enum no Inspector com @export

Aqui vem um dos usos mais práticos. Se você exportar uma variável tipada com um enum nomeado, a Godot desenha um dropdown no Inspector com os nomes das constantes, não os números:

extends CharacterBody2D

enum Dificuldade { FACIL, NORMAL, DIFICIL, PESADELO }

@export var dificuldade: Dificuldade = Dificuldade.NORMAL
@export var estado_inicial: Estado = Estado.PARADO

No Inspector, dificuldade aparece como um menu com FACIL, NORMAL, DIFICIL e PESADELO. Ninguém precisa saber que PESADELO é 3; a pessoa que está montando a fase só escolhe pelo nome, e é impossível digitar um valor inválido. Isso é ótimo pra deixar quem não programa ajustar o jogo, e evita a classe inteira de bug de "coloquei o número errado no Inspector".

Se você está começando com o @export e quer entender range, grupos e os outros hints do Inspector, vale saber que o enum é só uma das opções que ele oferece. E cada instância da cena guarda o próprio valor exportado, o que combina bem com grupos de nodes quando você quer, por exemplo, mandar todos os inimigos de uma dificuldade reagirem juntos.

Casos reais onde enum resolve

Fora estado de personagem, o enum aparece em quase todo sistema de jogo:

  • Tipos de item: enum TipoItem { ARMA, ARMADURA, POCAO, CHAVE }. O inventário decide o que fazer com o item pelo tipo, com um match limpo, em vez de comparar strings.
  • Dificuldade: como no exemplo do Inspector, um dropdown de FACIL a PESADELO que ajusta vida de inimigos e dano.
  • Fases e progressão: enum Fase { TUTORIAL, FLORESTA, CAVERNA, CASTELO } pra controlar em que ponto o jogador está. Quando você monta um sistema que desbloqueia conteúdo conforme o jogador avança, esse enum vira a espinha dorsal; eu mostro a mecânica completa no post sobre sistema de progressão.
  • Direções, resultados de combate, fases do turno: qualquer conjunto fechado e conhecido de opções é candidato a enum.

A regra é simples: se você tem um conjunto fixo de opções mutuamente exclusivas (o personagem está parado OU andando OU pulando, nunca dois ao mesmo tempo), enum é a ferramenta certa. Se as opções são abertas e imprevisíveis, ou é texto que o jogador digita, aí não é caso de enum.

Fechando

Enum não é um recurso avançado que você deixa pra depois. É uma das primeiras coisas que separam código de iniciante de código legível. Cada número mágico e cada string de estado que você troca por um enum nomeado é um bug de digitação a menos e uma linha a mais que se explica sozinha. Comece pequeno: pegue o estado do seu personagem, transforme os números soltos num enum Estado, troque o if/elif por match e exporte a dificuldade pro Inspector. Só isso já deixa o projeto visivelmente mais limpo.

Se você ainda está decidindo entre GDScript e outra linguagem pra levar esses hábitos adiante, vale ler C# vs GDScript na Godot antes de investir tempo. Mas pra aprender os fundamentos, como enum, match e tipagem, o GDScript é o caminho mais direto na Godot 4.

Perguntas frequentes

O que e um enum no GDScript?

E um jeito de dar nome a um conjunto de constantes inteiras relacionadas. Voce escreve enum Estado { PARADO, ANDANDO, PULANDO } e a Godot cria tres constantes int (0, 1, 2) agrupadas sob o nome Estado. Por baixo sao numeros, mas no codigo voce usa nomes legiveis como Estado.PULANDO em vez de decorar que 2 significa pular.

Qual a diferenca entre enum nomeado e enum anonimo?

O enum nomeado tem um nome (enum Dificuldade { ... }) e pode ser usado como tipo de variavel: var nivel: Dificuldade. O enum anonimo nao tem nome (enum { NORTE, SUL, LESTE, OESTE }) e so cria as constantes soltas no script, sem um tipo agrupador. Use o nomeado quando quiser tipar variaveis e parametros; o anonimo serve para constantes rapidas de uso interno.

enum no GDScript e realmente type-safe?

Parcialmente. Um enum nomeado usado como tipo ajuda a documentar a intencao e melhora o autocomplete, mas por baixo o valor e um int comum. A Godot nao impede que voce atribua um numero fora da faixa do enum a uma variavel tipada como enum. O ganho principal e legibilidade e evitar numeros magicos, nao uma barreira rigida em tempo de execucao.

Como exportar um enum para o Inspector na Godot 4?

Basta declarar o enum e exportar a variavel tipada com ele: @export var estado_inicial: Estado. A Godot mostra um dropdown no Inspector com os nomes das constantes (PARADO, ANDANDO, PULANDO), e nao os numeros. Assim voce escolhe o valor pelo nome, sem risco de digitar um int errado.

Posso definir valores customizados num enum?

Sim. Por padrao o primeiro membro vale 0 e cada seguinte soma 1, mas voce pode atribuir valores explicitos: enum CodigoDano { FOGO = 10, GELO = 20, VENENO = 30 }. Membros sem valor continuam a contagem a partir do ultimo definido. Isso e util para casar com IDs de rede, mascaras de bits ou codigos que precisam de numeros especificos.