diff --git a/state/assets/character_state_machine.json b/state/assets/character_state_machine.json new file mode 100644 index 0000000..3b6e3d4 --- /dev/null +++ b/state/assets/character_state_machine.json @@ -0,0 +1,134 @@ +{ + "start": { + "state": "Idle" + }, + "states": { + "Idle": { + "transitions": [ + { + "target": "PickupTrash", + "signal": "waste_detected", + "conditions": [ + { + "type": ">=", + "left": { + "value": 400 + }, + "right": { + "function": "distance", + "args": [ + { + "accessor": [ + "character", + "position" + ] + }, + { + "accessor": [ + "signals", + "waste_detected", + "args", + "waste", + "position" + ] + } + ] + } + } + ], + "transfer": { + "waste": { + "accessor": [ + "signals", + "waste_detected", + "args", + "waste" + ] + } + } + } + ] + }, + "PickupTrash": { + "transitions": [ + { + "target": "ThrowTrashAway", + "signal": "waste_detected", + "conditions": [ + { + "type": ">=", + "left": { + "value": 75 + }, + "right": { + "function": "distance", + "args": [ + { + "accessor": [ + "character", + "position" + ] + }, + { + "accessor": [ + "signals", + "waste_detected", + "args", + "waste", + "position" + ] + } + ] + } + } + ], + "transfer": { + "waste": { + "accessor": [ + "signals", + "waste_detected", + "args", + "waste" + ] + } + } + } + ] + }, + "ThrowTrashAway": { + "transitions": [ + { + "target": "Idle", + "conditions": [ + { + "type": ">=", + "left": { + "value": 90 + }, + "right": { + "function": "distance", + "args": [ + { + "accessor": [ + "character", + "position" + ] + }, + { + "accessor": [ + "root_nodes", + "StateMachineWorld", + "child_nodes", + "TrashBin", + "position" + ] + } + ] + } + } + ] + } + ] + } + } +} diff --git a/state/assets/cleaning_robot.png b/state/assets/cleaning_robot.png new file mode 100644 index 0000000..192ee80 Binary files /dev/null and b/state/assets/cleaning_robot.png differ diff --git a/state/assets/cleaning_robot.png.import b/state/assets/cleaning_robot.png.import new file mode 100644 index 0000000..0aad0b2 --- /dev/null +++ b/state/assets/cleaning_robot.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cx04xknqfdscp" +path="res://.godot/imported/cleaning_robot.png-014de1c8447fd18dff622d82a883fa1e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/cleaning_robot.png" +dest_files=["res://.godot/imported/cleaning_robot.png-014de1c8447fd18dff622d82a883fa1e.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/floor.png b/state/assets/floor.png new file mode 100644 index 0000000..4e03fa8 Binary files /dev/null and b/state/assets/floor.png differ diff --git a/state/assets/floor.png.import b/state/assets/floor.png.import new file mode 100644 index 0000000..15620e5 --- /dev/null +++ b/state/assets/floor.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dkqw1wsjbvl0i" +path="res://.godot/imported/floor.png-38406586736af2fe805d7b886727ee47.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/floor.png" +dest_files=["res://.godot/imported/floor.png-38406586736af2fe805d7b886727ee47.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/trash_bag.png b/state/assets/trash_bag.png new file mode 100644 index 0000000..480dab5 Binary files /dev/null and b/state/assets/trash_bag.png differ diff --git a/state/assets/trash_bag.png.import b/state/assets/trash_bag.png.import new file mode 100644 index 0000000..6f14201 --- /dev/null +++ b/state/assets/trash_bag.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://s0vco3jt5y8m" +path="res://.godot/imported/trash_bag.png-26088d6e8428343ce319693f7898cb67.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/trash_bag.png" +dest_files=["res://.godot/imported/trash_bag.png-26088d6e8428343ce319693f7898cb67.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/trash_bin.png b/state/assets/trash_bin.png new file mode 100644 index 0000000..d36f728 Binary files /dev/null and b/state/assets/trash_bin.png differ diff --git a/state/assets/trash_bin.png.import b/state/assets/trash_bin.png.import new file mode 100644 index 0000000..4731307 --- /dev/null +++ b/state/assets/trash_bin.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://di8eotyycurps" +path="res://.godot/imported/trash_bin.png-1284d871f56467e75f465b364d170d20.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/trash_bin.png" +dest_files=["res://.godot/imported/trash_bin.png-1284d871f56467e75f465b364d170d20.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/project.godot b/state/project.godot index a28ee76..de119e3 100644 --- a/state/project.godot +++ b/state/project.godot @@ -11,5 +11,18 @@ config_version=5 [application] config/name="state" +run/main_scene="res://scenes/state/StateMachineWorld.tscn" config/features=PackedStringArray("4.3", "Forward Plus") config/icon="res://icon.svg" + +[display] + +window/stretch/mode="viewport" + +[input] + +spawn_trash={ +"deadzone": 0.5, +"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":1,"canceled":false,"pressed":false,"double_click":false,"script":null) +] +} diff --git a/state/scenes/state/SMCharacter.gd b/state/scenes/state/SMCharacter.gd new file mode 100644 index 0000000..719fa35 --- /dev/null +++ b/state/scenes/state/SMCharacter.gd @@ -0,0 +1,95 @@ +extends CharacterBody2D + +@onready var state_machine: StateMachine = $StateMachine +# +@onready var sprite: Node = $Sprite2D +# +const MAX_SPEED: float = 150.0 +const MAX_ACCELERATION: float = 300.0 +const SLOWING_RADIUS: float = 80.0 +const SHAKE_INTENSITY: float = 8.0 +const ROTATION_SPEED: float = 5.0 # adians per second +# +@onready var detection_area: Area2D = $VisionCone +signal waste_detected(waste) + + +func _ready() -> void: + pass + + +# detection_area.body_entered.connect(Callable(self, "_on_body_entered")) +# detection_area.area_entered.connect(Callable(self, "_on_area_entered")) + + +# func _on_body_entered(body): +# if body.is_in_group("waste"): +# emit_signal("waste_detected", body) + + +# func _on_area_entered(area): +# if area.is_in_group("waste"): +# emit_signal("waste_detected", area) + + +func _draw() -> void: + if velocity.length() > 0.1: + var local_velocity: Vector2 = global_transform.basis_xform_inv(velocity.normalized() * 400) + draw_line(Vector2.ZERO, local_velocity, Color(1, 0, 0), 2) + + +func _physics_process(delta: float) -> void: + rotate_towards_velocity(delta) + detect_waste() + queue_redraw() + + +func detect_waste() -> void: + var overlapping_bodies: Array[Node2D] = detection_area.get_overlapping_bodies() + for body in overlapping_bodies: + if body.is_in_group("waste"): + emit_signal("waste_detected", body) + + +func move_towards(target: Vector2, delta: float) -> void: + var to_target: Vector2 = target - position + var distance: float = to_target.length() + + if distance < 5.0: + velocity = Vector2.ZERO + return + + var desired_speed: float = MAX_SPEED + if distance < SLOWING_RADIUS: + desired_speed = lerp(0, int(MAX_SPEED), distance / SLOWING_RADIUS) + + var desired_velocity: Vector2 = to_target.normalized() * desired_speed + + var steering: Vector2 = desired_velocity - velocity + + var steering_length: float = steering.length() + if steering_length > MAX_ACCELERATION * delta: + steering = steering.normalized() * MAX_ACCELERATION * delta + + velocity += steering + + if velocity.length() > 0.1: + var shake: Vector2 = Vector2( + rand_range(-SHAKE_INTENSITY, SHAKE_INTENSITY), + rand_range(-SHAKE_INTENSITY, SHAKE_INTENSITY) + ) + velocity += shake + + if velocity.length() > MAX_SPEED: + velocity = velocity.normalized() * MAX_SPEED + + +func rotate_towards_velocity(delta: float) -> void: + if velocity.length() > 0.1: + var target_angle: float = velocity.angle() + rotation = lerp_angle(rotation, target_angle, ROTATION_SPEED * delta) + sprite.rotation = -rotation + + +func rand_range(min: float, max: float) -> float: + return randf() * (max - min) + min diff --git a/state/scenes/state/SceneManagement.gd b/state/scenes/state/SceneManagement.gd new file mode 100644 index 0000000..9fb7fdb --- /dev/null +++ b/state/scenes/state/SceneManagement.gd @@ -0,0 +1,16 @@ +extends Node2D + +func _ready() -> void: + pass + + +func _process(delta: float) -> void: + if Input.is_action_just_pressed("spawn_trash"): + var trash_scene: PackedScene = preload("res://scenes/state/Waste.tscn") + var trash_instance: Node2D = trash_scene.instantiate() as Node2D + + var spawn_location: Vector2 = get_global_mouse_position() + trash_instance.position = Vector2(spawn_location) + + get_tree().get_root().add_child(trash_instance) + print("Spawned trash at: ", spawn_location, " ", trash_instance.position) diff --git a/state/scenes/state/State.gd b/state/scenes/state/State.gd index 328edb0..22c8eca 100644 --- a/state/scenes/state/State.gd +++ b/state/scenes/state/State.gd @@ -6,13 +6,28 @@ var character: CharacterBody2D func _ready() -> void: - pass + pass + func state_enter() -> void: - pass + pass + func state_process(delta: float) -> void: - pass + pass + func state_exit() -> void: - pass + pass + + +func contribute_transfer_variables(transfer_variables: Dictionary) -> void: + pass + + +func change_state(new_state: State) -> void: + state_machine.current_state = new_state + + +func rand_range(min: float, max: float) -> float: + return randf() * (max - min) + min diff --git a/state/scenes/state/StateMachine.gd b/state/scenes/state/StateMachine.gd index a069863..df7f988 100644 --- a/state/scenes/state/StateMachine.gd +++ b/state/scenes/state/StateMachine.gd @@ -1,19 +1,243 @@ class_name StateMachine extends Node +var character: CharacterBody2D +# json read from file, contains all states and transitions +var state_machine_data: Dictionary = {} +# records all signals that were fired during the last frame, dictionary contains signal name and arguments +var received_signals: Array[Dictionary] = [] +# state change parameters ("transfer") +var state_transfer_variables: Dictionary = {} + var current_state: State: - set(value): - if current_state: - current_state.state_exit() - current_state = value - current_state.state_enter() + set(value): + if current_state: + current_state.state_exit() + current_state = value + current_state.state_enter() +# INITIALIZATION func _ready() -> void: - for child in self.get_children(): - if true: - print(child) + if get_parent() is CharacterBody2D: + character = get_parent() + else: + push_error("No character as parent node.") + + for child in self.get_children(): + if child is State: + print('Detected state ', child) + child.state_machine = self + child.character = character + + var loaded_state_machine_data: JSON = load_json("res://assets/character_state_machine.json") + if loaded_state_machine_data: + state_machine_data = loaded_state_machine_data.get_data() as Dictionary + print("Loaded JSON character_state_machine.json ", state_machine_data) + else: + push_error("Failed to load JSON character_state_machine.json") + + current_state = find_state_by_name(state_machine_data["start"]["state"]) + + call_deferred("defer_ready") +func defer_ready(): + character.waste_detected.connect(Callable(self, "_on_waste_detected")) + + +# STATE MACHINE func _process(delta: float) -> void: - pass + if current_state: + current_state.state_process(delta) + check_transitions() + received_signals = [] + + +func check_transitions() -> void: + if current_state: + for transition in state_machine_data["states"][current_state.get_name()]["transitions"]: + + if "signal" in transition and transition["signal"] and not was_signal_received(transition["signal"]): + continue + + if "conditions" in transition and transition["conditions"]: + var all_conditions_met: bool = true + for condition in transition["conditions"]: + if not transition_check_condition(condition): + all_conditions_met = false + break + if not all_conditions_met: + continue + + # evaluate transfer parameters if any + state_transfer_variables = {} + if "transfer" in transition and transition["transfer"]: + for key in transition["transfer"]: + state_transfer_variables[key] = transition_resolve_parameter(transition["transfer"][key]) + + # allow current state to contribute to transfer variables + 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"]) + + +func transition_check_condition(condition: Dictionary) -> bool: + var type: String = condition["type"] + var left: Variant = transition_resolve_parameter(condition["left"]) + var right: Variant = transition_resolve_parameter(condition["right"]) + + var result: bool = false + if type == "<": + result = left < right + elif type == ">": + result = left > right + elif type == "<=": + result = left <= right + elif type == ">=": + result = left >= right + elif type == "==": + result = left == right + elif type == "!=": + result = left != right + else: + print("Unknown condition type [", type, "]") + + print("[check_condition] ", left, " ", type, " ", right, " = ", result) + return result + + +func transition_resolve_parameter(parameter: Dictionary) -> Variant: + if "value" in parameter: + return parameter["value"] + elif "function" in parameter: + return transition_resolve_function(parameter) + elif "accessor" in parameter: + return transition_resolve_accessor(parameter["accessor"]) + else: + print("Unknown parameter type") + return null + + +func transition_resolve_function(function: Dictionary) -> Variant: + var args: Array = [] + if "args" in function: + for arg in function["args"]: + if arg is Dictionary: + args.append(transition_resolve_parameter(arg)) + else: + print("Unknown argument type [", arg, "]") + + if function["function"] == "distance": + var result = args[0].distance_to(args[1]) + print("[resolve_function] distance(", args[0], ", ", args[1], ") = ", result) + return result + else: + print("Unknown function [", function, "]") + return null + + +func transition_resolve_accessor(accessor: Array) -> Variant: + var current_item: Variant = self + for next_item in accessor: + + print("[resolve_accessor] current_item: ", current_item, ' next_item: ', next_item) + + if current_item == null: + print("[resolve_accessor] Null value in ", accessor) + break + + if objects_equal(current_item, self): + if next_item == "signals": + current_item = received_signals + continue + + if objects_equal(current_item, received_signals): + var detected_signal: Dictionary = find_detected_signal(next_item) + if detected_signal.size() > 0: + current_item = detected_signal + continue + + if objects_equal(next_item, "child_nodes"): + # build a dictionary of names:child nodes + var child_nodes: Dictionary = {} + for child in current_item.get_children(): + child_nodes[child.get_name()] = child + current_item = child_nodes + continue + + if objects_equal(next_item, "root_nodes"): + var root_nodes: Dictionary = {} + for root in current_item.get_tree().get_root().get_children(): + root_nodes[root.get_name()] = root + current_item = root_nodes + continue + + if current_item is Dictionary: + print("[resolve_accessor] Dictionary accessor, keys: ", current_item.keys()) + if next_item in current_item: + current_item = current_item[next_item] + continue + else: + break + + current_item = current_item.get(next_item) + + if current_item == null: + print("[resolve_accessor] Failed to resolve accessor ", accessor) + + print("[resolve_accessor] resolved to ", current_item) + return current_item + + +# SIGNALS +func _on_waste_detected(waste): + received_signals.append({"signal": "waste_detected", "args": {"waste": waste}}) + + +func find_detected_signal(signal_name: String) -> Dictionary: + for received_signal in received_signals: + if received_signal["signal"] == signal_name: + return received_signal + return {} + + +# UTILITY FUNCTIONS +func find_state_by_name(name: String) -> State: + for child in self.get_children(): + if child is State: + if child.get_name() == name: + return child + return null + + +func was_signal_received(signal_name: String) -> bool: + for received_signal in received_signals: + if received_signal["signal"] == signal_name: + return true + return false + + +func load_json(path: String) -> JSON: + var file: FileAccess = FileAccess.open(path, FileAccess.READ) + if not file: + print("Cannot open file: ", path) + return JSON.new() + + var json_string: String = file.get_as_text() + file.close() + + var json = JSON.new() + var error = json.parse(json_string) + if error != OK: + print("JSON parse error: ", error) + return JSON.new() + + return json + + +func objects_equal(a: Variant, b: Variant) -> bool: + if typeof(a) != typeof(b): + return false + return a == b diff --git a/state/scenes/state/StateMachineWorld.tscn b/state/scenes/state/StateMachineWorld.tscn index 39076d2..4ff66d0 100644 --- a/state/scenes/state/StateMachineWorld.tscn +++ b/state/scenes/state/StateMachineWorld.tscn @@ -1,25 +1,63 @@ -[gd_scene load_steps=5 format=3 uid="uid://cgdvptekjbpgq"] +[gd_scene load_steps=12 format=3 uid="uid://cgdvptekjbpgq"] -[ext_resource type="Texture2D" uid="uid://beggt5ndi6j4p" path="res://icon.svg" id="1_jgx5y"] +[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"] +[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="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"] [sub_resource type="CapsuleShape2D" id="CapsuleShape2D_tr1gq"] radius = 60.0 height = 122.0 +[sub_resource type="RectangleShape2D" id="RectangleShape2D_ji64e"] +size = Vector2(601.536, 254.293) + [node name="StateMachineWorld" type="Node2D"] +script = ExtResource("1_s1suw") -[node name="CharacterBody2D" type="CharacterBody2D" parent="."] +[node name="Floor" type="Sprite2D" parent="."] +position = Vector2(573, 329) +scale = Vector2(0.635347, 0.635347) +texture = ExtResource("6_15pjb") -[node name="Sprite2D" type="Sprite2D" parent="CharacterBody2D"] -texture = ExtResource("1_jgx5y") +[node name="TrashBin" parent="." instance=ExtResource("9_sd6r3")] +position = Vector2(67, 78) +scale = Vector2(4.42446, 4.42446) -[node name="CollisionShape2D" type="CollisionShape2D" parent="CharacterBody2D"] +[node name="CleaningRobotCB2D" type="CharacterBody2D" parent="."] +position = Vector2(546, 342) +script = ExtResource("1_84313") + +[node name="Sprite2D" type="Sprite2D" parent="CleaningRobotCB2D"] +scale = Vector2(0.345498, 0.345498) +texture = ExtResource("2_cwwab") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="CleaningRobotCB2D"] +scale = Vector2(0.702629, 0.702629) shape = SubResource("CapsuleShape2D_tr1gq") -[node name="StateMachine" type="Node" parent="CharacterBody2D"] +[node name="StateMachine" type="Node" parent="CleaningRobotCB2D"] script = ExtResource("2_d1xqo") -[node name="idle" type="Node" parent="CharacterBody2D/StateMachine"] +[node name="Idle" type="Node" parent="CleaningRobotCB2D/StateMachine"] script = ExtResource("3_r1btx") + +[node name="PickupTrash" type="Node" parent="CleaningRobotCB2D/StateMachine"] +script = ExtResource("7_1rfah") + +[node name="ThrowTrashAway" type="Node" parent="CleaningRobotCB2D/StateMachine"] +script = ExtResource("8_677fi") + +[node name="VisionCone" type="Area2D" parent="CleaningRobotCB2D"] + +[node name="CollisionShape2D" type="CollisionShape2D" parent="CleaningRobotCB2D/VisionCone"] +position = Vector2(295.232, -1) +scale = Vector2(1, 0.48) +shape = SubResource("RectangleShape2D_ji64e") + +[node name="Node2D" type="Node2D" parent="CleaningRobotCB2D"] diff --git a/state/scenes/state/TrashBin.tscn b/state/scenes/state/TrashBin.tscn new file mode 100644 index 0000000..cb7a855 --- /dev/null +++ b/state/scenes/state/TrashBin.tscn @@ -0,0 +1,16 @@ +[gd_scene load_steps=3 format=3 uid="uid://dng6a8i8kp4kw"] + +[ext_resource type="Texture2D" uid="uid://di8eotyycurps" path="res://assets/trash_bin.png" id="1_eaj15"] + +[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_eaj15") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +shape = SubResource("CapsuleShape2D_bhmyx") diff --git a/state/scenes/state/Waste.tscn b/state/scenes/state/Waste.tscn new file mode 100644 index 0000000..2e9d573 --- /dev/null +++ b/state/scenes/state/Waste.tscn @@ -0,0 +1,17 @@ +[gd_scene load_steps=3 format=3 uid="uid://cqew2pkk5en21"] + +[ext_resource type="Texture2D" uid="uid://s0vco3jt5y8m" path="res://assets/trash_bag.png" id="1_mr4i5"] + +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_4fcy4"] +radius = 42.0 +height = 128.0 + +[node name="Waste" type="StaticBody2D" groups=["waste"]] + +[node name="Sprite2D" type="Sprite2D" parent="."] +scale = Vector2(0.5, 0.5) +texture = ExtResource("1_mr4i5") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +scale = Vector2(0.5, 0.5) +shape = SubResource("CapsuleShape2D_4fcy4") diff --git a/state/scenes/state/state_PickupTrash.gd b/state/scenes/state/state_PickupTrash.gd new file mode 100644 index 0000000..29b62aa --- /dev/null +++ b/state/scenes/state/state_PickupTrash.gd @@ -0,0 +1,15 @@ +extends State + +var pickup_trash_target: Vector2 = Vector2.ZERO + + +func state_enter(): + var waste = state_machine.state_transfer_variables["waste"] + pickup_trash_target = waste.position + print(waste, " ", pickup_trash_target) + + +func state_process(delta: float) -> void: + character.move_towards(pickup_trash_target, delta) + character.move_and_slide() + diff --git a/state/scenes/state/state_ThrowTrashAway.gd b/state/scenes/state/state_ThrowTrashAway.gd new file mode 100644 index 0000000..083ec67 --- /dev/null +++ b/state/scenes/state/state_ThrowTrashAway.gd @@ -0,0 +1,25 @@ +extends State + +@onready var trash_bin: Node2D = $"../../../TrashBin" + +var carry_trash: Node2D = null + + +func state_enter(): + carry_trash = state_machine.state_transfer_variables["waste"] + # disable it's collision to avoid it from colliding with the character + carry_trash.get_node("CollisionShape2D").disabled = true + print("Carry ", carry_trash, " to ", trash_bin.position) + + +func state_process(delta: float) -> void: + # put the trash on the character + carry_trash.position = character.position + # carry the trash to the trash bin + var target_position: Vector2 = trash_bin.position + character.move_towards(target_position, delta) + character.move_and_slide() + + +func state_exit() -> void: + carry_trash.queue_free() diff --git a/state/scenes/state/state_idle.gd b/state/scenes/state/state_idle.gd index 973446f..15041a0 100644 --- a/state/scenes/state/state_idle.gd +++ b/state/scenes/state/state_idle.gd @@ -1,10 +1,16 @@ extends State +var idle_target: Vector2 = Vector2.ZERO + + func state_enter(): - print("idle enter") + idle_target = Vector2(rand_range(0, get_viewport().size.x), rand_range(0, get_viewport().size.y)) + func state_process(delta: float) -> void: - print("idle process") - -func state_exit() -> void: - print("idle exit") + # move towards the idle_target until reached, then set a new target randomly on the screen + if character.position.distance_to(idle_target) < 10: + idle_target = Vector2(rand_range(0, get_viewport().size.x), rand_range(0, get_viewport().size.y)) + else: + character.move_towards(idle_target, delta) + character.move_and_slide()