Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions addons/behaviour_toolkit/finite_state_machine/fsm_transition.gd
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,41 @@ class_name FSMTransition extends BehaviourToolkit
## [code]use_event[/code] to true and set the event property to the name
## of the event you want to listen for.


## The state to transition to.
@export var next_state: FSMState:
set(value):
next_state = value
update_configuration_warnings()

@export_category("Transition Logic")

## Always transition, ignoring `is_valid()` or any events.
@export var always_transition: bool = false:
set(value):
always_transition = value
notify_property_list_changed()
update_configuration_warnings()


# Rather than using @export on these properties, we include them here so we can show them conditionally.
func _get_property_list() -> Array[Dictionary]:
var property_list: Array[Dictionary] = []

if not always_transition:
property_list.append({"name": "use_event", "type": TYPE_BOOL})
property_list.append({"name": "event", "type": TYPE_STRING})

return property_list


## If true, the FSM will check for the event to trigger the transition.
@export var use_event: bool = false:
var use_event: bool = false:
set(value):
use_event = value
update_configuration_warnings()

## The event that triggers the transition.
@export var event: String = "":
var event: String = "":
set(value):
event = value
update_configuration_warnings()
Expand All @@ -38,6 +58,9 @@ func _on_transition(_delta: float, _actor: Node, _blackboard: Blackboard) -> voi

## Evaluates true, if the transition conditions are met.
func is_valid(_actor: Node, _blackboard: Blackboard) -> bool:
if always_transition:
return true

return false


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
@tool
@icon("res://addons/behaviour_toolkit/icons/FSMSplitTransition.svg")
class_name FSMSplitTransition extends FSMTransition

## The state to transition to.
@export var next_state_true: FSMState:
set(value):
next_state_true = value
update_configuration_warnings()

@export var next_state_false: FSMState:
set(value):
next_state_false = value
update_configuration_warnings()

# Internal flag that determines which state gets transitioned to
var _transition_flag: bool


# Executed when the transition is taken.
func _on_transition(_delta: float, _actor: Node, _blackboard: Blackboard) -> void:
pass


# Always returns true, because this transition always triggers one way or another.
# Because `get_next_state()` doesn't have access to the actor or blackboard, a flag is
# set internally here which sets up the next transition.
func is_valid(actor: Node, blackboard: Blackboard) -> bool:
set_transition_flag(actor, blackboard)
return true


func set_transition_flag(_actor: Node, _blackboard: Blackboard) -> void:
pass


## Returns which state to transition to, based on internal transition flag set in `set_transition_flag()`.
func get_next_state() -> FSMState:
return next_state_true if _transition_flag else next_state_false


# Add custom configuration warnings
# Note: Can be deleted if you don't want to define your own warnings.
func _get_configuration_warnings() -> PackedStringArray:
var warnings: Array = []

var parent: Node = get_parent()
if not parent is FSMState:
warnings.append("FSMSplitTransition should be a child of FSMState.")

if next_state:
warnings.append("FSMSplitTransition has next state; unset and set true and false states.")

if not next_state_true:
warnings.append("FSMSplitTransition has no next state for true.")

if not next_state_false:
warnings.append("FSMSplitTransition has no next state for false.")

if use_event and event == "":
warnings.append("FSMSplitTransition has no event set.")

