Skip to content

Commit

Permalink
Merge pull request #208 from hpi-swa-lab/state-machines-improved
Browse files Browse the repository at this point in the history
Further improvements in State Machine
  • Loading branch information
leogeier authored Feb 10, 2024
2 parents 5984cf9 + b7d43e1 commit d2afe88
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 20 deletions.
18 changes: 10 additions & 8 deletions addons/pronto/behaviors/StateMachineBehavior.gd
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,26 @@
extends Behavior
class_name StateMachineBehavior

## The StateMachineBehavior is a [class Behavior] that acts as a purely graphic
## hint as to which [class StateBehavior] objects belong to the same state machine.
## The [class GroupDrawer] is used for this.
## The StateMachineBehavior is a [class Behavior] that is a part of a state
## state machine. Fill the state machine with states by creating
## [class StateBehavior] nodes as children.
## The StateMachineBehavior manages the active state and can receive triggers
## from outside, which are then available on the active state.

## Signal that gets emitted when the trigger method is called on this state machine.
signal triggered(trigger: String)

var active_state: StateBehavior = null

## Name of the trigger that is called in every frame.
const always_trigger = "always"

## List of the triggers that this state machine processes. Triggers are used in
## trigger method and the "on_trigger_received" method of [class StateBehavior].
## Both these ways use the [class StateTransitionConfigurator], which allows the creation
## of triggers.
## Both these ways use the [class StateTransitionConfigurator], which allows the
## creation of triggers.
@export var triggers: Array[String] = [always_trigger]

## Name of the trigger that is called in every frame.
const always_trigger = "always"

## When true, the state machine will trigger the "always" trigger on every frame,
## allowing state transitions without other triggers.
@export var trigger_always: bool = true
Expand Down
53 changes: 44 additions & 9 deletions addons/pronto/helpers/StateMachineInfo.gd
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,22 @@ var processing_trigger = false

@export var onlyInEditor: bool = true


# Called when the node enters the scene tree for the first time.
func _ready():
redraw_periodically()


func redraw_periodically():
while is_inside_tree():
await get_tree().create_timer(1).timeout
if not processing_trigger:
queue_redraw()


var active_trigger = ""


func _redraw_with_trigger(trigger):
if trigger != StateMachineBehavior.always_trigger:
active_trigger = trigger
Expand All @@ -29,33 +34,63 @@ func _redraw_with_trigger(trigger):
queue_redraw()
processing_trigger = false


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
pass


var label_position = Vector2.ZERO


func get_label_position():
var states = get_parent().get_children()
if states.size() == 0: return Vector2(150,0)
if states.size() == 0:
return Vector2(150, 0)
var max_x = 0
for node in states:
max_x = max(node.position.x/2, max_x)
max_x = max(node.position.x / 2, max_x)
return Vector2(max_x + 150, 0)


func _draw_trigger_as_active(trigger):
if trigger == active_trigger:
return true
if trigger == get_parent().always_trigger && get_parent().trigger_always:
return true
return false


func _draw():
if not get_parent() is StateMachineBehavior: return
if not get_parent() is StateMachineBehavior:
return
var state_machine: StateMachineBehavior = get_parent()
var font_size = 10


var label = "Triggers:"
var label_size = ThemeDB.fallback_font.get_string_size(label, HORIZONTAL_ALIGNMENT_CENTER, -1, font_size)
var label_size = ThemeDB.fallback_font.get_string_size(
label, HORIZONTAL_ALIGNMENT_CENTER, -1, font_size
)
label_position = get_label_position()
draw_string(ThemeDB.fallback_font, label_position, label, HORIZONTAL_ALIGNMENT_CENTER, -1, font_size, color)
draw_string(
ThemeDB.fallback_font,
label_position,
label,
HORIZONTAL_ALIGNMENT_CENTER,
-1,
font_size,
color
)
for trigger in state_machine.triggers:
label = trigger
label_position.y += label_size.y
var label_color = triggered_color if trigger == active_trigger else color
draw_string(ThemeDB.fallback_font, label_position, label, HORIZONTAL_ALIGNMENT_CENTER, -1, font_size, label_color)

