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.
Checking the stable version of the documentation...
Chargeurs de format de ressources personnalisés
Introduction
ResourceFormatLoader est une interface usine pour le chargement de fichiers. Les ressources sont des conteneurs primaires. Lorsque le chargement est appelé à nouveau sur le même chemin de fichier, la ressource précédemment chargée est référencée. Naturellement, les ressources chargées doivent être sans état(stateless).
Ce guide part du principe que le lecteur sait comment créer des modules C++ et des types de données Godot. Si ce n'est pas le cas, voir : Modules personnalisés en C++
References
Pourquoi ?
Ajout d'un nouveau support pour de nombreux formats de fichiers
Formats audio
Formats vidéo
Modèles de machine learning
Qu'est-ce qui ne l'est pas ?
Images raster
ImageFormatLoader doit être utilisé pour charger les images.
References
Création d'un ResourceFormatLoader
Chaque format de fichier se compose d'un conteneur de données et d'un ResourceFormatLoader
.
ResourceFormatLoaders are classes which return all the necessary metadata for supporting new extensions in Godot. The class must return the format name and the extension string.
De plus, les ResourceFormatLoaders doivent convertir les chemins d'accès aux fichiers en ressources avec la fonction load
. Pour charger une ressource, la fonction load
doit lire et gérer la sérialisation des données.
#ifndef RESOURCE_LOADER_JSON_H
#define RESOURCE_LOADER_JSON_H
#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;
};
#endif // RESOURCE_LOADER_JSON_H
#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");
}
Création d'un ResourceFormatSaver
Si vous souhaitez pouvoir modifier et sauvegarder une ressource, vous pouvez mettre en place un ResourceFormatSaver
:
#ifndef RESOURCE_SAVER_JSON_H
#define RESOURCE_SAVER_JSON_H
#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;
};
#endif // RESOURCE_SAVER_JSON_H
#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");
}
}
Création de types de données personnalisés
Godot n'a peut-être pas de substitut approprié dans ses Types cœur ou ses ressources gérées. Godot a besoin d'un nouveau type de données déclaré pour comprendre les formats binaires supplémentaires tels que les modèles de machine learning.
Voici un exemple de création d'un type de données personnalisé :
#ifndef RESOURCE_JSON_H
#define RESOURCE_JSON_H
#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();
};
#endif // RESOURCE_JSON_H
#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;
}
Considérations
Certaines bibliothèques peuvent ne pas définir certaines routines communes telles que la gestion des entrées-sorties. Par conséquent, les traductions d'appels Godot sont requises.
Par exemple, voici le code permettant de traduire les appels FileAccess
en 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;
};
References
Enregistrement du nouveau format de fichier
Godot enregistre ResourcesFormatLoader
avec un gestionnaire de ResourceLoader
. Le gestionnaire sélectionne automatiquement le chargeur approprié lorsque load
est appelé.
void register_json_types();
void unregister_json_types();
#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();
}
References
Chargement sur GDScript
Enregistrez un fichier appelé demo.json
avec le contenu suivant et placez-le dans le dossier racine du projet :
{
"savefilename": "demo.json",
"demo": [
"welcome",
"to",
"godot",
"resource",
"loaders"
]
}
Attachez ensuite le script suivant à n'importe quel nœud :
extends Node
@onready var json_resource = load("res://demo.json")
func _ready():
print(json_resource.get_dict())