自定义资源格式加载器
简介
ResourceFormatLoader
是一个用来加载文件资源的工厂接口。资源是基本容器。当再次在同一文件路径上调用load时,将引用先前加载的Resource。自然,加载的资源必须是无状态的。
本指南假定读者知道如何创建C++模块和Godot数据类型。如果没有,请参考指南 自定义C++模块。
参考
可以做什么?
- 添加对多种文件格式的新支持
- 音效格式
- 视频格式
- 机器学习模型
不可以做什么?
- 光栅图像
应使用ImageFormatLoader加载图像。
参考
创建一个资源格式加载器
每种文件格式都包含一个数据容器和一个 ResourceFormatLoader
。
ResourceFormatLoader通常是简单的类,它们返回所有必需的元数据以支持Godot中的新扩展。该类必须返回格式名称和扩展名字符串。
此外,ResourceFormatLoaders 必须使用 load
函数将文件路径转换为资源(Resource)。要加载资源,load
必须能够读取和处理序列化的资源数据。
/* resource_loader_json.h */
#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
/* 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");
}
Creating a ResourceFormatSaver
If you’d like to be able to edit and save a resource, you can implement a ResourceFormatSaver
:
/* resource_saver_json.h */
#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
/* 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");
}
}
创建自定义数据类型
Godot在其 核心类型 或托管的资源中可能没有适当的替代品。这时Godot需要新的注册数据类型来理解其他二进制格式,例如机器学习模型。
Here is an example of creating a custom datatype:
/* resource_json.h */
#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
/* 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;
}
注意事项
一些库可能未定义某些通用例程,例如IO处理。因此,需要Godot调用转换。
例如,下面是将 FileAccess
调用转换为 std::istream
的代码。
#include "core/os/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;
};
参考
注册新的文件格式
Godot 用 ResourceLoader
处理程序注册 ResourcesFormatLoader
。当调用 load
时,处理程序会自动选择合适的加载器。
/* 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.instance();
ResourceLoader::add_resource_format_loader(json_loader);
json_saver.instance();
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();
}
参考
将其加载到GDScript
Save a file called demo.json
with the following contents and place it in the project’s root folder:
{
"savefilename": "demo.json",
"demo": [
"welcome",
"to",
"godot",
"resource",
"loaders"
]
}
创建一个节点并附加下面的脚本:
extends Node
onready var json_resource = load("res://demo.json")
func _ready():
print(json_resource.get_dict())