Création de plugins pour l’écran principal

Ce que couvre ce tutoriel

Comme on le voit dans la page Création de plugins, réaliser un plugin de base qui étend l’éditeur est assez facile. Ce mécanisme de plugin vous permet également de créer de nouvelles interfaces utilisateur dans la partie centrale de l’éditeur, de la même manière que les vues de base en 2D, 3D, Script et AssetLib. Ces plugins d’éditeur sont appelés « Main screen plugins ».

Ce tutoriel vous guide à travers la création d’un plugin de base pour l’écran principal. Avec cet exemple de plugin, nous voulons démontrer :

  • Création d’un plugin pour l’écran principal
  • Lier l’écran principal à un autre élément de l’interface graphique du plugin (comme un panneau d’onglets, similaire à l’onglet Inspecteur)

Par souci de simplicité, les deux éléments de l’interface graphique de notre plugin pour l’écran principal seront tous deux constitués d’un Label et un Button. En appuyant sur le Button d’un élément, on peut afficher du texte sur le nœud Label de l’autre élément.

Initialisation du plugin

Le plugin lui-même est un projet Godot. Il est préférable de définir son contenu dans une structure addons/my_plugin_name/. Les seuls fichiers qui se trouvent dans le dossier racine sont le fichier project.godot, et l’icône du projet.

Dans le dossier addons/my_plugin_name/, nous créons le fichier plugin.cfg comme décris dans la page Création de plugins.

[plugin]
name="Main screen plugin demo"
description="A plugin that adds a main screen panel and a side-panel which communicate with each other."
author="Your Name Here"
version="1.0.0"
script="main_screen_plugin.gd"

Nous initialisons le fichier spécifié par la propriétés script= du fichier .cfg. Dans notre cas, main_screen_plugin.gd.

tool
extends EditorPlugin

func _enter_tree():
   pass


func _exit_tree():
   pass


func has_main_screen():
   return true


func make_visible(visible):
   pass


func get_plugin_name():
   return "Main Screen Plugin"


func get_plugin_icon():
   return get_editor_interface().get_base_control().get_icon("Node", "EditorIcons")

La partie importante de ce script est la fonction has_main_screen(), qui est surchargée de sorte qu’elle renvoie true. Cette fonction est automatiquement appelée par l’éditeur lors de l’activation du plugin, pour lui indiquer que ce plugin ajoute une nouvelle vue centrale à l’éditeur. Pour l’instant, nous allons laisser ce script tel quel et nous y reviendrons plus tard.

Les scènes

Le fichier main_screen_plugin.gd sera responsable de l’instanciation de chacun des éléments de l’interface utilisateur de notre plugin, et il gérera également la communication entre eux.

As a matter of fact, we wish to design each UI element in their own scene. Different scenes are not aware of each other unless they are both children of a parent scene, yet they will then require get_node("../sibling") accessors. Such practice is more likely to produce errors at runtime, especially if these scenes do not share the same parent node. This is why, they should only be allowed to access their children.

Ainsi, afin de communiquer des informations à une autre scène, la meilleure conception est de définir des signaux. Si une action utilisateur dans une scène d’interface utilisateur #1 doit déclencher quelque chose dans une autre scène d’interface utilisateur #2, cette action utilisateur doit émettre un signal à partir de la scène #1, et la scène #2 sera connectée à ce signal. Étant donné que toutes nos scènes d’interface utilisateur seront instanciées par le script main_screen_plugin.gd, ce script connectera également chacune d’entre elles aux signaux requis.

Note

If the main_screen_plugin.gd instantiates the UI scenes, won’t they be sibling nodes then?

Pas nécessairement : ce script peut ajouter toutes les scènes de l’interface utilisateur en tant qu’enfants du même nœud de l’arbre des scènes de l’éditeur - mais peut-être pas. Et le script main_screen_plugin.gd ne sera pas le noeud parent d’une scène instanciée parce que c’est un script, pas un noeud ! Ce script ne contiendra que des références à des scènes instanciées.

Scène de l’écran principal

Créer une nouvelle scène avec un noeud racine Panel. Sélectionnez ce nœud racine, et dans la fenêtre d’affichage, cliquez sur le menu Layout et sélectionnez Full Rect. Vous devez également activer le drapeau de taille verticale Expand dans l’inspecteur. Le panel utilise désormais tout l’espace disponible dans la fenêtre d’affichage. Maintenant, ajoutons un nouveau script sur le nœud racine. Nommez-le main_panel.gd.

Nous ajoutons ensuite 2 enfants à ce nœud Panel : d’abord un nœud Button. Placez-le n’importe où sur le Panel. Ensuite, ajoutez un noeud Label.

Nous devons maintenant définir un comportement lorsque ce bouton est pressé. Ceci est couvert par la page Handling a signal, cette partie ne sera donc pas décrite en détail dans ce tutoriel. Sélectionnez le nœud Button et cliquez sur le dock latéral Node. Sélectionnez le signal pressed() et cliquez sur le bouton Connect (vous pouvez également double-cliquer sur le signal pressed() à la place). Dans la fenêtre qui s’est ouverte, sélectionnez le nœud Panel (nous centraliserons tous les comportements dans son script). Conservez le nom de la fonction par défaut, assurez-vous que le bouton Make function est sur ON et appuyez sur Connect. Cela crée une fonction _on_Button_pressed() dans le script main_panel.gd, qui sera appelée à chaque fois que le bouton est pressé.

Lorsque le bouton est pressé, nous voulons que le nœud``Label`` du panneau latéral affiche un texte spécifique. Comme expliqué ci-dessus, nous ne pouvons pas accéder directement à la scène cible, nous allons donc émettre un signal à la place. Le script main_screen_plugin.gd va alors connecter ce signal à la scène cible. Continuons dans le script main_panel.gd :

