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?
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.
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.
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:
- Create a custom physics space
- Get selected nodes and add them to the custom space
- Deactivate the default physics space and activate the custom physics space
- 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:
- This function takes no parameters and returns an
RID
. - Call the
PhysicsServer2D.space_create
function to create a physics space. This returns anRID
, which gets stored in therid
variable. - 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. - 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:
- Get all selected nodes and store them in the
selected_nodes
variable. If there are no selected nodes, return from the function. - 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. - Iterate over all selected nodes.
- If the selected node is a
CollisionObject2D
… - Set the space of the selected node to the custom space by calling the
PhysicsServer2D.body_set_space
function. Thebody_set_space
function has two parameters: theRID
of the body and theRID
of the space. - 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. Theget_world_2d()
function returns an instance of theWorld2D
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:
- Deactivate the default physics space.
- Activate the custom physics space.
- 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.
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.
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:
- Deactivate the custom physics space by passing its RID to the
PhysicsServer2D.space_set_active
function. - Activate the default physics space by passing its RID to the
PhysicsServer2D.space_set_active
function. - 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.