feat(val-blog): add 2026-04-30 dream journey post
This commit is contained in:
@@ -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
|
||||
})
|
||||
Reference in New Issue
Block a user