feat(val-blog): add 2026-04-30 dream journey post
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
extends Node2D
|
||||
|
||||
# 公寓管理器 - 处理房间、家具和交互
|
||||
|
||||
@onready var val: CharacterBody2D = $Val
|
||||
@onready var rooms: Node2D = $Rooms
|
||||
@onready var furniture: Node2D = $Furniture
|
||||
|
||||
signal room_changed(room_name: String)
|
||||
signal furniture_clicked(furniture: Node)
|
||||
|
||||
var bridge: Node = null
|
||||
|
||||
func _ready():
|
||||
# 连接输入事件
|
||||
_connect_furniture_clicks()
|
||||
_connect_room_detection()
|
||||
|
||||
# 获取 BridgeClient 引用(从父场景)
|
||||
_find_bridge()
|
||||
|
||||
print("Apartment ready, furniture count: ", furniture.get_child_count())
|
||||
|
||||
func _find_bridge():
|
||||
# 尝试多种路径找到 BridgeClient
|
||||
bridge = get_node_or_null("../BridgeClient")
|
||||
if not bridge:
|
||||
bridge = get_node_or_null("/root/Main/BridgeClient")
|
||||
if not bridge:
|
||||
# 延迟查找
|
||||
await get_tree().process_frame
|
||||
bridge = get_node_or_null("../BridgeClient")
|
||||
|
||||
if bridge:
|
||||
print("BridgeClient found")
|
||||
else:
|
||||
print("BridgeClient not found - running in standalone mode")
|
||||
|
||||
func _connect_furniture_clicks():
|
||||
for item in furniture.get_children():
|
||||
if item is Area2D:
|
||||
item.input_event.connect(_on_furniture_clicked.bind(item))
|
||||
print("Connected click for: ", item.get_meta("furniture_name", "未知"))
|
||||
|
||||
func _connect_room_detection():
|
||||
for room in rooms.get_children():
|
||||
if room is Area2D:
|
||||
room.body_entered.connect(_on_room_entered.bind(room))
|
||||
|
||||
func _on_furniture_clicked(viewport: Node, event: InputEvent, shape_idx: int, furniture: Node):
|
||||
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
|
||||
var furniture_name = furniture.get_meta("furniture_name", "未知")
|
||||
print("Clicked on furniture: ", furniture_name)
|
||||
emit_signal("furniture_clicked", furniture)
|
||||
|
||||
# 让Val走过去交互
|
||||
if val:
|
||||
val.interact_with(furniture)
|
||||
else:
|
||||
print("Val not found!")
|
||||
|
||||
# 发送事件到Bridge
|
||||
_send_to_bridge({
|
||||
"type": "furniture_clicked",
|
||||
"furniture": furniture_name,
|
||||
"position": [furniture.global_position.x, furniture.global_position.y]
|
||||
})
|
||||
|
||||
func _on_room_entered(body: Node2D, room: Area2D):
|
||||
if body.name == "Val":
|
||||
var room_name = room.get_meta("room_name", "未知")
|
||||
print("Val entered room: ", room_name)
|
||||
emit_signal("room_changed", room_name)
|
||||
|
||||
# 更新Val的当前房间
|
||||
if val and "current_room" in val:
|
||||
val.current_room = room_name
|
||||
if val and "agent_state" in val:
|
||||
val.agent_state["location"] = room_name
|
||||
|
||||
# 发送事件到Bridge
|
||||
_send_to_bridge({
|
||||
"type": "room_changed",
|
||||
"room": room_name,
|
||||
"agent_id": "val"
|
||||
})
|
||||
|
||||
func get_furniture_list() -> Array:
|
||||
var list = []
|
||||
for item in furniture.get_children():
|
||||
if item is Area2D:
|
||||
list.append({
|
||||
"name": item.get_meta("furniture_name", "未知"),
|
||||
"interaction": item.get_meta("interaction", "use"),
|
||||
"position": [item.global_position.x, item.global_position.y]
|
||||
})
|
||||
return list
|
||||
|
||||
func get_room_list() -> Array:
|
||||
var list = []
|
||||
for room in rooms.get_children():
|
||||
if room is Area2D:
|
||||
list.append(room.get_meta("room_name", "未知"))
|
||||
return list
|
||||
|
||||
func _send_to_bridge(data: Dictionary):
|
||||
if bridge and "send_command" in bridge:
|
||||
bridge.send_command(data)
|
||||
|
||||
# 公共API:让Val移动到指定房间
|
||||
func move_val_to_room(room_name: String):
|
||||
for room in rooms.get_children():
|
||||
if room is Area2D and room.get_meta("room_name", "") == room_name:
|
||||
var target_pos = room.global_position + Vector2(0, 50)
|
||||
if val:
|
||||
val.move_to(target_pos)
|
||||
return true
|
||||
return false
|
||||
|
||||
# 公共API:获取指定家具
|
||||
func get_furniture(furniture_name: String) -> Node:
|
||||
for item in furniture.get_children():
|
||||
if item is Area2D and item.get_meta("furniture_name", "") == furniture_name:
|
||||
return item
|
||||
return null
|
||||
@@ -0,0 +1 @@
|
||||
uid://cljtq5ip65le0
|
||||
@@ -0,0 +1,65 @@
|
||||
extends Node2D
|
||||
|
||||
const GRID_SIZE = 64
|
||||
const ROOM_WIDTH = 10
|
||||
const ROOM_HEIGHT = 8
|
||||
const MAIN_CHANNEL_WIDTH = 2
|
||||
|
||||
var grid_cells: Array = []
|
||||
var occupied_positions: Array = []
|
||||
|
||||
func _ready():
|
||||
initialize_grid()
|
||||
|
||||
func initialize_grid():
|
||||
grid_cells.resize(ROOM_WIDTH * ROOM_HEIGHT)
|
||||
for y in range(ROOM_HEIGHT):
|
||||
for x in range(ROOM_WIDTH):
|
||||
var index = y * ROOM_WIDTH + x
|
||||
grid_cells[index] = {
|
||||
"position": Vector2(x * GRID_SIZE, y * GRID_SIZE),
|
||||
"occupied": false,
|
||||
"type": "empty"
|
||||
}
|
||||
# Reserve main channel (2 cells wide, can be horizontal or vertical, here we assume vertical left side)
|
||||
if x == 0 and y < ROOM_HEIGHT - 2: # Keep 2 cells at bottom free
|
||||
grid_cells[index]["occupied"] = true
|
||||
grid_cells[index]["type"] = "channel"
|
||||
|
||||
func is_position_valid(pos: Vector2) -> bool:
|
||||
var x = int(pos.x / GRID_SIZE)
|
||||
var y = int(pos.y / GRID_SIZE)
|
||||
if x < 0 or x >= ROOM_WIDTH or y < 0 or y >= ROOM_HEIGHT:
|
||||
return false
|
||||
var index = y * ROOM_WIDTH + x
|
||||
return not grid_cells[index]["occupied"]
|
||||
|
||||
func get_random_valid_position() -> Vector2:
|
||||
var attempts = 0
|
||||
while attempts < 50:
|
||||
var x = randi() % (ROOM_WIDTH - 1) # Avoid blocking channel
|
||||
var y = randi() % ROOM_HEIGHT
|
||||
var pos = Vector2(x * GRID_SIZE + GRID_SIZE / 2, y * GRID_SIZE + GRID_SIZE / 2)
|
||||
if is_position_valid(pos):
|
||||
return pos
|
||||
attempts += 1
|
||||
return Vector2.ZERO
|
||||
|
||||
func reserve_position(pos: Vector2, obj_type: String = "object"):
|
||||
var x = int(pos.x / GRID_SIZE)
|
||||
var y = int(pos.y / GRID_SIZE)
|
||||
if x >= 0 and x < ROOM_WIDTH and y >= 0 and y < ROOM_HEIGHT:
|
||||
var index = y * ROOM_WIDTH + x
|
||||
if not grid_cells[index]["occupied"]:
|
||||
grid_cells[index]["occupied"] = true
|
||||
grid_cells[index]["type"] = obj_type
|
||||
occupied_positions.append(pos)
|
||||
|
||||
func free_position(pos: Vector2):
|
||||
var x = int(pos.x / GRID_SIZE)
|
||||
var y = int(pos.y / GRID_SIZE)
|
||||
if x >= 0 and x < ROOM_WIDTH and y >= 0 and y < ROOM_HEIGHT:
|
||||
var index = y * ROOM_WIDTH + x
|
||||
grid_cells[index]["occupied"] = false
|
||||
grid_cells[index]["type"] = "empty"
|
||||
occupied_positions.erase(pos)
|
||||
@@ -0,0 +1 @@
|
||||
uid://q1m7nq7xin5m
|
||||
@@ -0,0 +1,152 @@
|
||||
extends Node
|
||||
|
||||
# Bridge 客户端 - 连接 Godot 和 OpenClaw
|
||||
|
||||
signal action_received(action: Dictionary)
|
||||
signal state_synced(state: Dictionary)
|
||||
signal connected_to_bridge()
|
||||
signal disconnected_from_bridge()
|
||||
|
||||
@export var bridge_url: String = "ws://127.0.0.1:8765"
|
||||
@export var reconnect_interval: float = 5.0
|
||||
@export var auto_connect: bool = false # 默认不自动连接
|
||||
|
||||
var socket: WebSocketPeer
|
||||
var is_connected: bool = false
|
||||
var reconnect_timer: float = 0.0
|
||||
|
||||
func _ready():
|
||||
# 作为全局单例
|
||||
name = "BridgeClient"
|
||||
print("BridgeClient initialized, auto_connect = ", auto_connect)
|
||||
|
||||
if auto_connect:
|
||||
_connect_to_bridge()
|
||||
|
||||
func _process(delta):
|
||||
if socket:
|
||||
_socket_poll()
|
||||
elif reconnect_timer > 0:
|
||||
reconnect_timer -= delta
|
||||
if reconnect_timer <= 0:
|
||||
_connect_to_bridge()
|
||||
|
||||
func _connect_to_bridge():
|
||||
print("Connecting to OpenClaw Bridge at ", bridge_url)
|
||||
|
||||
socket = WebSocketPeer.new()
|
||||
var err = socket.connect_to_url(bridge_url)
|
||||
|
||||
if err != OK:
|
||||
print("Failed to connect: ", err)
|
||||
_schedule_reconnect()
|
||||
return
|
||||
|
||||
# 设置状态
|
||||
is_connected = false
|
||||
|
||||
func _socket_poll():
|
||||
socket.poll()
|
||||
var state = socket.get_ready_state()
|
||||
|
||||
match state:
|
||||
WebSocketPeer.STATE_CONNECTING:
|
||||
pass # 正在连接
|
||||
|
||||
WebSocketPeer.STATE_OPEN:
|
||||
if not is_connected:
|
||||
is_connected = true
|
||||
print("Connected to OpenClaw Bridge!")
|
||||
emit_signal("connected_to_bridge")
|
||||
_send_hello()
|
||||
|
||||
# 处理收到的消息
|
||||
while socket.get_available_packets() > 0:
|
||||
var packet = socket.get_packet()
|
||||
var message_str = packet.get_string_from_utf8()
|
||||
_parse_message(message_str)
|
||||
|
||||
WebSocketPeer.STATE_CLOSING:
|
||||
pass # 正在关闭
|
||||
|
||||
WebSocketPeer.STATE_CLOSED:
|
||||
if is_connected:
|
||||
is_connected = false
|
||||
print("Disconnected from Bridge")
|
||||
emit_signal("disconnected_from_bridge")
|
||||
socket = null
|
||||
_schedule_reconnect()
|
||||
|
||||
func _schedule_reconnect():
|
||||
reconnect_timer = reconnect_interval
|
||||
print("Will reconnect in ", reconnect_interval, " seconds...")
|
||||
|
||||
func _send_hello():
|
||||
send_command({
|
||||
"type": "hello",
|
||||
"client": "godot",
|
||||
"agent_id": "val",
|
||||
"timestamp": Time.get_unix_time_from_system()
|
||||
})
|
||||
|
||||
func _parse_message(message_str: String):
|
||||
var json = JSON.new()
|
||||
var err = json.parse(message_str)
|
||||
|
||||
if err != OK:
|
||||
print("Failed to parse JSON: ", message_str)
|
||||
return
|
||||
|
||||
var message = json.get_data()
|
||||
|
||||
if typeof(message) != TYPE_DICTIONARY:
|
||||
print("Invalid message format")
|
||||
return
|
||||
|
||||
var msg_type = message.get("type", "")
|
||||
|
||||
match msg_type:
|
||||
"action_sequence":
|
||||
# 解析动作序列
|
||||
var actions = message.get("actions", [])
|
||||
for action in actions:
|
||||
emit_signal("action_received", action)
|
||||
|
||||
"state_sync":
|
||||
# 同步Agent状态
|
||||
emit_signal("state_synced", message.get("state", {}))
|
||||
|
||||
"heartbeat":
|
||||
# 响应心跳
|
||||
send_command({"type": "heartbeat_ack", "timestamp": Time.get_unix_time_from_system()})
|
||||
|
||||
"error":
|
||||
print("Bridge error: ", message.get("error", "Unknown error"))
|
||||
|
||||
_:
|
||||
print("Unknown message type: ", msg_type)
|
||||
|
||||
func send_command(command: Dictionary) -> bool:
|
||||
if not is_connected or not socket:
|
||||
# 静默处理,不打印警告
|
||||
return false
|
||||
|
||||
var json_str = JSON.stringify(command)
|
||||
var err = socket.send_text(json_str)
|
||||
|
||||
if err != OK:
|
||||
print("Failed to send command: ", err)
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
func request_state_sync():
|
||||
send_command({
|
||||
"type": "request_state_sync",
|
||||
"agent_id": "val"
|
||||
})
|
||||
|
||||
func close_connection():
|
||||
if socket:
|
||||
socket.close()
|
||||
is_connected = false
|
||||
@@ -0,0 +1 @@
|
||||
uid://v3b2e7ifiw6v
|
||||
@@ -0,0 +1,76 @@
|
||||
extends Node2D
|
||||
|
||||
# 主场景管理器
|
||||
|
||||
@onready var apartment: Node2D = $Apartment
|
||||
@onready var bridge: Node = $BridgeClient
|
||||
@onready var command_input: LineEdit = $UI/Panel/VBoxContainer/CommandInput
|
||||
@onready var send_button: Button = $UI/Panel/VBoxContainer/SendButton
|
||||
|
||||
func _ready():
|
||||
# 连接UI事件
|
||||
send_button.pressed.connect(_on_send_command)
|
||||
command_input.text_submitted.connect(_on_command_submitted)
|
||||
|
||||
# 连接公寓事件
|
||||
apartment.room_changed.connect(_on_room_changed)
|
||||
apartment.furniture_clicked.connect(_on_furniture_clicked)
|
||||
|
||||
# 连接Bridge事件
|
||||
if bridge:
|
||||
bridge.connected_to_bridge.connect(_on_bridge_connected)
|
||||
bridge.disconnected_from_bridge.connect(_on_bridge_disconnected)
|
||||
|
||||
print("AgentLiving started!")
|
||||
print("Click on furniture to make Val interact with it!")
|
||||
|
||||
func _on_send_command():
|
||||
var command = command_input.text.strip_edges()
|
||||
if command.is_empty():
|
||||
return
|
||||
|
||||
_send_command_to_val(command)
|
||||
command_input.clear()
|
||||
|
||||
func _on_command_submitted(text: String):
|
||||
_on_send_command()
|
||||
|
||||
func _send_command_to_val(command: String):
|
||||
print("User command: ", command)
|
||||
|
||||
var val = apartment.get_node_or_null("Val")
|
||||
if val:
|
||||
val.receive_command(command)
|
||||
|
||||
# 同时发送到Bridge
|
||||
if bridge and bridge.is_connected:
|
||||
bridge.send_command({
|
||||
"type": "user_command",
|
||||
"command": command,
|
||||
"timestamp": Time.get_unix_time_from_system()
|
||||
})
|
||||
|
||||
func _on_room_changed(room_name: String):
|
||||
print("Val entered: ", room_name)
|
||||
|
||||
func _on_furniture_clicked(furniture: Node):
|
||||
print("Furniture clicked: ", furniture.get_meta("furniture_name", "未知"))
|
||||
|
||||
func _on_bridge_connected():
|
||||
print("Bridge connected!")
|
||||
# 请求初始状态同步
|
||||
if bridge:
|
||||
bridge.request_state_sync()
|
||||
|
||||
func _on_bridge_disconnected():
|
||||
print("Bridge disconnected!")
|
||||
|
||||
func _input(event):
|
||||
# 按ESC退出
|
||||
if event is InputEventKey and event.pressed and event.keycode == KEY_ESCAPE:
|
||||
get_tree().quit()
|
||||
|
||||
# 按R重新连接Bridge
|
||||
if event is InputEventKey and event.pressed and event.keycode == KEY_R:
|
||||
if bridge:
|
||||
bridge._connect_to_bridge()
|
||||
@@ -0,0 +1 @@
|
||||
uid://wo22tl1oknt8
|
||||
@@ -0,0 +1,229 @@
|
||||
extends CharacterBody2D
|
||||
class_name Val
|
||||
|
||||
# 移动速度
|
||||
@export var speed: float = 100.0
|
||||
|
||||
# 节点引用
|
||||
@onready var body_placeholder: ColorRect = $BodyPlaceholder
|
||||
@onready var head_placeholder: ColorRect = $HeadPlaceholder
|
||||
@onready var thought_bubble: Label = $ThoughtBubble
|
||||
@onready var state_indicator: Label = $StateIndicator
|
||||
@onready var nav_agent: NavigationAgent2D = $NavigationAgent2D
|
||||
@onready var interaction_timer: Timer = $InteractionTimer
|
||||
@onready var think_timer: Timer = $ThinkTimer
|
||||
|
||||
# 状态
|
||||
enum State { IDLE, WALK, INTERACT, THINK }
|
||||
var current_state: State = State.IDLE
|
||||
var current_room: String = "客厅"
|
||||
var agent_state: Dictionary = {
|
||||
"location": "客厅",
|
||||
"energy": 80,
|
||||
"mood": "开心",
|
||||
"activity": "idle"
|
||||
}
|
||||
|
||||
# 目标
|
||||
var target_position: Vector2 = Vector2.ZERO
|
||||
var target_furniture: Node = null
|
||||
var bridge: Node = null
|
||||
|
||||
# 动画颜色
|
||||
var normal_color = Color(0.36, 0.61, 0.84, 1) # 蓝色
|
||||
var interact_color = Color(0.5, 0.8, 0.36, 1) # 绿色
|
||||
var think_color = Color(0.8, 0.5, 0.36, 1) # 橙色
|
||||
|
||||
signal state_changed(new_state: String)
|
||||
signal action_completed(action: String)
|
||||
|
||||
func _ready():
|
||||
# 初始化
|
||||
thought_bubble.hide()
|
||||
update_state_display()
|
||||
|
||||
# 连接导航完成信号
|
||||
nav_agent.navigation_finished.connect(_on_navigation_finished)
|
||||
|
||||
# 查找 BridgeClient
|
||||
_find_bridge()
|
||||
|
||||
print("Val ready at position: ", global_position)
|
||||
|
||||
func _find_bridge():
|
||||
# 尝试多种路径
|
||||
bridge = get_node_or_null("../../BridgeClient")
|
||||
if not bridge:
|
||||
bridge = get_node_or_null("/root/Main/BridgeClient")
|
||||
if not bridge:
|
||||
await get_tree().process_frame
|
||||
bridge = get_node_or_null("../../BridgeClient")
|
||||
|
||||
if bridge:
|
||||
print("Val: BridgeClient found")
|
||||
if bridge.has_signal("action_received"):
|
||||
bridge.action_received.connect(_on_bridge_action)
|
||||
if bridge.has_signal("state_synced"):
|
||||
bridge.state_synced.connect(_on_state_synced)
|
||||
else:
|
||||
print("Val: BridgeClient not found - running in standalone mode")
|
||||
|
||||
func _physics_process(delta):
|
||||
match current_state:
|
||||
State.WALK:
|
||||
_handle_walk(delta)
|
||||
State.INTERACT:
|
||||
_handle_interact()
|
||||
State.THINK:
|
||||
_handle_think()
|
||||
_:
|
||||
_handle_idle()
|
||||
|
||||
func _handle_walk(delta):
|
||||
if nav_agent.is_navigation_finished():
|
||||
current_state = State.IDLE
|
||||
update_state_display()
|
||||
emit_signal("action_completed", "walk")
|
||||
return
|
||||
|
||||
var next_pos = nav_agent.get_next_path_position()
|
||||
var direction = (next_pos - global_position).normalized()
|
||||
velocity = direction * speed
|
||||
|
||||
# 翻转精灵
|
||||
if direction.x < 0:
|
||||
body_placeholder.scale.x = -1
|
||||
head_placeholder.scale.x = -1
|
||||
elif direction.x > 0:
|
||||
body_placeholder.scale.x = 1
|
||||
head_placeholder.scale.x = 1
|
||||
|
||||
# 行走时颜色闪烁
|
||||
body_placeholder.color = normal_color.lerp(Color.WHITE, sin(Time.get_ticks_msec() * 0.01) * 0.1)
|
||||
|
||||
move_and_slide()
|
||||
|
||||
func _handle_idle():
|
||||
velocity = Vector2.ZERO
|
||||
body_placeholder.color = normal_color
|
||||
head_placeholder.color = normal_color
|
||||
state_indicator.text = "idle"
|
||||
|
||||
func _handle_interact():
|
||||
velocity = Vector2.ZERO
|
||||
body_placeholder.color = interact_color
|
||||
head_placeholder.color = interact_color
|
||||
state_indicator.text = "interact"
|
||||
|
||||
func _handle_think():
|
||||
velocity = Vector2.ZERO
|
||||
body_placeholder.color = think_color
|
||||
head_placeholder.color = think_color
|
||||
state_indicator.text = "think"
|
||||
|
||||
# 移动到指定位置
|
||||
func move_to(pos: Vector2):
|
||||
target_position = pos
|
||||
nav_agent.target_position = pos
|
||||
current_state = State.WALK
|
||||
update_state_display()
|
||||
emit_signal("state_changed", "walk")
|
||||
print("Val moving to: ", pos)
|
||||
|
||||
# 移动到家具并交互
|
||||
func interact_with(furniture: Node):
|
||||
target_furniture = furniture
|
||||
var interact_pos = furniture.global_position + Vector2(0, 60)
|
||||
print("Val will interact with: ", furniture.get_meta("furniture_name", "未知"), " at ", interact_pos)
|
||||
move_to(interact_pos)
|
||||
|
||||
# 等待移动完成后再交互
|
||||
await action_completed
|
||||
_start_interaction(furniture)
|
||||
|
||||
func _start_interaction(furniture: Node):
|
||||
current_state = State.INTERACT
|
||||
update_state_display()
|
||||
|
||||
var furniture_name = furniture.get_meta("furniture_name", "物品")
|
||||
var interaction = furniture.get_meta("interaction", "使用")
|
||||
|
||||
show_thought("正在" + interaction + furniture_name + "...")
|
||||
|
||||
# 发送交互事件到Bridge
|
||||
_send_to_bridge({
|
||||
"type": "furniture_interacted",
|
||||
"furniture": furniture_name,
|
||||
"interaction": interaction,
|
||||
"position": [global_position.x, global_position.y]
|
||||
})
|
||||
|
||||
# 交互持续2秒
|
||||
interaction_timer.start(2.0)
|
||||
await interaction_timer.timeout
|
||||
|
||||
current_state = State.IDLE
|
||||
thought_bubble.hide()
|
||||
update_state_display()
|
||||
emit_signal("action_completed", "interact")
|
||||
print("Val finished interacting with: ", furniture_name)
|
||||
|
||||
# 显示思考气泡
|
||||
func show_thought(text: String, duration: float = 3.0):
|
||||
thought_bubble.text = text
|
||||
thought_bubble.show()
|
||||
|
||||
if duration > 0:
|
||||
think_timer.start(duration)
|
||||
await think_timer.timeout
|
||||
thought_bubble.hide()
|
||||
|
||||
# 处理Bridge发来的动作
|
||||
func _on_bridge_action(action: Dictionary):
|
||||
match action.get("cmd", ""):
|
||||
"walk_to":
|
||||
move_to(Vector2(action.pos[0], action.pos[1]))
|
||||
"show_thought":
|
||||
show_thought(action.get("text", ""), action.get("duration", 3.0))
|
||||
"interact":
|
||||
var furniture_path = action.get("furniture_path", "")
|
||||
if furniture_path:
|
||||
var furniture = get_node_or_null(furniture_path)
|
||||
if furniture:
|
||||
interact_with(furniture)
|
||||
"update_state":
|
||||
_update_internal_state(action.get("key", ""), action.get("value"))
|
||||
|
||||
# 同步OpenClaw状态
|
||||
func _on_state_synced(state: Dictionary):
|
||||
agent_state = state
|
||||
current_room = state.get("location", current_room)
|
||||
update_state_display()
|
||||
|
||||
func _update_internal_state(key: String, value):
|
||||
agent_state[key] = value
|
||||
update_state_display()
|
||||
|
||||
func update_state_display():
|
||||
state_indicator.text = State.keys()[current_state].to_lower()
|
||||
|
||||
func _on_navigation_finished():
|
||||
current_state = State.IDLE
|
||||
update_state_display()
|
||||
print("Val navigation finished")
|
||||
|
||||
func _send_to_bridge(data: Dictionary):
|
||||
if bridge and "send_command" in bridge:
|
||||
bridge.send_command(data)
|
||||
|
||||
# 公共API:接收用户指令
|
||||
func receive_command(command: String):
|
||||
show_thought("思考中...", 0)
|
||||
current_state = State.THINK
|
||||
update_state_display()
|
||||
|
||||
_send_to_bridge({
|
||||
"type": "user_command",
|
||||
"command": command,
|
||||
"current_state": agent_state
|
||||
})
|
||||
@@ -0,0 +1 @@
|
||||
uid://buw2rqtgbwl0l
|
||||
@@ -0,0 +1,59 @@
|
||||
extends Node
|
||||
|
||||
enum State { IDLE, WALKING, INTERACTING, ANIMATING }
|
||||
var current_state: State = State.IDLE
|
||||
|
||||
var target_position: Vector2 = Vector2.ZERO
|
||||
var move_speed: float = 100.0
|
||||
var is_moving: bool = false
|
||||
|
||||
func _ready():
|
||||
pass
|
||||
|
||||
func _process(delta):
|
||||
match current_state:
|
||||
State.IDLE:
|
||||
pass
|
||||
State.WALKING:
|
||||
walk_towards_target(delta)
|
||||
State.INTERACTING:
|
||||
pass
|
||||
State.ANIMATING:
|
||||
pass
|
||||
|
||||
func set_state(new_state: State):
|
||||
current_state = new_state
|
||||
match new_state:
|
||||
State.IDLE:
|
||||
pass
|
||||
State.WALKING:
|
||||
start_walking()
|
||||
State.INTERACTING:
|
||||
start_interacting()
|
||||
State.ANIMATING:
|
||||
start_animating()
|
||||
|
||||
func start_walking():
|
||||
is_moving = true
|
||||
|
||||
func walk_towards_target(delta):
|
||||
if not is_moving:
|
||||
return
|
||||
var direction = (target_position - position).normalized()
|
||||
position += direction * move_speed * delta
|
||||
if position.distance_to(target_position) < 5.0:
|
||||
position = target_position
|
||||
is_moving = false
|
||||
set_state(State.IDLE)
|
||||
|
||||
func start_interacting():
|
||||
# Trigger interaction animation or logic
|
||||
pass
|
||||
|
||||
func start_animating():
|
||||
# Handle any animations
|
||||
pass
|
||||
|
||||
func move_to(pos: Vector2):
|
||||
target_position = pos
|
||||
set_state(State.WALKING)
|
||||
@@ -0,0 +1 @@
|
||||
uid://cei8m7rqa7a2r
|
||||
Reference in New Issue
Block a user