Proper generation of the node graph for target selectors, refactored carrying logic
parent
7879bb4656
commit
b688b5c8f5
|
@ -201,7 +201,8 @@
|
||||||
"ignore": [
|
"ignore": [
|
||||||
"RechargeBattery",
|
"RechargeBattery",
|
||||||
"GoToBattery"
|
"GoToBattery"
|
||||||
]
|
],
|
||||||
|
"restore_transfer_variables": true
|
||||||
},
|
},
|
||||||
"conditions": [
|
"conditions": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,8 +14,10 @@ const ROTATION_SPEED: float = 5.0 # adians per second
|
||||||
signal waste_detected(waste)
|
signal waste_detected(waste)
|
||||||
#
|
#
|
||||||
var battery_charge: float = 100.0
|
var battery_charge: float = 100.0
|
||||||
const battery_charge_drain_per_second: float = 4.0
|
const battery_charge_drain_per_second: float = 5.0
|
||||||
const battery_charge_recharge_per_second: float = 27.0
|
const battery_charge_recharge_per_second: float = 30.0
|
||||||
|
# carry item
|
||||||
|
var carry_item: Node2D = null
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
@ -43,7 +45,11 @@ func _draw() -> void:
|
||||||
|
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
|
battery_charge -= battery_charge_drain_per_second * delta
|
||||||
%StateMachineInfoPanel.values["battery_charge"] = battery_charge
|
%StateMachineInfoPanel.values["battery_charge"] = battery_charge
|
||||||
|
if carry_item:
|
||||||
|
# put the carry_item on the character
|
||||||
|
carry_item.position = position
|
||||||
|
|
||||||
|
|
||||||
func _physics_process(delta: float) -> void:
|
func _physics_process(delta: float) -> void:
|
||||||
|
@ -51,8 +57,6 @@ func _physics_process(delta: float) -> void:
|
||||||
detect_waste()
|
detect_waste()
|
||||||
queue_redraw()
|
queue_redraw()
|
||||||
|
|
||||||
battery_charge -= battery_charge_drain_per_second * delta
|
|
||||||
|
|
||||||
|
|
||||||
func detect_waste() -> void:
|
func detect_waste() -> void:
|
||||||
var overlapping_bodies: Array[Node2D] = detection_area.get_overlapping_bodies()
|
var overlapping_bodies: Array[Node2D] = detection_area.get_overlapping_bodies()
|
||||||
|
|
|
@ -17,7 +17,7 @@ func state_process(delta: float) -> void:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
func state_exit() -> void:
|
func state_exit(new_state: State) -> void:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,14 +10,13 @@ var state_machine_data: Dictionary = {}
|
||||||
var received_signals: Array[Dictionary] = []
|
var received_signals: Array[Dictionary] = []
|
||||||
# state change parameters ("transfer")
|
# state change parameters ("transfer")
|
||||||
var state_transfer_variables: Dictionary = {}
|
var state_transfer_variables: Dictionary = {}
|
||||||
|
|
||||||
var state_history: Array = []
|
var state_history: Array = []
|
||||||
|
|
||||||
var current_state: State:
|
var current_state: State:
|
||||||
set(value):
|
set(value):
|
||||||
if current_state:
|
if current_state:
|
||||||
current_state.state_exit()
|
current_state.state_exit(value)
|
||||||
state_history.append(value)
|
state_history.append({"state": value, "transfer": state_transfer_variables})
|
||||||
current_state = value
|
current_state = value
|
||||||
current_state.state_enter()
|
current_state.state_enter()
|
||||||
|
|
||||||
|
@ -95,16 +94,30 @@ func check_transitions() -> void:
|
||||||
%StateMachineInfoPanel.values["transitioned to"] = transition["target"]
|
%StateMachineInfoPanel.values["transitioned to"] = transition["target"]
|
||||||
|
|
||||||
# evaluate transfer parameters if any
|
# evaluate transfer parameters if any
|
||||||
state_transfer_variables = {}
|
if transition.has("copy_transfer_variables") and transition["copy_transfer_variables"]:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
state_transfer_variables = {}
|
||||||
|
|
||||||
if "transfer" in transition and transition["transfer"]:
|
if "transfer" in transition and transition["transfer"]:
|
||||||
for key in transition["transfer"]:
|
for key in transition["transfer"]:
|
||||||
state_transfer_variables[key] = transition_resolve_parameter(transition["transfer"][key])
|
state_transfer_variables[key] = transition_resolve_parameter(transition["transfer"][key])
|
||||||
%StateMachineInfoPanel.values["transfer [" + key + "]"] = state_transfer_variables[key]
|
|
||||||
|
|
||||||
# allow current state to contribute to transfer variables
|
# allow current state to contribute to transfer variables
|
||||||
current_state.contribute_transfer_variables(state_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)
|
print("All criteria were met for switching to state ", transition["target"])
|
||||||
|
|
||||||
|
if transition["target"] is Dictionary:
|
||||||
|
if transition["target"].has("restore_transfer_variables") and transition["target"]["restore_transfer_variables"]:
|
||||||
|
var transfer_variables: Dictionary = find_state_by_selector(transition["target"])["transfer"]
|
||||||
|
for key in transfer_variables:
|
||||||
|
state_transfer_variables[key] = transfer_variables[key]
|
||||||
|
|
||||||
|
print("Transfer variables: ", state_transfer_variables)
|
||||||
|
for key in state_transfer_variables:
|
||||||
|
%StateMachineInfoPanel.values["transfer [" + str(key) + "]"] = state_transfer_variables[key]
|
||||||
|
|
||||||
current_state = find_state(transition["target"])
|
current_state = find_state(transition["target"])
|
||||||
|
|
||||||
|
|
||||||
|
@ -233,7 +246,7 @@ func find_detected_signal(signal_name: String) -> Dictionary:
|
||||||
# UTILITY FUNCTIONS
|
# UTILITY FUNCTIONS
|
||||||
func find_state(name) -> State:
|
func find_state(name) -> State:
|
||||||
if name is Dictionary:
|
if name is Dictionary:
|
||||||
return find_state_by_selector(name)
|
return find_state_by_selector(name)["state"]
|
||||||
return find_state_by_name(name)
|
return find_state_by_name(name)
|
||||||
|
|
||||||
|
|
||||||
|
@ -242,10 +255,11 @@ func find_state_by_name(name: String) -> State:
|
||||||
if child is State:
|
if child is State:
|
||||||
if child.get_name() == name:
|
if child.get_name() == name:
|
||||||
return child
|
return child
|
||||||
return null
|
# return the default state as a fallback
|
||||||
|
return self.get_child(state_machine_data["start"]["state"])
|
||||||
|
|
||||||
|
|
||||||
func find_state_by_selector(name: Dictionary) -> State:
|
func find_state_by_selector(name: Dictionary) -> Dictionary:
|
||||||
# {
|
# {
|
||||||
# "type": "history_first",
|
# "type": "history_first",
|
||||||
# "ignore": [
|
# "ignore": [
|
||||||
|
@ -256,10 +270,12 @@ func find_state_by_selector(name: Dictionary) -> State:
|
||||||
if name["type"] == "history_first":
|
if name["type"] == "history_first":
|
||||||
var index: int = state_history.size() - 1
|
var index: int = state_history.size() - 1
|
||||||
while index >= 0:
|
while index >= 0:
|
||||||
if not state_history[index].get_name() in name["ignore"]:
|
if not state_history[index]["state"].get_name() in name["ignore"]:
|
||||||
return state_history[index]
|
return state_history[index]
|
||||||
index -= 1
|
index -= 1
|
||||||
return null
|
|
||||||
|
# return the default state as a fallback
|
||||||
|
return {"state": self.get_child(state_machine_data["start"]["state"]), "transfer": {}}
|
||||||
|
|
||||||
|
|
||||||
func was_signal_received(signal_name: String) -> bool:
|
func was_signal_received(signal_name: String) -> bool:
|
||||||
|
|
|
@ -6,20 +6,23 @@ var carry_trash: Node2D = null
|
||||||
|
|
||||||
|
|
||||||
func state_enter():
|
func state_enter():
|
||||||
carry_trash = state_machine.state_transfer_variables["waste"]
|
if state_machine.state_transfer_variables.has("waste"):
|
||||||
# disable it's collision to avoid it from colliding with the character
|
carry_trash = state_machine.state_transfer_variables["waste"]
|
||||||
carry_trash.get_node("CollisionShape2D").disabled = true
|
character.carry_item = carry_trash
|
||||||
|
# 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)
|
print("Carry ", carry_trash, " to ", trash_bin.position)
|
||||||
|
|
||||||
|
|
||||||
func state_process(delta: float) -> void:
|
func state_process(delta: float) -> void:
|
||||||
# put the trash on the character
|
|
||||||
carry_trash.position = character.position
|
|
||||||
# carry the trash to the trash bin
|
# carry the trash to the trash bin
|
||||||
var target_position: Vector2 = trash_bin.position
|
var target_position: Vector2 = trash_bin.position
|
||||||
character.move_towards(target_position, delta)
|
character.move_towards(target_position, delta)
|
||||||
character.move_and_slide()
|
character.move_and_slide()
|
||||||
|
|
||||||
|
|
||||||
func state_exit() -> void:
|
func state_exit(new_state: State) -> void:
|
||||||
carry_trash.queue_free()
|
if new_state.get_name() != "GoToBattery":
|
||||||
|
character.carry_item = null
|
||||||
|
carry_trash.queue_free()
|
||||||
|
|
|
@ -59,12 +59,16 @@ func build_tree():
|
||||||
# Set up labels (input and output)
|
# Set up labels (input and output)
|
||||||
populate_node_labels(graph_node, state_name, state_data, all_node_names_to_nodes)
|
populate_node_labels(graph_node, state_name, state_data, all_node_names_to_nodes)
|
||||||
|
|
||||||
|
# Arrange nodes
|
||||||
|
var idx: int = 0
|
||||||
|
for graph_node in all_nodes:
|
||||||
@warning_ignore("integer_division")
|
@warning_ignore("integer_division")
|
||||||
var y_pos: int = randi() % (y_spacing * 2) - y_spacing
|
var y_pos: int = randi() % (y_spacing * 2) - y_spacing
|
||||||
var x_pos: int = x_spacing * i
|
var x_pos: int = x_spacing * idx
|
||||||
y_pos += randi() % 200 - 100
|
y_pos += randi() % 200 - 100
|
||||||
graph_node.position_offset = Vector2(x_pos, y_pos)
|
graph_node.position_offset = Vector2(x_pos, y_pos)
|
||||||
node_positions.append(graph_node)
|
node_positions.append(graph_node)
|
||||||
|
idx += 1
|
||||||
|
|
||||||
|
|
||||||
func _on_graph_edit_connection_request(from_node: StringName, from_port: int, to_node: StringName, to_port: int) -> void:
|
func _on_graph_edit_connection_request(from_node: StringName, from_port: int, to_node: StringName, to_port: int) -> void:
|
||||||
|
@ -82,7 +86,10 @@ func populate_node_labels(node: DTreeNode, state_name: String, state_data: Dicti
|
||||||
|
|
||||||
if not transition.has("target"):
|
if not transition.has("target"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not transition['target'] is String:
|
if not transition['target'] is String:
|
||||||
|
if transition['target'] is Dictionary:
|
||||||
|
populate_node_labels_state_dict(node, state_name, node_names_to_nodes, transition, port_out)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
var target_state_name: String = transition['target']
|
var target_state_name: String = transition['target']
|
||||||
|
@ -100,3 +107,39 @@ 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])
|
print("Connecting %s [%d] to %s [%d]" % [node.get_name(), port_out, target_node.get_name(), port_in])
|
||||||
|
|
||||||
|
|
||||||
|
func populate_node_labels_state_dict(node: DTreeNode, state_name: String, node_names_to_nodes: Dictionary, transition: Dictionary, port_out: Vector3i) -> void:
|
||||||
|
# {
|
||||||
|
# "type": "history_first",
|
||||||
|
# "ignore": [
|
||||||
|
# "RechargeBattery",
|
||||||
|
# "GoToBattery"
|
||||||
|
# ],
|
||||||
|
# "restore_transfer_variables": true
|
||||||
|
# }
|
||||||
|
# display a new node for the target state with it's information
|
||||||
|
var target_state_name: String = node.get_name() + "_" + transition['target']['type']
|
||||||
|
var target_node: DTreeNode = null
|
||||||
|
if node_names_to_nodes.has(target_state_name):
|
||||||
|
target_node = node_names_to_nodes[target_state_name]
|
||||||
|
else:
|
||||||
|
target_node = D_TREE_NODE.instantiate()
|
||||||
|
graph_edit.add_child(target_node)
|
||||||
|
target_node.title = transition['target']['type']
|
||||||
|
all_nodes.append(target_node)
|
||||||
|
all_node_names_to_nodes[target_state_name] = target_node
|
||||||
|
|
||||||
|
# add the "ignore" states as input ports
|
||||||
|
if transition['target'].has("ignore"):
|
||||||
|
target_node.add_label("ignore:", false, false)
|
||||||
|
for ignore_state in transition['target']['ignore']:
|
||||||
|
target_node.add_label("- %s" % [ignore_state], false, false)
|
||||||
|
|
||||||
|
var port_in: Vector3i = target_node.add_label("%s" % [state_name], true, false)
|
||||||
|
|
||||||
|
# connect the nodes
|
||||||
|
graph_edit.connect_node(
|
||||||
|
node.get_name(), port_out.z,
|
||||||
|
target_node.get_name(), port_in.y
|
||||||
|
)
|
||||||
|
print("Connecting %s [%d] to %s [%d]" % [node.get_name(), port_out, target_node.get_name(), port_in])
|
Loading…
Reference in New Issue