Voltar para o Blog
Quest Log

Como Carregar JSON e Dados Externos no Godot 4 com GDScript

Arquivo de dados de itens ao lado de codigo GDScript no editor do Godot

Aprenda a carregar JSON e dados externos no Godot 4 com GDScript tipado: ler com FileAccess, parse com JSON.parse_string, tratar erro e instanciar itens.

Mais cedo ou mais tarde todo jogo cresce e voce percebe que colocar cada item, inimigo ou dialogo direto no codigo virou uma bagunca. A solucao madura e separar dados de logica: em vez de escrever var espada = {"dano": 25} no meio de um script, voce guarda tudo num arquivo de texto e faz o jogo carregar JSON de dados externos no boot. Neste tutorial voce vai aprender a carregar JSON e dados externos no Godot 4 com GDScript tipado, usando FileAccess para ler o arquivo, JSON.parse_string para fazer o parse, tratamento de erro de verdade e um exemplo concreto que le uma lista de itens e instancia cada um deles.

Por que separar dados do codigo (data-driven design)

Data-driven design e a ideia de que os numeros e textos que definem o seu jogo (o dano de uma arma, o preco de uma pocao, a fala de um NPC) devem viver fora do codigo, em arquivos de dados. O codigo vira um motor que le esses dados e monta o jogo a partir deles.

As vantagens sao praticas. Voce ajusta o balanceamento de cinquenta itens editando um arquivo de texto, sem tocar em nenhuma linha de GDScript nem re-testar a compilacao. Um designer que nao programa consegue mexer nos numeros. Voce pode ate deixar o arquivo aberto para a comunidade criar mods. E o mesmo motor que carrega cinco itens carrega quinhentos, sem crescer em complexidade.

Hardcodar dados no codigo faz o oposto: cada novo inimigo exige mexer no script, cada ajuste de preco vira um novo commit, e a fronteira entre "regra do jogo" e "conteudo do jogo" some. Um arquivo JSON externo devolve essa fronteira para voce.

Como e um JSON de itens

Vamos usar um exemplo concreto o tutorial inteiro: uma loja com uma lista de itens. Cada item tem id, nome, dano e preco. Salve isto como res://data/itens.json:

{
  "itens": [
    { "id": 1, "nome": "Espada de Ferro", "dano": 25, "preco": 120 },
    { "id": 2, "nome": "Arco Curto", "dano": 15, "preco": 80 },
    { "id": 3, "nome": "Cajado Arcano", "dano": 40, "preco": 300 }
  ]
}

Repare na estrutura: um objeto no topo com a chave itens, cujo valor e um array de objetos. Cada objeto do array vira um Dictionary no Godot, e o array inteiro vira um Array. Guardar tudo dentro de uma chave raiz (itens) em vez de deixar o array solto no topo facilita adicionar mais coisas depois, como uma chave versao ou moeda, sem quebrar quem le o arquivo.

Lendo o arquivo com FileAccess

Antes de fazer o parse voce precisa do conteudo do arquivo como texto. No Godot 4 isso e feito com FileAccess.open, que substitui a antiga classe File do Godot 3. Ele devolve um objeto de arquivo ou null se algo deu errado (caminho invalido, arquivo inexistente, permissao negada).

extends Node

func _ler_texto(caminho: String) -> String:
    var file := FileAccess.open(caminho, FileAccess.READ)
    if file == null:
        var erro: int = FileAccess.get_open_error()
        push_error("Nao consegui abrir %s (erro %d)" % [caminho, erro])
        return ""

    var conteudo: String = file.get_as_text()
    file.close()
    return conteudo

Sempre cheque o null. Um caminho digitado errado nao levanta excecao no GDScript, ele so devolve null, e se voce seguir usando o arquivo o jogo quebra la na frente com uma mensagem confusa. Checar na hora e chamar FileAccess.get_open_error() te da o motivo real na saida de erro.

Fazendo o parse com JSON.parse_string e tratando erro

Com o texto em maos, o parse no Godot 4 tem duas formas. A rapida e JSON.parse_string, um metodo estatico que devolve o dado convertido ou null se o JSON for invalido. A completa usa uma instancia de JSON com parse, que devolve um codigo de erro e ainda te informa a linha exata do problema. Para dados que voce controla, parse_string basta; para arquivos vindos de fora, a versao com diagnostico compensa.

func _parse_seguro(texto: String) -> Variant:
    var json := JSON.new()
    var erro: int = json.parse(texto)
    if erro != OK:
        push_error("JSON invalido na linha %d: %s" % [json.get_error_line(), json.get_error_message()])
        return null
    return json.data

O retorno e do tipo Variant de proposito: dependendo do JSON, o topo pode ser um Dictionary, um Array, um numero ou uma String. No nosso caso o topo e um Dictionary, entao a proxima etapa e confirmar isso antes de confiar no resultado.

Uma armadilha classica de tipos: todo numero do JSON vira float no Godot, mesmo 25 sem casa decimal. Se voce precisa de um int (o id de um item, uma quantidade, um dano em pontos inteiros), faca o cast explicito com int(valor). Ignorar isso te da bugs silenciosos, como um id que vira 1.0 e nunca bate numa comparacao com 1.

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

Iterando e validando os dados

Agora juntamos as pecas: ler, parsear, validar a estrutura e percorrer os itens. Validar dados ausentes e o que separa um carregador de brinquedo de um que aguenta arquivo mal formado. Nunca assuma que a chave existe: use has() antes de acessar, ou o operador de valor padrao dict.get("chave", padrao).

