Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

Carregadores de recurso em formato personalizado

Introdução

ResourceFormatLoader é uma interface de criação para carregamento de recursos. Os Recursos são contêineres primários. Quando o carregamento é chamado novamente no mesmo caminho de arquivo, o Recurso carregado anteriormente será referenciado. Naturalmente, os recursos carregados devem ser sem estado (stateless).

Este guia pressupõe que o leitor saiba como criar módulos C++ e tipos de dados do Godot. Se não souber, consulte este guia: Módulos personalizados em C++

Referências

Para que serve?

  • Adicionando novo suporte para muitos formatos de arquivo

  • Formatos de Áudio

  • Formatos de Vídeo

  • Modelos de aprendizado de máquina

Para que não serve?

  • Imagens Rasterizadas

ImageFormatLoader deve ser utilizado para carregar imagens.

Referências

Criando um ResourceFormatLoader

Cada formato de arquivo consiste de um contêiner de dados e um ResourceFormatLoader.

ResourceFormatLoaders são classes que retornam todos os metadados necessários para suportar novas extensões no Godot. A classe deve retornar o nome do formato e a string da extensão.

Além disso, ResourceFormatLoaders deve converter os caminhos de arquivo em recursos com a função load. Para carregar um recurso, a função load deve ler e manipular a serialização de dados.

resource_loader_json.h
#pragma once

#include "core/io/resource_loader.h"

class ResourceFormatLoaderJson : public ResourceFormatLoader {
    GDCLASS(ResourceFormatLoaderJson, ResourceFormatLoader);
public:
    virtual RES load(const String &p_path, const String &p_original_path, Error *r_error = NULL);
    virtual void get_recognized_extensions(List<String> *r_extensions) const;
    virtual bool handles_type(const String &p_type) const;
    virtual String get_resource_type(const String &p_path) const;
};
resource_loader_json.cpp
#include "resource_loader_json.h"

#include "resource_json.h"

RES ResourceFormatLoaderJson::load(const String &p_path, const String &p_original_path, Error *r_error) {
Ref<JsonResource> json = memnew(JsonResource);
    if (r_error) {
        *r_error = OK;
    }
    Error err = json->load_file(p_path);
    return json;
}

void ResourceFormatLoaderJson::get_recognized_extensions(List<String> *r_extensions) const {
    if (!r_extensions->find("json")) {
        r_extensions->push_back("json");
    }
}

String ResourceFormatLoaderJson::get_resource_type(const String &p_path) const {
    return "Resource";
}

bool ResourceFormatLoaderJson::handles_type(const String &p_type) const {
    return ClassDB::is_parent_class(p_type, "Resource");
}

Criando um ResourceFormatSaver

Se você quiser ser capaz de editar e salvar um recurso, você pode implementar um ResourceFormatSaver:

resource_saver_json.h
#pragma once

#include "core/io/resource_saver.h"

class ResourceFormatSaverJson : public ResourceFormatSaver {
    GDCLASS(ResourceFormatSaverJson, ResourceFormatSaver);
public:
    virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
    virtual bool recognize(const RES &p_resource) const;
    virtual void get_recognized_extensions(const RES &p_resource, List<String> *r_extensions) const;
};
resource_saver_json.cpp
#include "resource_saver_json.h"

#include "resource_json.h"
#include "scene/resources/resource_format_text.h"

Error ResourceFormatSaverJson::save(const String &p_path, const RES &p_resource, uint32_t p_flags) {
    Ref<JsonResource> json = memnew(JsonResource);
    Error error = json->save_file(p_path, p_resource);
    return error;
}

bool ResourceFormatSaverJson::recognize(const RES &p_resource) const {
    return Object::cast_to<JsonResource>(*p_resource) != NULL;
}

void ResourceFormatSaverJson::get_recognized_extensions(const RES &p_resource, List<String> *r_extensions) const {
    if (Object::cast_to<JsonResource>(*p_resource)) {
        r_extensions->push_back("json");
    }
}

Criando tipos de dados personalizados

Godot pode não ter um substituto adequado de Tipos de núcleos ou recursos gerenciados. Godot precisa de um novo tipo de dados registrado para reconhecer formatos binários adicionais, como modelos de aprendizado de máquina.

Eis um exemplo de como criar um tipo de dados personalizado:

