State machine
parent
63aa1c0cb5
commit
0930edc477
|
@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 82 KiB |
|
@ -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
|
Binary file not shown.
After Width: | Height: | Size: 935 KiB |
|
@ -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
|
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
|
@ -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
|
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
|
@ -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
|
|
@ -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)
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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)
|
|
@ -8,11 +8,26 @@ var character: CharacterBody2D
|
|||
func _ready() -> void:
|
||||
pass
|
||||
|
||||
|
||||
func state_enter() -> void:
|
||||
pass
|
||||
|
||||
|
||||
func state_process(delta: float) -> void:
|
||||
pass
|
||||
|
||||
|
||||
func state_exit() -> void:
|
||||
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
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
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:
|
||||
|
@ -9,11 +17,227 @@ var current_state: State:
|
|||
current_state.state_enter()
|
||||
|
||||
|
||||
# INITIALIZATION
|
||||
func _ready() -> void:
|
||||
if get_parent() is CharacterBody2D:
|
||||
character = get_parent()
|
||||
else:
|
||||
push_error("No character as parent node.")
|
||||
|
||||
for child in self.get_children():
|
||||
if true:
|
||||
print(child)
|
||||
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
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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")
|
|
@ -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")
|
|
@ -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()
|
||||
|
|
@ -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()
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue