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.

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!

2D Physics in Godot

Godot handles 2D physics separately from 3D physics; it uses a specialized physics server for each. The 2D physics server is aptly named PhysicsServer2D and it’s responsible for creating and simulating physics objects. It’s important to understand how these physics objects relate to each other before manipulating them via code.

There are five physics objects: space, shape, body, area, and joint. Here’s a summary of each:

  • space: A self-contained world that contains bodies, shapes and joints. A space holds parameters like gravity and you can get collision and intersection info from it. Godot automatically creates a default space and adds all other physics objects you create to it.
  • shape: This a geometric shape like a rectangle or circle. You add shapes to bodies and areas to define their collision shape.
  • body: This physical object holds information like the position and velocity. A body is solid and can collide with other bodies and areas.
  • area: A region in space that’s used to detect bodies and other areas entering or leaving it. You can use an area as a trigger or sensor.
  • joint: A constraint between two bodies or between a body and a point. You can use joints to create physical connections between bodies. This can be used to create chains for example.

It’s important to note that these physics objects aren’t necessarily tied to nodes. You can create any of these physics objects via code and add them to the physics server without touching the scene tree. You’ll soon discover why this is useful.