resource_json.h
#pragma once

#include "core/io/json.h"
#include "core/variant_parser.h"

class JsonResource : public Resource {
    GDCLASS(JsonResource, Resource);

protected:
    static void _bind_methods() {
        ClassDB::bind_method(D_METHOD("set_dict", "dict"), &JsonResource::set_dict);
        ClassDB::bind_method(D_METHOD("get_dict"), &JsonResource::get_dict);

        ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "content"), "set_dict", "get_dict");
    }

private:
    Dictionary content;

public:
    Error load_file(const String &p_path);
    Error save_file(const String &p_path, const RES &p_resource);

    void set_dict(const Dictionary &p_dict);
    Dictionary get_dict();
};
resource_json.cpp
#include "resource_json.h"

Error JsonResource::load_file(const String &p_path) {
    Error error;
    FileAccess *file = FileAccess::open(p_path, FileAccess::READ, &error);
    if (error != OK) {
        if (file) {
            file->close();
        }
        return error;
    }

    String json_string = String("");
    while (!file->eof_reached()) {
        json_string += file->get_line();
    }
    file->close();

    String error_string;
    int error_line;
    JSON json;
    Variant result;
    error = json.parse(json_string, result, error_string, error_line);
    if (error != OK) {
        file->close();
        return error;
    }

    content = Dictionary(result);
    return OK;
}

Error JsonResource::save_file(const String &p_path, const RES &p_resource) {
    Error error;
    FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &error);
    if (error != OK) {
        if (file) {
            file->close();
        }
        return error;
    }

    Ref<JsonResource> json_ref = p_resource.get_ref_ptr();
    JSON json;

    file->store_string(json.print(json_ref->get_dict(), "    "));
    file->close();
    return OK;
}

void JsonResource::set_dict(const Dictionary &p_dict) {
    content = p_dict;
}

Dictionary JsonResource::get_dict() {
    return content;
}

Considerações

Algumas bibliotecas podem não definir certas rotinas comuns, como manipulação de E/S. Portanto, são necessárias traduções de chamadas do Godot.

Por exemplo, aqui está o código para traduzir chamadas de FileAccess em std::istream.

#include "core/io/file_access.h"

#include <istream>
#include <streambuf>

class GodotFileInStreamBuf : public std::streambuf {

public:
    GodotFileInStreamBuf(FileAccess *fa) {
        _file = fa;
    }
    int underflow() {
        if (_file->eof_reached()) {
            return EOF;
        } else {
            size_t pos = _file->get_position();
            uint8_t ret = _file->get_8();
            _file->seek(pos); // Required since get_8() advances the read head.
            return ret;
        }
    }
    int uflow() {
        return _file->eof_reached() ? EOF : _file->get_8();
    }

private:
    FileAccess *_file;
};

Referências

Registrando o novo formato de arquivo

O Godot registra o ResourcesFormatLoader com um manipulador ResourceLoader. O manipulador seleciona automaticamente o carregador adequado quando load é chamado.

register_types.h
void register_json_types();
void unregister_json_types();
register_types.cpp
#include "register_types.h"

#include "core/class_db.h"
#include "resource_loader_json.h"
#include "resource_saver_json.h"
#include "resource_json.h"

static Ref<ResourceFormatLoaderJson> json_loader;
static Ref<ResourceFormatSaverJson> json_saver;

void register_json_types() {
    ClassDB::register_class<JsonResource>();

    json_loader.instantiate();
    ResourceLoader::add_resource_format_loader(json_loader);

    json_saver.instantiate();
    ResourceSaver::add_resource_format_saver(json_saver);
}

void unregister_json_types() {
    ResourceLoader::remove_resource_format_loader(json_loader);
    json_loader.unref();

    ResourceSaver::remove_resource_format_saver(json_saver);
    json_saver.unref();
}

Referências

Carregando no GDScript

Salve um arquivo chamado demo.json com o conteúdo a seguir e coloque-o na pasta raiz do projeto:

{
  "savefilename": "demo.json",
  "demo": [
    "welcome",
    "to",
    "godot",
    "resource",
    "loaders"
  ]
}

Em seguida, anexe o seguinte script a qualquer nó:

extends Node

@onready var json_resource = load("res://demo.json")

func _ready():
    print(json_resource.get_dict())