Compare commits

...

6 Commits

Author SHA1 Message Date
speak 388236a291
Merge 2dfe775f7b into 7aebed5897 2025-01-25 16:13:21 +01:00
namelessvoid 7aebed5897
Update Readme to accomodate new documentation structure
(cherry picked from commit fe87a87759)
2025-01-22 01:18:37 +01:00
Steven Schoen a6613c32f9
Navigate to subtree resource when clicked in editor (#276)
Co-authored-by: Steven Schoen <digisteve5@gmail.com>
2025-01-22 01:05:51 +01:00
Serhii Snitsaruk 76e02a75e5
Fix BTInstance not registered with debugger on BTPlayer.set_bt_instance() (#275) 2025-01-21 23:43:52 +01:00
Serhii Snitsaruk 1b9cf17339
Fix BTPlayer.set_behavior_tree() not populating Blackboard (#274) 2025-01-21 23:35:28 +01:00
Jesse Viikari 2dfe775f7b Fix compatibility with Godot dev 4.4dev4 2024-11-13 14:04:44 +02:00
9 changed files with 61 additions and 39 deletions

View File

@ -16,7 +16,7 @@
**LimboAI** is an open-source C++ plugin for **Godot Engine 4** providing a combination of **LimboAI** is an open-source C++ plugin for **Godot Engine 4** providing a combination of
**Behavior Trees** and **State Machines**, which can be used together to create complex AI behaviors. **Behavior Trees** and **State Machines**, which can be used together to create complex AI behaviors.
It comes with a behavior tree editor, built-in documentation, visual debugger, extensive demo project with a tutorial, and more! It comes with a behavior tree editor, built-in documentation, visual debugger, extensive demo project with a tutorial, and more!
While it is implemented in C++, it fully supports GDScript for [creating your own tasks](https://limboai.readthedocs.io/en/stable/getting-started/custom-tasks.html) and [states](https://limboai.readthedocs.io/en/stable/getting-started/hsm.html). While it is implemented in C++, it fully supports GDScript for [creating your own tasks](https://limboai.readthedocs.io/en/stable/behavior-trees/custom-tasks.html) and [states](https://limboai.readthedocs.io/en/stable/hierarchical-state-machines/create-hsm.html).
If you enjoy using LimboAI, please **consider supporting** my efforts with a donation on Ko-fi 😊 Your contribution will help me continue developing and improving it. If you enjoy using LimboAI, please **consider supporting** my efforts with a donation on Ko-fi 😊 Your contribution will help me continue developing and improving it.
@ -24,7 +24,7 @@ If you enjoy using LimboAI, please **consider supporting** my efforts with a don
![Textured screenshot](doc/images/behavior-tree-editor-debugger.png) ![Textured screenshot](doc/images/behavior-tree-editor-debugger.png)
Behavior Trees are powerful hierarchical structures used to model and control the behavior of agents in a game (e.g., characters, enemies). They are designed to make it easier to create rich and highly modular behaviors for your games. To learn more about behavior trees, check out [Introduction to Behavior Trees](https://limboai.readthedocs.io/en/stable/getting-started/introduction.html) and our demo project, which includes a tutorial. Behavior Trees are powerful hierarchical structures used to model and control the behavior of agents in a game (e.g., characters, enemies). They are designed to make it easier to create rich and highly modular behaviors for your games. To learn more about behavior trees, check out [Introduction to Behavior Trees](https://limboai.readthedocs.io/en/stable/behavior-trees/introduction.html) and our demo project, which includes a tutorial.
## Demonstration ## Demonstration
@ -50,13 +50,13 @@ Behavior Trees are powerful hierarchical structures used to model and control th
- Execute `BehaviorTree` resources using the `BTPlayer` node. - Execute `BehaviorTree` resources using the `BTPlayer` node.
- Create complex behaviors by combining and nesting tasks in a hierarchy. - Create complex behaviors by combining and nesting tasks in a hierarchy.
- Control execution flow using composite, decorator, and condition tasks. - Control execution flow using composite, decorator, and condition tasks.
- [Create custom tasks](https://limboai.readthedocs.io/en/stable/getting-started/custom-tasks.html) by extending core classes: `BTAction`, `BTCondition`, `BTDecorator`, and `BTComposite`. - [Create custom tasks](https://limboai.readthedocs.io/en/stable/behavior-trees/custom-tasks.html) by extending core classes: `BTAction`, `BTCondition`, `BTDecorator`, and `BTComposite`.
- Built-in class documentation. - Built-in class documentation.
- Blackboard system: Share data seamlessly between tasks using the `Blackboard`. - Blackboard system: Share data seamlessly between tasks using the `Blackboard`.
- Blackboard plans: Define variables in the BehaviorTree resource and override their values in the BTPlayer node. - Blackboard plans: Define variables in the BehaviorTree resource and override their values in the BTPlayer node.
- Plan editor: Manage variables, their data types and property hints. - Plan editor: Manage variables, their data types and property hints.
- Blackboard scopes: Prevent name conflicts and enable advanced techniques like [sharing data between several agents](https://limboai.readthedocs.io/en/stable/getting-started/using-blackboard.html#sharing-data-between-several-agents). - Blackboard scopes: Prevent name conflicts and enable advanced techniques like [sharing data between several agents](https://limboai.readthedocs.io/en/stable/behavior-trees/using-blackboard.html#sharing-data-between-several-agents).
- Blackboard parameters: [Export a BB parameter](https://limboai.readthedocs.io/en/stable/getting-started/using-blackboard.html#task-parameters), for which user can provide a value or bind it to a blackboard variable (can be used in custom tasks). - Blackboard parameters: [Export a BB parameter](https://limboai.readthedocs.io/en/stable/behavior-trees/using-blackboard.html#task-parameters), for which user can provide a value or bind it to a blackboard variable (can be used in custom tasks).
- Inspector support for specifying blackboard variables (custom editor for exported `StringName` properties ending with "_var"). - Inspector support for specifying blackboard variables (custom editor for exported `StringName` properties ending with "_var").
- Use the `BTSubtree` task to execute a tree from a different resource file, promoting organization and reusability. - Use the `BTSubtree` task to execute a tree from a different resource file, promoting organization and reusability.
- Visual Debugger: Inspect the execution of any BT in a running scene to identify and troubleshoot issues. - Visual Debugger: Inspect the execution of any BT in a running scene to identify and troubleshoot issues.
@ -67,24 +67,24 @@ Behavior Trees are powerful hierarchical structures used to model and control th
- Extend the `LimboState` class to implement state logic. - Extend the `LimboState` class to implement state logic.
- `LimboHSM` node serves as a state machine that manages `LimboState` instances and transitions. - `LimboHSM` node serves as a state machine that manages `LimboState` instances and transitions.
- `LimboHSM` is a state itself and can be nested within other `LimboHSM` instances. - `LimboHSM` is a state itself and can be nested within other `LimboHSM` instances.
- [Event-based](https://limboai.readthedocs.io/en/stable/getting-started/hsm.html#events-and-transitions): Transitions are associated with events and are triggered by the state machine when the relevant event is dispatched, allowing for better decoupling of transitions from state logic. - [Event-based](https://limboai.readthedocs.io/en/stable/hierarchical-state-machines/create-hsm.html#events-and-transitions): Transitions are associated with events and are triggered by the state machine when the relevant event is dispatched, allowing for better decoupling of transitions from state logic.
- Combine state machines with behavior trees using `BTState` for advanced reactive AI. - Combine state machines with behavior trees using `BTState` for advanced reactive AI.
- Delegation Option: Using the vanilla `LimboState`, [delegate the implementation](https://limboai.readthedocs.io/en/stable/getting-started/hsm.html#single-file-state-machine-setup) to your callback functions, making it perfect for rapid prototyping and game jams. - Delegation Option: Using the vanilla `LimboState`, [delegate the implementation](https://limboai.readthedocs.io/en/stable/hierarchical-state-machines/create-hsm.html#single-file-state-machine-setup) to your callback functions, making it perfect for rapid prototyping and game jams.
- 🛈 Note: State machine setup and initialization require code; there is no GUI editor. - 🛈 Note: State machine setup and initialization require code; there is no GUI editor.
- **Tested:** Behavior tree tasks and HSM are covered by unit tests. - **Tested:** Behavior tree tasks and HSM are covered by unit tests.
- **GDExtension:** LimboAI can be [used as extension](https://limboai.readthedocs.io/en/stable/getting-started/gdextension.html). Custom engine builds are not necessary. - **GDExtension:** LimboAI can be [used as extension](https://limboai.readthedocs.io/en/stable/getting-started/getting-limboai.html#get-gdextension-version). Custom engine builds are not necessary.
- **Demo + Tutorial:** Check out our extensive demo project, which includes an introduction to behavior trees using examples. - **Demo + Tutorial:** Check out our extensive demo project, which includes an introduction to behavior trees using examples.
## First steps ## First steps
Follow the [First steps](https://limboai.readthedocs.io/en/stable/index.html#first-steps) guide to learn how to get started with LimboAI and the demo project. Follow the [Getting started](https://limboai.readthedocs.io/en/stable/getting-started/getting-limboai.html) guide to learn how to get started with LimboAI and the demo project.
## Getting LimboAI ## Getting LimboAI
LimboAI can be used as either a C++ module or as a GDExtension shared library. GDExtension version is more convenient to use but somewhat limited in features. Whichever you choose to use, your project will stay compatible with both and you can switch from one to the other any time. See [Using GDExtension](https://limboai.readthedocs.io/en/stable/getting-started/gdextension.html). LimboAI can be used as either a C++ module or as a GDExtension shared library. GDExtension version is more convenient to use but somewhat limited in features. Whichever you choose to use, your project will stay compatible with both and you can switch from one to the other any time. See [Using GDExtension](https://limboai.readthedocs.io/en/stable/getting-started/getting-limboai.html#get-gdextension-version).
### Precompiled builds ### Precompiled builds
@ -109,15 +109,15 @@ LimboAI can be used as either a C++ module or as a GDExtension shared library. G
## Using the plugin ## Using the plugin
- Online Documentation: [stable](https://limboai.readthedocs.io/en/stable/index.html), [latest](https://limboai.readthedocs.io/en/latest/index.html) - Online Documentation: [stable](https://limboai.readthedocs.io/en/stable/index.html), [latest](https://limboai.readthedocs.io/en/latest/index.html)
- [First steps](https://limboai.readthedocs.io/en/stable/index.html#first-steps) - [Getting started](https://limboai.readthedocs.io/en/stable/getting-started/getting-limboai.html)
- [Introduction to Behavior Trees](https://limboai.readthedocs.io/en/stable/getting-started/introduction.html) - [Introduction to Behavior Trees](https://limboai.readthedocs.io/en/stable/behavior-trees/introduction.html)
- [Creating custom tasks in GDScript](https://limboai.readthedocs.io/en/stable/getting-started/custom-tasks.html) - [Creating custom tasks in GDScript](https://limboai.readthedocs.io/en/stable/behavior-trees/custom-tasks.html)
- [Sharing data using Blackboard](https://limboai.readthedocs.io/en/stable/getting-started/using-blackboard.html) - [Sharing data using Blackboard](https://limboai.readthedocs.io/en/stable/behavior-trees/using-blackboard.html)
- [Accessing nodes in the scene tree](https://limboai.readthedocs.io/en/stable/getting-started/accessing-nodes.html) - [Accessing nodes in the scene tree](https://limboai.readthedocs.io/en/stable/behavior-trees/accessing-nodes.html)
- [State machines](https://limboai.readthedocs.io/en/stable/getting-started/hsm.html) - [State machines](https://limboai.readthedocs.io/en/stable/hierarchical-state-machines/create-hsm.html)
- [Using GDExtension](https://limboai.readthedocs.io/en/stable/getting-started/gdextension.html) - [Using GDExtension](https://limboai.readthedocs.io/en/stable/getting-started/getting-limboai.html#get-gdextension-version)
- [Using LimboAI with C#](https://limboai.readthedocs.io/en/stable/getting-started/c-sharp.html) - [Using LimboAI with C#](https://limboai.readthedocs.io/en/stable/getting-started/c-sharp.html)
- [Class reference](https://limboai.readthedocs.io/en/stable/getting-started/featured-classes.html) - [Class reference](https://limboai.readthedocs.io/en/stable/classes/featured-classes.html)
## Contributing ## Contributing

View File

@ -83,7 +83,7 @@ void BTInstance::register_with_debugger() {
void BTInstance::unregister_with_debugger() { void BTInstance::unregister_with_debugger() {
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
if (LimboDebugger::get_singleton()->is_active()) { if (LimboDebugger::get_singleton() && LimboDebugger::get_singleton()->is_active()) {
LimboDebugger::get_singleton()->unregister_bt_instance(get_instance_id()); LimboDebugger::get_singleton()->unregister_bt_instance(get_instance_id());
} }
#endif #endif
@ -151,5 +151,6 @@ BTInstance::~BTInstance() {
emit_signal(LW_NAME(freed)); emit_signal(LW_NAME(freed));
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
_remove_custom_monitor(); _remove_custom_monitor();
unregister_with_debugger();
#endif #endif
} }

View File

@ -42,7 +42,7 @@
VARIANT_ENUM_CAST(BTPlayer::UpdateMode); VARIANT_ENUM_CAST(BTPlayer::UpdateMode);
void BTPlayer::_load_tree() { void BTPlayer::_instantiate_bt() {
bt_instance.unref(); bt_instance.unref();
ERR_FAIL_COND_MSG(!behavior_tree.is_valid(), "BTPlayer: Initialization failed - needs a valid behavior tree."); ERR_FAIL_COND_MSG(!behavior_tree.is_valid(), "BTPlayer: Initialization failed - needs a valid behavior tree.");
ERR_FAIL_COND_MSG(!behavior_tree->get_root_task().is_valid(), "BTPlayer: Initialization failed - behavior tree has no valid root task."); ERR_FAIL_COND_MSG(!behavior_tree->get_root_task().is_valid(), "BTPlayer: Initialization failed - behavior tree has no valid root task.");
@ -70,6 +70,19 @@ void BTPlayer::_update_blackboard_plan() {
blackboard_plan->set_base_plan(behavior_tree.is_valid() ? behavior_tree->get_blackboard_plan() : nullptr); blackboard_plan->set_base_plan(behavior_tree.is_valid() ? behavior_tree->get_blackboard_plan() : nullptr);
} }
void BTPlayer::_initialize() {
if (blackboard.is_null()) {
blackboard = Ref<Blackboard>(memnew(Blackboard));
}
if (blackboard_plan.is_valid()) {
// Don't overwrite existing blackboard values as they may be initialized from code.
blackboard_plan->populate_blackboard(blackboard, false, this, _get_scene_root());
}
if (behavior_tree.is_valid()) {
_instantiate_bt();
}
}
void BTPlayer::set_bt_instance(const Ref<BTInstance> &p_bt_instance) { void BTPlayer::set_bt_instance(const Ref<BTInstance> &p_bt_instance) {
ERR_FAIL_COND_MSG(p_bt_instance.is_null(), "BTPlayer: Failed to set behavior tree instance - instance is null."); ERR_FAIL_COND_MSG(p_bt_instance.is_null(), "BTPlayer: Failed to set behavior tree instance - instance is null.");
ERR_FAIL_COND_MSG(!p_bt_instance->is_instance_valid(), "BTPlayer: Failed to set behavior tree instance - instance is not valid."); ERR_FAIL_COND_MSG(!p_bt_instance->is_instance_valid(), "BTPlayer: Failed to set behavior tree instance - instance is not valid.");
@ -78,6 +91,11 @@ void BTPlayer::set_bt_instance(const Ref<BTInstance> &p_bt_instance) {
blackboard = p_bt_instance->get_blackboard(); blackboard = p_bt_instance->get_blackboard();
agent_node = p_bt_instance->get_agent()->get_path(); agent_node = p_bt_instance->get_agent()->get_path();
#ifdef DEBUG_ENABLED
bt_instance->set_monitor_performance(monitor_performance);
bt_instance->register_with_debugger();
#endif // DEBUG_ENABLED
blackboard_plan.unref(); blackboard_plan.unref();
behavior_tree.unref(); behavior_tree.unref();
} }
@ -104,7 +122,8 @@ void BTPlayer::set_behavior_tree(const Ref<BehaviorTree> &p_tree) {
} else { } else {
behavior_tree = p_tree; behavior_tree = p_tree;
if (get_owner() && is_inside_tree()) { if (get_owner() && is_inside_tree()) {
_load_tree(); _update_blackboard_plan();
_initialize();
} }
} }
} }
@ -179,16 +198,7 @@ void BTPlayer::_notification(int p_notification) {
} break; } break;
case NOTIFICATION_READY: { case NOTIFICATION_READY: {
if (!Engine::get_singleton()->is_editor_hint()) { if (!Engine::get_singleton()->is_editor_hint()) {
if (blackboard.is_null()) { _initialize();
blackboard = Ref<Blackboard>(memnew(Blackboard));
}
if (blackboard_plan.is_valid()) {
// Don't overwrite existing blackboard values as they may be initialized from code.
blackboard_plan->populate_blackboard(blackboard, false, this, _get_scene_root());
}
if (behavior_tree.is_valid()) {
_load_tree();
}
} else { } else {
_update_blackboard_plan(); _update_blackboard_plan();
} }

View File

@ -48,8 +48,9 @@ private:
Ref<BTInstance> bt_instance; Ref<BTInstance> bt_instance;
void _load_tree(); void _instantiate_bt();
void _update_blackboard_plan(); void _update_blackboard_plan();
void _initialize();
_FORCE_INLINE_ Node *_get_scene_root() const { return scene_root_hint ? scene_root_hint : get_owner(); } _FORCE_INLINE_ Node *_get_scene_root() const { return scene_root_hint ? scene_root_hint : get_owner(); }
protected: protected:

View File

@ -287,14 +287,14 @@ void EditorPropertyBBParam::update_property() {
variable_editor->update_property(); variable_editor->update_property();
variable_editor->show(); variable_editor->show();
bottom_container->hide(); bottom_container->hide();
type_choice->set_icon(get_editor_theme_icon(SNAME("LimboExtraVariable"))); type_choice->set_button_icon(get_editor_theme_icon(SNAME("LimboExtraVariable")));
} else { } else {
_create_value_editor(param->get_type()); _create_value_editor(param->get_type());
variable_editor->hide(); variable_editor->hide();
value_editor->show(); value_editor->show();
value_editor->set_object_and_property(param.ptr(), SNAME("saved_value")); value_editor->set_object_and_property(param.ptr(), SNAME("saved_value"));
value_editor->update_property(); value_editor->update_property();
type_choice->set_icon(get_editor_theme_icon(Variant::get_type_name(param->get_type()))); type_choice->set_button_icon(get_editor_theme_icon(Variant::get_type_name(param->get_type())));
} }
} }
@ -316,7 +316,7 @@ void EditorPropertyBBParam::_notification(int p_what) {
{ {
String type = Variant::get_type_name(_get_edited_param()->get_type()); String type = Variant::get_type_name(_get_edited_param()->get_type());
type_choice->set_icon(get_editor_theme_icon(type)); type_choice->set_button_icon(get_editor_theme_icon(type));
} }
// Initialize type choice. // Initialize type choice.

View File

@ -893,6 +893,14 @@ void LimboAIEditor::_on_tree_task_activated() {
Ref<Script> scr = selected->get_script(); Ref<Script> scr = selected->get_script();
if (scr.is_valid()) { if (scr.is_valid()) {
EDIT_SCRIPT(scr->get_path()); EDIT_SCRIPT(scr->get_path());
} else if (IS_CLASS(selected, BTSubtree)) {
Ref<BehaviorTree> subtree = static_cast<Ref<BTSubtree>>(selected)->get_subtree();
if (subtree.is_valid()) {
EDIT_RESOURCE(subtree);
} else {
LimboUtility::get_singleton()->open_doc_class(selected->get_class());
}
EDIT_RESOURCE(subtree);
} else { } else {
LimboUtility::get_singleton()->open_doc_class(selected->get_class()); LimboUtility::get_singleton()->open_doc_class(selected->get_class());
} }

View File

@ -27,6 +27,7 @@
#include "core/object/object.h" #include "core/object/object.h"
#include "core/templates/hash_set.h" #include "core/templates/hash_set.h"
#include "editor/editor_node.h" #include "editor/editor_node.h"
#include "editor/editor_main_screen.h"
#include "editor/editor_undo_redo_manager.h" #include "editor/editor_undo_redo_manager.h"
#include "editor/gui/editor_spin_slider.h" #include "editor/gui/editor_spin_slider.h"
#include "editor/plugins/editor_plugin.h" #include "editor/plugins/editor_plugin.h"

View File

@ -17,6 +17,7 @@
#include "core/io/resource.h" #include "core/io/resource.h"
#include "core/variant/variant.h" #include "core/variant/variant.h"
#include "editor/editor_node.h" #include "editor/editor_node.h"
#include "editor/editor_main_screen.h"
#include "editor/plugins/script_editor_plugin.h" #include "editor/plugins/script_editor_plugin.h"
#endif // TOOLS_ENABLED #endif // TOOLS_ENABLED
@ -213,7 +214,7 @@ Variant VARIANT_DEFAULT(Variant::Type p_type) {
void SHOW_BUILTIN_DOC(const String &p_topic) { void SHOW_BUILTIN_DOC(const String &p_topic) {
#ifdef LIMBOAI_MODULE #ifdef LIMBOAI_MODULE
ScriptEditor::get_singleton()->goto_help(p_topic); ScriptEditor::get_singleton()->goto_help(p_topic);
EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT);
#elif LIMBOAI_GDEXTENSION #elif LIMBOAI_GDEXTENSION
TypedArray<ScriptEditorBase> open_editors = EditorInterface::get_singleton()->get_script_editor()->get_open_script_editors(); TypedArray<ScriptEditorBase> open_editors = EditorInterface::get_singleton()->get_script_editor()->get_open_script_editors();
ERR_FAIL_COND_MSG(open_editors.size() == 0, "Can't open help page. Need at least one script open in the script editor."); ERR_FAIL_COND_MSG(open_editors.size() == 0, "Can't open help page. Need at least one script open in the script editor.");

View File

@ -28,7 +28,7 @@
#define EDITOR_FILE_SYSTEM() (EditorFileSystem::get_singleton()) #define EDITOR_FILE_SYSTEM() (EditorFileSystem::get_singleton())
#define EDITOR_SETTINGS() (EditorSettings::get_singleton()) #define EDITOR_SETTINGS() (EditorSettings::get_singleton())
#define BASE_CONTROL() (EditorNode::get_singleton()->get_gui_base()) #define BASE_CONTROL() (EditorNode::get_singleton()->get_gui_base())
#define MAIN_SCREEN_CONTROL() (EditorNode::get_singleton()->get_main_screen_control()) #define MAIN_SCREEN_CONTROL() (EditorNode::get_singleton()->get_editor_main_screen()->get_control())
#define SCENE_TREE() (SceneTree::get_singleton()) #define SCENE_TREE() (SceneTree::get_singleton())
#define IS_DEBUGGER_ACTIVE() (EngineDebugger::is_active()) #define IS_DEBUGGER_ACTIVE() (EngineDebugger::is_active())
#define FS_DOCK_SELECT_FILE(m_path) FileSystemDock::get_singleton()->select_file(m_path) #define FS_DOCK_SELECT_FILE(m_path) FileSystemDock::get_singleton()->select_file(m_path)
@ -37,7 +37,7 @@
#define IS_CLASS(m_obj, m_class) (m_obj->is_class_ptr(m_class::get_class_ptr_static())) #define IS_CLASS(m_obj, m_class) (m_obj->is_class_ptr(m_class::get_class_ptr_static()))
#define RAND_RANGE(m_from, m_to) (Math::random(m_from, m_to)) #define RAND_RANGE(m_from, m_to) (Math::random(m_from, m_to))
#define RANDF() (Math::randf()) #define RANDF() (Math::randf())
#define BUTTON_SET_ICON(m_btn, m_icon) m_btn->set_icon(m_icon) #define BUTTON_SET_ICON(m_btn, m_icon) m_btn->set_button_icon(m_icon)
#define RESOURCE_LOAD(m_path, m_hint) ResourceLoader::load(m_path, m_hint) #define RESOURCE_LOAD(m_path, m_hint) ResourceLoader::load(m_path, m_hint)
#define RESOURCE_LOAD_NO_CACHE(m_path, m_hint) ResourceLoader::load(m_path, m_hint, ResourceFormatLoader::CACHE_MODE_IGNORE) #define RESOURCE_LOAD_NO_CACHE(m_path, m_hint) ResourceLoader::load(m_path, m_hint, ResourceFormatLoader::CACHE_MODE_IGNORE)
#define RESOURCE_SAVE(m_res, m_path, m_flags) ResourceSaver::save(m_res, m_path, m_flags) #define RESOURCE_SAVE(m_res, m_path, m_flags) ResourceSaver::save(m_res, m_path, m_flags)
@ -47,7 +47,7 @@
#define GET_PROJECT_SETTINGS_DIR() EditorPaths::get_singleton()->get_project_settings_dir() #define GET_PROJECT_SETTINGS_DIR() EditorPaths::get_singleton()->get_project_settings_dir()
#define EDIT_RESOURCE(m_res) EditorNode::get_singleton()->edit_resource(m_res) #define EDIT_RESOURCE(m_res) EditorNode::get_singleton()->edit_resource(m_res)
#define INSPECTOR_GET_EDITED_OBJECT() (InspectorDock::get_inspector_singleton()->get_edited_object()) #define INSPECTOR_GET_EDITED_OBJECT() (InspectorDock::get_inspector_singleton()->get_edited_object())
#define SET_MAIN_SCREEN_EDITOR(m_name) (EditorNode::get_singleton()->select_editor_by_name(m_name)) #define SET_MAIN_SCREEN_EDITOR(m_name) (EditorNode::get_singleton()->get_editor_main_screen()->select_by_name(m_name))
#define FILE_EXISTS(m_path) FileAccess::exists(m_path) #define FILE_EXISTS(m_path) FileAccess::exists(m_path)
#define DIR_ACCESS_CREATE() DirAccess::create(DirAccess::ACCESS_RESOURCES) #define DIR_ACCESS_CREATE() DirAccess::create(DirAccess::ACCESS_RESOURCES)
#define PERFORMANCE_ADD_CUSTOM_MONITOR(m_id, m_callable) (Performance::get_singleton()->add_custom_monitor(m_id, m_callable, Variant())) #define PERFORMANCE_ADD_CUSTOM_MONITOR(m_id, m_callable) (Performance::get_singleton()->add_custom_monitor(m_id, m_callable, Variant()))