return warnings
72 changes: 72 additions & 0 deletions addons/behaviour_toolkit/icons/FSMSplitTransition.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions addons/behaviour_toolkit/icons/FSMSplitTransition.svg.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://c4vs4uicrouno"
path="res://.godot/imported/FSMSplitTransition.svg-e6773317add235944b13c8c864f570b3.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://addons/behaviour_toolkit/icons/FSMSplitTransition.svg"
dest_files=["res://.godot/imported/FSMSplitTransition.svg-e6773317add235944b13c8c864f570b3.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false
1 change: 1 addition & 0 deletions addons/behaviour_toolkit/ui/toolkit_ui.gd
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func _ready():
%ButtonAddFSM.connect("pressed", _on_button_pressed.bind(FiniteStateMachine, "FiniteStateMachine"))
%ButtonState.connect("pressed", _on_button_pressed.bind(FSMState, "FSMState"))
%ButtonTransition.connect("pressed", _on_button_pressed.bind(FSMTransition, "FSMTransition"))
%ButtonSplitTransition.connect("pressed", _on_button_pressed.bind(FSMSplitTransition, "FSMSplitTransition"))
%ButtonStateIntegratedBT.connect("pressed", _on_button_pressed.bind(FSMStateIntegratedBT, "FSMStateIntegratedBT"))
%ButtonStateIntegrationReturn.connect("pressed", _on_button_pressed.bind(FSMStateIntegrationReturn, "FSMStateIntegrationReturn"))

Expand Down
10 changes: 9 additions & 1 deletion addons/behaviour_toolkit/ui/toolkit_ui.tscn
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[gd_scene load_steps=39 format=3 uid="uid://o16emp4t4wb1"]
[gd_scene load_steps=40 format=3 uid="uid://o16emp4t4wb1"]

[ext_resource type="Script" path="res://addons/behaviour_toolkit/ui/toolkit_ui.gd" id="1_51rvx"]
[ext_resource type="Texture2D" uid="uid://boof0yioplbqr" path="res://addons/behaviour_toolkit/icons/FSMState.svg" id="1_hqqj5"]
Expand Down Expand Up @@ -26,6 +26,7 @@
[ext_resource type="Texture2D" uid="uid://b45qiy5niriwu" path="res://addons/behaviour_toolkit/icons/BTLeafPrint.svg" id="10_ji4ht"]
[ext_resource type="Texture2D" uid="uid://xi8li3jk2vs6" path="res://addons/behaviour_toolkit/icons/BTLeafWait.svg" id="10_n2nk3"]
[ext_resource type="Texture2D" uid="uid://hn65lqk8tero" path="res://addons/behaviour_toolkit/icons/BTDecoratorRepeat.svg" id="10_nvbp1"]
[ext_resource type="Texture2D" uid="uid://c4vs4uicrouno" path="res://addons/behaviour_toolkit/icons/FSMSplitTransition.svg" id="10_ypexi"]
[ext_resource type="Texture2D" uid="uid://cwvb6d7kvo53u" path="res://addons/behaviour_toolkit/icons/BTLeafCall.svg" id="12_axgn0"]
[ext_resource type="Texture2D" uid="uid://by4ymq46fp1ra" path="res://addons/behaviour_toolkit/icons/BTLeafCondition.svg" id="13_6h0vl"]
[ext_resource type="Texture2D" uid="uid://ddx7a726xhria" path="res://addons/behaviour_toolkit/icons/BTCompositeIntegration.svg" id="13_gmc6x"]
Expand Down Expand Up @@ -187,6 +188,13 @@ layout_mode = 2
text = "New Transition"
icon = ExtResource("2_24di4")

[node name="ButtonSplitTransition" type="Button" parent="ScrollContainer/MarginContainer/VBoxContainer/Panel/ScrollContainer/Toolbox/FiniteStateMachine"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
text = "Split Transition"
icon = ExtResource("10_ypexi")

[node name="BehaviourTree" type="VBoxContainer" parent="ScrollContainer/MarginContainer/VBoxContainer/Panel/ScrollContainer/Toolbox"]
unique_name_in_owner = true
layout_mode = 2
Expand Down
18 changes: 18 additions & 0 deletions docs/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ The node icons where designed/choosen to give you a quick overview of their purp
- [Signals](#signals)
- [ FSMState](#-fsmstate)
- [Methods](#methods-1)
- [Extras][#extras]
- [ FSMTransition](#-fsmtransition)
- [Properties](#properties-1)
- [Methods](#methods-2)
- [Extras][#extras-1]
- [Behaviour Tree](#behaviour-tree)
- [Usage](#usage-1)
- [Tree Nodes](#tree-nodes)
Expand Down Expand Up @@ -109,6 +111,12 @@ This is the base class for all states. On ready, all `FSMTransition` child nodes
- void `_on_exit(actor: Node, blackboard: Blackboard)`
- Called when the state is exited.

#### Extras
There are extra states that you can use out of the box:

- ![PrintOnEnter Icon](../addons/behaviour_toolkit/icons/BTLeafPrint.svg) Print on Enter
- Prints a message to the console when entering this state. Very handy for debugging.


### ![FSM Transition Icon](../addons/behaviour_toolkit/icons/FSMTransition.svg) FSMTransition
This is the base class for all transitions. To implement your logic you can override the `_on_transition` method when extending the node's script. To setup custom conditions you can override the `is_valid` method. If you want to use events to trigger the transition, set `use_event` to `true` and set the `event` property to the name of the event you want to listen for.
Expand All @@ -128,6 +136,16 @@ This is the base class for all transitions. To implement your logic you can over
- bool `is_valid`
- Should return `true` if the conditions for the transition are met.

#### Extras
There are a few extra transitions that you can use out of the box:

- ![SplitTransition Icon](../addons/behaviour_toolkit/icons/FSMSplitTransition.svg) Split
- Always transition to one of two states, based on a coded boolean check.
- ![AlwaysTransition Icon](../addons/behaviour_toolkit/icons/AlwaysTransition.svg) Always
- Always transition, useful for states that only act once.

Script templates are also available to make it easier to extend the above classes.


# Behaviour Tree
A behavior tree is a mathematical model of plan execution used in computer science, robotics, control systems and video games. They describe switchings between a finite set of tasks in a modular fashion. Their strength comes from their ability to create very complex tasks composed of simple tasks, without worrying how the simple tasks are implemented. ([Wikipedia](https://en.wikipedia.org/wiki/Behavior_tree_(artificial_intelligence,_robotics_and_control)))
Expand Down
53 changes: 53 additions & 0 deletions examples/fsm_split_transition/fsm_split_transition_example.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
[gd_scene load_steps=7 format=3 uid="uid://dd1t17mhnvpn4"]

[ext_resource type="Script" path="res://addons/behaviour_toolkit/finite_state_machine/fsm.gd" id="1_ugani"]
[ext_resource type="Script" path="res://examples/fsm_split_transition/fsm_state_print_counter.gd" id="2_dxxwi"]
[ext_resource type="Script" path="res://examples/fsm_split_transition/fsm_state_print_on_enter.gd" id="2_s4dqp"]
[ext_resource type="Script" path="res://examples/fsm_split_transition/fsm_transition_to_finish.gd" id="3_mm16c"]
[ext_resource type="Script" path="res://examples/fsm_split_transition/fsm_transition_even_or_odd.gd" id="4_kimeb"]
[ext_resource type="Script" path="res://addons/behaviour_toolkit/finite_state_machine/fsm_transition.gd" id="6_am5vp"]

[node name="SplitTransitionExample" type="Node2D"]

[node name="FiniteStateMachine" type="Node" parent="." node_paths=PackedStringArray("initial_state")]
script = ExtResource("1_ugani")
autostart = true
initial_state = NodePath("PrintCounter")

[node name="PrintCounter" type="Node" parent="FiniteStateMachine"]
script = ExtResource("2_dxxwi")

[node name="TransitionToFinish" type="Node" parent="FiniteStateMachine/PrintCounter" node_paths=PackedStringArray("next_state")]
script = ExtResource("3_mm16c")
next_state = NodePath("../../PrintDone")
use_event = false
event = ""

[node name="GoEvenOrOdd" type="Node" parent="FiniteStateMachine/PrintCounter" node_paths=PackedStringArray("next_state_true", "next_state_false")]
script = ExtResource("4_kimeb")
next_state_true = NodePath("../../PrintEven")
next_state_false = NodePath("../../PrintOdd")
use_event = false
event = ""

[node name="PrintEven" type="Node" parent="FiniteStateMachine"]
script = ExtResource("2_s4dqp")
custom_text = "Even"

[node name="ReturnToStart" type="Node" parent="FiniteStateMachine/PrintEven" node_paths=PackedStringArray("next_state")]
script = ExtResource("6_am5vp")
next_state = NodePath("../../PrintCounter")
always_transition = true

[node name="PrintOdd" type="Node" parent="FiniteStateMachine"]
script = ExtResource("2_s4dqp")
custom_text = "Odd"

[node name="ReturnToStart" type="Node" parent="FiniteStateMachine/PrintOdd" node_paths=PackedStringArray("next_state")]
script = ExtResource("6_am5vp")
next_state = NodePath("../../PrintCounter")
always_transition = true

[node name="PrintDone" type="Node" parent="FiniteStateMachine"]
script = ExtResource("2_s4dqp")
custom_text = "Done"
19 changes: 19 additions & 0 deletions examples/fsm_split_transition/fsm_state_print_counter.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@tool
extends FSMState


# Executes after the state is entered.
func _on_enter(_actor: Node, _blackboard: Blackboard) -> void:
if _blackboard.get_value("counter") == null:
_blackboard.set_value("counter", 0)

var counter: int = _blackboard.get_value("counter")
if counter == null:
print(0)
else:
print(counter)

## Executes before the state is exited.
func _on_exit(_actor: Node, _blackboard: Blackboard) -> void:
var counter: int = _blackboard.get_value("counter")
_blackboard.set_value("counter", counter + 1)
13 changes: 13 additions & 0 deletions examples/fsm_split_transition/fsm_state_print_on_enter.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@tool
@icon("res://addons/behaviour_toolkit/icons/BTLeafPrint.svg")
extends FSMState

@export var custom_text: String


# Executes after the state is entered.
func _on_enter(_actor: Node, _blackboard: Blackboard) -> void:
if custom_text != "":
print(custom_text)
else:
print("Hello World!")
Loading