Work in progress
The content of this page was not yet updated for Godot
4.4
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.
Importer des plugins
Note
Ce tutoriel suppose que vous savez déjà comment créer des plugins génériques. En cas de doute, reportez-vous à la page Création de plugins. Cela suppose également que vous connaissiez le système d'importation de Godot.
Introduction
Un plugin d'importation est un type spécial d'outil de l'éditeur qui permet aux ressources personnalisées d'être importées par Godot et d'être traitées comme des ressources de première classe. L'éditeur lui-même est fourni avec de nombreux plugins d'importation pour gérer les ressources communes comme les images PNG, les modèles Collada et glTF, les sons Ogg Vorbis, et bien d'autres.
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
Configuration
Tout d'abord, nous avons besoin d'un plugin générique qui va prendre en charge l'initialisation et la destruction de notre plugin d'importation. Ajoutons d'abord le fichier 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"
Puis, nous avons besoin du fichier material_import.gd pour ajouter et enlever le plugin d'importation quand nous en aurons besoin :
# 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
Quand ce plugin est activé, il va créer une nouvelle instance du plugin d'importation (que nous allons bientôt faire) et l'ajouter à l'éditeur en utilisant la méthode add_import_plugin(). Nous stockons une référence à cette méthode dans un membre de classe import_plugin pour pouvoir y faire référence plus tard lors de sa suppression. La méthode remove_import_plugin() est appelée quand le plugin est désactivé pour nettoyer la mémoire et laisser l'éditeur savoir que le plugin d'importation n'est plus disponible.
Notez que le plugin d'importation est de type référence, il n'as donc pas besoin d’être explicitement libéré de la mémoire en utilisant la fonction free(). Il sera libéré automatiquement par le moteur.
La classe EditorImportPlugin
Le personnage principal de ce spectacle est la classe EditorImportPlugin class. Elle est responsable de l'implémentation des méthodes qui sont appelée par Godot quand il a besoin de savoir comment gérer des fichiers.
Commençons a coder notre plugin, une méthode a la fois :
# import_plugin.gd
@tool
extends EditorImportPlugin
func _get_importer_name():
return "demos.sillymaterial"
La première méthode est la _get_importer_name(). Il s'agit d'un nom unique pour votre plugin qui est utilisé par Godot pour savoir quelle importation a été utilisée dans un certain fichier. Lorsque les fichiers doivent être réimportés, l'éditeur saura quel plugin appeler.
func _get_visible_name():
return "Silly Material"
La méthode _get_visible_name() est chargée de renvoyer le nom du type importé et il sera affiché à l'utilisateur dans le dock d'importation.
Vous devez choisir ce nom comme suite à "Import as", par exemple "Import as Silly Material ". Vous pouvez lui donner le nom que vous voulez, mais nous vous recommandons d'utiliser un nom descriptif pour votre plugin.
func _get_recognized_extensions():
return ["mtxt"]
Le système d'importation de Godot détecte les types de fichiers par leur extension. Dans la méthode _get_recognized_extensions() vous retournez un tableau de chaînes de caractères pour représenter chaque extension que ce plugin peut comprendre. Si une extension est reconnue par plus d'un plugin, l'utilisateur peut choisir celui qu'il souhaite utiliser lors de l'importation des fichiers.
Astuce
Des extensions fréquentes comme .json et .txt peuvent être utilisées par plusieurs plugins. Il peut aussi y avoir des fichiers dans le projet qui ne contiennent que des données pour le jeu et ne doivent pas être importés. Il faut faire attention au moment de l’importation de valider les données, et ne jamais compter sur le fait que le fichier soit bien formaté.
func _get_save_extension():
return "material"
Les fichiers importés sont enregistrés dans le dossier .import à la racine du projet. Leur extension doit correspondre au type de ressource que vous importez, mais comme Godot ne peut pas savoir ce que vous utiliserez (car il peut y avoir plusieurs extensions valides pour la même ressource), il faut déclarer ce qui sera utilisé pour l’importation.
Puisqu’on importe un Material, nous allons utiliser une extension spéciale pour ce type de ressource. Si vous importez une scène, vous pouvez utiliser scn. Les ressources génériques peuvent utiliser l’extension res. Cependant, le moteur n’impose rien.
func _get_resource_type():
return "StandardMaterial3D"
La ressource importée a un type spécifique, pour que l’éditeur puisse savoir à quel emplacement de propriété elle appartient. Cela permet de faire un glisser-déposer depuis le dock Système de fichiers vers une propriété dans l’Inspecteur.
Dans notre cas c’est un StandardMaterial3D, qui peut être appliqué à des objets 3D.
Note
Si vous avez besoin d’importer différents types depuis la même extension, il faudra créer plusieurs plugins d’importation. Vous pouvez abstraire le code d’importation dans un autre fichier pour éviter la duplication.
Options et préréglages
Votre plugin peut offrir différentes options pour permettre à l’utilisateur de contrôler la manière dont la ressource sera importée. Si un ensemble d’options est courant, vous pouvez aussi créer des préréglages pour simplifier la vie à l’utilisateur. L’image suivante montre comment les options apparaîtront dans l’éditeur :
Puisqu’il peut y avoir de nombreux préréglages et qu’ils sont identifiés par un nombre, il est de bonne pratique d’utiliser une énumération pour pouvoir s’y référer en utilisant des noms.
@tool
extends EditorImportPlugin
enum Presets { DEFAULT }
...
Maintenant que l’énumération est définie, revenons aux méthodes d’un plugin d’importation :
func _get_preset_count():
return Presets.size()
La méthode _get_preset_count() retourne le nombre de préréglages définis par ce plugin. Il n’y a qu’un seul préréglage pour le moment, mais on peut s’assurer que cette méthode marche encore à l’avenir en retournant la taille de notre énumération Presets.
func _get_preset_name(preset_index):
match preset_index:
Presets.DEFAULT:
return "Default"
_:
return "Unknown"
Nous avons ensuite la méthode _get_preset_name(), qui nomme les préréglages tels qu’ils seront présentés à l’utilisateur, donc choisissez des noms courts et clairs.
On peut utiliser l’instruction match pour mieux structurer le code. Il sera ainsi facile d’ajouter des nouveaux préréglages à l’avenir. On utilise aussi le pattern joker pour toujours retourner quelque chose. Bien que Godot ne demande pas de préréglages au-delà du nombre que vous avez défini, il vaut toujours mieux être prudent.
Si vous n’avez qu’un seul préréglage, vous pouvez simplement retourner directement son nom, mais il faut dans ce cas faire attention quand vous ajouterez d’autres préréglages.
func _get_import_options(path, preset_index):
match preset_index:
Presets.DEFAULT:
return [{
"name": "use_red_anyway",
"default_value": false
}]
_:
return []
Voici la méthode qui définit les options disponibles. _get_import_options() renvoie un tableau de dictionnaires, et chaque dictionnaire contient quelques clés qui sont testées pour personnaliser l’option affichée à l’utilisateur. La table suivante montre les clés possibles :
Clé |
Type |
Description |
|---|---|---|
|
Chaîne de caractères |
Le nom de l’option. À l’affichage, les tirets bas deviennent des espaces et les premières lettres des mots sont passées en capitales. |
|
N’importe |
La valeur par défaut de l’option pour ce préréglage. |
|
Valeur d’énumération |
Une des valeurs de PropertyHint, à utiliser pour comme indication. |
|
Chaîne de caractères |
Le texte d’indication de la propriété. Le même qu’on ajouterait à l’instruction |
|
Valeur d’énumération |
Une des valeurs de PropertyUsageFlags définissant l’usage. |
Les clés name et default_value sont obligatoires, les autres sont optionnelles.
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.
Avertissement
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
Pour la méthode _get_option_visibility(), nous renvoyons simplement true car toutes nos options (c'est-à-dire celle que nous avons définie) sont visibles en permanence.
Si vous devez rendre certaines options visibles uniquement si une autre est définie avec une certaine valeur, vous pouvez ajouter la logique dans cette méthode.
La méthode import
La partie la plus lourde du processus, responsable de la conversion des fichiers en ressources, est couverte par la méthode _import(). Notre exemple de code est un peu long, alors séparons-le en quelques parties :
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()
La première partie de notre méthode d'importation ouvre et lit le fichier source. Nous utilisons la classe FileAccess pour faire cela, en passant le paramètre source_file qui est fourni par l'éditeur.
S'il y a une erreur lors de l'ouverture du fichier, nous la retournons pour faire savoir à l'éditeur que l'importation n'a pas réussi.
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]))
Ce code prend la ligne du fichier qu'il a lu auparavant et la divise en morceaux séparés par une virgule. S'il y a plus ou moins que les trois valeurs, il considère le fichier comme non valide et signale une erreur.
Ensuite, il crée une nouvelle variable Color et fixe ses valeurs en fonction du fichier d'entrée. Si l'option use_red_anyway est activée, alors elle définit la couleur comme un rouge pur à la place.
var material = StandardMaterial3D.new()
material.albedo_color = color
Cette partie crée un nouveau StandardMaterial3D qui est la ressource importée. Nous en créons une nouvelle instance, puis définissons sa couleur d'albédo comme la valeur que nous avons obtenue auparavant.
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.
Nous renvoyons également le résultat de la méthode ResourceSaver.save(), donc s'il y a une erreur dans cette étape, l'éditeur en sera informé.
Variantes de plate-forme et fichiers générés
Vous avez peut-être remarqué que notre plugin a ignoré deux arguments de la méthode import. Ce sont des arguments de retour (d'où le r au début de leur nom), ce qui signifie que l'éditeur les lira après avoir appelé votre méthode d'importation. Les deux sont des tableaux que vous pouvez remplir avec des informations.
L'argument r_platform_variants est utilisé si vous avez besoin d'importer la ressource différemment selon la plate-forme cible. Bien qu'il soit appelé variantes de plate-forme, il est basé sur la présence de feature tags, donc la même plateforme peut avoir plusieurs variantes selon la configuration.
Pour importer une variante de plate-forme, vous devez l'enregistrer avec la balise de fonctionnalité avant l'extension, puis pousser la balise dans le tableau r_platform_variants pour que l'éditeur sache que vous l'avez fait.
Par exemple, disons que nous enregistrons un matériau différent pour une plate-forme mobile. Nous aurions besoin de faire quelque chose comme ceci :
r_platform_variants.push_back("mobile")
return ResourceSaver.save(mobile_material, "%s.%s.%s" % [save_path, "mobile", _get_save_extension()])
L'argument r_gen_files est destiné aux fichiers supplémentaires qui sont générés au cours de votre processus d'importation et doivent être conservés. L'éditeur l'examinera pour comprendre les dépendances et s'assurer que le fichier supplémentaire n'est pas supprimé par inadvertance.
Il s'agit également d'un tableau et doit être rempli avec les chemins d'accès complets des fichiers que vous enregistrez. À titre d'exemple, créons un autre matériau pour la prochaine passe et enregistrons-le dans un fichier différent :
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)
Essayer le plugin
Cela a été théorique, mais maintenant que le plugin d'importation est fait, testons-le. Assurez-vous que vous avez créé le fichier d'exemple (avec le contenu décrit dans la section d'introduction) et enregistrez-le sous le nom test.mtxt. Ensuite, activez le plugin dans les Paramètres du projet.
Si tout se passe bien, le plugin d'importation est ajouté à l'éditeur et le système de fichiers est scanné, faisant apparaître la ressource personnalisée sur le dock du système de fichiers. Si vous la sélectionnez et focalisez le dock d'importation, vous pouvez voir la seule option à sélectionner à cet endroit.
Créez un nœud MeshInstance3D dans la scène et pour sa propriété Mesh, configurez un nouveau SphereMesh. Dépliez la section Matériau dans l'inspecteur, puis faites glisser le fichier du dock FileSystem vers la propriété du matériau. L'objet sera mis à jour dans la fenêtre avec la couleur bleue du matériau importé.
Allez dans le dock d'importation, activez l'option "Use Red Anyway" et cliquez sur "Reimport". Cela mettra à jour le matériel importé et devrait automatiquement mettre à jour la vue montrant la couleur rouge à la place.
Et c'est tout ! Votre premier plugin d'importation est terminé ! Faites preuve de créativité et créez des plugins pour vos formats préférés. Cela peut être très utile pour écrire vos données dans un format personnalisé puis les utiliser dans Godot comme s'il s'agissait de ressources natives. Cela montre à quel point le système d'importation est puissant et extensible.