making_plugins.rst 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. .. _doc_making_plugins:
  2. Making plugins
  3. ==============
  4. About plugins
  5. -------------
  6. A plugin is a great way to extend the editor with useful tools. It can be made
  7. entirely with GDScript and standard scenes, without even reloading the editor.
  8. Unlike modules, you don't need to create C++ code nor recompile the engine.
  9. While this makes plugins less powerful, there are still many things you can
  10. do with them. Note that a plugin is similar to any scene you can already
  11. make, except it is created using a script to add editor functionality.
  12. This tutorial will guide you through the creation of two plugins so
  13. you can understand how they work and be able to develop your own. The first
  14. is a custom node that you can add to any scene in the project, and the
  15. other is a custom dock added to the editor.
  16. Creating a plugin
  17. -----------------
  18. Before starting, create a new empty project wherever you want. This will serve
  19. as a base to develop and test the plugins.
  20. The first thing you need for the editor to identify a new plugin is to
  21. create two files: a ``plugin.cfg`` for configuration and a tool script with the
  22. functionality. Plugins have a standard path like ``addons/plugin_name`` inside
  23. the project folder. Godot provides a dialog for generating those files and
  24. placing them where they need to be.
  25. In the main toolbar, click the ``Project`` dropdown. Then click
  26. ``Project Settings...``. Go to the ``Plugins`` tab and then click
  27. on the :button:`Create New Plugin` button in the top-right.
  28. You will see the dialog appear, like so:
  29. .. image:: img/making_plugins-create_plugin_dialog.webp
  30. The placeholder text in each field describes how it affects the plugin's
  31. creation of the files and the config file's values.
  32. To continue with the example, use the following values:
  33. .. tabs::
  34. .. code-tab:: ini GDScript
  35. Plugin Name: My Custom Node
  36. Subfolder: my_custom_node
  37. Description: A custom node made to extend the Godot Engine.
  38. Author: Your Name Here
  39. Version: 1.0.0
  40. Language: GDScript
  41. Script Name: custom_node.gd
  42. .. code-tab:: ini C#
  43. Plugin Name: My Custom Node
  44. Subfolder: MyCustomNode
  45. Description: A custom node made to extend the Godot Engine.
  46. Author: Your Name Here
  47. Version: 1.0.0
  48. Language: C#
  49. Script Name: CustomNode.cs
  50. .. warning::
  51. In C#, the EditorPlugin script needs to be compiled, which
  52. requires building the project. After building the project the plugin can be
  53. enabled in the ``Plugins`` tab of ``Project Settings``.
  54. You should end up with a directory structure like this:
  55. .. image:: img/making_plugins-my_custom_mode_folder.webp
  56. ``plugin.cfg`` is an INI file with metadata about your plugin.
  57. The name and description help people understand what it does.
  58. Your name helps you get properly credited for your work.
  59. The version number helps others know if they have an outdated version;
  60. if you are unsure on how to come up with the version number, check out `Semantic Versioning <https://semver.org/>`_.
  61. The main script file will instruct Godot what your plugin does in the editor
  62. once it is active.
  63. The script file
  64. ~~~~~~~~~~~~~~~
  65. Upon creation of the plugin, the dialog will automatically open the
  66. EditorPlugin script for you. The script has two requirements that you cannot
  67. change: it must be a ``@tool`` script, or else it will not load properly in the
  68. editor, and it must inherit from :ref:`class_EditorPlugin`.
  69. .. warning::
  70. In addition to the EditorPlugin script, any other GDScript that your plugin uses
  71. must *also* be a tool. Any GDScript without ``@tool`` used by the editor
  72. will act like an empty file!
  73. It's important to deal with initialization and clean-up of resources.
  74. A good practice is to use the virtual function
  75. :ref:`_enter_tree() <class_Node_private_method__enter_tree>` to initialize your plugin and
  76. :ref:`_exit_tree() <class_Node_private_method__exit_tree>` to clean it up. Thankfully,
  77. the dialog generates these callbacks for you. Your script should look something
  78. like this:
  79. .. _doc_making_plugins_template_code:
  80. .. tabs::
  81. .. code-tab:: gdscript GDScript
  82. @tool
  83. extends EditorPlugin
  84. func _enter_tree():
  85. # Initialization of the plugin goes here.
  86. pass
  87. func _exit_tree():
  88. # Clean-up of the plugin goes here.
  89. pass
  90. .. code-tab:: csharp
  91. #if TOOLS
  92. using Godot;
  93. [Tool]
  94. public partial class CustomNode : EditorPlugin
  95. {
  96. public override void _EnterTree()
  97. {
  98. // Initialization of the plugin goes here.
  99. }
  100. public override void _ExitTree()
  101. {
  102. // Clean-up of the plugin goes here.
  103. }
  104. }
  105. #endif
  106. This is a good template to use when creating new plugins.
  107. A custom node
  108. -------------
  109. Sometimes you want a certain behavior in many nodes, such as a custom scene
  110. or control that can be reused. Instancing is helpful in a lot of cases, but
  111. sometimes it can be cumbersome, especially if you're using it in many
  112. projects. A good solution to this is to make a plugin that adds a node with a
  113. custom behavior.
  114. .. warning::
  115. Nodes added via an EditorPlugin's :ref:`add_custom_type() <class_EditorPlugin_method_add_custom_type>`
  116. function are "custom type" nodes. While they work
  117. with any scripting language, they have fewer features than
  118. :ref:`the Script Class system <doc_gdscript_basics_class_name>`. If you
  119. are using GDScript or GDExtension, we recommend using Script Classes instead.
  120. Custom types are still the recommended approach for C#, as it does not support
  121. Script Classes.
  122. To create a new node type, you can use the function
  123. :ref:`add_custom_type() <class_EditorPlugin_method_add_custom_type>` from the
  124. :ref:`class_EditorPlugin` class. This function can add new types to the editor
  125. (nodes or resources). However, before you can create the type, you need a script
  126. that will act as the logic for the type. While that script doesn't have to use
  127. the ``@tool`` annotation, it can be added so the script runs in the editor.
  128. For this tutorial, we'll create a button that prints a message when
  129. clicked. For that, we'll need a script that extends from
  130. :ref:`class_Button`. It could also extend
  131. :ref:`class_BaseButton` if you prefer:
  132. .. tabs::
  133. .. code-tab:: gdscript GDScript
  134. @tool
  135. extends Button
  136. func _enter_tree():
  137. pressed.connect(clicked)
  138. func clicked():
  139. print("You clicked me!")
  140. .. code-tab:: csharp
  141. using Godot;
  142. [Tool]
  143. public partial class MyButton : Button
  144. {
  145. public override void _EnterTree()
  146. {
  147. Pressed += Clicked;
  148. }
  149. public void Clicked()
  150. {
  151. GD.Print("You clicked me!");
  152. }
  153. }
  154. That's it for our basic button. You can save this as ``my_button.gd`` inside the
  155. plugin folder. You'll also need a 16×16 icon to show in the scene tree. If you
  156. don't have one, you can grab the default one from the engine and save it in your
  157. `addons/my_custom_node` folder as `icon.png`, or use the default Godot logo
  158. (`preload("res://icon.svg")`).
  159. .. tip::
  160. SVG images that are used as custom node icons should have the
  161. **Editor > Scale With Editor Scale** and **Editor > Convert Colors With Editor Theme**
  162. :ref:`import options <doc_importing_images_editor_import_options>` enabled. This allows
  163. icons to follow the editor's scale and theming settings if the icons are designed with
  164. the same color palette as Godot's own icons.
  165. .. image:: img/making_plugins-custom_node_icon.png
  166. Now, we need to add it as a custom type so it shows on the **Create New Node**
  167. dialog. For that, change the ``custom_node.gd`` script to the following:
  168. .. tabs::
  169. .. code-tab:: gdscript GDScript
  170. @tool
  171. extends EditorPlugin
  172. func _enter_tree():
  173. # Initialization of the plugin goes here.
  174. # Add the new type with a name, a parent type, a script and an icon.
  175. #
  176. # NOTE: If `my_button.gd` uses `class_name MyButton`, do not call `add_custom_type()`
  177. # and leave this function empty instead with `pass`.
  178. # Script Classes and custom types will conflict if the same name is used for both.
  179. add_custom_type("MyButton", "Button", preload("my_button.gd"), preload("icon.png"))
  180. func _exit_tree():
  181. # Clean-up of the plugin goes here.
  182. # Always remember to remove it from the engine when deactivated.
  183. #
  184. # NOTE: This should not be called if Script Classes are used instead.
  185. # In this case, leave this function empty with `pass`.
  186. remove_custom_type("MyButton")
  187. .. code-tab:: csharp
  188. #if TOOLS
  189. using Godot;
  190. [Tool]
  191. public partial class CustomNode : EditorPlugin
  192. {
  193. public override void _EnterTree()
  194. {
  195. // Initialization of the plugin goes here.
  196. // Add the new type with a name, a parent type, a script and an icon.
  197. var script = GD.Load<Script>("res://addons/MyCustomNode/MyButton.cs");
  198. var texture = GD.Load<Texture2D>("res://addons/MyCustomNode/Icon.png");
  199. AddCustomType("MyButton", "Button", script, texture);
  200. }
  201. public override void _ExitTree()
  202. {
  203. // Clean-up of the plugin goes here.
  204. // Always remember to remove it from the engine when deactivated.
  205. RemoveCustomType("MyButton");
  206. }
  207. }
  208. #endif
  209. With that done, the plugin should already be available in the plugin list in the
  210. **Project Settings**, so activate it as explained in `Checking the results`_.
  211. Then try it out by adding your new node:
  212. .. image:: img/making_plugins-custom_node_create.webp
  213. When you add the node, you can see that it already has the script you created
  214. attached to it. Set a text to the button, save and run the scene. When you
  215. click the button, you can see some text in the console:
  216. .. image:: img/making_plugins-custom_node_console.webp
  217. A custom dock
  218. -------------
  219. Sometimes, you need to extend the editor and add tools that are always available.
  220. An easy way to do it is to add a new dock with a plugin. Docks are just scenes
  221. based on Control, so they are created in a way similar to usual GUI scenes.
  222. Creating a custom dock is done just like a custom node. Create a new
  223. ``plugin.cfg`` file in the ``addons/my_custom_dock`` folder, then
  224. add the following content to it:
  225. .. tabs::
  226. .. code-tab:: gdscript GDScript
  227. [plugin]
  228. name="My Custom Dock"
  229. description="A custom dock made so I can learn how to make plugins."
  230. author="Your Name Here"
  231. version="1.0"
  232. script="custom_dock.gd"
  233. .. code-tab:: csharp
  234. [plugin]
  235. name="My Custom Dock"
  236. description="A custom dock made so I can learn how to make plugins."
  237. author="Your Name Here"
  238. version="1.0"
  239. script="CustomDock.cs"
  240. Then create the script ``custom_dock.gd`` in the same folder. Fill it with the
  241. :ref:`template we've seen before <doc_making_plugins_template_code>` to get a
  242. good start.
  243. Since we're trying to add a new custom dock, we need to create the contents of
  244. the dock. This is nothing more than a standard Godot scene: just create
  245. a new scene in the editor then edit it.
  246. For an editor dock, the root node **must** be a :ref:`Control <class_Control>`
  247. or one of its child classes. For this tutorial, you can create a single button.
  248. Don't forget to add some text to your button.
  249. .. image:: img/making_plugins-my_custom_dock_scene.webp
  250. Save this scene as ``my_dock.tscn``. Now, we need to grab the scene we created
  251. then add it as a dock in the editor. For this, you can rely on the function
  252. :ref:`add_dock() <class_EditorPlugin_method_add_dock>` from the
  253. :ref:`EditorPlugin <class_EditorPlugin>` class.
  254. You need to select a dock position and define the control to add
  255. (which is the scene you just created). Don't forget to
  256. **remove the dock** when the plugin is deactivated.
  257. The script could look like this:
  258. .. tabs::
  259. .. code-tab:: gdscript GDScript
  260. @tool
  261. extends EditorPlugin
  262. # A class member to hold the dock during the plugin life cycle.
  263. var dock
  264. func _enter_tree():
  265. # Initialization of the plugin goes here.
  266. # Load the dock scene and instantiate it.
  267. var dock_scene = preload("res://addons/my_custom_dock/my_dock.tscn").instantiate()
  268. # Create the dock and add the loaded scene to it.
  269. dock = EditorDock.new()
  270. dock.add_child(dock_scene)
  271. dock.title = "My Dock"
  272. # Note that LEFT_UL means the left of the editor, upper-left dock.
  273. dock.default_slot = DOCK_SLOT_LEFT_UL
  274. # Allow the dock to be on the left or right of the editor, and to be made floating.
  275. dock.available_layouts = EditorDock.DOCK_LAYOUT_VERTICAL | EditorDock.DOCK_LAYOUT_FLOATING
  276. add_dock(dock)
  277. func _exit_tree():
  278. # Clean-up of the plugin goes here.
  279. # Remove the dock.
  280. remove_dock(dock)
  281. # Erase the control from the memory.
  282. dock.queue_free()
  283. .. code-tab:: csharp
  284. #if TOOLS
  285. using Godot;
  286. [Tool]
  287. public partial class CustomDock : EditorPlugin
  288. {
  289. private EditorDock _dock;
  290. public override void _EnterTree()
  291. {
  292. var _dock_scene = GD.Load<PackedScene>("res://addons/MyCustomDock/MyDock.tscn").Instantiate<Control>();
  293. AddControlToDock(DockSlot.LeftUl, _dock);
  294. // Create the dock and add the loaded scene to it.
  295. _dock = new EditorDock();
  296. _dock.AddChild(dock_scene);
  297. _dock.Title = "My Dock";
  298. // Note that LeftUl means the left of the editor, upper-left dock.
  299. _dock.DefaultSlot = DockSlot.LeftUl;
  300. // Allow the dock to be on the left or right of the editor, and to be made floating.
  301. _dock.AvailableLayouts = DockLayout.Horizontal | DockLayout.Floating;
  302. AddDock(_dock);
  303. }
  304. public override void _ExitTree()
  305. {
  306. // Clean-up of the plugin goes here.
  307. // Remove the dock.
  308. RemoveDock(_dock);
  309. // Erase the control from the memory.
  310. _dock.QueueFree();
  311. }
  312. }
  313. #endif
  314. Note that, while the dock will initially appear at its specified position,
  315. the user can freely change its position and save the resulting layout.
  316. Checking the results
  317. ~~~~~~~~~~~~~~~~~~~~
  318. It's now time to check the results of your work. Open the **Project
  319. Settings** and click on the **Plugins** tab. Your plugin should be the only one
  320. on the list.
  321. .. image:: img/making_plugins-project_settings.webp
  322. You can see the plugin is not enabled.
  323. Click the **Enable** checkbox to activate the plugin.
  324. The dock should become visible before you even close
  325. the settings window. You should now have a custom dock:
  326. .. image:: img/making_plugins-custom_dock.webp
  327. .. _doc_making_plugins_autoload:
  328. Registering autoloads/singletons in plugins
  329. -------------------------------------------
  330. It is possible for editor plugins to automatically register
  331. :ref:`autoloads <doc_singletons_autoload>` when the plugin is enabled.
  332. This also includes unregistering the autoload when the plugin is disabled.
  333. This makes setting up plugins faster for users, as they no longer have to manually
  334. add autoloads to their project settings if your editor plugin requires the use of
  335. an autoload.
  336. Use the following code to register a singleton from an editor plugin:
  337. .. tabs::
  338. .. code-tab:: gdscript GDScript
  339. @tool
  340. extends EditorPlugin
  341. # Replace this value with a PascalCase autoload name, as per the GDScript style guide.
  342. const AUTOLOAD_NAME = "SomeAutoload"
  343. func _enable_plugin():
  344. # The autoload can be a scene or script file.
  345. add_autoload_singleton(AUTOLOAD_NAME, "res://addons/my_addon/some_autoload.tscn")
  346. func _disable_plugin():
  347. remove_autoload_singleton(AUTOLOAD_NAME)
  348. .. code-tab:: csharp
  349. #if TOOLS
  350. using Godot;
  351. [Tool]
  352. public partial class MyEditorPlugin : EditorPlugin
  353. {
  354. // Replace this value with a PascalCase autoload name.
  355. private const string AutoloadName = "SomeAutoload";
  356. public override void _EnablePlugin()
  357. {
  358. // The autoload can be a scene or script file.
  359. AddAutoloadSingleton(AutoloadName, "res://addons/MyAddon/SomeAutoload.tscn");
  360. }
  361. public override void _DisablePlugin()
  362. {
  363. RemoveAutoloadSingleton(AutoloadName);
  364. }
  365. }
  366. #endif
  367. Using sub-plugins
  368. -----------------
  369. Often a plugin adds multiple things, for example a custom node and a panel.
  370. In those cases it might be easier to have a separate plugin script for each of those features.
  371. Sub-plugins can be used for this.
  372. First create all plugins and sub plugins as normal plugins:
  373. .. image:: img/sub_plugin_creation.webp
  374. Then move the sub plugins into the main plugin folder:
  375. .. image:: img/sub_plugin_moved.webp
  376. Godot will hide sub-plugins from the plugin list, so that a user can't enable or disable them.
  377. Instead the main plugin script should enable and disable sub-plugins like this:
  378. .. tabs::
  379. .. code-tab:: gdscript GDScript
  380. @tool
  381. extends EditorPlugin
  382. # The main plugin is located at res://addons/my_plugin/
  383. const PLUGIN_NAME = "my_plugin"
  384. func _enable_plugin():
  385. EditorInterface.set_plugin_enabled(PLUGIN_NAME + "/node", true)
  386. EditorInterface.set_plugin_enabled(PLUGIN_NAME + "/panel", true)
  387. func _disable_plugin():
  388. EditorInterface.set_plugin_enabled(PLUGIN_NAME + "/node", false)
  389. EditorInterface.set_plugin_enabled(PLUGIN_NAME + "/panel", false)
  390. Going beyond
  391. ------------
  392. Now that you've learned how to make basic plugins, you can extend the editor in
  393. several ways. Lots of functionality can be added to the editor with GDScript;
  394. it is a powerful way to create specialized editors without having to delve into
  395. C++ modules.
  396. You can make your own plugins to help yourself and share them in the
  397. `Asset Library <https://godotengine.org/asset-library/>`_ so that people
  398. can benefit from your work.