var label_color = triggered_color if _draw_trigger_as_active(trigger) else color
draw_string(
ThemeDB.fallback_font,
label_position,
label,
HORIZONTAL_ALIGNMENT_CENTER,
-1,
font_size,
label_color
)
16 changes: 14 additions & 2 deletions addons/pronto/signal_connecting/state_transition_configurator.gd
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ func redraw_receiver_label():
else:
%ReceiverLabel.text = "Connecting to state ${0} ({1})".format([from.get_path_to(receiver), receiver.name])

func _get_trigger_for_connection(connection: Connection):
return connection.trigger if connection.is_state_transition() else extract_trigger_from_trigger_argument_script(connection.arguments[0])

func set_existing_connection(from: Node, connection: Connection):
self.from = from
existing_connection = connection
Expand All @@ -129,7 +132,7 @@ func set_existing_connection(from: Node, connection: Connection):
#%SharedLinksNote.visible = total["total"] > 1
#%SharedLinksCount.text = "This connection is linked to {0} other node{1}.".format([total["total"] - 1, "s" if total["total"] != 2 else ""])
reload_triggers()
var selected_trigger = connection.trigger if connection.is_state_transition() else extract_trigger_from_trigger_argument_script(connection.arguments[0])
var selected_trigger = _get_trigger_for_connection(connection)
for i in range(%TriggerSelection.item_count):
if %TriggerSelection.get_item_text(i) == selected_trigger:
%TriggerSelection.select(i)
Expand Down Expand Up @@ -188,9 +191,12 @@ func argument_names():

func argument_types():
return argument_names_and_types().map(func (a): return a[1])

func _get_selected_trigger():
return %TriggerSelection.get_item_text(%TriggerSelection.get_selected_id())

func save():
var trigger = %TriggerSelection.get_item_text(%TriggerSelection.get_selected_id())
var trigger = _get_selected_trigger()
if existing_connection:
Utils.commit_undoable(undo_redo, "Update condition of connection", existing_connection.only_if,
{"source_code": %Condition.text}, "reload")
Expand Down Expand Up @@ -345,6 +351,12 @@ func _on_add_trigger_pressed():
%TriggerSelection.add_item(new_trigger)
%TriggerSelection.select(len(get_state_machine().triggers)-1)
%TriggerEdit.text = ""

func _on_trigger_selection_item_selected(index):
mark_changed(true)

func _on_condition_text_changed():
mark_changed(true)

func _on_done_pressed():
save()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ sync_handles_x = true

[node name="Condition" parent="MarginContainer/VBoxContainer/HBoxContainer2/ResizableContainer2" instance=ExtResource("3_immw5")]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 43.9353)
custom_minimum_size = Vector2(0, 43.3363)
layout_mode = 2
default_text = "true"
max_width = 500
Expand Down Expand Up @@ -205,7 +205,9 @@ layout_mode = 2
[connection signal="gui_input" from="." to="." method="_on_gui_input"]
[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/AddReferenceButton" to="." method="_on_add_reference_button_pressed"]
[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/remove" to="." method="_on_remove_pressed"]
[connection signal="item_selected" from="MarginContainer/VBoxContainer/TriggerContainer/HBoxContainer/TriggerSelection" to="." method="_on_trigger_selection_item_selected"]
[connection signal="pressed" from="MarginContainer/VBoxContainer/TriggerContainer/TriggerCreationContainer/create_trigger" to="." method="_on_add_trigger_pressed"]
[connection signal="text_changed" from="MarginContainer/VBoxContainer/HBoxContainer2/ResizableContainer2/Condition" to="." method="_on_condition_text_changed"]
[connection signal="pressed" from="MarginContainer/VBoxContainer/ButtonList/done" to="." method="_on_done_pressed"]
[connection signal="pressed" from="MarginContainer/VBoxContainer/ButtonList/cancel" to="." method="_on_cancel_pressed"]
[connection signal="pressed" from="MarginContainer/VBoxContainer/ButtonList/open_in_connection_editor" to="." method="_on_open_in_connection_editor_pressed"]

0 comments on commit d2afe88

Please sign in to comment.