func carregar_itens(caminho: String) -> Array[Dictionary]:
    var itens: Array[Dictionary] = []

    var texto: String = _ler_texto(caminho)
    if texto.is_empty():
        return itens

    var dados: Variant = _parse_seguro(texto)
    if not dados is Dictionary:
        push_error("Esperava um objeto no topo do JSON, veio outra coisa")
        return itens

    var raiz: Dictionary = dados
    if not raiz.has("itens"):
        push_error("JSON sem a chave 'itens'")
        return itens

    var lista: Array = raiz["itens"]
    for entrada in lista:
        if not entrada is Dictionary:
            push_warning("Entrada ignorada: nao e um objeto")
            continue

        var item: Dictionary = {
            "id": int(entrada.get("id", 0)),
            "nome": String(entrada.get("nome", "Sem nome")),
            "dano": int(entrada.get("dano", 0)),
            "preco": int(entrada.get("preco", 0)),
        }
        itens.append(item)

    return itens

Tres detalhes valem atencao. O is Dictionary protege contra um JSON cujo topo nao e um objeto. O has("itens") evita acessar uma chave inexistente. E o entrada.get("id", 0) devolve um padrao quando o campo falta, entao um item sem preco no arquivo entra como 0 em vez de derrubar o jogo. Note tambem o cast int(...) em cada numero, resolvendo a questao do float que mencionei acima.

Para consumir o resultado e instanciar algo de verdade, basta percorrer o array tipado:

func _ready() -> void:
    var itens: Array[Dictionary] = carregar_itens("res://data/itens.json")
    for item in itens:
        print("%s | dano %d | %d moedas" % [item["nome"], item["dano"], item["preco"]])
        _instanciar_item(item)

func _instanciar_item(dados: Dictionary) -> void:
    var no := Node.new()
    no.name = "Item_%d" % dados["id"]
    no.set_meta("dano", dados["dano"])
    no.set_meta("preco", dados["preco"])
    add_child(no)

Aqui cada dicionario vira um no na cena com metadados. Na pratica voce trocaria esse Node.new() por um preload da sua cena de item, mas a ideia e a mesma: o JSON descreve o que existe e o codigo transforma cada descricao num objeto vivo do jogo.

Quando usar JSON, Resource ou ConfigFile

JSON nao e a unica forma de guardar dados no Godot, e escolher errado custa caro depois. Vale conhecer as tres opcoes principais.

Use JSON quando os dados precisam ser lidos ou escritos por algo de fora do Godot: uma planilha exportada, uma API, um sistema de mods aberto para a comunidade, ou dados que uma pessoa nao programadora vai editar num editor de texto qualquer. A forca do JSON e ser um formato universal e legivel.

Use um Resource customizado (.tres) quando os dados sao internos do jogo e voce quer tipagem forte e validacao ja no editor. Um Resource te da campos com tipo definido, autocomplete, e carrega mais rapido que parsear texto. O custo e ficar preso ao Godot: ninguem edita um .tres fora da engine com conforto. Se esse for o seu caso, veja como montar um Resource customizado no Godot para dados de itens tipados de ponta a ponta.

Use ConfigFile para preferencias e configuracoes em pares chave-valor por secao, como volume e resolucao. Ele nao foi feito para listas grandes de conteudo, mas brilha em opcoes: o proprio Godot faz o cast de tipos e o arquivo .ini fica legivel. O tutorial de salvar opcoes com ConfigFile mostra esse caminho em detalhe. Se o seu objetivo e guardar progresso do jogador e nao conteudo estatico, o sistema de save e load no Godot cobre esse outro cenario.

Uma nota sobre linguagem: todos os exemplos aqui usam GDScript tipado, que e a escolha natural para a maioria dos projetos em Godot. Se voce esta em duvida entre linguagens antes de investir num sistema de dados, o comparativo de GDScript ou C-Sharp no Godot ajuda a decidir, e vale saber que a API de JSON e FileAccess funciona quase igual nas duas.

Fechando o ciclo

Carregar dados externos muda a forma como voce constroi um jogo. Em vez de o codigo carregar a responsabilidade de descrever cada item, ele vira um motor que le descricoes e as transforma em objetos. O caminho e sempre o mesmo: abrir o arquivo com FileAccess.open e checar null, converter o texto com JSON.parse_string ou JSON.parse e tratar o erro, confirmar a estrutura com is e has, e iterar validando cada campo com get e valor padrao, sem esquecer o cast de float para int.

Comece pequeno: pegue o JSON de itens deste tutorial, cole no seu projeto e faca a lista aparecer no console. Depois troque o Node.new() pela sua cena real de item e voce ja tem um sistema data-driven de verdade rodando. A partir dai, adicionar um novo item e so escrever mais uma linha no arquivo, sem tocar no codigo.

Perguntas frequentes

Por que numeros do JSON viram float no Godot?

O parser JSON do Godot converte todo numero para float, sem distinguir inteiro de decimal. Se voce precisa de um int (id, quantidade, dano em pontos inteiros), faca o cast explicito com int() ao ler o valor do dicionario.

Qual a diferenca entre carregar JSON e usar um Resource customizado?

JSON e texto editavel por qualquer pessoa e por ferramentas externas, otimo para dados abertos ou vindos de fora do projeto. O Resource customizado (.tres) e tipado, valida no editor e carrega mais rapido, mas fica preso ao Godot. Use JSON para dados externos e mod-friendly, e Resource para dados internos do jogo.

Preciso fechar o arquivo depois de ler com FileAccess?

No Godot 4 o FileAccess fecha sozinho quando a variavel sai de escopo, mas chamar file.close() explicitamente deixa claro o fim da leitura e evita segurar o arquivo por mais tempo que o necessario.