自定义资源格式加载器
前言
ResourceFormatLoader 是一个用来加载文件资源的工厂接口。资源是基本容器。当再次对同一文件路径调用 load 时,将引用先前加载的 Resource。自然,加载的资源必须是无状态的。
本指南假定读者知道如何创建C++模块和Godot数据类型. 如果不知道, 请参考本指南 自定义 C++ 模块 .
参考
可以做什么?
添加对多种文件格式的新支持
音频格式
视频格式
机器学习模型
不可以做什么?
- 光栅图像
应使用ImageFormatLoader加载图像.
参考
创建 ResourceFormatLoader
每种文件格式都包含一个数据容器和一个 ResourceFormatLoader
.
ResourceFormatLoaders通常是简单的类, 返回支持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");
}
创建 ResourceFormatSaver
如果您希望能够编辑和保存资源, 则可以实现 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需要新的注册数据类型来理解其他二进制格式, 例如机器学习模型.
下面是创建自定义数据类型的示例:
/* 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 中加载
保存具有以下内容的名为 demo.json
的文件,并将其放置在项目的根文件夹中:
{
"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())