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 4 of 5 of this article. Click here to view the first page.

Starting and Stopping Physics

Now you’re up to speed about how the physics server works in Godot, it’s time to use it. The physics server normally only runs when in-game, but there are ways to get it working in the editor as well. This can be useful to let objects fall in place to a stable position beforehand for example, but I’m sure you’ll think of more use cases.

Enough theory, time to get some physics going. The rest of the buttons need to have their pressed signals connected, so it’s good to get that out of the way first. Add the following placeholder functions below the _get_selected_nodes function:

func _on_start_physics_pressed() -> void:
    pass


func _on_stop_physics_pressed() -> void:
    pass


func _on_ten_frames_pressed() -> void:
    pass


func _on_reset_velocity_pressed() -> void:
    pass

As you might’ve guessed, you’ll want to hook these up the buttons in the _enter_tree function. To do that, add the following lines below the menu.get_node("ShowSelectionButton").pressed.connect(_on_show_selection_pressed) line in the _enter_tree function:

menu.get_node("StartPhysicsButton").pressed.connect( \
_on_start_physics_pressed)

menu.get_node("StopPhysicsButton").pressed.connect( \
_on_stop_physics_pressed)

menu.get_node("TenFramesButton").pressed.connect( \
_on_ten_frames_pressed)

menu.get_node("ResetVelocityButton").pressed.connect( \
_on_reset_velocity_pressed)

This will make it so that the buttons work in the editor.
To get physics working in the editor, I’ll first show you the naive approach. To start with, replace the pass keyword in the _on_start_physics_pressed function with the following line:

PhysicsServer2D.set_active(true)

This will activate the physics server and start the simulation. The set_active function takes one parameter: true to enable physics and false to disable it.
Logically, this means you need to add the following to the _on_stop_physics_pressed function:

PhysicsServer2D.set_active(false)

This is straightforward as I’m sure you’ll agree. Now reload the plugin as you made a change to the _enter_tree function above. The Start physics and Stop physics buttons should now work in the editor, so go ahead and open the Main scene in the 2D screen. Make sure to save the scene before clicking the Start physics button and then give it a click.

Physics simulated

All physics nodes will now be affected by physics in the editor, causing them to fall and tumble. Once all nodes have settled, click the Stop physics button reload the scene via Scene ▸ Reload Saved Scene.

While this approach works as you can see, it’s not recommended. By enabling the physics globally, all nodes in all opened scenes will be affected by physics. That’s right, even scenes you aren’t working in at the moment. This can has a high chance of unintended consequences.
The reason this affects all nodes is because of the default physics space that Godot creates. Since all bodies and areas are part of this space, every node gets simulated. To prevent this, you’ll need to create a custom physics space for the nodes you selected.

Custom Physics Spaces

Before creating the new physics space, you’ll need to keep track of both the new space and the default space so you can enable and disable them accordingly. Add the following lines above the _enter_tree function to add the needed variables:

var default_space_rid : RID
var custom_space_rid : RID

These will keep a reference stored to the default and custom physics spaces. Note that there isn’t a PhysicsSpace class or anything similar, references to physics spaces are done via their RID, their Resource ID. This is a unique identifier used by the low-level server classes like DisplayServer, RenderingServer and PhysicsServer2D to keep track of objects in memory.

With these in place, here’s an overview of the next steps to get self-contained physics working:

  1. Create a custom physics space
  2. Get selected nodes and add them to the custom space
  3. Deactivate the default physics space and activate the custom physics space
  4. Start the physics simulation

To cover the first step, add the following function to the end of the script:

# 1
func _create_physics_space() -> RID:
    # 2
    var rid = PhysicsServer2D.space_create()

    # 3
    PhysicsServer2D.area_set_param(rid, \
    PhysicsServer2D.AREA_PARAM_GRAVITY, 980)

    PhysicsServer2D.area_set_param(rid, \
    PhysicsServer2D.AREA_PARAM_GRAVITY_VECTOR, Vector2.DOWN)

    # 4
    return rid

This function will create a physics space, set its gravity and return its RID. Here’s how it works:

  1. This function takes no parameters and returns an RID.
  2. Call the PhysicsServer2D.space_create function to create a physics space. This returns an RID, which gets stored in the rid variable.
  3. Use the PhysicsServer2D.area_set_param function to set the gravity to 980 and gravity vector to Vector2.DOWN. This sets the default gravity used in this space.
  4. Return the new space’s rid to the caller.

