自定义资源格式加载器

简介

ResourceFormatLoader 是一个用来加载文件资源的工厂接口。资源是基本容器。当再次在同一文件路径上调用load时,将引用先前加载的Resource。自然,加载的资源必须是无状态的。

本指南假定读者知道如何创建C++模块和Godot数据类型。如果没有,请参考指南 自定义C++模块

参考

可以做什么?

  • 添加对多种文件格式的新支持
  • 音效格式
  • 视频格式
  • 机器学习模型

不可以做什么?

  • 光栅图像

应使用ImageFormatLoader加载图像。

创建一个资源格式加载器

每种文件格式都包含一个数据容器和一个 ResourceFormatLoader

ResourceFormatLoader通常是简单的类,它们返回所有必需的元数据以支持Godot中的新扩展。该类必须返回格式名称和扩展名字符串。

此外,ResourceFormatLoaders 必须使用 load 函数将文件路径转换为资源(Resource)。要加载资源,load 必须能够读取和处理序列化的资源数据。

#ifndef MY_JSON_LOADER_H
#define MY_JSON_LOADER_H

#include "core/io/resource_loader.h"

class ResourceFormatLoaderMyJson : public 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> *p_extensions) const;
        virtual bool handles_type(const String &p_type) const;
        virtual String get_resource_type(const String &p_path) const;

        ResourceFormatLoaderMyJson();
        virtual ~ResourceFormatLoaderMyJson() {}
};
#endif // MY_JSON_LOADER_H
#include "my_json_loader.h"
#include "my_json.h"

ResourceFormatLoaderMyJson::ResourceFormatLoaderMyJson() {
}

RES ResourceFormatLoaderMyJson::load(const String &p_path, const String &p_original_path, Error *r_error) {
        MyJson *my = memnew(MyJson);
        if (r_error)
                *r_error = OK;
        Error err = my->set_file(p_path);
        return Ref<MyJson>(my);
}

void ResourceFormatLoaderMyJson::get_recognized_extensions(List<String> *p_extensions) const {
        p_extensions->push_back("mjson");
}

String ResourceFormatLoaderMyJson::get_resource_type(const String &p_path) const {

        if (p_path.get_extension().to_lower() == "mjson")
                return "MyJson";
        return "";
}

bool ResourceFormatLoaderMyJson::handles_type(const String &p_type) const {
        return (p_type == "MyJson");
}

创建自定义数据类型

Godot在其 核心类型 或托管的资源中可能没有适当的替代品。这时Godot需要新的注册数据类型来理解其他二进制格式,例如机器学习模型。

这里有一个创建自定义数据类型的示例

#ifndef MY_JSON_H
#define MY_JSON_H

#include "core/dictionary.h"
#include "core/io/json.h"
#include "core/reference.h"
#include "core/variant.h"
#include "core/variant_parser.h"

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

protected:
        static void _bind_methods() {
                ClassDB::bind_method(D_METHOD("to_string"), &MyJson::to_string);
        }

private:
        Dictionary dict;

public:
        Error set_file(const String &p_path) {
                Error error_file;
                FileAccess *file = FileAccess::open(p_path, FileAccess::READ, &error_file);

                String buf = String("");
                while (!file->eof_reached()) {
                        buf += file->get_line();
                }
                String err_string;
                int err_line;
                JSON cmd;
                Variant ret;
                Error err = cmd.parse(buf, ret, err_string, err_line);
                dict = Dictionary(ret);
                file->close();
                return OK;
        }

        String to_string() const {
                return String(*this);
        }

        operator String() const {
                JSON a;
                return a.print(dict);
        }

        MyJson() {};
        ~MyJson() {};
};
#endif // MY_JSON_H

注意事项

一些库可能未定义某些通用例程,例如IO处理。因此,需要Godot调用转换。

例如,下面是将 FileAccess 调用转换为 std::istream 的代码。

#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.cpp */
#include "register_types.h"
#include "core/class_db.h"

#include "my_json_loader.h"
#include "my_json.h"

static ResourceFormatLoaderMyJson *my_json_loader = NULL;
void register_my_json_types() {
        my_json_loader = memnew(ResourceFormatLoaderMyJson);
        ResourceLoader::add_resource_format_loader(my_json_loader);
        ClassDB::register_class<MyJson>();
}

void unregister_my_json_types() {
        memdelete(my_json_loader);
}

将其加载到GDScript

{
  "savefilename" : "demo.mjson",
  "demo": [
    "welcome",
    "to",
    "godot",
    "resource",
    "loaders"
  ]
}
extends Node

func _ready():
    var myjson = load("res://demo.mjson")
    print(myjson.to_string())