limboai/doc/source/getting-started/custom-tasks.rst

142 lines
3.8 KiB
ReStructuredText

.. _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_in_range:
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