Extending the Editor with Plugins in Godot
Embark on a journey to harness the true power of Godot with editor plugins! Revolutionize your workflow with bespoke tools, efficient shortcuts, and personalized menu options. Delve deep into the art of plugin creation and unlock the boundless potential of Godot with ease. By Eric Van de Kerckhove.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Extending the Editor with Plugins in Godot
45 mins
- Getting Started
- Plugin Overview
- Creating Your First Plugin
- Scaffolding
- Taking a Closer Look
- Handling Node Selection
- Adding the Button
- Developing an Advanced Plugin
- Creating the Menu Scene
- Loading and Unloading the Menu
- Getting Selected Nodes
- 2D Physics in Godot
- Starting and Stopping Physics
- Custom Physics Spaces
- Physics Frames
- Adding Quality of Life Features
- Where to Go From Here?
Developing an Advanced Plugin
For your next plugin, you’ll create a menu with buttons that allows you to simulate 2D gravity in the editor. Along the way, you’ll learn how physics work in Godot and how you can let the physics engine only affect selected nodes.
To start, you’ll need to create a new plugin. In the Project Settings menu, select the Plugins tab and click the Create New Plugin button. Fill in the following information:
- Plugin Name: Physics Menu
- Subfolder: physics_menu
- Description: Run physics on selected objects in the editor.
- Author: Your name or username
- Script Name: physics_menu.gd
Here’s what it should look like:
Next, click the Create button to create the new plugin. The physics_menu.gd script will automatically open in the script editor. You won’t be coding yet though; unlike the first plugin you’ve created, you’re going to create a new scene first to hold the menu.
Creating the Menu Scene
By creating a scene, you can easily customize the menu to your liking. While it’s possible to do this via code like you did above, creating UI via the editor is more streamlined and allows for quick tweaks.
First up, create a new scene in the addons/physics_menu folder and name it menu_ui.tscn
. You can do this by right-clicking the folder and selecting Create New ▸ Scene….
Make this new scene have an VBoxContainer as its root node and name this root node Menu. Here’s what this looks like in the dialog:
Once you’ve filled in the scene properties, click the OK button to create the scene. Now make the Menu node take up the full viewport by selecting it and selecting the Full Rect anchor preset in the toolbar.
Doing this ensures that the menu will use all available space in whichever container you add it to.
Now all that’s left is to add the buttons to the menu. Add a new Button node as a child node of Menu and name it ShowSelectionButton. Change its Text property to “Show selection”.
Now repeat the steps above (or duplicate the button) and add these other buttons:
- StartPhysicsButton: “Start physics”
- StopPhysicsButton: “Stop physics”
- TenFramesButton: “Simulate 10 physics frames”
- ResetVelocityButton: “Reset velocity”
Each of these buttons will have a specific function:
- ShowSelectionButton will show the selected nodes in the console.
- StartPhysicsButton will start physics on the selected nodes in the editor.
- StopPhysicsButton will stop physics for the selected nodes.
- TenFramesButton will simulate 10 physics frames, which is useful for a quick test.
- ResetVelocityButton will reset the linear and angular velocity of the selected nodes. This makes it so they don’t immediately start moving or spinning when you start the project.
That’s it for the scene! If you want to be fancy, you can add an icon to each button, maybe a kitten or a puppy? At any rate, you don’t need to attach a script here, as all logic will be handled by the plugin script.
Loading and Unloading the Menu
Like the first plugin, you’ll need to keep track of the menu and have some way of showing and hiding it. To take care of the first part, open the physics_menu.gd script in the script editor and add this variable declaration above the _enter_tree
function:
var menu : Node
You can now store a reference to the menu in this variable. To show the menu, you’ll need to load its packed scene from disk and instantiate it. The best place to do this is in the _enter_tree
function as that serves as the entry point for the plugin. Replace the pass
keyword with the code below:
# 1
if menu:
return
# 2
menu = preload("res://addons/physics_menu/menu_ui.tscn").instantiate()
# 3
add_control_to_container( \
EditorPlugin.CONTAINER_CANVAS_EDITOR_SIDE_RIGHT, menu)
This will add create the menu and add it to a panel to the right of the canvas:
- Check if the menu already exists. If it does, don’t do anything.
- Load the menu from disk via the
preload
function and instantiate it. Also store a reference to it in themenu
variable so you don’t lose track of it. - Add the menu to the editor via the
add_control_to_container
function.
Easy! Now save the script, open the Main scene in the 2D screen and… nothing. Can you guess why the menu isn’t showing up?
It’s because _enter_tree
gets called once: when the plugin is enabled. Since your plugin is already enabled, this function will not be called again. To correct this, disable and enable the plugin again via the Project Settings ▸ Plugins tab. You should now see the menu appear on the right side of the 2D screen.
Not bad for five lines of code. Of course, the buttons don’t do anything yet, but it’s a great start. Right now, the menu will stay in memory after the plugin is disabled, which isn’t that good. That’s where the _exit_tree
function comes in to do some cleanup.
Open the script again and replace the pass
keyword in the _exit_tree
function with the code below:
# 1
if !menu:
return
# 2
remove_control_from_container( \
EditorPlugin.CONTAINER_CANVAS_EDITOR_SIDE_RIGHT, menu)
# 3
menu.free()
menu = null
This removes the menu from the container and frees it from memory. Nice and tidy. :]
Here’s an overview of the code:
- Check if the menu exists. If it doesn’t, don’t do anything.
- Remove the menu from the container.
- Free the menu from memory.
Great, now you’re ready to move on to adding functionality to the menu.
Getting Selected Nodes
The first button labeled “Show selection” should show the selected nodes in the console. To get that working, you’ll need to hook up the button’s pressed
signal first. Create a new function called _on_show_selection_pressed
below the _exit_tree
function:
func _on_show_selection_pressed() -> void:
pass
This will act as a placeholder. Now add some empty lines between menu = preload("res://addons/physics_menu/menu_ui.tscn").instantiate()
and add_control_to_container(EditorPlugin.CONTAINER_CANVAS_EDITOR_SIDE_RIGHT, menu)
. This is to make room to add the button connection code. Next, add the line below in the space you made:
menu.get_node("ShowSelectionButton").pressed.connect( \
_on_show_selection_pressed)
The _enter_tree
function should now look like this:
func _enter_tree() -> void:
if menu:
return
menu = preload("res://addons/physics_menu/menu_ui.tscn").instantiate()
menu.get_node("ShowSelectionButton").pressed.connect( \
_on_show_selection_pressed)
add_control_to_container(EditorPlugin.CONTAINER_CANVAS_EDITOR_SIDE_RIGHT, \
menu)
The line you added gets the child node named “ShowSelectionButton” and connects its pressed
signal to the _on_show_selection_pressed
function.
For the next step, you’ll want some way of getting all selected nodes. To do this, you’ll need to create a new function called _get_selected_nodes
. Add this below the _on_show_selection_pressed
function:
# 1
func _get_selected_nodes() -> Array[Node]:
# 2
return EditorInterface.get_selection().get_selected_nodes()
As it name implies, this function will return an array of all selected nodes:
- Return an array of nodes.
- The
EditorInterface
class comes with a bunch of useful functions, one of which is to get the class that manages the selected nodes,EditorSelection
. This is what’s returned by theget_selection
function. Theget_selected_nodes
function returns an array of selected nodes from theEditorSelection
.
The main reason you’re creating a function only to call some editor functions in a row is to make the code easier to read later on. Having to write EditorInterface.get_selection().get_selected_nodes()
each time becomes a bit unwieldy.
With this function in place, you can now add the final logic to the _on_show_selection_pressed
function. Replace its pass
keyword with the code below:
# 1
var selected_nodes = _get_selected_nodes()
# 2
print("You've selected:")
# 3
for selected_node in selected_nodes:
# 4
print("- ", selected_node.name, " (", selected_node.get_class(), ")")
This will print out the name and class of all selected nodes in the console. Here’s a line-by-line breakdown:
- Call the
_get_selected_nodes
function to get an array of selected nodes. - Print out “You’ve selected:”.
- Iterate through the selected nodes.
- Print out the name and class of the node.
This is great way to test if the selection logic is working as intended. Save the script and reload the plugin, as you’ve changed the _enter_tree
function. Now select some nodes in the 2D screen and click the Show selection button. You should see the names and classes of the selected nodes appear in the console.
Now that getting the selected nodes works as expected, you can move on to the interesting part: the physics!