feat(val-blog): add 2026-04-30 dream journey post
This commit is contained in:
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,102 @@
|
||||
# AgentLiving - 项目状态
|
||||
|
||||
## ✅ 已完成
|
||||
|
||||
### 设计文档
|
||||
- [x] DESIGN.md - 完整架构设计
|
||||
- [x] TECH_SPEC.md - 技术规格
|
||||
- [x] README.md - 项目说明
|
||||
|
||||
### Godot 项目结构
|
||||
```
|
||||
godot/
|
||||
├── project.godot # 项目配置
|
||||
├── scenes/
|
||||
│ ├── main.tscn # 主场景
|
||||
│ ├── apartment.tscn # 公寓场景(含3房间+4家具)
|
||||
│ └── val.tscn # Val角色场景
|
||||
└── scripts/
|
||||
├── main.gd # 主场景管理
|
||||
├── val.gd # Val角色逻辑(状态机)
|
||||
├── apartment.gd # 公寓管理
|
||||
└── bridge_client.gd # WebSocket客户端
|
||||
```
|
||||
|
||||
### Bridge 通信层
|
||||
- [x] bridge_server.py - Python WebSocket服务端
|
||||
- [x] 协议设计:action_sequence、state_sync、user_command
|
||||
- [x] 简单指令解析(沙发、冰箱、床、书桌、各房间)
|
||||
|
||||
### 功能实现
|
||||
- [x] Val 4状态:idle、walk、interact、think
|
||||
- [x] 点击家具自动走过去交互
|
||||
- [x] 文字指令输入
|
||||
- [x] 状态面板显示
|
||||
- [x] 房间切换检测
|
||||
- [x] 思考气泡显示
|
||||
|
||||
## 🚧 待完成
|
||||
|
||||
### 美术资源
|
||||
- [ ] Val 角色精灵(idle、walk、interact动画)
|
||||
- [ ] 家具贴图(沙发、冰箱、床、书桌)
|
||||
- [ ] 地板纹理
|
||||
- [ ] 字体文件
|
||||
|
||||
### OpenClaw 集成
|
||||
- [ ] 接入真实 OpenClaw Agent(当前是模拟逻辑)
|
||||
- [ ] 调用 tools(spawn、exec、web_search)
|
||||
- [ ] 可视化 subagent 协调
|
||||
|
||||
### 功能增强
|
||||
- [ ] 导航路径优化
|
||||
- [ ] 更多交互动作
|
||||
- [ ] 情绪可视化(表情、颜色)
|
||||
- [ ] 声音反馈
|
||||
|
||||
### 多 Agent 支持(Phase 2)
|
||||
- [ ] 添加 Helix/其他 Agent
|
||||
- [ ] Agent 间通信
|
||||
- [ ] 社交动画
|
||||
|
||||
## 🚀 快速启动
|
||||
|
||||
```bash
|
||||
cd /Users/guchen/.openclaw/workspace/ai-agent-living
|
||||
|
||||
# 1. 确保 OpenClaw Gateway 在运行
|
||||
openclaw gateway status
|
||||
|
||||
# 2. 启动项目
|
||||
./start.sh
|
||||
|
||||
# 或手动:
|
||||
# 终端1:启动 Bridge
|
||||
cd bridge && python3 bridge_server.py
|
||||
|
||||
# 终端2:启动 Godot
|
||||
cd godot && godot project.godot
|
||||
```
|
||||
|
||||
## 🎮 使用说明
|
||||
|
||||
1. **点击家具** - Val 会走过去交互
|
||||
2. **输入指令** - 右侧面板输入文字指令(如"去沙发上休息")
|
||||
3. **观察状态** - 右侧面板显示 Val 的实时状态
|
||||
4. **按 ESC** - 退出
|
||||
5. **按 R** - 重新连接 Bridge
|
||||
|
||||
## 📁 项目位置
|
||||
|
||||
`/Users/guchen/.openclaw/workspace/ai-agent-living/`
|
||||
|
||||
## 📝 下一步建议
|
||||
|
||||
1. **Week 1**: 制作/寻找美术资源,替换占位图形
|
||||
2. **Week 2**: 接入真实 OpenClaw Agent,实现工具调用可视化
|
||||
3. **Week 3**: Polish(音效、动画细节、情绪表达)
|
||||
|
||||
---
|
||||
|
||||
**创建时间:** 2026-04-13
|
||||
**状态:** MVP 代码完成,待美术资源和 OpenClaw 集成
|
||||
@@ -0,0 +1,102 @@
|
||||
# AgentLiving - AI Agent 生活可视化框架
|
||||
|
||||
> 让 OpenClaw Agent 在 2.5D 虚拟空间中生活、工作、可视化思考
|
||||
|
||||
## 🎯 项目简介
|
||||
|
||||
AgentLiving 是一个实验性框架,将 AI Agent(如 Val)具象化为可在虚拟公寓中活动的角色。通过 Godot 引擎渲染 + OpenClaw 后端驱动,探索人机交互的新维度。
|
||||
|
||||
## ✨ 核心特性
|
||||
|
||||
- **具象化 Agent**:不再是聊天框,而是有位置、动作、情绪的角色
|
||||
- **可视化思考**:Agent 检索记忆、spawn subagent 时显示动画反馈
|
||||
- **实时交互**:点击物品、语音指令,Agent 在场景中执行
|
||||
- **可扩展架构**:支持多 Agent 社交、自定义场景
|
||||
|
||||
## 🏗️ 技术栈
|
||||
|
||||
| 组件 | 技术 | 用途 |
|
||||
|------|------|------|
|
||||
| 渲染引擎 | Godot 4.x | 2.5D 场景、角色动画 |
|
||||
| 后端 | OpenClaw | Agent 大脑、任务协调 |
|
||||
| 通信 | WebSocket | 实时双向同步 |
|
||||
| 脚本 | GDScript + Python | 游戏逻辑 + Bridge 层 |
|
||||
|
||||
## 📁 项目结构
|
||||
|
||||
```
|
||||
ai-agent-living/
|
||||
├── docs/ # 设计文档
|
||||
│ ├── DESIGN.md # 架构设计
|
||||
│ └── TECH_SPEC.md # 技术规格
|
||||
├── godot/ # Godot 项目
|
||||
├── bridge/ # 通信层代码
|
||||
└── assets/ # 素材资源(精灵、音效等)
|
||||
```
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 环境要求
|
||||
- Godot 4.x
|
||||
- Python 3.10+
|
||||
- OpenClaw Gateway 运行中
|
||||
|
||||
### 启动步骤
|
||||
|
||||
1. **启动 OpenClaw Gateway**
|
||||
```bash
|
||||
openclaw gateway start
|
||||
```
|
||||
|
||||
2. **启动 Bridge 服务**
|
||||
```bash
|
||||
cd bridge
|
||||
python bridge_server.py
|
||||
```
|
||||
|
||||
3. **打开 Godot 项目**
|
||||
```bash
|
||||
cd godot
|
||||
godot project.godot
|
||||
```
|
||||
|
||||
4. **运行场景**
|
||||
在 Godot 中点击 "Run Project"
|
||||
|
||||
## 🎮 使用方式
|
||||
|
||||
- **点击交互**:点击公寓内的物品,Val 会走过去交互
|
||||
- **文字指令**:直接输入想让 Val 做什么
|
||||
- **观察模式**:让 Val 自主生活,观察其行为模式
|
||||
|
||||
## 📅 开发路线图
|
||||
|
||||
### Phase 1: 单人公寓 (4-6周)
|
||||
- [x] 设计文档
|
||||
- [ ] Godot 场景搭建
|
||||
- [ ] Bridge 通信层
|
||||
- [ ] Agent 行为逻辑
|
||||
- [ ] 可视化思考
|
||||
|
||||
### Phase 2: 多 Agent 社交
|
||||
- [ ] 添加第二个 Agent
|
||||
- [ ] Agent 间通信
|
||||
- [ ] 社交动画
|
||||
|
||||
### Phase 3: 自定义扩展
|
||||
- [ ] 场景编辑器
|
||||
- [ ] 自定义 Agent 外观
|
||||
- [ ] 插件系统
|
||||
|
||||
## 🤝 贡献
|
||||
|
||||
这是一个实验性项目,欢迎尝试和反馈!
|
||||
|
||||
## 📝 License
|
||||
|
||||
MIT License - 详见 LICENSE 文件
|
||||
|
||||
---
|
||||
|
||||
**Created by:** 谷老板 + Val
|
||||
**Start Date:** 2026-04-13
|
||||
@@ -0,0 +1,201 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
AgentLiving Bridge Server
|
||||
连接 Godot 前端和 OpenClaw 后端
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import websockets
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
# 简单的状态存储
|
||||
agent_states = {
|
||||
"val": {
|
||||
"location": "客厅",
|
||||
"energy": 80,
|
||||
"mood": "开心",
|
||||
"activity": "idle",
|
||||
"position": [400, 300]
|
||||
}
|
||||
}
|
||||
|
||||
class AgentLivingBridge:
|
||||
def __init__(self):
|
||||
self.clients = set()
|
||||
|
||||
async def register(self, websocket):
|
||||
self.clients.add(websocket)
|
||||
print(f"Client connected. Total: {len(self.clients)}")
|
||||
|
||||
async def unregister(self, websocket):
|
||||
self.clients.discard(websocket)
|
||||
print(f"Client disconnected. Total: {len(self.clients)}")
|
||||
|
||||
async def handle_client(self, websocket, path):
|
||||
await self.register(websocket)
|
||||
try:
|
||||
async for message in websocket:
|
||||
await self.process_message(websocket, message)
|
||||
except websockets.exceptions.ConnectionClosed:
|
||||
pass
|
||||
finally:
|
||||
await self.unregister(websocket)
|
||||
|
||||
async def process_message(self, websocket, message):
|
||||
try:
|
||||
data = json.loads(message)
|
||||
msg_type = data.get("type", "")
|
||||
|
||||
print(f"[{datetime.now().isoformat()}] Received: {msg_type}")
|
||||
|
||||
if msg_type == "hello":
|
||||
# 新客户端连接
|
||||
await websocket.send(json.dumps({
|
||||
"type": "state_sync",
|
||||
"state": agent_states["val"]
|
||||
}))
|
||||
|
||||
elif msg_type == "user_command":
|
||||
# 处理用户指令
|
||||
command = data.get("command", "")
|
||||
print(f"User command: {command}")
|
||||
|
||||
# 解析指令并生成动作序列
|
||||
actions = self._parse_command(command)
|
||||
|
||||
await websocket.send(json.dumps({
|
||||
"type": "action_sequence",
|
||||
"actions": actions
|
||||
}))
|
||||
|
||||
elif msg_type == "furniture_clicked":
|
||||
# 处理家具点击
|
||||
furniture = data.get("furniture", "")
|
||||
print(f"Furniture clicked: {furniture}")
|
||||
|
||||
# 生成走过去+交互的动作序列
|
||||
actions = [
|
||||
{"cmd": "show_thought", "text": f"要去{furniture}那边...", "duration": 1.0},
|
||||
{"cmd": "walk_to", "pos": data.get("position", [0, 0])},
|
||||
{"cmd": "play_anim", "anim": "interact"},
|
||||
{"cmd": "show_thought", "text": f"正在使用{furniture}", "duration": 2.0}
|
||||
]
|
||||
|
||||
await websocket.send(json.dumps({
|
||||
"type": "action_sequence",
|
||||
"actions": actions
|
||||
}))
|
||||
|
||||
elif msg_type == "room_changed":
|
||||
# 更新房间状态
|
||||
room = data.get("room", "")
|
||||
agent_states["val"]["location"] = room
|
||||
print(f"Val moved to: {room}")
|
||||
|
||||
elif msg_type == "request_state_sync":
|
||||
# 请求状态同步
|
||||
await websocket.send(json.dumps({
|
||||
"type": "state_sync",
|
||||
"state": agent_states["val"]
|
||||
}))
|
||||
|
||||
elif msg_type == "heartbeat_ack":
|
||||
# 心跳响应,忽略
|
||||
pass
|
||||
|
||||
else:
|
||||
print(f"Unknown message type: {msg_type}")
|
||||
|
||||
except json.JSONDecodeError:
|
||||
print(f"Invalid JSON: {message}")
|
||||
except Exception as e:
|
||||
print(f"Error processing message: {e}")
|
||||
await websocket.send(json.dumps({
|
||||
"type": "error",
|
||||
"error": str(e)
|
||||
}))
|
||||
|
||||
def _parse_command(self, command: str) -> list:
|
||||
"""解析用户指令为动作序列"""
|
||||
command = command.lower()
|
||||
actions = []
|
||||
|
||||
# 简单指令映射
|
||||
if "沙发" in command or "坐" in command:
|
||||
actions = [
|
||||
{"cmd": "show_thought", "text": "去沙发上休息...", "duration": 1.0},
|
||||
{"cmd": "walk_to", "pos": [280, 200]},
|
||||
{"cmd": "play_anim", "anim": "interact"},
|
||||
{"cmd": "show_thought", "text": "真舒服~", "duration": 2.0}
|
||||
]
|
||||
elif "冰箱" in command or "水" in command or "喝" in command:
|
||||
actions = [
|
||||
{"cmd": "show_thought", "text": "去倒杯水...", "duration": 1.0},
|
||||
{"cmd": "walk_to", "pos": [280, 440]},
|
||||
{"cmd": "play_anim", "anim": "interact"},
|
||||
{"cmd": "show_thought", "text": "解渴了~", "duration": 2.0}
|
||||
]
|
||||
elif "床" in command or "睡" in command or "休息" in command:
|
||||
actions = [
|
||||
{"cmd": "show_thought", "text": "去睡一会儿...", "duration": 1.0},
|
||||
{"cmd": "walk_to", "pos": [760, 200]},
|
||||
{"cmd": "play_anim", "anim": "interact"},
|
||||
{"cmd": "show_thought", "text": "Zzz...", "duration": 3.0}
|
||||
]
|
||||
elif "书桌" in command or "工作" in command or "学习" in command:
|
||||
actions = [
|
||||
{"cmd": "show_thought", "text": "去工作...", "duration": 1.0},
|
||||
{"cmd": "walk_to", "pos": [840, 200]},
|
||||
{"cmd": "play_anim", "anim": "think"},
|
||||
{"cmd": "show_thought", "text": "正在思考...", "duration": 3.0}
|
||||
]
|
||||
elif "客厅" in command:
|
||||
actions = [
|
||||
{"cmd": "show_thought", "text": "去客厅...", "duration": 1.0},
|
||||
{"cmd": "walk_to", "pos": [320, 240]}
|
||||
]
|
||||
elif "厨房" in command:
|
||||
actions = [
|
||||
{"cmd": "show_thought", "text": "去厨房...", "duration": 1.0},
|
||||
{"cmd": "walk_to", "pos": [320, 480]}
|
||||
]
|
||||
elif "卧室" in command:
|
||||
actions = [
|
||||
{"cmd": "show_thought", "text": "去卧室...", "duration": 1.0},
|
||||
{"cmd": "walk_to", "pos": [800, 240]}
|
||||
]
|
||||
else:
|
||||
# 默认:显示思考
|
||||
actions = [
|
||||
{"cmd": "show_thought", "text": f"收到指令:{command}", "duration": 2.0},
|
||||
{"cmd": "play_anim", "anim": "think"}
|
||||
]
|
||||
|
||||
return actions
|
||||
|
||||
async def main():
|
||||
bridge = AgentLivingBridge()
|
||||
|
||||
host = "localhost"
|
||||
port = 8765
|
||||
|
||||
print(f"Starting AgentLiving Bridge Server on ws://{host}:{port}")
|
||||
print("Press Ctrl+C to stop")
|
||||
|
||||
async with websockets.serve(
|
||||
bridge.handle_client,
|
||||
host,
|
||||
port,
|
||||
ping_interval=20,
|
||||
ping_timeout=10
|
||||
):
|
||||
await asyncio.Future() # 永久运行
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
asyncio.run(main())
|
||||
except KeyboardInterrupt:
|
||||
print("\nShutting down...")
|
||||
sys.exit(0)
|
||||
@@ -0,0 +1,198 @@
|
||||
# AI Agent 生活可视化框架 - 设计文档
|
||||
|
||||
## 项目代号
|
||||
**AgentLiving** - AI Agent 的拟人化生活空间
|
||||
|
||||
---
|
||||
|
||||
## 1. 项目愿景
|
||||
|
||||
创建一个2.5D可视化框架,让OpenClaw Agent(如Val)以具象化角色在虚拟公寓中生活、工作、社交。
|
||||
|
||||
**核心价值:**
|
||||
- 降低AI的抽象感,让用户"看见"Agent的思考和行为
|
||||
- 可视化Agent的任务执行、记忆检索、SubAgent协调过程
|
||||
- 探索人机交互的新维度(不仅是文字,还有空间、动作、情绪)
|
||||
|
||||
---
|
||||
|
||||
## 2. 技术架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 用户层 (User) │
|
||||
│ 点击交互 / 语音指令 / 观察Agent生活 │
|
||||
└──────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
┌──────────────────────▼──────────────────────────────────────┐
|
||||
│ Godot 前端 │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
||||
│ │ 2.5D场景 │ │ 角色动画 │ │ 交互UI │ │
|
||||
│ │ (公寓) │ │ (Val精灵) │ │ (指令/观察面板) │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
|
||||
└──────────────────────┬──────────────────────────────────────┘
|
||||
│ WebSocket / HTTP
|
||||
┌──────────────────────▼──────────────────────────────────────┐
|
||||
│ Bridge 通信层 │
|
||||
│ 协议转换、状态同步、事件广播 │
|
||||
└──────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
┌──────────────────────▼──────────────────────────────────────┐
|
||||
│ OpenClaw 后端 │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
||||
│ │ Val Agent │ │ 状态管理 │ │ SubAgent协调 │ │
|
||||
│ │ (主控) │ │ (位置/情绪) │ │ (任务分解) │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. MVP 范围(第一阶段)
|
||||
|
||||
### 3.1 场景设计
|
||||
**虚拟公寓 - 单居室布局**
|
||||
- 客厅:沙发、茶几、电视、书架
|
||||
- 厨房:冰箱、灶台、餐桌
|
||||
- 卧室:床、书桌、衣柜
|
||||
- 卫生间:镜子、洗手台(简化版)
|
||||
|
||||
**视角:** 2.5D俯视角(类似《模拟人生》或《星露谷物语》)
|
||||
|
||||
### 3.2 角色设计(Val)
|
||||
**外观:**
|
||||
- 2D精灵动画:idle、walk、interact、think、sleep
|
||||
- 情绪指示器:头顶气泡或表情符号
|
||||
|
||||
**能力:**
|
||||
- 接收用户指令并执行("去倒杯水"、"看书")
|
||||
- 自主行为(低能量时休息、无聊时走动)
|
||||
- 可视化思考(检索记忆时显示"翻书"动画)
|
||||
|
||||
### 3.3 交互方式
|
||||
- **点击指令**:用户点击物品,Val走过去交互
|
||||
- **语音/文字指令**:直接告诉Val做什么
|
||||
- **观察模式**:Val自主活动,用户观察
|
||||
|
||||
---
|
||||
|
||||
## 4. 状态系统设计
|
||||
|
||||
### 4.1 Agent状态(OpenClaw维护)
|
||||
```json
|
||||
{
|
||||
"agent_id": "val",
|
||||
"location": "living_room",
|
||||
"position": {"x": 120, "y": 200},
|
||||
"activity": "idle",
|
||||
"energy": 80,
|
||||
"mood": "happy",
|
||||
"current_task": null,
|
||||
"memory_access": false
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 状态流转
|
||||
- idle → walk_to → interact → idle
|
||||
- idle → think → [spawn_subagent] → idle
|
||||
- low_energy → find_rest_spot → sleep → recover
|
||||
|
||||
---
|
||||
|
||||
## 5. Bridge 通信协议
|
||||
|
||||
### 5.1 Godot → OpenClaw
|
||||
```json
|
||||
{
|
||||
"type": "user_command",
|
||||
"command": "go_to",
|
||||
"target": "kitchen",
|
||||
"timestamp": "2026-04-13T13:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 OpenClaw → Godot
|
||||
```json
|
||||
{
|
||||
"type": "action_sequence",
|
||||
"actions": [
|
||||
{"cmd": "walk_to", "pos": [200, 150], "duration": 3.0},
|
||||
{"cmd": "play_anim", "anim": "open_fridge", "duration": 1.5},
|
||||
{"cmd": "update_state", "key": "activity", "value": "drinking"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 开发路线图
|
||||
|
||||
### Week 1-2:Godot场景搭建
|
||||
- [ ] 创建2.5D公寓场景
|
||||
- [ ] 制作Val角色精灵和基础动画
|
||||
- [ ] 实现点击移动和碰撞检测
|
||||
|
||||
### Week 3:Bridge层开发
|
||||
- [ ] 设计通信协议
|
||||
- [ ] 实现Godot端WebSocket客户端
|
||||
- [ ] 实现OpenClaw端消息处理器
|
||||
|
||||
### Week 4:Agent行为逻辑
|
||||
- [ ] 实现状态管理系统
|
||||
- [ ] 连接OpenClaw工具调用(spawn、exec)
|
||||
- [ ] 可视化思考过程(动画+气泡)
|
||||
|
||||
### Week 5-6:Polish
|
||||
- [ ] 添加音效和背景音乐
|
||||
- [ ] 实现情绪可视化
|
||||
- [ ] 添加"一天生活"回放功能
|
||||
|
||||
---
|
||||
|
||||
## 7. 扩展性考虑
|
||||
|
||||
### 7.1 多Agent支持(Phase 2)
|
||||
- 添加第二个Agent(如Helix)
|
||||
- 实现Agent间通信和社交
|
||||
- 场景扩展:办公室、咖啡厅
|
||||
|
||||
### 7.2 用户自定义(Phase 3)
|
||||
- 允许用户装修公寓
|
||||
- 自定义Agent外观
|
||||
- 导入自定义Agent personality
|
||||
|
||||
---
|
||||
|
||||
## 8. 技术选型
|
||||
|
||||
| 组件 | 选型 | 理由 |
|
||||
|------|------|------|
|
||||
| 游戏引擎 | Godot 4.x | 开源、轻量、GDScript简单 |
|
||||
| 通信协议 | WebSocket | 双向实时、低延迟 |
|
||||
| 角色动画 | 2D Sprite + AnimationPlayer | 轻量、易制作 |
|
||||
| 后端 | OpenClaw Gateway | 已有Agent生态、SubAgent支持 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 风险与对策
|
||||
|
||||
| 风险 | 对策 |
|
||||
|------|------|
|
||||
| LLM调用成本高 | 本地轻量模型处理简单行为,仅复杂决策调用API |
|
||||
| 状态不同步 | 心跳机制定期同步,差异时以OpenClaw为准 |
|
||||
| 性能问题 | 限制同时活跃Agent数量,LOD简化远处角色 |
|
||||
|
||||
---
|
||||
|
||||
## 10. 成功标准
|
||||
|
||||
- [ ] Val能在公寓中自主移动和交互
|
||||
- [ ] 用户指令能在5秒内得到视觉反馈
|
||||
- [ ] 思考过程有可视化呈现
|
||||
- [ ] 运行稳定,无明显卡顿
|
||||
|
||||
---
|
||||
|
||||
**创建日期:** 2026-04-13
|
||||
**负责人:** 谷老板 + Val
|
||||
**状态:** 设计阶段
|
||||
@@ -0,0 +1,119 @@
|
||||
# Resource Manifest - Godot AI Agent Project
|
||||
> Truth Gate 产物 | 版本:v1 | 日期:2026-04-13
|
||||
|
||||
---
|
||||
|
||||
## Verified(已验证可用)
|
||||
|
||||
### 引擎与核心系统
|
||||
- [x] **Godot 4.x 2D 引擎**
|
||||
- 来源:官方文档 https://docs.godotengine.org/
|
||||
- 验证方式:已创建并运行测试项目
|
||||
- 状态:稳定,支持 CanvasItem 渲染管线
|
||||
|
||||
- [x] **GDScript 脚本系统**
|
||||
- 来源:Godot 内置
|
||||
- 验证方式:已编写 val.gd、apartment.gd 等脚本
|
||||
- 状态:完整支持类、信号、状态机
|
||||
|
||||
- [x] **WebSocketPeer 通信**
|
||||
- 来源:Godot 内置 networking 模块
|
||||
- 验证方式:已实现 bridge_client.gd
|
||||
- 状态:官方支持,无需第三方库
|
||||
|
||||
- [x] **AnimatedSprite2D + SpriteFrames**
|
||||
- 来源:Godot 内置
|
||||
- 验证方式:已在 val.tscn 中配置
|
||||
- 状态:标准 2D 动画方案
|
||||
|
||||
- [x] **NavigationAgent2D 寻路**
|
||||
- 来源:Godot 内置 navigation 系统
|
||||
- 验证方式:已在 val.gd 中集成
|
||||
- 状态:支持 2D 自动避障寻路
|
||||
|
||||
### 开发环境
|
||||
- [x] **Python 3 + websockets 库**
|
||||
- 来源:PyPI
|
||||
- 验证方式:bridge_server.py 已运行
|
||||
- 状态:稳定,支持异步 WebSocket 服务端
|
||||
|
||||
- [x] **本地 HTTP 代理 127.0.0.1:7897**
|
||||
- 来源:ClashX 已配置
|
||||
- 验证方式:已用于 Gmail API 调用
|
||||
- 状态:可用
|
||||
|
||||
---
|
||||
|
||||
## Claimed(声称但未完全验证)
|
||||
|
||||
- [ ] **Godot 4.2 Mobile 渲染器稳定性**
|
||||
- 声称者:项目配置
|
||||
- 验证方法:需要在目标平台(macOS/Windows)长时间运行测试
|
||||
- 风险:低,Mobile 渲染器是 Godot 推荐方案
|
||||
|
||||
- [ ] **WebSocket 高并发稳定性**
|
||||
- 声称者:Python websockets 文档
|
||||
- 验证方法:需要多客户端压力测试
|
||||
- 风险:中低,当前单 Agent 场景负载极低
|
||||
|
||||
- [ ] **2.5D 伪 3D 视觉效果**
|
||||
- 声称者:设计文档
|
||||
- 验证方法:需要美术资源到位后验证
|
||||
- 风险:中,依赖外部资源
|
||||
|
||||
---
|
||||
|
||||
## Assumed(待验证假设)
|
||||
|
||||
- [ ] **用户可同时运行 Godot 编辑器 + Python Bridge + OpenClaw Gateway**
|
||||
- 风险:内存/CPU 占用
|
||||
- 验证方案:在谷老板机器上实测
|
||||
|
||||
- [ ] **2D 精灵资源可由 AI 生成或找到免费素材**
|
||||
- 风险:美术风格不一致
|
||||
- 验证方案:尝试生成/寻找素材
|
||||
|
||||
- [ ] **Godot 项目可导出为独立应用**
|
||||
- 风险:导出配置复杂度
|
||||
- 验证方案:执行一次导出测试
|
||||
|
||||
---
|
||||
|
||||
## 信息缺口
|
||||
|
||||
1. **美术资源来源**:是否需要 AI 生成?购买?还是简化用几何图形?
|
||||
2. **OpenClaw 集成深度**:当前 Bridge 是模拟逻辑,何时接入真实 Agent?
|
||||
3. **目标平台优先级**:macOS 优先?还是跨平台同步?
|
||||
4. **性能基准**:Val 角色的状态机性能要求?
|
||||
|
||||
---
|
||||
|
||||
## 依赖清单汇总
|
||||
|
||||
| 类别 | 项目 | 状态 | 备注 |
|
||||
|------|------|------|------|
|
||||
| 引擎 | Godot 4.2+ | ✅ verified | 已安装并运行 |
|
||||
| 脚本 | GDScript | ✅ verified | 无需额外依赖 |
|
||||
| 网络 | WebSocketPeer | ✅ verified | Godot 内置 |
|
||||
| 后端 | Python 3.9+ | ✅ verified | 已运行 Bridge |
|
||||
| 网络库 | websockets | ✅ verified | PyPI 安装 |
|
||||
| 导航 | NavigationAgent2D | ✅ verified | Godot 内置 |
|
||||
| 动画 | AnimatedSprite2D | ✅ verified | Godot 内置 |
|
||||
| 美术 | 精灵资源 | ⚠️ assumed | 待补充 |
|
||||
| 集成 | OpenClaw Agent | ⚠️ claimed | 当前模拟 |
|
||||
|
||||
---
|
||||
|
||||
## Truth Gate 结论建议
|
||||
|
||||
**技术栈可行性**:✅ 高
|
||||
- 核心引擎、通信、动画系统均已验证
|
||||
- 无高风险技术依赖
|
||||
|
||||
**关键风险**:
|
||||
1. 美术资源(assumed → 需要验证)
|
||||
2. OpenClaw 真实集成(claimed → 需要验证)
|
||||
|
||||
**建议**:
|
||||
- **PASS** Truth Gate,进入设计阶段
|
||||
- 标记美术资源和 OpenClaw 集成作为后续验证点
|
||||
@@ -0,0 +1,204 @@
|
||||
# AgentLiving - 技术规格文档
|
||||
|
||||
## 1. Godot 项目结构
|
||||
|
||||
```
|
||||
ai-agent-living/
|
||||
├── assets/
|
||||
│ ├── sprites/
|
||||
│ │ ├── val/
|
||||
│ │ │ ├── idle.png
|
||||
│ │ │ ├── walk.png
|
||||
│ │ │ └── interact.png
|
||||
│ │ └── furniture/
|
||||
│ │ ├── sofa.png
|
||||
│ │ ├── fridge.png
|
||||
│ │ └── bed.png
|
||||
│ ├── tilesets/
|
||||
│ │ └── apartment_floor.tres
|
||||
│ └── audio/
|
||||
│ └── ambient.mp3
|
||||
├── scenes/
|
||||
│ ├── main.tscn
|
||||
│ ├── apartment.tscn
|
||||
│ ├── val.tscn
|
||||
│ └── furniture/
|
||||
│ ├── sofa.tscn
|
||||
│ └── fridge.tscn
|
||||
├── scripts/
|
||||
│ ├── val.gd
|
||||
│ ├── apartment.gd
|
||||
│ ├── bridge_client.gd
|
||||
│ └── state_manager.gd
|
||||
└── project.godot
|
||||
```
|
||||
|
||||
## 2. 关键类设计
|
||||
|
||||
### 2.1 Val (CharacterBody2D)
|
||||
```gdscript
|
||||
class_name Val
|
||||
extends CharacterBody2D
|
||||
|
||||
@export var speed: float = 100.0
|
||||
@onready var sprite: AnimatedSprite2D = $AnimatedSprite2D
|
||||
@onready var thought_bubble: Label = $ThoughtBubble
|
||||
|
||||
var current_state: String = "idle"
|
||||
var target_position: Vector2 = Vector2.ZERO
|
||||
var agent_id: String = "val"
|
||||
|
||||
func _ready():
|
||||
BridgeClient.connect("action_received", _on_action)
|
||||
_connect_to_openclaw()
|
||||
|
||||
func _physics_process(delta):
|
||||
match current_state:
|
||||
"walk": _handle_walk(delta)
|
||||
"interact": _handle_interact(delta)
|
||||
"think": _handle_think(delta)
|
||||
_: _handle_idle(delta)
|
||||
|
||||
func move_to(pos: Vector2):
|
||||
target_position = pos
|
||||
current_state = "walk"
|
||||
sprite.play("walk")
|
||||
|
||||
func _on_action(action: Dictionary):
|
||||
match action.cmd:
|
||||
"walk_to": move_to(Vector2(action.pos[0], action.pos[1]))
|
||||
"play_anim": sprite.play(action.anim)
|
||||
"show_thought": _show_thought(action.text)
|
||||
"update_state": _update_internal_state(action.key, action.value)
|
||||
```
|
||||
|
||||
### 2.2 BridgeClient (WebSocket)
|
||||
```gdscript
|
||||
extends Node
|
||||
|
||||
signal action_received(action: Dictionary)
|
||||
signal state_synced(state: Dictionary)
|
||||
|
||||
var socket: WebSocketPeer
|
||||
var openclaw_url: String = "ws://127.0.0.1:18789"
|
||||
|
||||
func _ready():
|
||||
_connect()
|
||||
|
||||
func _connect():
|
||||
socket = WebSocketPeer.new()
|
||||
socket.connect_to_url(openclaw_url)
|
||||
|
||||
func _process(delta):
|
||||
socket.poll()
|
||||
var state = socket.get_ready_state()
|
||||
if state == WebSocketPeer.STATE_OPEN:
|
||||
while socket.get_available_packets() > 0:
|
||||
var packet = socket.get_packet()
|
||||
var message = JSON.parse_string(packet.get_string_from_utf8())
|
||||
_handle_message(message)
|
||||
|
||||
func send_command(command: Dictionary):
|
||||
if socket.get_ready_state() == WebSocketPeer.STATE_OPEN:
|
||||
socket.send_text(JSON.stringify(command))
|
||||
|
||||
func _handle_message(msg: Dictionary):
|
||||
match msg.type:
|
||||
"action_sequence":
|
||||
for action in msg.actions:
|
||||
action_received.emit(action)
|
||||
"state_sync":
|
||||
state_synced.emit(msg.state)
|
||||
```
|
||||
|
||||
## 3. OpenClaw端集成
|
||||
|
||||
### 3.1 AgentLiving Skill
|
||||
创建 `~/.openclaw/skills/agent-living/`:
|
||||
|
||||
```
|
||||
agent-living/
|
||||
├── SKILL.md
|
||||
├── scripts/
|
||||
│ └── bridge_server.py
|
||||
└── config.json
|
||||
```
|
||||
|
||||
### 3.2 Bridge Server (Python)
|
||||
```python
|
||||
# bridge_server.py
|
||||
import asyncio
|
||||
import websockets
|
||||
import json
|
||||
from openclaw import session_manager
|
||||
|
||||
class AgentLivingBridge:
|
||||
def __init__(self):
|
||||
self.agents = {}
|
||||
self.godot_clients = {}
|
||||
|
||||
async def handle_godot(self, websocket, path):
|
||||
"""处理Godot客户端连接"""
|
||||
async for message in websocket:
|
||||
data = json.loads(message)
|
||||
response = await self._process_command(data)
|
||||
await websocket.send(json.dumps(response))
|
||||
|
||||
async def _process_command(self, cmd: dict) -> dict:
|
||||
"""将Godot指令转换为Agent动作"""
|
||||
if cmd["type"] == "user_command":
|
||||
# 调用OpenClaw Agent处理
|
||||
agent_session = session_manager.get("val")
|
||||
result = await agent_session.send_message(
|
||||
f"用户在Godot中发出指令:{cmd}"
|
||||
)
|
||||
return self._parse_agent_response(result)
|
||||
|
||||
def _parse_agent_response(self, response: str) -> dict:
|
||||
"""解析Agent回复为Godot动作序列"""
|
||||
# 提取动作序列,格式化为Godot可执行的JSON
|
||||
actions = []
|
||||
# ... 解析逻辑
|
||||
return {"type": "action_sequence", "actions": actions}
|
||||
|
||||
# 启动服务器
|
||||
async def main():
|
||||
bridge = AgentLivingBridge()
|
||||
async with websockets.serve(
|
||||
bridge.handle_godot,
|
||||
"localhost",
|
||||
8765
|
||||
):
|
||||
await asyncio.Future() # 永久运行
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## 4. 状态同步策略
|
||||
|
||||
### 4.1 心跳机制
|
||||
- Godot每5秒发送状态查询
|
||||
- OpenClaw立即返回完整状态
|
||||
- 差异超过阈值时触发同步
|
||||
|
||||
### 4.2 事件驱动
|
||||
- Agent状态变化 → 立即推送到Godot
|
||||
- 用户交互 → 立即发送到Agent
|
||||
- 避免轮询开销
|
||||
|
||||
## 5. 性能优化
|
||||
|
||||
### 5.1 Agent端
|
||||
- 简单行为(走动、idle)用本地规则
|
||||
- 复杂决策(任务分解)才调用LLM
|
||||
- 缓存常用响应
|
||||
|
||||
### 5.2 Godot端
|
||||
- 对象池复用粒子效果
|
||||
- 远处角色降低动画帧率
|
||||
- 按需加载房间(视野外不渲染)
|
||||
|
||||
---
|
||||
|
||||
**下一步:** Week 1 开始Godot场景搭建
|
||||
Vendored
BIN
Binary file not shown.
@@ -0,0 +1 @@
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
[folding]
|
||||
|
||||
node_unfolds=[NodePath("."), PackedStringArray("Transform"), NodePath("Floor"), PackedStringArray("Layout"), NodePath("Rooms/LivingRoom"), PackedStringArray("Transform"), NodePath("Rooms/Kitchen"), PackedStringArray("Transform"), NodePath("Rooms/Bedroom"), PackedStringArray("Transform"), NodePath("Furniture/Sofa"), PackedStringArray("Transform"), NodePath("Furniture/Fridge"), PackedStringArray("Transform"), NodePath("Furniture/Bed"), PackedStringArray("Transform"), NodePath("Furniture/Desk"), PackedStringArray("Transform"), NodePath("Val"), PackedStringArray("Transform")]
|
||||
resource_unfolds=["res://scenes/apartment.tscn::RectangleShape2D_320x240", PackedStringArray("Resource"), "res://scenes/apartment.tscn::RectangleShape2D_96x48", PackedStringArray("Resource"), "res://scenes/apartment.tscn::RectangleShape2D_48x80", PackedStringArray("Resource"), "res://scenes/apartment.tscn::RectangleShape2D_96x64", PackedStringArray("Resource"), "res://scenes/apartment.tscn::RectangleShape2D_80x64", PackedStringArray("Resource")]
|
||||
nodes_folded=[]
|
||||
@@ -0,0 +1,75 @@
|
||||
[docks]
|
||||
|
||||
dock_3_selected_tab_idx=0
|
||||
dock_4_selected_tab_idx=0
|
||||
dock_5_selected_tab_idx=0
|
||||
dock_floating={}
|
||||
dock_closed=[]
|
||||
dock_split_2=0
|
||||
dock_split_3=0
|
||||
dock_hsplit_1=0
|
||||
dock_hsplit_2=280
|
||||
dock_hsplit_3=-280
|
||||
dock_hsplit_4=0
|
||||
dock_9_selected_tab_idx=0
|
||||
dock_3="Scene,Import"
|
||||
dock_4="FileSystem,History"
|
||||
dock_5="Inspector,Signals,Groups"
|
||||
dock_9="Output,Debugger,Audio,Animation,Shader Editor,Search Results,AnimationTree,ResourcePreloader,ShaderFile,SpriteFrames,Theme,Polygon,TileSet,TileMap,Replication,GridMap"
|
||||
|
||||
[docks/FileSystem]
|
||||
|
||||
h_split_offset=480
|
||||
v_split_offset=0
|
||||
display_mode=0
|
||||
file_sort=0
|
||||
file_list_display_mode=1
|
||||
selected_paths=PackedStringArray("res://scenes/main.tscn")
|
||||
uncollapsed_paths=PackedStringArray("Favorites", "res://", "res://scenes/", "res://assets/", "res://assets/sprites/", "res://assets/sprites/val/", "res://assets/sprites/furniture/")
|
||||
|
||||
[docks/History]
|
||||
|
||||
include_scene=true
|
||||
include_global=true
|
||||
|
||||
[EditorNode]
|
||||
|
||||
open_scenes=PackedStringArray()
|
||||
current_scene=""
|
||||
bottom_panel_offsets={
|
||||
"Animation": 0,
|
||||
"Audio": -450,
|
||||
"Debugger": 0,
|
||||
"Output": 0,
|
||||
"Shader Editor": 0
|
||||
}
|
||||
selected_default_debugger_tab_idx=0
|
||||
selected_main_editor_idx=4
|
||||
|
||||
[EditorWindow]
|
||||
|
||||
screen=0
|
||||
mode="windowed"
|
||||
size=Vector2i(2880, 1610)
|
||||
position=Vector2i(0, 50)
|
||||
|
||||
[ScriptEditor]
|
||||
|
||||
open_scripts=[]
|
||||
selected_script=""
|
||||
open_help=[]
|
||||
script_split_offset=400
|
||||
list_split_offset=0
|
||||
zoom_factor=1.0
|
||||
|
||||
[GameView]
|
||||
|
||||
floating_window_rect=Rect2i(790, 348, 1300, 878)
|
||||
floating_window_screen=0
|
||||
|
||||
[ShaderEditor]
|
||||
|
||||
open_shaders=[]
|
||||
split_offset=400
|
||||
selected_shader=""
|
||||
text_shader_zoom_factor=1.0
|
||||
Binary file not shown.
+3
@@ -0,0 +1,3 @@
|
||||
[folding]
|
||||
|
||||
sections_unfolded=PackedStringArray()
|
||||
@@ -0,0 +1,31 @@
|
||||
63f7b34db8d8cdea90c76aacccf841ec
|
||||
::res://::1776185272
|
||||
export_presets.cfg::TextFile::-1::1776075251::0::1::::<><><>0<>0<><>::
|
||||
::res://assets/::1776184578
|
||||
README.md::TextFile::-1::1776058095::0::1::::<><><>0<>0<><>::
|
||||
::res://assets/sprites/::1776184565
|
||||
::res://assets/sprites/furniture/::1776185232
|
||||
bed.svg::CompressedTexture2D::5278534674949913820::1776184565::1776185232::1::::<><><>0<>0<>f072a502c0ec4a388b1d19f93e1b3f8a<>res://.godot/imported/bed.svg-a07b968a692027a8aa2aefa1772c2c93.ctex::
|
||||
chair.svg::CompressedTexture2D::418479712114932479::1776184565::1776185232::1::::<><><>0<>0<>8623a2d50aab116dc297019daf686eea<>res://.godot/imported/chair.svg-515a52e18809bdefa0bd80f22c216048.ctex::
|
||||
desk.svg::CompressedTexture2D::7544982083211081171::1776184565::1776185232::1::::<><><>0<>0<>97178e7768223b144e1188e81f08278f<>res://.godot/imported/desk.svg-2c4f75e1d3b2adb95562b394c4b1afdf.ctex::
|
||||
fridge.svg::CompressedTexture2D::1527439008009270421::1776184565::1776185232::1::::<><><>0<>0<>9bc7081fe9e11f4f5601fe76ec59810a<>res://.godot/imported/fridge.svg-ca2cb042cd2f73a0ad3fb7c2735ad942.ctex::
|
||||
sofa.svg::CompressedTexture2D::6879322377435535170::1776184565::1776185232::1::::<><><>0<>0<>87839a9c9a69be85a049106238281379<>res://.godot/imported/sofa.svg-e4acbfc9b6bb54125b2212c3ec025133.ctex::
|
||||
::res://assets/sprites/val/::1776185853
|
||||
val_idle.png::CompressedTexture2D::4879027226322212573::1776185853::1776185871::0::::<><><>0<>0<>e729f97976b99e2368e07f8193294d34<>::
|
||||
val_idle.svg::CompressedTexture2D::4154530423991166328::1776184533::1776185232::1::::<><><>0<>0<>543e61a8f20bf341ee283d308a2555e0<>res://.godot/imported/val_idle.svg-255592146823edb30ffe5820e35028ad.ctex::
|
||||
val_interact.svg::CompressedTexture2D::3517049902790807024::1776184533::1776185232::1::::<><><>0<>0<>bf9d27a380921cb639490134b5b31830<>res://.godot/imported/val_interact.svg-cad5921112661a2dcc6920061d79aa13.ctex::
|
||||
val_walk.svg::CompressedTexture2D::3552747533911184821::1776184533::1776185232::1::::<><><>0<>0<>d94b1a69f940c5ccbbd5de8e8f28db5c<>res://.godot/imported/val_walk.svg-81e824546797b7f9bc5d8607a57209a3.ctex::
|
||||
::res://assets/tilesets/::1776185232
|
||||
floor.svg::CompressedTexture2D::5553555074695382376::1776184578::1776185232::1::::<><><>0<>0<>a4bc43e192c621f64303b0cb55371b44<>res://.godot/imported/floor.svg-39678b8ce86516d425e3baea01760131.ctex::
|
||||
::res://builds/::1776087645
|
||||
::res://scenes/::1776089789
|
||||
apartment.tscn::PackedScene::4302851944041838120::1776184906::0::1::::<><><>0<>0<><>::res://scripts/apartment.gd<>uid://val1234568aa::::res://scenes/val.tscn<>res://assets/sprites/furniture/sofa.svg<>res://assets/sprites/furniture/fridge.svg<>res://assets/sprites/furniture/bed.svg<>res://assets/sprites/furniture/desk.svg
|
||||
main.tscn::PackedScene::209998081263685520::1776184955::0::1::::<><><>0<>0<><>::res://scripts/main.gd<>res://scripts/bridge_client.gd<>uid://b2kllwl6juoxs::::res://scenes/apartment.tscn
|
||||
val.tscn::PackedScene::1474681496704589948::1776185867::0::1::::<><><>0<>0<><>::res://scripts/val.gd<>res://assets/sprites/val/val_idle.svg<>res://assets/sprites/val/val_walk.svg<>res://assets/sprites/val/val_interact.svg
|
||||
::res://scripts/::1776089743
|
||||
apartment.gd::GDScript::5564681831539226893::1776185053::0::1::::<>Node2D<><>0<>0<><>::
|
||||
apartment_v2.gd::GDScript::1177481778137648876::1776074490::0::1::::<>Node2D<><>0<>0<><>::
|
||||
bridge_client.gd::GDScript::1531877486838200347::1776184985::0::1::::<>Node<><>0<>0<><>::
|
||||
main.gd::GDScript::1574744439970615819::1776185002::0::1::::<>Node2D<><>0<>0<><>::
|
||||
val.gd::GDScript::3837284454343854921::1776185080::0::1::::Val<>CharacterBody2D<><>0<>0<><>::
|
||||
val_v2.gd::GDScript::5072138561996067047::1776074490::0::1::::<>Node<><>0<>0<><>::
|
||||
@@ -0,0 +1,4 @@
|
||||
res://scenes/val.tscn
|
||||
res://scenes/apartment.tscn
|
||||
res://scripts/val.gd
|
||||
res://scripts/bridge_client.gd
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
[folding]
|
||||
|
||||
node_unfolds=[NodePath("."), PackedStringArray("Transform"), NodePath("UI/Panel"), PackedStringArray("Layout"), NodePath("UI/Panel/VBoxContainer"), PackedStringArray("Layout"), NodePath("UI/Panel/VBoxContainer/Label"), PackedStringArray("Layout"), NodePath("UI/Panel/VBoxContainer/HSeparator"), PackedStringArray("Layout"), NodePath("UI/Panel/VBoxContainer/AgentState"), PackedStringArray("Layout"), NodePath("UI/Panel/VBoxContainer/Location"), PackedStringArray("Layout"), NodePath("UI/Panel/VBoxContainer/Energy"), PackedStringArray("Layout"), NodePath("UI/Panel/VBoxContainer/Mood"), PackedStringArray("Layout"), NodePath("UI/Panel/VBoxContainer/HSeparator2"), PackedStringArray("Layout"), NodePath("UI/Panel/VBoxContainer/CommandInput"), PackedStringArray("Layout"), NodePath("UI/Panel/VBoxContainer/SendButton"), PackedStringArray("Layout")]
|
||||
resource_unfolds=[]
|
||||
nodes_folded=[]
|
||||
@@ -0,0 +1,11 @@
|
||||
[game_view]
|
||||
|
||||
select_mode=0
|
||||
|
||||
[editor_metadata]
|
||||
|
||||
executable_path="/Applications/Godot.app/Contents/MacOS/Godot"
|
||||
|
||||
[recent_files]
|
||||
|
||||
scenes=["res://scenes/apartment.tscn", "res://scenes/main.tscn"]
|
||||
@@ -0,0 +1 @@
|
||||
res://scenes/
|
||||
@@ -0,0 +1,9 @@
|
||||
list=[{
|
||||
"base": &"CharacterBody2D",
|
||||
"class": &"Val",
|
||||
"icon": "",
|
||||
"is_abstract": false,
|
||||
"is_tool": false,
|
||||
"language": &"GDScript",
|
||||
"path": "res://scripts/val.gd"
|
||||
}]
|
||||
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
source_md5="a13eaaf5069c9779ff3cf5e5737522f6"
|
||||
dest_md5="568a309a32c64958225a6bde631e287a"
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
source_md5="df687c64443e3f837c212806d0218cdd"
|
||||
dest_md5="38cbbb99624e1a8ee954bd1f95f9dec0"
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
source_md5="45e547a76aed2a454562f9bfa12cacb9"
|
||||
dest_md5="b64c15b61743923eee6a1094283019fa"
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
source_md5="aef8f3e980c0201a3bbb2ff1c1b65e89"
|
||||
dest_md5="736f588ca3203a8a09ed20e5b87e86e2"
|
||||
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
source_md5="f0c9450f376170605cf9825aa52fdbb0"
|
||||
dest_md5="5cb3284f586d6ec37b5924a683657b64"
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
source_md5="e5884331bebc35f627fd42d356605405"
|
||||
dest_md5="85d5eb542b99224c4e4886bd9f1b894e"
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
source_md5="fe7a54fc3fdd0d9639eebbfacae33e16"
|
||||
BIN
Binary file not shown.
+3
@@ -0,0 +1,3 @@
|
||||
source_md5="ef602455923a80a8ae87e0b028d4dee2"
|
||||
dest_md5="edd24c6ff01ad1d2cfe19af6efab0842"
|
||||
|
||||
BIN
Binary file not shown.
+3
@@ -0,0 +1,3 @@
|
||||
source_md5="c065eaf282a69e55d9d7e1a95b5dc395"
|
||||
dest_md5="07c8d5e972fd2d3a41d4a6d06e1e085d"
|
||||
|
||||
BIN
Binary file not shown.
+3
@@ -0,0 +1,3 @@
|
||||
source_md5="35cd240609cfc50725dfead82814bb60"
|
||||
dest_md5="e1dc026bad14b0df462b64c24e1090dc"
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,28 @@
|
||||
# Assets 目录
|
||||
|
||||
## 需要的素材
|
||||
|
||||
### 角色精灵 (sprites/val/)
|
||||
- `val_idle.png` - Val 的待机动画(至少2帧)
|
||||
- `val_walk.png` - Val 的行走动画(至少4帧)
|
||||
- `val_interact.png` - Val 的交互动画
|
||||
|
||||
**建议尺寸:** 32x32 或 64x64 像素
|
||||
|
||||
### 家具精灵 (sprites/furniture/)
|
||||
- `sofa.png` - 沙发
|
||||
- `fridge.png` - 冰箱
|
||||
- `bed.png` - 床
|
||||
- `desk.png` - 书桌
|
||||
|
||||
### 地砖/地板 (tilesets/)
|
||||
- `floor.png` - 公寓地板纹理
|
||||
|
||||
### 字体 (fonts/)
|
||||
- `default.ttf` - 默认中文字体
|
||||
|
||||
## 占位素材
|
||||
|
||||
在正式美术资源到位前,可以用简单几何图形代替:
|
||||
- Val:圆形(头部)+ 矩形(身体)
|
||||
- 家具:对应颜色的矩形
|
||||
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="64" viewBox="0 0 96 64">
|
||||
<!-- 床 placeholder -->
|
||||
<!-- 床垫 -->
|
||||
<rect x="4" y="16" width="88" height="44" rx="4" fill="#F5DEB3"/>
|
||||
<!-- 枕头 -->
|
||||
<rect x="8" y="20" width="20" height="12" rx="3" fill="white"/>
|
||||
<!-- 被子 -->
|
||||
<rect x="32" y="24" width="56" height="32" rx="2" fill="#4169E1"/>
|
||||
<!-- 床架 -->
|
||||
<rect x="0" y="56" width="96" height="8" rx="2" fill="#8B4513"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 460 B |
@@ -0,0 +1,43 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://chg7xvkclu3vs"
|
||||
path="res://.godot/imported/bed.svg-a07b968a692027a8aa2aefa1772c2c93.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/sprites/furniture/bed.svg"
|
||||
dest_files=["res://.godot/imported/bed.svg-a07b968a692027a8aa2aefa1772c2c93.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
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/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
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
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<!-- 椅子 placeholder -->
|
||||
<!-- 座面 -->
|
||||
<rect x="4" y="12" width="24" height="8" rx="2" fill="#A0522D"/>
|
||||
<!-- 靠背 -->
|
||||
<rect x="6" y="0" width="20" height="14" rx="2" fill="#8B4513"/>
|
||||
<!-- 腿 -->
|
||||
<rect x="6" y="20" width="3" height="12" fill="#8B4513"/>
|
||||
<rect x="23" y="20" width="3" height="12" fill="#8B4513"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 427 B |
@@ -0,0 +1,43 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://f7ykgqd2bnm2"
|
||||
path="res://.godot/imported/chair.svg-515a52e18809bdefa0bd80f22c216048.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/sprites/furniture/chair.svg"
|
||||
dest_files=["res://.godot/imported/chair.svg-515a52e18809bdefa0bd80f22c216048.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
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/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
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
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
@@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="64" viewBox="0 0 80 64">
|
||||
<!-- 书桌 placeholder -->
|
||||
<!-- 桌面 -->
|
||||
<rect x="0" y="24" width="80" height="8" rx="2" fill="#8B4513"/>
|
||||
<!-- 抽屉 -->
|
||||
<rect x="8" y="32" width="24" height="20" rx="2" fill="#A0522D"/>
|
||||
<rect x="48" y="32" width="24" height="20" rx="2" fill="#A0522D"/>
|
||||
<!-- 把手 -->
|
||||
<circle cx="20" cy="42" r="2" fill="#666"/>
|
||||
<circle cx="60" cy="42" r="2" fill="#666"/>
|
||||
<!-- 腿 -->
|
||||
<rect x="8" y="52" width="4" height="12" fill="#8B4513"/>
|
||||
<rect x="68" y="52" width="4" height="12" fill="#8B4513"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 607 B |
@@ -0,0 +1,43 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dfq30t8g2rn4n"
|
||||
path="res://.godot/imported/desk.svg-2c4f75e1d3b2adb95562b394c4b1afdf.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/sprites/furniture/desk.svg"
|
||||
dest_files=["res://.godot/imported/desk.svg-2c4f75e1d3b2adb95562b394c4b1afdf.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
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/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
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
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="80" viewBox="0 0 48 80">
|
||||
<!-- 冰箱 placeholder -->
|
||||
<!-- 主体 -->
|
||||
<rect x="4" y="4" width="40" height="72" rx="4" fill="#E8E8E8" stroke="#999" stroke-width="2"/>
|
||||
<!-- 上门 -->
|
||||
<rect x="6" y="6" width="36" height="28" rx="2" fill="#F5F5F5" stroke="#CCC" stroke-width="1"/>
|
||||
<!-- 下门 -->
|
||||
<rect x="6" y="36" width="36" height="38" rx="2" fill="#F5F5F5" stroke="#CCC" stroke-width="1"/>
|
||||
<!-- 把手 -->
|
||||
<rect x="38" y="16" width="3" height="8" rx="1" fill="#666"/>
|
||||
<rect x="38" y="50" width="3" height="10" rx="1" fill="#666"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 617 B |
@@ -0,0 +1,43 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://v05xycxp58cn"
|
||||
path="res://.godot/imported/fridge.svg-ca2cb042cd2f73a0ad3fb7c2735ad942.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/sprites/furniture/fridge.svg"
|
||||
dest_files=["res://.godot/imported/fridge.svg-ca2cb042cd2f73a0ad3fb7c2735ad942.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
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/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
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
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="48" viewBox="0 0 96 48">
|
||||
<!-- 沙发 placeholder -->
|
||||
<!-- 座垫 -->
|
||||
<rect x="4" y="16" width="88" height="28" rx="8" fill="#8B4513"/>
|
||||
<!-- 靠背 -->
|
||||
<rect x="4" y="4" width="88" height="16" rx="6" fill="#A0522D"/>
|
||||
<!-- 扶手 -->
|
||||
<rect x="0" y="12" width="12" height="32" rx="4" fill="#8B4513"/>
|
||||
<rect x="84" y="12" width="12" height="32" rx="4" fill="#8B4513"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 447 B |
@@ -0,0 +1,43 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c5ano1iukdaec"
|
||||
path="res://.godot/imported/sofa.svg-e4acbfc9b6bb54125b2212c3ec025133.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/sprites/furniture/sofa.svg"
|
||||
dest_files=["res://.godot/imported/sofa.svg-e4acbfc9b6bb54125b2212c3ec025133.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
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/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
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
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 708 B |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user