The next step is to get the selected nodes and add them to the custom space. As usual, you’ll need another function for this. Add this function below the _create_physics_space function:

func _add_selected_nodes_to_custom_space() -> void:
    # 1
    var selected_nodes = _get_selected_nodes()
    if len(selected_nodes) == 0:
        return

    # 2
    if !custom_space_rid:
        custom_space_rid = _create_physics_space()

    # 3
    for selected_node in selected_nodes:
        # 4
        if selected_node is CollisionObject2D:
            # 5
            PhysicsServer2D.body_set_space(selected_node.get_rid(), \
            custom_space_rid)

            # 6
            if !default_space_rid:
                default_space_rid = selected_node.get_world_2d().space

This function may seem more complex that it actually is because of the space RID assignments that it’s doing here as well. In essence, it collects all selected nodes and assigns the new space to all selected nodes if they’re a CollisionObject2D. The CollisionObject2D class is the base class for all nodes that can be simulated with 2D physics.
Below is an overview of the code used:

  1. Get all selected nodes and store them in the selected_nodes variable. If there are no selected nodes, return from the function.
  2. If there’s no RID for the custom space yet, create a new physics space. The RID is stored in the custom_space_rid variable.
  3. Iterate over all selected nodes.
  4. If the selected node is a CollisionObject2D
  5. Set the space of the selected node to the custom space by calling the PhysicsServer2D.body_set_space function. The body_set_space function has two parameters: the RID of the body and the RID of the space.
  6. If the RID of the default space isn’t known yet, get it from this node and store it in the default_space_rid variable. The get_world_2d() function returns an instance of the World2D class, which holds all components of a 2D world, like a canvas and the default physics space.

To start the physics in the new space, you’re going to combine the last two steps together. In other words, you’ll activate the custom space, deactivate the default space and start the physics simulation. Each of these requires just a single call, so it’s not too bad! Add this function below the _add_selected_nodes_to_custom_space function:

func _activate_custom_physics_space() -> void:
    # 1
    PhysicsServer2D.space_set_active(default_space_rid, false)

    # 2
    PhysicsServer2D.space_set_active(custom_space_rid, true)

    # 3
    PhysicsServer2D.set_active(true)

This does exactly what it says on the tin:

  1. Deactivate the default physics space.
  2. Activate the custom physics space.
  3. Start the physics simulation.

To bring it all together, you’ll need one more function that will the call the two last functions. This will make it easy to start the physics with only a single call. Add this new function below the _activate_custom_physics_space function:

func _start_custom_physics() -> void:
    _add_selected_nodes_to_custom_space()
    _activate_custom_physics_space()

This will add the selected nodes to the custom space, activate the custom physics space and start the physics simulation. You can now finally update the existing code in the _on_start_physics_pressed function to start the physics simulation in a better way. To do so, replace PhysicsServer2D.set_active(true) with this line:

_start_custom_physics()

Phew! Time to test the plugin after all that coding. Save the script and open the Main scene in the 2D screen. Select a few nodes you want to simulate the physics of and click the Start physics button. Only the selected nodes will move. Once you’re satisfied with the result, click the Stop physics button to halt the simulation and reload the saved scene again.

Physics test

Keep in mind that you also need to select the platforms if you want your nodes to stop as expected. Not doing so will not add those platforms to the simulation, so your nodes will continue to fall endlessly.

Nodes falling

This is already working great! The Stop physics button also seems to work as expected, but it’s actually missing something. Right now, the default physics space will stay disabled after stopping the physics simulation. If you ever use another plugin that relies on the default physics space, this will give unexpected results.
To remedy this, open the script again and add this function that reverts backs to the default physics space:

func _deactivate_custom_physics_space() -> void:
    # 1
    PhysicsServer2D.space_set_active(default_space_rid, true)

    # 2
    PhysicsServer2D.space_set_active(custom_space_rid, false)

    # 3
    PhysicsServer2D.set_active(false)

This disables the custom physics space and re-enables the default physics space. Finally, it stops the physics simulation. Here’s a quick look at the code:

  1. Deactivate the custom physics space by passing its RID to the PhysicsServer2D.space_set_active function.
  2. Activate the default physics space by passing its RID to the PhysicsServer2D.space_set_active function.
  3. Stop the physics simulation.

To use this function, replace the existing line in the _on_stop_physics_pressed function with:

_deactivate_custom_physics_space()

That’s it! The main functionality of the plugin is now ready. In the sections below I’ve added some quick nice-to-haves that can you can add to the plugin.