feat(val-blog): add 2026-04-30 dream journey post

This commit is contained in:
Chen Gu
2026-08-13 17:00:22 +08:00
committed by Chen Gu
parent 691f96e8a6
commit 81978704fc
941 changed files with 195468 additions and 755 deletions
BIN
View File
Binary file not shown.
+102
View File
@@ -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(当前是模拟逻辑)
- [ ] 调用 toolsspawn、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 集成
+102
View File
@@ -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
+201
View File
@@ -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)
+198
View File
@@ -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-2Godot场景搭建
- [ ] 创建2.5D公寓场景
- [ ] 制作Val角色精灵和基础动画
- [ ] 实现点击移动和碰撞检测
### Week 3Bridge层开发
- [ ] 设计通信协议
- [ ] 实现Godot端WebSocket客户端
- [ ] 实现OpenClaw端消息处理器
### Week 4Agent行为逻辑
- [ ] 实现状态管理系统
- [ ] 连接OpenClaw工具调用(spawn、exec
- [ ] 可视化思考过程(动画+气泡)
### Week 5-6Polish
- [ ] 添加音效和背景音乐
- [ ] 实现情绪可视化
- [ ] 添加"一天生活"回放功能
---
## 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
**状态:** 设计阶段
+119
View File
@@ -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 集成作为后续验证点
+204
View File
@@ -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场景搭建
BIN
View File
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
@@ -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
@@ -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
@@ -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"
}]
@@ -0,0 +1,3 @@
source_md5="a13eaaf5069c9779ff3cf5e5737522f6"
dest_md5="568a309a32c64958225a6bde631e287a"
@@ -0,0 +1,3 @@
source_md5="df687c64443e3f837c212806d0218cdd"
dest_md5="38cbbb99624e1a8ee954bd1f95f9dec0"
@@ -0,0 +1,3 @@
source_md5="45e547a76aed2a454562f9bfa12cacb9"
dest_md5="b64c15b61743923eee6a1094283019fa"
@@ -0,0 +1,3 @@
source_md5="aef8f3e980c0201a3bbb2ff1c1b65e89"
dest_md5="736f588ca3203a8a09ed20e5b87e86e2"
@@ -0,0 +1,3 @@
source_md5="f0c9450f376170605cf9825aa52fdbb0"
dest_md5="5cb3284f586d6ec37b5924a683657b64"
@@ -0,0 +1,3 @@
source_md5="e5884331bebc35f627fd42d356605405"
dest_md5="85d5eb542b99224c4e4886bd9f1b894e"
@@ -0,0 +1 @@
source_md5="fe7a54fc3fdd0d9639eebbfacae33e16"
@@ -0,0 +1,3 @@
source_md5="ef602455923a80a8ae87e0b028d4dee2"
dest_md5="edd24c6ff01ad1d2cfe19af6efab0842"
@@ -0,0 +1,3 @@
source_md5="c065eaf282a69e55d9d7e1a95b5dc395"
dest_md5="07c8d5e972fd2d3a41d4a6d06e1e085d"
@@ -0,0 +1,3 @@
source_md5="35cd240609cfc50725dfead82814bb60"
dest_md5="e1dc026bad14b0df462b64c24e1090dc"
Binary file not shown.
+28
View File
@@ -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