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.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 3 of 5 of this article. Click here to view the first page.

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.

Physics buttons menu

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:

Physics menu plugin info

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

Create a 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:

New scene properties

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.

Full rect

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

Show selection button

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”

All buttons

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:

  1. Check if the menu already exists. If it does, don’t do anything.
  2. Load the menu from disk via the preload function and instantiate it. Also store a reference to it in the menu variable so you don’t lose track of it.
  3. 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.

Toggle physics plugin

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:

  1. Check if the menu exists. If it doesn’t, don’t do anything.
  2. Remove the menu from the container.
  3. 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:

  1. Return an array of nodes.
  2. 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 the get_selection function. The get_selected_nodes function returns an array of selected nodes from the EditorSelection.

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:

  1. Call the _get_selected_nodes function to get an array of selected nodes.
  2. Print out “You’ve selected:”.
  3. Iterate through the selected nodes.
  4. 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.

Click show selection button

Selection output

Now that getting the selected nodes works as expected, you can move on to the interesting part: the physics!