导入插件

介绍

导入插件是一种特殊类型的编辑器工具,允许godot导入自定义资源,并将其视为一流资源。编辑器本身捆绑了许多导入插件来处理公共资源,如PNG图像、collada和gltf模型、ogg vorbis声音等等。

本教程将向您展示如何创建一个简单的导入插件,以将自定义文本文件作为材料资源加载。此文本文件将包含三个用逗号分隔的数值,逗号代表一种颜色的三个通道,结果颜色将用作导入材料的反照率(主颜色)。

注解

本教程假设您已经知道如何制作通用插件。如果有疑问,请参阅 制作插件 页。这也假设您熟悉Godot的进口系统。

要导入的示例文件只包含表示纯蓝色(零红色、零绿色和全蓝色)的行:

0,0,255

配置

首先,我们需要一个通用插件来处理导入插件的初始化和销毁。让我们添加 plugin.cfg 文件优先:

[plugin]

name="Silly Material Importer"
description="Imports a 3D Material from an external text file."
author="Yours Truly"
version="1.0"
script="material_import.gd"

然后我们需要 material_import.gd 需要时添加和删除导入插件的文件:

# material_import.gd
tool
extends EditorPlugin

var import_plugin

func _enter_tree():
    import_plugin = preload("import_plugin.gd").new()
    add_import_plugin(import_plugin)

func _exit_tree():
    remove_import_plugin(import_plugin)
    import_plugin = null

当这个插件被激活时,它将创建一个新的导入插件实例(我们很快就会创建),并使用 add_import_plugin() 方法。我们将对它的引用存储在类成员中 import_plugin 所以我们可以在以后移除它时参考它。这个 remove_import_plugin() 当插件被停用以清理内存并让编辑器知道导入插件不再可用时,调用方法。

请注意,导入插件是引用类型,因此不需要使用 free() 功能。当超出范围时,引擎会自动释放。

editorimportplugin类

节目的主要特点是 EditorImportPlugin class . 它负责实现Godot在需要知道如何处理文件时调用的方法。

让我们开始编写插件代码,一次一个方法:

# import_plugin.gd
tool
extends EditorImportPlugin

func get_importer_name():
    return "demos.sillymaterial"

第一种方法是 get_importer_name() . 这是您的插件的唯一名称,godot使用它来知道某个文件中使用了哪个导入。当需要重新导入文件时,编辑器将知道要调用哪个插件。

func get_visible_name():
    return "Silly Material"

这个 get_visible_name() 方法负责通知它导入的类型的名称,并将在导入停靠中显示给用户。

您应该选择此名称作为“导入为”的延续。如。 “作为愚蠢的材料进口” . 是的,这个有点傻,但是你当然可以为你的插件起一个描述性的名字。

func get_recognized_extensions():
    return ["mtxt"]

Godot的导入系统通过扩展名检测文件类型。在 get_recognized_extensions() 方法返回一个字符串数组来表示该插件可以理解的每个扩展。如果一个扩展被多个插件识别,那么用户可以选择在导入文件时使用哪个插件。

小技巧

常见扩展,如 .json.txt 可能被许多插件使用。此外,项目中可能有一些文件只是游戏的数据,不应导入。导入以验证数据时必须小心。不要期望文件的格式正确。

func get_save_extension():
    return "material"

导入的文件保存在 .import 项目根目录下的文件夹。它们的扩展名应该与您导入的资源类型相匹配,但是由于godot不能告诉您将使用什么(因为同一资源可能有多个有效的扩展名),您需要通知导入中将使用什么。

因为我们要导入一种材料,所以我们将使用这种资源类型的特殊扩展。如果要导入场景,可以使用 scn . 一般资源可以使用 res 扩展。但是,引擎不会以任何方式强制执行此操作。

func get_resource_type():
    return "SpatialMaterial"

导入的资源具有特定的类型,因此编辑器可以知道它属于哪个属性槽。这允许从文件系统Dock拖放到Inspector中的属性。

在我们的情况下,这是一个 SpatialMaterial ,可应用于三维对象。

注解

如果需要从同一扩展导入不同的类型,则必须创建多个导入插件。您可以在另一个文件上抽象导入代码,以避免在这方面重复。

选项和预设

您的插件可以提供不同的选项,允许用户控制如何导入资源。如果一组选定的选项是常见的,您还可以创建不同的预设,以方便用户使用。下图显示了选项在编辑器中的显示方式:

../../../_images/import_plugin_options.png

因为可能有许多预设,并且它们是用数字标识的,所以使用枚举是一个很好的实践,这样您就可以使用名称来引用它们。

tool
extends EditorImportPlugin

enum Presets { PRESET_DEFAULT }

...

既然已经定义了枚举,那么让我们继续研究导入插件的方法:

func get_preset_count():
    return Presets.size()

这个 get_preset_count() 方法返回此插件定义的预设量。我们现在只有一个预设,但是我们可以通过返回 Presets 枚举。

func get_preset_name(preset):
    match preset:
        PRESET_DEFAULT:
            return "Default"
        _:
            return "Unknown"

这里有 get_preset_name() 方法,它为预设指定名称,因为它们将显示给用户,所以一定要使用简短和清晰的名称。

我们可以使用 match 这里的语句使代码更加结构化。这样就可以很容易地在将来添加新的预设。我们也使用catch all模式返回一些内容。虽然Godot不会要求超出您定义的预设数量的预设,但最好是在安全方面。

如果你只有一个预设,你可以直接返回它的名字,但是如果你这样做,你必须小心添加更多的预设。

func get_import_options(preset):
    match preset:
        PRESET_DEFAULT:
            return [{
                       "name": "use_red_anyway",
                       "default_value": false
                    }]
        _:
            return []

