From 7879bb4656fbecbee119f9fe146f41080c771cab Mon Sep 17 00:00:00 2001 From: Yan Wittmann Date: Fri, 13 Dec 2024 19:53:58 +0100 Subject: [PATCH] Added target state selector logic, added battery logic --- state/assets/battery.png | Bin 0 -> 32077 bytes state/assets/battery.png.import | 34 +++++++ state/assets/character_state_machine.json | 90 ++++++++++++++++++ state/project.godot | 10 ++ state/scenes/state/Battery.tscn | 16 ++++ state/scenes/state/SMCharacter.gd | 10 ++ state/scenes/state/StateMachine.gd | 51 ++++++++-- state/scenes/state/StateMachineWorld.tscn | 15 ++- state/scenes/state/state_GoToBattery.gd | 13 +++ state/scenes/state/state_RechargeBattery.gd | 16 ++++ .../scenes/state/visualization/d_tree_node.gd | 7 +- .../visualization/state_machine_info_panel.gd | 15 ++- .../state/visualization/tree_visualizer.gd | 47 +++------ 13 files changed, 276 insertions(+), 48 deletions(-) create mode 100644 state/assets/battery.png create mode 100644 state/assets/battery.png.import create mode 100644 state/scenes/state/Battery.tscn create mode 100644 state/scenes/state/state_GoToBattery.gd create mode 100644 state/scenes/state/state_RechargeBattery.gd diff --git a/state/assets/battery.png b/state/assets/battery.png new file mode 100644 index 0000000000000000000000000000000000000000..ad8e0efefb99f89c46b87303908ee365cdeabae1 GIT binary patch literal 32077 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Ⓢ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 OTvk#^qFT&2=>Gt}`}O<) literal 0 HcmV?d00001 diff --git a/state/assets/battery.png.import b/state/assets/battery.png.import new file mode 100644 index 0000000..73a8ad2 --- /dev/null +++ b/state/assets/battery.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://buoiey8bh6i0e" +path="res://.godot/imported/battery.png-0291d92f3cb8a965d4b9647e0eab9582.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/battery.png" +dest_files=["res://.godot/imported/battery.png-0291d92f3cb8a965d4b9647e0eab9582.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 diff --git a/state/assets/character_state_machine.json b/state/assets/character_state_machine.json index 3b6e3d4..2d80e47 100644 --- a/state/assets/character_state_machine.json +++ b/state/assets/character_state_machine.json @@ -2,9 +2,31 @@ "start": { "state": "Idle" }, + "template_transitions": { + "battery_low": { + "target": "GoToBattery", + "conditions": [ + { + "type": ">=", + "left": { + "value": 20 + }, + "right": { + "accessor": [ + "character", + "battery_charge" + ] + } + } + ] + } + }, "states": { "Idle": { "transitions": [ + { + "template": "battery_low" + }, { "target": "PickupTrash", "signal": "waste_detected", @@ -51,6 +73,9 @@ }, "PickupTrash": { "transitions": [ + { + "template": "battery_low" + }, { "target": "ThrowTrashAway", "signal": "waste_detected", @@ -97,6 +122,9 @@ }, "ThrowTrashAway": { "transitions": [ + { + "template": "battery_low" + }, { "target": "Idle", "conditions": [ @@ -129,6 +157,68 @@ ] } ] + }, + "GoToBattery": { + "transitions": [ + { + "target": "RechargeBattery", + "conditions": [ + { + "type": ">=", + "left": { + "value": 90 + }, + "right": { + "function": "distance", + "args": [ + { + "accessor": [ + "character", + "position" + ] + }, + { + "accessor": [ + "root_nodes", + "StateMachineWorld", + "child_nodes", + "Battery", + "position" + ] + } + ] + } + } + ] + } + ] + }, + "RechargeBattery": { + "transitions": [ + { + "target": { + "type": "history_first", + "ignore": [ + "RechargeBattery", + "GoToBattery" + ] + }, + "conditions": [ + { + "type": "<=", + "left": { + "value": 100 + }, + "right": { + "accessor": [ + "character", + "battery_charge" + ] + } + } + ] + } + ] } } } diff --git a/state/project.godot b/state/project.godot index 08c6976..82c76c3 100644 --- a/state/project.godot +++ b/state/project.godot @@ -26,3 +26,13 @@ spawn_trash={ "events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":2,"canceled":false,"pressed":false,"double_click":false,"script":null) ] } +debug_vis_1={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) +] +} +debug_vis_2={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":66,"key_label":0,"unicode":98,"location":0,"echo":false,"script":null) +] +} diff --git a/state/scenes/state/Battery.tscn b/state/scenes/state/Battery.tscn new file mode 100644 index 0000000..137d5df --- /dev/null +++ b/state/scenes/state/Battery.tscn @@ -0,0 +1,16 @@ +[gd_scene load_steps=3 format=3 uid="uid://blp3dyd38b8i2"] + +[ext_resource type="Texture2D" uid="uid://buoiey8bh6i0e" path="res://assets/battery.png" id="1_jitck"] + +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_bhmyx"] +radius = 0.0 +height = 0.0 + +[node name="TrashBin" type="StaticBody2D"] + +[node name="Sprite2D" type="Sprite2D" parent="."] +scale = Vector2(0.0900715, 0.0900715) +texture = ExtResource("1_jitck") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +shape = SubResource("CapsuleShape2D_bhmyx") diff --git a/state/scenes/state/SMCharacter.gd b/state/scenes/state/SMCharacter.gd index 719fa35..b233ecb 100644 --- a/state/scenes/state/SMCharacter.gd +++ b/state/scenes/state/SMCharacter.gd @@ -12,6 +12,10 @@ const ROTATION_SPEED: float = 5.0 # adians per second # @onready var detection_area: Area2D = $VisionCone signal waste_detected(waste) +# +var battery_charge: float = 100.0 +const battery_charge_drain_per_second: float = 4.0 +const battery_charge_recharge_per_second: float = 27.0 func _ready() -> void: @@ -38,11 +42,17 @@ func _draw() -> void: draw_line(Vector2.ZERO, local_velocity, Color(1, 0, 0), 2) +func _process(delta: float) -> void: + %StateMachineInfoPanel.values["battery_charge"] = battery_charge + + func _physics_process(delta: float) -> void: rotate_towards_velocity(delta) detect_waste() queue_redraw() + battery_charge -= battery_charge_drain_per_second * delta + func detect_waste() -> void: var overlapping_bodies: Array[Node2D] = detection_area.get_overlapping_bodies() diff --git a/state/scenes/state/StateMachine.gd b/state/scenes/state/StateMachine.gd index f0fcb78..1039c72 100644 --- a/state/scenes/state/StateMachine.gd +++ b/state/scenes/state/StateMachine.gd @@ -11,10 +11,13 @@ var received_signals: Array[Dictionary] = [] # state change parameters ("transfer") var state_transfer_variables: Dictionary = {} +var state_history: Array = [] + var current_state: State: set(value): if current_state: current_state.state_exit() + state_history.append(value) current_state = value current_state.state_enter() @@ -39,7 +42,7 @@ func _ready() -> void: else: push_error("Failed to load JSON character_state_machine.json") - current_state = find_state_by_name(state_machine_data["start"]["state"]) + current_state = find_state(state_machine_data["start"]["state"]) call_deferred("defer_ready") @@ -63,6 +66,7 @@ func _process(delta: float) -> void: %StateMachineInfoPanel.values["signal_" + sgl["signal"]] = sgl["args"] var transition_names: Array = [] for transition in state_machine_data["states"][current_state.get_name()]["transitions"]: + transition = resolve_transition_template(transition) transition_names.append(transition["target"]) %StateMachineInfoPanel.values["Transitions"] = transition_names @@ -72,6 +76,7 @@ func _process(delta: float) -> void: func check_transitions() -> void: if current_state: for transition in state_machine_data["states"][current_state.get_name()]["transitions"]: + transition = resolve_transition_template(transition) if "signal" in transition and transition["signal"] and not was_signal_received(transition["signal"]): continue @@ -82,7 +87,7 @@ func check_transitions() -> void: var condition_met: bool = transition_check_condition(condition) if not condition_met: all_conditions_met = false - %StateMachineInfoPanel.values["condition failed for " + transition["target"]] = human_readable_condition(condition) + %StateMachineInfoPanel.values["condition failed for " + str(transition["target"])] = human_readable_condition(condition) break if not all_conditions_met: continue @@ -100,7 +105,7 @@ func check_transitions() -> void: current_state.contribute_transfer_variables(state_transfer_variables) print("All criteria were met for switching to state ", transition["target"], " with transfer variables ", state_transfer_variables) - current_state = find_state_by_name(transition["target"]) + current_state = find_state(transition["target"]) func transition_check_condition(condition: Dictionary) -> bool: @@ -226,6 +231,12 @@ func find_detected_signal(signal_name: String) -> Dictionary: # UTILITY FUNCTIONS +func find_state(name) -> State: + if name is Dictionary: + return find_state_by_selector(name) + return find_state_by_name(name) + + func find_state_by_name(name: String) -> State: for child in self.get_children(): if child is State: @@ -234,6 +245,23 @@ func find_state_by_name(name: String) -> State: return null +func find_state_by_selector(name: Dictionary) -> State: + # { + # "type": "history_first", + # "ignore": [ + # "RechargeBattery", + # "GoToBattery" + # ] + # } + if name["type"] == "history_first": + var index: int = state_history.size() - 1 + while index >= 0: + if not state_history[index].get_name() in name["ignore"]: + return state_history[index] + index -= 1 + return null + + func was_signal_received(signal_name: String) -> bool: for received_signal in received_signals: if received_signal["signal"] == signal_name: @@ -259,20 +287,27 @@ func load_json(path: String) -> JSON: return json +func resolve_transition_template(data: Dictionary) -> Dictionary: + if data.has("template"): + return state_machine_data["template_transitions"][data["template"]] + return data + + func objects_equal(a: Variant, b: Variant) -> bool: if typeof(a) != typeof(b): return false return a == b -func human_readable_transition(condition: Dictionary) -> String: +func human_readable_transition(transition: Dictionary) -> String: + transition = resolve_transition_template(transition) var parts: Array[Variant] = [] - if condition.has("signal"): - parts.append(condition["signal"]) + if transition.has("signal"): + parts.append(transition["signal"]) - if condition.has("conditions"): - parts.append(human_readable_condition(condition)) + if transition.has("conditions"): + parts.append(human_readable_condition(transition)) return " & ".join(parts) diff --git a/state/scenes/state/StateMachineWorld.tscn b/state/scenes/state/StateMachineWorld.tscn index 84ae2e6..584a017 100644 --- a/state/scenes/state/StateMachineWorld.tscn +++ b/state/scenes/state/StateMachineWorld.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=14 format=3 uid="uid://cgdvptekjbpgq"] +[gd_scene load_steps=17 format=3 uid="uid://cgdvptekjbpgq"] [ext_resource type="Script" path="res://scenes/state/SMCharacter.gd" id="1_84313"] [ext_resource type="Script" path="res://scenes/state/SceneManagement.gd" id="1_s1suw"] @@ -6,11 +6,14 @@ [ext_resource type="Texture2D" uid="uid://cx04xknqfdscp" path="res://assets/cleaning_robot.png" id="2_cwwab"] [ext_resource type="Script" path="res://scenes/state/StateMachine.gd" id="2_d1xqo"] [ext_resource type="Script" path="res://scenes/state/state_idle.gd" id="3_r1btx"] +[ext_resource type="PackedScene" uid="uid://blp3dyd38b8i2" path="res://scenes/state/Battery.tscn" id="5_pml1y"] [ext_resource type="Texture2D" uid="uid://dkqw1wsjbvl0i" path="res://assets/floor.png" id="6_15pjb"] [ext_resource type="Script" path="res://scenes/state/state_PickupTrash.gd" id="7_1rfah"] [ext_resource type="Script" path="res://scenes/state/state_ThrowTrashAway.gd" id="8_677fi"] [ext_resource type="PackedScene" uid="uid://dng6a8i8kp4kw" path="res://scenes/state/TrashBin.tscn" id="9_sd6r3"] [ext_resource type="Script" path="res://scenes/state/visualization/state_machine_info_panel.gd" id="11_70vf4"] +[ext_resource type="Script" path="res://scenes/state/state_GoToBattery.gd" id="12_e37qa"] +[ext_resource type="Script" path="res://scenes/state/state_RechargeBattery.gd" id="13_qf7y1"] [sub_resource type="CapsuleShape2D" id="CapsuleShape2D_tr1gq"] radius = 60.0 @@ -35,6 +38,10 @@ texture = ExtResource("6_15pjb") position = Vector2(67, 78) scale = Vector2(4.42446, 4.42446) +[node name="Battery" parent="." instance=ExtResource("5_pml1y")] +position = Vector2(124, 578) +scale = Vector2(6.12, 6.12) + [node name="CleaningRobotCB2D" type="CharacterBody2D" parent="."] position = Vector2(546, 342) script = ExtResource("1_84313") @@ -59,6 +66,12 @@ script = ExtResource("7_1rfah") [node name="ThrowTrashAway" type="Node" parent="CleaningRobotCB2D/StateMachine"] script = ExtResource("8_677fi") +[node name="GoToBattery" type="Node" parent="CleaningRobotCB2D/StateMachine"] +script = ExtResource("12_e37qa") + +[node name="RechargeBattery" type="Node" parent="CleaningRobotCB2D/StateMachine"] +script = ExtResource("13_qf7y1") + [node name="VisionCone" type="Area2D" parent="CleaningRobotCB2D"] [node name="CollisionShape2D" type="CollisionShape2D" parent="CleaningRobotCB2D/VisionCone"] diff --git a/state/scenes/state/state_GoToBattery.gd b/state/scenes/state/state_GoToBattery.gd new file mode 100644 index 0000000..6da427d --- /dev/null +++ b/state/scenes/state/state_GoToBattery.gd @@ -0,0 +1,13 @@ +extends State + +@onready var battery: Node2D = $"../../../Battery" + + +func state_enter(): + print("Go to ", battery.position, " to recharge battery") + + +func state_process(delta: float) -> void: + var target_position: Vector2 = battery.position + character.move_towards(target_position, delta) + character.move_and_slide() diff --git a/state/scenes/state/state_RechargeBattery.gd b/state/scenes/state/state_RechargeBattery.gd new file mode 100644 index 0000000..95da2dd --- /dev/null +++ b/state/scenes/state/state_RechargeBattery.gd @@ -0,0 +1,16 @@ +extends State + +@onready var battery: Node2D = $"../../../Battery" + + +func state_enter(): + print("Arrived at ", battery.position, " to recharge battery") + + +func state_process(delta: float) -> void: + var target_position: Vector2 = battery.position + target_position += Vector2(rand_range(-10, 10), rand_range(-10, 10)) + character.move_towards(target_position, delta) + character.move_and_slide() + + character.battery_charge += character.battery_charge_recharge_per_second * delta diff --git a/state/scenes/state/visualization/d_tree_node.gd b/state/scenes/state/visualization/d_tree_node.gd index bedba1d..c5172ff 100644 --- a/state/scenes/state/visualization/d_tree_node.gd +++ b/state/scenes/state/visualization/d_tree_node.gd @@ -1,8 +1,8 @@ class_name DTreeNode extends GraphNode -var left_slots: Array[String] = [] -var right_slots: Array[String] = [] +var left_slots: Array[String] = [] +var right_slots: Array[String] = [] var color_highlighted: StyleBoxFlat = StyleBoxFlat.new() var color_normal: StyleBoxFlat = StyleBoxFlat.new() @@ -29,12 +29,11 @@ func add_label(label_text: String, left: bool, right: bool) -> Vector3i: self.set_slot_color_right(child_index, Color(0.9, 0.9, 0.9, 1)) right_slots.append(label_text) - # the port index is counted separately from the left and right slots. + # the port index is counted separately from the left and right slots return Vector3i(child_index, left_slots.size() - 1, right_slots.size() - 1) func set_highlighted(highlight: bool) -> void: - # set the background color of the node if highlight: self.add_theme_stylebox_override("panel", color_highlighted) else: diff --git a/state/scenes/state/visualization/state_machine_info_panel.gd b/state/scenes/state/visualization/state_machine_info_panel.gd index a358847..ab9f9a1 100644 --- a/state/scenes/state/visualization/state_machine_info_panel.gd +++ b/state/scenes/state/visualization/state_machine_info_panel.gd @@ -3,7 +3,11 @@ extends VBoxContainer var values: Dictionary = {} var last_values: Dictionary = {} -var value_order: Array = [] # Keep track of the order of values +var value_order: Array = [] + + +func _ready() -> void: + hide() func to_str(value) -> String: @@ -19,6 +23,12 @@ func to_str(value) -> String: func _process(delta: float) -> void: + if Input.is_action_just_pressed("debug_vis_2"): + if is_visible(): + hide() + else: + show() + for child in self.get_children(): if child is Label: child.queue_free() @@ -26,7 +36,8 @@ func _process(delta: float) -> void: # 1. Update and Track Order of Values for value in values.keys(): if value in value_order: - value_order.erase(value) # Move to the end (most recent) + # Move to the end (most recent) + value_order.erase(value) value_order.append(value) # 2. Display Current Values (Most Recent First) diff --git a/state/scenes/state/visualization/tree_visualizer.gd b/state/scenes/state/visualization/tree_visualizer.gd index cef45a3..8da84c8 100644 --- a/state/scenes/state/visualization/tree_visualizer.gd +++ b/state/scenes/state/visualization/tree_visualizer.gd @@ -11,7 +11,7 @@ var all_node_names_to_nodes: Dictionary = {} func _process(delta: float) -> void: - if Input.is_action_just_pressed("ui_accept"): + if Input.is_action_just_pressed("debug_vis_1"): if is_visible(): hide() else: @@ -35,7 +35,6 @@ func build_tree(): var node_positions: Array[DTreeNode] = [] var y_spacing: int = 300 var x_spacing: int = 600 - var depths: Dictionary = calculate_node_depths() var states = state_machine.state_machine_data["states"] @@ -60,12 +59,9 @@ func build_tree(): # Set up labels (input and output) populate_node_labels(graph_node, state_name, state_data, all_node_names_to_nodes) - # Calculate position based on tree depth - var depth: int = depths[i] - var siblings: Array = get_nodes_at_depth(depth, depths) @warning_ignore("integer_division") - var y_pos: int = y_spacing * (siblings.find(i) - (siblings.size() - 1) / 2) - var x_pos: int = x_spacing * depth + var y_pos: int = randi() % (y_spacing * 2) - y_spacing + var x_pos: int = x_spacing * i y_pos += randi() % 200 - 100 graph_node.position_offset = Vector2(x_pos, y_pos) node_positions.append(graph_node) @@ -80,8 +76,18 @@ func populate_node_labels(node: DTreeNode, state_name: String, state_data: Dicti # start from the node passed in and only use the output connections to determine the input connections on the other nodes var output_transitions: Array = state_data.get("transitions", []) for transition in output_transitions: + transition = state_machine.resolve_transition_template(transition) + + var port_out: Vector3i = node.add_label(state_machine.human_readable_transition(transition), false, true) + + if not transition.has("target"): + continue + if not transition['target'] is String: + continue + var target_state_name: String = transition['target'] - var port_out: Vector3i = node.add_label(state_machine.human_readable_transition(transition), false, true) + if not node_names_to_nodes.has(target_state_name): + continue var target_node: DTreeNode = node_names_to_nodes[target_state_name] if target_node: @@ -94,28 +100,3 @@ func populate_node_labels(node: DTreeNode, state_name: String, state_data: Dicti ) print("Connecting %s [%d] to %s [%d]" % [node.get_name(), port_out, target_node.get_name(), port_in]) - -func calculate_node_depths() -> Dictionary: - var depths: Dictionary = {} - depths[0] = 0 # Root is at depth 0 - var state_names = state_machine.state_machine_data["states"].keys() - for i in range(state_names.size()): - var state_name = state_names[i] - var state_data = state_machine.state_machine_data["states"][state_name] - if state_data.has("transitions"): - for transition in state_data["transitions"]: - if transition.has("target"): - var target_state_name = transition["target"] - var target_state_index = state_names.find(target_state_name) - if target_state_index != -1: - depths[target_state_index] = depths[i] + 1 - return depths - - -func get_nodes_at_depth(depth: int, depths: Dictionary) -> Array: - var nodes_at_depth: Array[Variant] = [] - for i in depths.keys(): - if depths[i] == depth: - nodes_at_depth.append(i) - return nodes_at_depth -