diff --git a/doc/source/conf.py b/doc/source/conf.py index ff4c3c5..357da0d 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -14,7 +14,7 @@ release = '1.0' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration -extensions = ['sphinx_markdown_builder', 'sphinx_rtd_dark_mode'] +extensions = ['myst_parser', 'sphinx_markdown_builder', 'sphinx_rtd_dark_mode'] templates_path = ['_templates'] exclude_patterns = ['_build'] diff --git a/doc/source/getting-started/custom-tasks.rst b/doc/source/getting-started/custom-tasks.rst new file mode 100644 index 0000000..e2aa084 --- /dev/null +++ b/doc/source/getting-started/custom-tasks.rst @@ -0,0 +1,139 @@ +.. _custom_tasks: + +Creating custom tasks in GDScript +================================= + +By default, user tasks should be placed in the ``res://ai/tasks`` +directory. You can set an alternative location for user tasks in the +``Project Settings → Limbo AI`` (To see those options, +``Advanced Settings`` should be enabled in the Project Settings). + +Each subdirectory within the user tasks directory is treated as a category. +Therefore, if you create a subdirectory named “motion_and_physics,” your +custom tasks in that directory will automatically be categorized under +“Motion And Physics.” + + **🛈 Note:** To help you write new tasks, you can add a script template to + your project using “Misc → Create script template” menu option. + +Task anatomy +------------ + +.. code:: gdscript + + @tool + extends BTAction + + # Task parameters. + @export var parameter1: float + @export var parameter2: Vector2 + + ## Note: Each method declaration is optional. + ## At minimum, you only need to define the "_tick" method. + + + # Called to generate a display name for the task (requires @tool). + func _generate_name() -> String: + return "MyTask" + + + # Called to initialize the task. + func _setup() -> void: + pass + + + # Called when the task is entered. + func _enter() -> void: + pass + + + # Called when the task is exited. + func _exit() -> void: + pass + + + # Called each time this task is ticked (aka executed). + func _tick(delta: float) -> Status: + return SUCCESS + + + # Strings returned from this method are displayed as warnings in the editor. + func _get_configuration_warnings() -> PackedStringArray: + var warnings := PackedStringArray() + return warnings + + +Example 1: A simple action +-------------------------- + +.. code:: gdscript + + @tool + extends BTAction + + ## Shows or hides a node and returns SUCCESS. + ## Returns FAILURE if the node is not found. + + # Task parameters. + @export var node_path: NodePath + @export var visible := true + + + # Called to generate a display name for the task (requires @tool). + func _generate_name() -> String: + return "SetVisible %s node_path: \"%s\"" % [visible, node_path] + + + # Called each time this task is ticked (aka executed). + func _tick(p_delta: float) -> Status: + var n: CanvasItem = agent.get_node_or_null(node_path) + if is_instance_valid(n): + n.visible = visible + return SUCCESS + return FAILURE + + +Example 2: InRange condition +---------------------------- + +.. code:: gdscript + + @tool + extends BTCondition + + ## InRange condition checks if the agent is within a range of target, + ## defined by distance_min and distance_max. + ## Returns SUCCESS if the agent is within the defined range; + ## otherwise, returns FAILURE. + + @export var distance_min: float + @export var distance_max: float + @export var target_var := "target" + + var _min_distance_squared: float + var _max_distance_squared: float + + + # Called to generate a display name for the task. + func _generate_name() -> String: + return "InRange (%d, %d) of %s" % [distance_min, distance_max, + LimboUtility.decorate_var(target_var)] + + + # Called to initialize the task. + func _setup() -> void: + _min_distance_squared = distance_min * distance_min + _max_distance_squared = distance_max * distance_max + + + # Called when the task is executed. + func _tick(_delta: float) -> Status: + var target: Node2D = blackboard.get_var(target_var, null) + if not is_instance_valid(target): + return FAILURE + + var dist_sq: float = agent.global_position.distance_squared_to(target.global_position) + if dist_sq >= _min_distance_squared and dist_sq <= _max_distance_squared: + return SUCCESS + else: + return FAILURE diff --git a/doc/source/getting-started/featured-classes.rst b/doc/source/getting-started/featured-classes.rst new file mode 100644 index 0000000..f2a51eb --- /dev/null +++ b/doc/source/getting-started/featured-classes.rst @@ -0,0 +1,17 @@ +Important classes +================= + +There are a lot of classes introduced in LimboAI. Here is a list of the most important ones you should know about: + +.. toctree:: + :maxdepth: 1 + + BTTask: Foundation for all behavior tree tasks <../classes/class_bttask> + BTPlayer: A node that executes BehaviorTree resources <../classes/class_btplayer> + Blackboard: Sharing data between tasks <../classes/class_blackboard> + BBParam: Parametrization helper for your tasks <../classes/class_bbparam> + LimboState: A state node for hierarchical state machine <../classes/class_limbostate> + LimboHSM: Event-based hierarchical state machine <../classes/class_limbohsm> + BTState: A state node for LimboHSM that hosts a BehaviorTree <../classes/class_btstate> + +Full class reference is available in the side bar. diff --git a/doc/source/getting-started/gdextension.rst b/doc/source/getting-started/gdextension.rst new file mode 100644 index 0000000..b0d805f --- /dev/null +++ b/doc/source/getting-started/gdextension.rst @@ -0,0 +1,59 @@ +.. _gdextension: + +Using GDExtension +================= + + **🛈 See also:** `What is GDExtension? `_ + +LimboAI can be used as either a C++ module or as a GDExtension shared library. +The module version is the most feature-full and slightly more performant, but +it requires using custom engine builds including the export templates. + + **🛈 Note:** Precompiled builds are available on the official + `LimboAI GitHub `_ page. + +GDExtension version is more convenient to use, as you don't need a custom engine +build. You can simply download the extension and put it inside your project. +However, it has certain limitations, described in detail in the next section. + +Whichever you choose to use, remember, your project will stay compatible with +both and you can transition from one to the other any time. + + +Limitations of the GDExtension version +-------------------------------------- + +GDExtension is the most convenient way of using the LimboAI plugin, but it comes +with certain limitations. + +The biggest one is that marking methods as virtual for scripting is not +currently possible in godot-cpp. We use these methods to allow creating custom +behavior tree tasks in GDScript. +Due to a workaround we employ, the editor will complain about native +methods being overridden. And by default, such warnings are treated as errors. +You have two options... + +In your Project Settings, you can edit ``Debug -> GDScript -> Warnings -> Native Methods Override`` +and set it to either ``Warn`` or ``Ignore``. +Those settings are hidden, unless ``Advanced Settings`` toggle is switched on! + +Alternatively, in your custom tasks, you can add specific instructions for +the parser to ignore the warning. For example: + +.. code:: gdscript + + # The following line instructs parser to ignore the warning: + @warning_ignore("native_method_override") + func _tick(p_delta: float) -> Status: + return SUCCESS + +You would have to do that for each overridden method in your custom tasks. +It's up to you which option you prefer. Personally, I'd set it to ``Warn`` and +add ignores only if it gets overwhelming. Hopefully, this will be fixed in the +future releases of Godot! + +**Other GDExtension limitations** + +* In-editor documentation is not available. The plugin will open online documentation instead when requested. +* Documentation tooltips are not available. +* Handy :ref:`class_BBParam` property editor is not available in the extension due to dependencies on the engine classes that are not available in the Godot API. diff --git a/doc/source/getting-started/introduction.rst b/doc/source/getting-started/introduction.rst new file mode 100644 index 0000000..b4bb155 --- /dev/null +++ b/doc/source/getting-started/introduction.rst @@ -0,0 +1,45 @@ +.. _introduction: + +Introduction to Behavior Trees +============================== + + +**Behavior Trees (BT)** are hierarchical structures used to model and +control the behavior of agents in a game (e.g., characters, enemies, +entities). They are designed to make it easier to create complex and +highly modular behaviors for your games. + +Behavior Trees are composed of tasks that represent specific actions or +decision-making rules. Tasks can be broadly categorized into two main +types: control tasks and leaf tasks. Control tasks determine the +execution flow within the tree. They include :ref:`Sequence`, +:ref:`Selector`, and +:ref:`Invert`. Leaf tasks represent specific actions +to perform, like moving or attacking, or conditions that need to be +checked. The :ref:`BTTask` class provides the foundation for various +building blocks of the Behavior Trees. BT tasks can share data with the +help of the :ref:`Blackboard`. + + **🛈 Note:** To create your own actions, extend the :ref:`BTAction` + class. + +The Behavior Tree is executed from the root task and follows the rules +specified by the control tasks, all the way down to the leaf tasks, +which represent the actual actions that the agent should perform or +conditions that should be checked. Each task returns a status when it is +executed. It can be ``SUCCESS``, ``RUNNING``, or ``FAILURE``. These +statuses determine how the tree progresses. They are defined in +:ref:`BT.Status `. + +Behavior Trees handle conditional logic using condition tasks. These +tasks check for specific conditions and return either ``SUCCESS`` or +``FAILURE`` based on the state of the agent or its environment (e.g., +“IsLowOnHealth”, “IsTargetInSight”). Conditions can be used together +with :ref:`Sequence` and :ref:`Selector` +to craft your decision-making logic. + + **🛈 Note:** To create your own conditions, extend the :ref:`BTCondition` + class. + +Check out the :ref:`BTTask` class documentation, which +provides the foundation for various building blocks of Behavior Trees. diff --git a/doc/source/index.rst b/doc/source/index.rst index f2bd1a0..43d6c79 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -1,24 +1,53 @@ -LimboAI's Class Reference -========================== +LimboAI Documentation +===================== -Highlights ----------- -.. toctree:: - :maxdepth: 1 +About +----- - BehaviorTree - BTTask: Foundation for all behavior tree tasks - BTPlayer: A node that executes BehaviorTree resources - LimboState: A state node for hierarchical state machine - LimboHSM: Event-based hierarchical state machine - BTState: A state node for LimboHSM that hosts a BehaviorTree +**LimboAI** is an open-source C++ module for Godot Engine 4 providing a combination of +**Behavior Trees** and **State Machines** for crafting your game’s AI. It comes with a +behavior tree editor, built-in documentation, visual debugger, and more! While +it is implemented in C++, it fully supports GDScript for :ref:`creating your own tasks ` +and states. The full list of features is available on the +`LimboAI GitHub `_ page. -.. * :ref:`search` +.. SCREENSHOT -Class Reference +**Behavior Trees** are hierarchical structures used to model and control the behavior +of agents in a game (e.g., characters, enemies, entities). They are designed to +make it easier to create complex and highly modular behaviors for your games. +To learn more about behavior trees, check out :ref:`introduction`. + + +Getting LimboAI --------------- + +Precompiled builds are available on the official +`LimboAI GitHub `_ page, +and in the Asset Library (coming soon!). + +LimboAI can be used as either a C++ module or as a GDExtension shared library. +There are some differences between the two. In short, 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. For more information on this topic, see :ref:`gdextension`. + + **🛈 Note:** Class reference is available in the side bar. + .. toctree:: - :maxdepth: 2 + :hidden: + :maxdepth: 1 + :caption: Getting started - classes/index + getting-started/introduction + getting-started/custom-tasks + getting-started/gdextension + getting-started/featured-classes +.. toctree:: + :hidden: + :maxdepth: 1 + :caption: Class reference + :glob: + + classes/class_*