This package makes it possible to develop a godot plugin and a corresponding standalone app using the same codebase.
- Add your plugin to the
res://addons
folder or create a new plugin. - Copy
res://standalone_plugin/editor.gd
into your plugins directory. (And rename it if you want.) - Modify the main script of your plugin to inherit the copied file from step 2.
- You are ready to go. 🎉
Step 2 and 3 can also be done automatically by calling
Project>Tools>Make Plugin Standalone
If you use any of the helper options inside of the tools menu it is highly recommended to restart the edior afterwards. The build in script editor may not update the changed scripts otherwise. (No way to do it by code :/)
Not every method of EditorPlugin
can be reproduced without Godot internals like the import or export systems. Therefore not all methods of EditorPlugin
will have an effect when running standalone. This won't throw an error though, in case your plugin has more functionality in Godot but should still run standalone.
Calling unsuported methods won't throw an error but when it comes to return values there can be problems.
You should add null
checks for all return values (e.g. get_script_create_dialog
will return null
on runtime). Same goes for everything returned from EditorInterface
If your plugin relies on godot internal systems adapting to standalone may require to disable some features. You can easily test whether you are running in the editor by calling Engine.is_editor_hint
. This allows you to enable certain features only when running standalone or as plugin. For example the standalone app may require additional steps to select a file to edit.
Method | Standalone | Editor | May Return null |
---|---|---|---|
_apply_changes virtual |
❌ | ✔️ | |
_build virtual |
❌ | ✔️ | |
_clear virtual |
❌ | ✔️ | |
_disable_plugin virtual |
✔️ | ✔️ | |
_edit virtual |
❌ | ✔️ | |
_enable_plugin virtual |
✔️ | ✔️ | |
_forward_3d_draw_over_viewport virtual |
❌ | ✔️ | |
_forward_3d_force_draw_over_viewport virtual |
❌ | ✔️ | |
_forward_3d_gui_input virtual |
❌ | ✔️ | |
_forward_canvas_draw_over_viewport virtual |
❌ | ✔️ | |
_forward_canvas_force_draw_over_viewport virtual |
❌ | ✔️ | |
_forward_canvas_gui_input virtual |
❌ | ✔️ | |
_get_breakpoints virtual |
❌ | ✔️ | |
_get_plugin_icon virtual |
✔️ | ✔️ | |
_get_plugin_name virtual |
✔️ | ✔️ | |
_get_state virtual |
❌ | ✔️ | |
_get_window_layout virtual |
✔️ | ✔️ | |
_handles virtual |
❌ | ✔️ | |
_has_main_screen virtual |
✔️ | ✔️ | |
_make_visible virtual |
✔️ | ✔️ | |
_save_external_data virtual |
✔️ only when exiting | ✔️ | |
_set_state virtual |
❌ | ✔️ | |
_set_window_layout virtual |
✔️ | ✔️ | |
add_autoload_singelton |
🔶 not at runtime but when registered in editor plugin it is still in the export | ✔️ | |
add_control_to_bottom_panel |
✔️ | ✔️ | |
add_control_to_container |
🔶 Only CONTAINER_TOOLBAR . Also adds new containers that are not supported in editor: CONTAINER_LAUNCH_PAD |
✔️ | |
add_control_to_dock |
✔️ | ✔️ | |
add_custom_type |
❌ | ✔️ | |
add_debugger_plugin |
❌ | ✔️ | |
add_export_plugin |
❌ | ✔️ | |
add_import_plugin |
❌ | ✔️ | |
add_inspector_plugin |
❌ | ✔️ | |
add_scene_format_importer_plugin |
❌ | ✔️ | |
add_scene_post_import_plugin |
❌ | ✔️ | |
add_spacial_gizmo_plugin |
❌ | ✔️ | |
add_tool_menu_item |
🔶 use the standalone exclusive menu api | ✔️ | |
add_tool_submenu_item |
🔶 use the standalone exclusive menu api | ✔️ | |
add_translation_parser_plugin |
❌ | ✔️ | |
add_undo_redo_inspector_hook_callback |
❌ | ✔️ | |
get_editor_interface |
🔶 returns an standalone interface | ✔️ | No |
get_export_as_menu |
❌ | ✔️ | Yes |
get_script_create_dialog |
❌ | ✔️ | Yes |
get_undo_redo |
✔️ | ✔️ | No |
hide_bottom_panel |
✔️ | ✔️ | |
make_bottom_panel_item_visible |
✔️ | ✔️ | |
queue_save_layout |
✔️ | ✔️ | |
remove_autoload_singelton |
❌ | ✔️ | |
remove_control_from_bottom_panel |
✔️ | ✔️ | |
remove_control_from_container |
🔶 | ✔️ | |
remove_control_from_docks |
✔️ | ✔️ | |
remove_custom_type |
❌ | ✔️ | |
remove_debugger_plugin |
❌ | ✔️ | |
remove_export_plugin |
❌ | ✔️ | |
remove_import_plugin |
❌ | ✔️ | |
remove_inspector_plugin |
❌ | ✔️ | |
remove_scene_format_importer_plugin |
❌ | ✔️ | |
remove_scene_post_import_plugin |
❌ | ✔️ | |
remove_spatial_gizmo_plugin |
❌ | ✔️ | |
remove_tool_menu_item |
❌ | ✔️ | |
remove_translation_parser_plugin |
❌ | ✔️ | |
remove_undo_redo_inspector_hook_callback |
❌ | ✔️ | |
set_force_draw_over_forwarding_enabled |
❌ | ✔️ | |
update_overlays |
❌ | ✔️ | |
add_menu_popup |
✔️🚧 | ❌ | |
remove_menu_popup |
✔️🚧 | ❌ |
Method | Standalone | Editor | May Return null |
---|---|---|---|
edit_node |
❌ | ✔️ | |
edit_resource |
❌ | ✔️ | |
edit_script |
❌ | ✔️ | |
get_base_control |
✔️ | ✔️ | No |
get_command_palette |
❌ | ✔️ | Yes |
get_current_path |
🔶 will always return user:// |
✔️ | No |
get_edited_scene_root |
❌ | ✔️ | Yes |
get_editor_main_screen |
✔️ | ✔️ | No |
get_editor_paths |
✔️ the paths are diffrent | ✔️ | No |
get_editor_scale |
✔️ will always return 1.0 |
✔️ | No |
get_editor_settings |
❌ | ✔️ | Yes |
get_file_system_dock |
❌ | ✔️ | Yes |
get_inspector |
❌ | ✔️ | Yes |
get_open_scenes |
🔶 will always be empty | ✔️ | No |
get_playing_scene |
🔶 will always be empty | ✔️ | No |
get_resource_filesystem |
❌ | ✔️ | Yes |
get_resource_previewer |
❌ | ✔️ | Yes |
get_script_editor |
❌ | ✔️ | Yes |
get_selected_path |
🔶 will always return user:// |
✔️ | No |
get_selection |
❌ | ✔️ | Yes |
inspect_object |
❌ | ✔️ | |
is_playing_scene |
🔶 will always be false |
✔️ | No |
is_plugin_enabled |
✔️ | ✔️ | No |
make_mesh_previews |
❌ will return PlaceholderTexture2D |
✔️ | No |
open_scene_from_path |
❌ | ✔️ | |
play_current_scene |
❌ | ✔️ | |
play_custom_scene |
❌ | ✔️ | |
play_main_scene |
❌ | ✔️ | |
reload_scene_from_path |
❌ | ✔️ | |
restart_editor |
❌ | ✔️ | |
save_scene |
❌ will always return OK |
✔️ | No |
save_scene_as |
❌ | ✔️ | |
select_file |
❌ | ✔️ | |
set_main_screen_editor |
✔️ | ✔️ | |
set_plugin_enabled |
✔️ | ✔️ | |
stop_playing_scene |
❌ | ✔️ |
The script you copied before starts with the following line of code.
#@standalone
Before building your project the package will loop over all activated plugins. It will check for each whether the main script inherits a script which starts with this very line.
In this way a list of plugins that should run standalone is obtained. This list is then stored in your ProjectSettings (the key is editor_plugins/standalone
).
When running your app the runtime compontents will read the list from the settings and there you have it.
The script in your plugins directory does. This is the reason why your plugin can still be used in the godot editor. But this package does another usefull thing on build time. With the list of standalone plugins it also has a list of all scripts, that inherit EditorPlugin
. Luckily Godot has the powerfull feature to load resource packs on runtime (read more on it here). This resource packs can override files at certain paths.
While building a resource pack is created, that overrides all the scripts inheriting EditorPlugin
. They get replaced with a version that hooks the methods into the standalone UI.
Of course one still needs an ExportPlugin
that ensures that the resource pack and all the plugins are included in the export. But you must have thought of that yourself, haven't you?