tool
extends Panel

signal main_button_pressed(value)

func _on_Button_pressed():
   emit_signal("main_button_pressed", "Hello from main screen!")

De la même manière, le nœud Label de cette scène principale doit afficher une valeur lorsqu’il reçoit un signal spécifique. Créons une nouvelle fonction on_side_button_pressed(text_to_show) à cet effet :

func _on_side_button_pressed(text_to_show):
   $Label.text = text_to_show

Nous avons terminé pour le panneau de l’écran principal. Enregistrez la scène sous le nom de main_panel.tscn.

Scène de panneau à onglets

La scène du panneau à onglets est presque identique à la scène du panneau principal. Vous pouvez soit dupliquer le fichier main_panel.tscn et nommer le nouveau fichier side_panel.tscn, soit le recréer à partir d’une nouvelle scène en suivant à nouveau la section précédente. Cependant, vous devrez créer un nouveau script et l’attacher au nœud racine Panel. Enregistrez-le sous le nom de side_panel.gd. Son contenu est légèrement différent, car le signal émis et la fonction cible portent des noms différents. Voici le contenu complet du script :

tool
extends Panel

signal side_button_pressed(value)

func _on_Button_pressed():
   emit_signal("side_button_pressed", "Hello from side panel!")


func _on_main_button_pressed(text_to_show):
   $Label.text = text_to_show

Connecter les deux scènes dans le script du plugin

Nous devons maintenant mettre à jour le script main_screen_plugin.gd afin que le plugin instance nos 2 scènes d’interface graphique et les place aux bons endroits dans l’éditeur. Voici le main.gd complet :

tool
extends EditorPlugin

const MainPanel = preload("res://addons/my_plugin_name/main_panel.tscn")
const SidePanel = preload("res://addons/my_plugin_name/side_panel.tscn")

var main_panel_instance
var side_panel_instance

func _enter_tree():
   main_panel_instance = MainPanel.instance()
   side_panel_instance = SidePanel.instance()

   # Add the main panel to the editor's main viewport.
   get_editor_interface().get_editor_viewport().add_child(main_panel_instance)

   # Add the side panel to the Upper Left (UL) dock slot of the left part of the editor.
   # The editor has 4 dock slots (UL, UR, BL, BR) on each side (left/right) of the main screen.
   add_control_to_dock(DOCK_SLOT_LEFT_UL, side_panel_instance)

   # Hide the main panel
   make_visible(false)


func _exit_tree():
   main_panel_instance.queue_free()
   side_panel_instance.queue_free()


func _ready():
   main_panel_instance.connect("main_button_pressed", side_panel_instance, "_on_main_button_pressed")
   side_panel_instance.connect("side_button_pressed", main_panel_instance, "_on_side_button_pressed")


func has_main_screen():
   return true


func make_visible(visible):
   if visible:
      main_panel_instance.show()
   else:
      main_panel_instance.hide()


func get_plugin_name():
   return "Main Screen Plugin"


func get_plugin_icon():
   # Must return some kind of Texture for the icon.
   return get_editor_interface().get_base_control().get_icon("Node", "EditorIcons")

Quelques lignes spécifiques ont été ajoutées. Tout d’abord, nous avons défini les constantes qui contiennent nos deux scènes d’interface graphique (MainPanel et SidePanel). Nous utiliserons ces ressources pour instancier les deux scènes.

La fonction _enter_tree() est appelée avant _ready(). C’est là que nous avons intégré les deux scènes d’interface graphique et que nous les avons ajoutées en tant qu’enfants de certaines parties de l’éditeur. Le cas du panneau latéral est similaire à l’exemple présenté dans la page Création de plugins : nous ajoutons la scène dans un dock de l’éditeur. Nous avons précisé qu’il serait placé dans le dock de gauche, onglet en haut à gauche.

La classe EditorPlugin ne fournit aucune fonction pour ajouter un élément dans la fenêtre principale. Nous devons donc utiliser la fonction get_editor_interface().get_editor_viewport() pour obtenir ce viewport et y ajouter notre instance de panel principal en tant qu’enfant. Nous appelons la fonction make_visible(false) pour cacher le panneau principal afin qu’il ne soit pas directement affiché lors de la première activation du plugin.

L”_exit_tree() est assez simple. Il est automatiquement appelé lorsque le plugin est désactivé. Il est alors important de queue_free() les éléments précédemment instanciés pour préserver la mémoire. Si vous ne le faites pas, les éléments seront effectivement invisibles dans l’éditeur, mais ils resteront présents dans la mémoire. Les désactivations/réactivations multiples augmenteront alors l’utilisation de la mémoire sans aucun moyen de la libérer, ce qui n’est pas bon.

Enfin, la fonction make_visible() est surchargée pour cacher ou montrer le panneau principal selon les besoins. Cette fonction est automatiquement appelée par l’éditeur lorsque l’utilisateur clique sur un autre bouton de la fenêtre principale comme 2D, 3D ou Script.

Essayez le plugin

Activez le plugin dans les Paramètres du projet. Vous observerez un nouveau bouton à côté de 2D, 3D, Script au-dessus de la fenêtre principale. Vous remarquerez également un nouvel onglet dans le dock de gauche. Essayez de cliquer sur les boutons des panneaux latéraux et principal : les événements sont émis et captés par la scène cible correspondante pour modifier la légende du Label à l’intérieur de celle-ci.

If you would like to see a more complete example of what main screen plugins are capable of, check out the 2.5D demo projects here: https://github.com/godotengine/godot-demo-projects/tree/master/misc/2.5d