Work in progress

The content of this page was not yet updated for Godot 4.2 and may be outdated. If you know how to improve this page or you can confirm that it's up to date, feel free to open a pull request.

Importar plugins

Nota

Este tutorial asume que ya sabe cómo crear complementos genéricos. En caso de duda, consulte la página: ref: doc_making_plugins. Esto también asume que está familiarizado con el sistema de importación de Godot.

Introducción

Un complemento de importación es un tipo especial de herramienta de edición que permite que Godot importe recursos personalizados y los trate como recursos de primera clase. El editor en sí viene incluido con una gran cantidad de complementos de importación para manejar los recursos comunes como imágenes PNG, modelos Collada y glTF, sonidos Ogg Vorbis y muchos más.

This tutorial shows how to create an import plugin to load a custom text file as a material resource. This text file will contain three numeric values separated by comma, which represents the three channels of a color, and the resulting color will be used as the albedo (main color) of the imported material. In this example it contains the pure blue color (zero red, zero green, and full blue):

0,0,255

Configuración

Primero necesitamos un complemento genérico que se encargue de la inicialización y destrucción de nuestro complemento de importación. Primero agreguemos el archivo `` 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"

Luego, necesitamos el archivo `` material_import.gd '' para agregar y eliminar el complemento de importación cuando sea necesario:

# 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

Cuando este complemento está activado, creará una nueva instancia del complemento de importación (que pronto crearemos) y lo agregará al editor usando el método: ref: add_import_plugin () <class_EditorPlugin_method_add_import_plugin>. Almacenamos una referencia a él en un miembro de la clase import_plugin para que podamos consultarlo más adelante cuando lo eliminemos. El método: ref: remove_import_plugin () <class_EditorPlugin_method_remove_import_plugin> se llama cuando el complemento está desactivado para limpiar la memoria y hacerle saber al editor que el complemento de importación ya no está disponible.

Tenga en cuenta que el complemento de importación es un tipo de referencia, por lo que no es necesario liberarlo explícitamente de la memoria con la función free (). Será liberado automáticamente por el motor cuando salga del alcance.

La clase EditorImportPlugin

El personaje principal del programa es: ref: EditorImportPlugin class <class_EditorImportPlugin>. Es responsable de implementar los métodos que llama Godot cuando necesita saber cómo manejar archivos.

Comencemos a codificar nuestro complemento, un método a la vez:

# import_plugin.gd
@tool
extends EditorImportPlugin


func _get_importer_name():
    return "demos.sillymaterial"

The first method is the _get_importer_name(). This is a unique name for your plugin that is used by Godot to know which import was used in a certain file. When the files needs to be reimported, the editor will know which plugin to call.

func _get_visible_name():
    return "Silly Material"

The _get_visible_name() method is responsible for returning the name of the type it imports and it will be shown to the user in the Import dock.

Debe elegir este nombre como continuación de "Importar como", p. Ej. * "Importar como material" *. Puede nombrarlo como desee, pero le recomendamos un nombre descriptivo para su complemento.

func _get_recognized_extensions():
    return ["mtxt"]

Godot's import system detects file types by their extension. In the _get_recognized_extensions() method you return an array of strings to represent each extension that this plugin can understand. If an extension is recognized by more than one plugin, the user can select which one to use when importing the files.

Truco

Muchos complementos pueden usar extensiones comunes como .json y .txt. Además, podría haber archivos en el proyecto que sean solo datos del juego y no se deben importar. Debe tener cuidado al importar para validar los datos. Nunca espere que el archivo esté bien formado.

func _get_save_extension():
    return "material"

Los archivos importados se guardan en la carpeta .import en la raíz del proyecto. Su extensión debe coincidir con el tipo de recurso que está importando, pero dado que Godot no puede decir qué usará (porque puede haber varias extensiones válidas para el mismo recurso), debe declarar qué se usará en la importación.

Dado que estamos importando un Material, usaremos la extensión especial para tales tipos de recursos. Si está importando una escena, puede usar scn. Los recursos genéricos pueden usar la extensión res. Sin embargo, esto no se aplica de ninguna manera por el motor.

