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.

Este tutorial le mostrará cómo crear un complemento de importación simple para cargar un archivo de texto personalizado como recurso material. Este archivo de texto contendrá tres valores numéricos separados por comas, que representan los tres canales de un color, y el color resultante se utilizará como albedo (color principal) del material importado. En este ejemplo, contendrá el color azul puro (cero rojo, cero verde y azul completo):

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"

El primer método es: ref: get_importer_name () <class_EditorImportPlugin_method_get_importer_name>. Este es un nombre único para su complemento que usa Godot para saber qué importación se usó en un archivo determinado. Cuando sea necesario volver a importar los archivos, el editor sabrá a qué complemento llamar.

func get_visible_name():
    return "Silly Material"

El método get_visible_name() es responsable de devolver el nombre del tipo que importa y se mostrará al usuario en el panel de Importación.

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

El sistema de importación de Godot detecta los tipos de archivos por su extensión. En el método get_recognized_extensions() devuelve un array de strings para representar cada extensión que este plugin pueda entender. Si una extensión es reconocida por más de un plugin, el usuario puede seleccionar cuál usar al importar los archivos.

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 "SpatialMaterial"

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.

En nuestro caso es un: ref: class_SpatialMaterial, que se puede aplicar a objetos 3D.

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

El método get_preset_count() devuelve la cantidad de presets que definen estos plugins. Sólo tenemos un preset ahora, pero podemos hacer que este método esté preparado para el futuro devolviendo el tamaño de nuestra enumeración Presets.

func get_preset_name(preset):
    match preset:
        Presets.DEFAULT:
            return "Default"
        _:
            return "Unknown"

Aquí tenemos el método: ref: get_preset_name () <class_EditorImportPlugin_method_get_preset_name>, que da nombres a los preajustes tal como se le presentarán al usuario, así que asegúrese de usar nombres cortos y claros.

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(preset):
    match preset:
        Presets.DEFAULT:
            return [{
                       "name": "use_red_anyway",
                       "default_value": false
                    }]
        _:
            return []

Este es el método que define las opciones disponibles. get_import_options() devuelve un array de diccionarios, y cada diccionario contiene unas cuantas claves que se comprueban para personalizar la opción tal y como se muestra al usuario. La siguiente tabla muestra las posibles claves:

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.

Tenga en cuenta que el método get_import_options recibe el número de preset, por lo que puedes configurar las opciones para cada preset diferente (especialmente el valor por defecto). En este ejemplo usamos la sentencia match pero si tienes muchas opciones y los presets sólo cambian el valor, es posible que primero quieras crear un array de opciones y luego cambiarlo basado en el preset.

Advertencia

El método get_import_options se llama incluso si no se definen los presets (haciendo que get_preset_count devuelva cero). Tienes que devolver un array aunque esté vacío, de lo contrario puedes obtener errores.

func get_option_visibility(option, options):
    return true

Para el método: ref: get_option_visibility () <class_EditorImportPlugin_method_get_option_visibility>, simplemente devolvemos true porque todas nuestras opciones (es decir, la única que definimos) son visibles todo el tiempo.

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

La parte pesada del proceso, responsable de convertir los archivos en recursos, está cubierta por el método: ref: import () <class_EditorImportPlugin_method_import>. Nuestro código de muestra es un poco largo, así que vamos a dividirlo en algunas partes:

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

La primera parte de nuestro método de importación se abre y lee el archivo fuente. Usamos la clase: ref: File <class_File> para hacer eso, pasando el parámetro `` source_file '' que es provisto por el 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 = SpatialMaterial.new()
material.albedo_color = color

Esta parte crea un nuevo: ref: SpatialMaterial <class_SpatialMaterial> que es el recurso importado. Creamos una nueva instancia de él y luego establecemos su color de albedo como el valor que obtuvimos antes.

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

Esta es la última parte y bastante importante, porque aquí guardamos el recurso creado en el disco. La ruta del archivo guardado es generada e informada por el editor a través del parámetro `` save_path ''. Tenga en cuenta que esto viene ** sin ** la extensión, así que lo agregamos usando: ref: string formatting <doc_gdscript_printf>. Para esto, llamamos al método `` get_save_extension '' que definimos anteriormente, así podemos estar seguros de que no se desincronizarán.

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("%s.%s.%s" % [save_path, "mobile", get_save_extension()], mobile_material)

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

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í.

Crea un nodo MeshInstance en la escena y para su propiedad Mesh configura un nuevo SphereMesh. Despliega la sección Material en el Inspector y luego arrastra el archivo desde el panel de Sistema de Archivos hasta la propiedad material. El objeto se actualizará en la ventana de visualización con el color azul del material importado.

../../../_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.