这是定义可用选项的方法。 get_import_options() 返回一个字典数组,并且每个字典都包含一些键,这些键被选中以自定义选项,如向用户显示的那样。下表显示了可能的键:

类型

描述

name

选项的名称。显示时,下划线变为空格,首字母大写。

default_value

任何

此预设的选项的默认值。

property_hint

枚举值

其中之一 PropertyHint 用作提示的值。

hint_string

属性的提示文本。和你加进去的一样 export gdscript中的语句。

usage

枚举值

其中之一 PropertyUsageFlags 用于定义用法的值。

这个 namedefault_value 钥匙是 强制性的 ,其余为可选。

请注意 get_import_options 方法接收预设值,因此可以为每个不同的预设值(尤其是默认值)配置选项。在这个例子中,我们使用 match 语句,但如果您有很多选项,并且预设值只更改值,则可能需要先创建选项数组,然后根据预设值更改它。

警告

这个 get_import_options 即使不定义预设(通过 get_preset_count 返回零)。即使数组是空的,也必须返回它,否则可能会出错。

func get_option_visibility(option, options):
    return true

对于 get_option_visibility() 方法,我们只是返回 true 因为我们所有的选项(即我们定义的单个选项)都是可见的。

如果只在另一个选项设置了某个值时才需要使该选项可见,则可以在此方法中添加逻辑。

这个 import 方法

负责将文件转换为资源的过程的重要部分由 import() 方法。我们的示例代码有点长,所以我们分成几个部分:

func import(source_file, save_path, options, r_platform_variants, r_gen_files):
    var file = File.new()
    var err = file.open(source_file, File.READ)
    if err != OK:
        return err

    var line = file.get_line()

    file.close()

导入方法的第一部分打开并读取源文件。我们使用 File 通过 source_file 由编辑器提供的参数。

如果打开文件时出现错误,我们将返回该文件,让编辑器知道导入失败。

var channels = line.split(",")
if channels.size() != 3:
    return ERR_PARSE_ERROR

var color
if options.use_red_anyway:
    color = Color8(255, 0, 0)
else:
    color = Color8(int(channels[0]), int(channels[1]), int(channels[2]))

这段代码获取它以前读取的文件的行,并将其拆分为由逗号分隔的部分。如果超过或少于三个值,则认为文件无效并报告错误。

然后它创建了一个新的 Color 变量并根据输入文件设置其值。如果 use_red_anyway 选项被启用,然后它将颜色设置为纯红色。

var material = SpatialMaterial.new()
material.albedo_color = color

这部分是新的 SpatialMaterial 这是导入的资源。我们创建了一个新的实例,然后将它的反照率颜色设置为我们以前得到的值。

return ResourceSaver.save("%s.%s" % [save_path, get_save_extension()], material)

这是最后一部分,也是非常重要的一部分,因为这里我们将生成的资源保存到磁盘上。保存文件的路径由编辑器通过 save_path 参数。注意这是 没有 扩展名,因此我们使用 string formatting . 为此我们称之为 get_save_extension 我们之前定义的方法,因此我们可以确保它们不会失去同步。

我们还返回 ResourceSaver.save() 方法,因此如果这一步中有错误,编辑器就会知道它。

平台变体和生成的文件

您可能已经注意到我们的插件忽略了 import 方法。这些是 返回参数 (因此 r 在其名称的开头),这意味着编辑器将在调用导入方法后从中读取。它们都是可以用信息填充的数组。

这个 r_platform_variants 如果需要根据目标平台不同地导入资源,则使用参数。当它被召唤的时候 平台 变量,它是基于 feature tags 因此,即使是同一个平台也可以根据设置有多个变体。

要导入平台变量,您需要在扩展之前将其与功能标记一起保存,然后将标记推送到 r_platform_variants 数组,以便编辑器知道您做了什么。

例如,假设我们为移动平台保存了不同的材料。我们需要做如下的事情:

r_platform_variants.push_back("mobile")
return ResourceSaver.save("%s.%s.%s" % [save_path, "mobile", get_save_extension()], mobile_material)

这个 r_gen_files 参数用于导入过程中生成的需要保留的额外文件。编辑器将查看它以了解依赖项,并确保不会无意中删除额外的文件。

这也是一个数组,应该用保存的文件的完整路径填充。例如,让我们为下一个步骤创建另一个材质,并将其保存到另一个文件中:

var next_pass = SpatialMaterial.new()
next_pass.albedo_color = color.inverted()
var next_pass_path = "%s.next_pass.%s" % [save_path, get_save_extension()]

err = ResourceSaver.save(next_pass_path, next_pass)
if err != OK:
    return err
r_gen_files.push_back(next_pass_path)

正在尝试插件

这是理论上的,但是现在导入插件完成了,让我们测试一下。确保创建了示例文件(包含“简介”部分中描述的内容),并将其另存为 test.mtxt . 然后在项目设置中激活插件。

如果一切正常,导入插件将添加到编辑器中,并扫描文件系统,使自定义资源显示在文件系统基座上。如果选择它并聚焦导入停靠,则可以看到在那里选择的唯一选项。

在场景中创建一个网格实例节点,并为其网格属性设置一个新的球体。展开Inspector中的Material部分,然后将文件从文件系统Dock拖动到Material属性。对象将在视区中更新为导入材料的蓝色。

../../../_images/import_plugin_trying.png

转到导入平台,启用“无论如何使用红色”选项,然后单击“重新导入”。这将更新导入的材料,并应自动更新显示红色的视图。

就这样!第一个导入插件完成!现在有创意,并为自己喜欢的格式制作插件。这对于以自定义格式编写数据,然后在Godot中像本地资源一样使用数据非常有用。这显示了导入系统的强大性和可扩展性。