func _get_resource_type():
    return "StandardMaterial3D"

El recurso importado tiene un tipo específico, por lo que el editor puede saber a qué ranura de propiedades pertenece. Esto permite arrastrar y soltar desde el panel de Sistema de Archivos a una propiedad en el Inspector.

In our case it's a StandardMaterial3D, which can be applied to 3D objects.

Nota

Si necesita importar diferentes tipos de la misma extensión, debe crear varios complementos de importación. Puede abstraer el código de importación en otro archivo para evitar la duplicación en este sentido.

Opciones y presets

Su complemento puede proporcionar diferentes opciones para permitir al usuario controlar cómo se importará el recurso. Si un conjunto de opciones seleccionadas es común, también puede crear diferentes ajustes preestablecidos para que sea más fácil para el usuario. La siguiente imagen muestra cómo aparecerán las opciones en el editor:

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

Dado que puede haber muchos ajustes preestablecidos y se identifican con un número, es una buena práctica usar una enumeración para poder referirse a ellos usando nombres.

@tool
extends EditorImportPlugin


enum Presets { DEFAULT }


...

Ahora que la enumeración está definida, sigamos mirando los métodos de un complemento de importación:

func _get_preset_count():
    return Presets.size()

The _get_preset_count() method returns the amount of presets that this plugins defines. We only have one preset now, but we can make this method future-proof by returning the size of our Presets enumeration.

func _get_preset_name(preset_index):
    match preset_index:
        Presets.DEFAULT:
            return "Default"
        _:
            return "Unknown"

Here we have the _get_preset_name() method, which gives names to the presets as they will be presented to the user, so be sure to use short and clear names.

Aquí podemos usar la sentencia match para hacer el código más estructurado. De esta manera es más fácil añadir nuevos presets en el futuro. Usamos el patrón de catch all para devolver algo también. Aunque Godot no pedirá presets más allá del número de presets que definiste, siempre es mejor ser precavido.

Si sólo tiene un preset puede simplemente devolver su nombre directamente, pero si lo hace debe tener cuidado al añadir más presets.

func _get_import_options(path, preset_index):
    match preset_index:
        Presets.DEFAULT:
            return [{
                       "name": "use_red_anyway",
                       "default_value": false
                    }]
        _:
            return []

This is the method which defines the available options. _get_import_options() returns an array of dictionaries, and each dictionary contains a few keys that are checked to customize the option as its shown to the user. The following table shows the possible keys:

Clave

Tipo

Descripción

nombre

Cadena

El nombre de la opción. Cuando se muestran, los guiones bajos se convierten en espacios y las primeras letras se escriben en mayúscula.

valor por defecto

Algún

El valor por defecto de la opción para este preset.

sugerencia_propiedad

Valor de enumeración

Uno de los valores: ref: PropertyHint <enum_ @ GlobalScope_PropertyHint> para usar como sugerencia.

hint_string

Cadena

El texto de sugerencia de la propiedad. Lo mismo que agregaría en la declaración exportar en GDScript.

uso

Valor de enumeración

Uno de los valores: ref: PropertyUsageFlags <enum_@GlobalScope_PropertyUsageFlags> para definir el uso.

Las claves nombre y valor_predeterminado son obligatorias, el resto son opcionales.

Note that the _get_import_options method receives the preset number, so you can configure the options for each different preset (especially the default value). In this example we use the match statement, but if you have lots of options and the presets only change the value you may want to create the array of options first and then change it based on the preset.

Advertencia

The _get_import_options method is called even if you don't define presets (by making _get_preset_count return zero). You have to return an array even it's empty, otherwise you can get errors.

func _get_option_visibility(path, option_name, options):
    return true

For the _get_option_visibility() method, we simply return true because all of our options (i.e. the single one we defined) are visible all the time.

Si necesita hacer visible cierta opción solo si se establece otra con un valor determinado, puede agregar la lógica en este método.

El método de importación

The heavy part of the process, responsible for converting the files into resources, is covered by the _import() method. Our sample code is a bit long, so let's split in a few parts:

func _import(source_file, save_path, options, r_platform_variants, r_gen_files):
    var file = FileAccess.open(source_file, FileAccess.READ)
    if file == null:
        return FileAccess.get_open_error()

    var line = file.get_line()

The first part of our import method opens and reads the source file. We use the FileAccess class to do that, passing the source_file parameter which is provided by the editor.

Si hay un error al abrir el archivo, lo devolvemos para que el editor sepa que la importación no se realizó correctamente.

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]))

Este código toma la línea del archivo que leyó antes y la divide en partes separadas por una coma. Si hay más o menos de los tres valores, considera que el archivo no es válido y reporta un error.

Luego crea una nueva variable: ref: Color <class_Color> y establece sus valores de acuerdo con el archivo de entrada. Si la opción `` use_red_anyway '' está habilitada, entonces establece el color como rojo puro.

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

This part makes a new StandardMaterial3D that is the imported resource. We create a new instance of it and then set its albedo color as the value we got before.

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

This is the last part and quite an important one, because here we save the made resource to the disk. The path of the saved file is generated and informed by the editor via the save_path parameter. Note that this comes without the extension, so we add it using string formatting. For this we call the _get_save_extension method that we defined earlier, so we can be sure that they won't get out of sync.

También devolvemos el resultado del método ResourceSaver.save(), así que si hay un error en este paso, el editor lo sabrá.

Variantes de plataforma y archivos generados

Habrás notado que nuestro plugin ignoró dos argumentos del método import. Esos son * argumentos de retorno* (de ahí la r al principio de su nombre), lo que significa que el editor los leerá después de llamar a su método de importación. Ambos son arrays que puedes rellenar con información.

El argumento r_platform_variants se usa si necesita importar el recurso de manera diferente según la plataforma de destino. Si bien se llama variantes de * plataforma *, se basa en la presencia de: ref: etiquetas de características <doc_feature_tags>, por lo que incluso la misma plataforma puede tener múltiples variantes según la configuración.

Para importar una variante de plataforma, necesitas guardarla con la etiqueta de la característica antes de la extensión, y luego empujar la etiqueta a un array r_platform_variants para que el editor pueda saber que lo hiciste.

Por ejemplo, digamos que guardamos un material diferente para una plataforma móvil. Necesitaríamos hacer algo como lo siguiente:

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

El argumento r_gen_files está destinado a archivos adicionales que se generan durante su proceso de importación y deben conservarse. El editor lo examinará para comprender las dependencias y asegurarse de que el archivo adicional no se elimine inadvertidamente.

Esto también es un array y debería estar lleno de rutas completas de los archivos que guardas. Por ejemplo, creamos otro material para la siguiente pasada y lo guardamos en un archivo diferente:

var next_pass = StandardMaterial3D.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, next_pass_path)
if err != OK:
    return err
r_gen_files.push_back(next_pass_path)

Probar el complemento

Esto ha sido teórico, pero ahora que el complemento de importación está listo, probémoslo. Asegúrese de haber creado el archivo de muestra (con el contenido descrito en la sección de introducción) y guárdelo como `` test.mtxt ''. Luego active el complemento en la Configuración del proyecto.

Si todo va bien, se agrega el plugin de importación al editor y se escanea el sistema de archivos, haciendo que el recurso personalizado aparezca en el panel de Sistema de Archivos. Si lo seleccionas y entras en el panel de Importación, podrás ver la única opción para seleccionar allí.

Create a MeshInstance3D node in the scene, and for its Mesh property set up a new SphereMesh. Unfold the Material section in the Inspector and then drag the file from the FileSystem dock to the material property. The object will update in the viewport with the blue color of the imported material.

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

Ve al panel Importar, activa la opción "Usar rojo de todos modos" y haz clic en "Reimportar". Esto actualizará el material importado y debería actualizar automáticamente la vista mostrando el color rojo en su lugar.

¡Y eso es todo! ¡Tu primer plugin de importación está hecho! Ahora sé creativo y haz plugins para tus propios formatos queridos. Esto puede ser bastante útil para escribir los datos en un formato personalizado y luego usarlos en Godot como si fueran recursos nativos. Esto muestra cómo el sistema de importación es potente y extensible.