feat(world-sketcher): 新增本地服务器、标签显示和README重写

- 新增 server.py + start.bat: 本地保存/加载服务器
- sketch.html: 切换层级时自动绘制元素标签
- L1 显示区域名称+地形标高,L2 显示建筑名称+高度
- 保存优先POST到服务器,加载优先从output/读取
- 命名弹窗区分'地形标高'和'建筑高度'
- README 全面重写:双模式启动、文件结构、API端点、高度语义
This commit is contained in:
2026-06-10 15:49:12 +08:00
parent b8f27221b2
commit c509223e08
4 changed files with 409 additions and 66 deletions
+130 -56
View File
@@ -3,17 +3,66 @@
> 在场景概念与关卡执行案之间,提供可视化布局沟通工具。
> 用颜色画笔绘制世界布局 → AI 解读 → 双方确认 → 产出执行案。
---
## 快速开始
1. 双击 `sketch.html` 在浏览器中打开(推荐 Chrome / Edge)
2. 左侧设置场景名称和世界尺寸(米)
3. 选择画笔,在画布上绘制
4. **双击** 已绘制区域 → 弹出命名对话框:
### 方式一:本地服务器(推荐)
保存/加载直接读写 `output/` 目录,无需手动下载。
1. 双击 `start.bat`
2. 浏览器自动打开 `http://localhost:8765/sketch.html`
3. 开始绘制,点"保存"直接存入 `output/`
> 需要 Python 3。关闭终端窗口即可停止服务器。
### 方式二:直接打开 HTML
双击 `sketch.html` 在浏览器中打开(推荐 Chrome / Edge)。
- 保存时通过浏览器下载 ZIP 文件
- 加载时通过浏览器文件选择器选择 ZIP
> **注意**:直接打开 HTML 时加载独立的 `.json` 文件**不会恢复画布位图**——位图数据存储在 ZIP 包内的 PNG 文件中,必须加载 `.zip` 文件才能完整恢复。
---
## 文件结构
```
world-sketcher/
├── start.bat ← 启动脚本(双击运行)
├── server.py ← 本地保存/加载服务器
├── sketch.html ← 主工具(单文件,浏览器运行)
├── README.md
└── output/ ← 保存/加载目录
├── 迈皋桥-scene.zip ← 完整场景包(JSON + 所有 PNG)
├── 迈皋桥-scene.json ← 结构化数据(不含位图)
├── 迈皋桥-L1-drawing.png # L1 画布像素数据
├── 迈皋桥-L1-overview-preview.png # L1 合并预览
└── 迈皋桥-L2-*-drawing.png # L2 各区域画布数据
```
---
## 基本操作
1. 左侧设置**场景名称**和**世界尺寸**(米)
2. 选择画笔,在画布上绘制
3. **双击** 已绘制区域 → 弹出命名对话框:
- 输入名称(如"家"、"小卖部")
- 设置**高度**(建筑/区域/不可通行类型时显示,默认 3m)
- 勾选**连通到**其他已命名区域(建立区域间连接关系)
- **L1 区域**:设置**地形标高**(米,可为负值表示洼地)
- **L2 建筑**:设置**建筑高度**(米)
- 勾选**连通到**其他已命名区域(建立连接关系)
- 勾选「可进入」→ 创建下级详图(L1 → L2)
5. 保存 ZIP(含 PNG + JSON)
4. 保存 → 存入 `output/`(服务器模式)或浏览器下载(离线模式)
### 画布标签
- **L1 层**:显示区域名称 + 地形标高(如 `棚户区 (标高2m)`)
- **L2 层**:显示建筑名称 + 建筑高度(如 `棚户1 (3m)`)
- 标高为 0 时不显示高度
---
@@ -30,36 +79,30 @@
- L1 画布上,双击已命名的区域并勾选「可进入」
- 左侧元素列表中点击 **"进入 →"**
- 面包屑导航点击 **"场景总览"** 返回上级
- 返回 L1 时,L2 的绘制内容会自动合成回 L1 对应区域
---
## 画笔图例
| 画笔 | 颜色 | 用途 | 对应 UE 材质 | 默认高度 |
|------|------|------|-------------|---------|
| 道路 Road | 灰 `#909090` | 道路、马路、小路 | MI_DevGray | — |
| 建筑 Building | 橙 `#E07020` | 建筑物 | MI_DevOrange | 3m |
| 水域 Water | 天蓝 `#40A0D0` | 河流、水塘、水体 | MI_DevSkyBlue | — |
| 植被 Greenery | 绿 `#40B040` | 树木、草地、绿化带 | MI_DevGreen | 2m |
| 不可通行 Blocked | 棕 `#A07040` | 山体、围墙、不可达区域 | MI_DevBrown | 3m |
| 区域 Zone | 红 `#D04040` | 区域边界、地块划分 | MI_DevRed | 3m |
| 橡皮擦 Eraser | — | 擦除 | — | — |
**L1 层**主要用:区域、道路、水域
**L2 层**主要用:建筑、道路、植被、水域、不可通行
| 画笔 | 颜色 | 用途 | 对应 UE 材质 | 可用层级 | 高度含义 |
|------|------|------|-------------|---------|---------|
| 道路 Road | 灰 `#909090` | 道路、马路、小路 | MI_DevGray | L1/L2 | — |
| 建筑 Building | 橙 `#E07020` | 建筑物 | MI_DevOrange | L2 | 建筑高度 |
| 水域 Water | 天蓝 `#40A0D0` | 河流、水塘、水体 | MI_DevSkyBlue | L1/L2 | — |
| 植被 Greenery | 绿 `#40B040` | 树木、草地、绿化带 | MI_DevGreen | L2 | 2m |
| 不可通行 Blocked | 棕 `#A07040` | 山体、围墙、不可达区域 | MI_DevBrown | L1/L2 | 3m |
| 区域 Zone | 红 `#D04040` | 区域边界、地块划分 | MI_DevRed | L1 | 地形标高 |
| 橡皮擦 Eraser | — | 擦除 | — | L1/L2 | — |
---
## 绘图工具
| 工具 | 快捷键 | 说明 |
|------|--------|------|
| 画笔 | P | 自由绘制,按住鼠标拖动 |
| 矩形 | R | 绘制矩形区域,拖动定义两角 |
| 橡皮擦 | E | 擦除画布内容 |
## 快捷键
| 操作 | 快捷键 |
|------|--------|
| 画笔工具 | P |
| 橡皮擦工具 | E |
| 撤销 | Ctrl+Z |
| 重做 | Ctrl+Y |
| 网格开关 | G |
@@ -74,57 +117,87 @@
- **单位**:厘米(与 UE5 一致,1 uu = 1 cm)
- 画布顶部 = 世界最大 Y,画布底部 = 世界 Y=0
状态栏实时显示鼠标位置的世界坐标。
状态栏实时显示鼠标位置的像素坐标和世界坐标。
---
## 输出文件
保存全部时生成:
```
<场景名>-L1-overview.png # L1 画布截图
<场景名>-scene.json # 完整场景数据(所有层级)
```
## 输出格式
### scene.json 结构
```json
{
"scene_name": "城郊小街",
"scene_name": "迈皋桥",
"version": "v02",
"created": "2026-06-09T16:46:32",
"origin": "SW corner, X+ East, Y+ North",
"brush_legend": { "色值": { "type": "类型", "mi_material": "MI_Dev*" } },
"brush_legend": {
"#D04040": { "type": "zone", "label": "区域 Zone", "mi_material": "MI_DevRed" }
},
"levels": {
"L1": {
"world_size_m": [500, 400],
"canvas_size_px": [1000, 800],
"scale_m_per_px": 0.5,
"elements": [
{ "id": "el_001", "type": "zone", "name": "家", "height_m": 3,
"connections": ["el_003"], "has_detail": false },
{ "id": "el_002", "type": "zone", "name": "小卖部", "height_m": 3,
"connections": ["el_003"], "has_detail": false },
{ "id": "el_003", "type": "zone", "name": "主街道", "height_m": 3,
"connections": ["el_001", "el_002", "el_004"], "has_detail": true, "detail_ref": "L2-el_003" }
{
"id": "el_001", "type": "zone", "name": "棚户区",
"height_m": 2, "connections": ["el_002"],
"has_detail": true, "detail_ref": "L2-el_001"
}
],
"regions": [
{ "id": "r_001", "type": "zone", "name": "家",
"bounds_world_m": [5.0, 5.0, 12.5, 15.0], "area_px": 15000 }
]
{
"id": "r_001", "type": "zone", "name": "棚户区",
"bounds_world_m": [5.0, 5.0, 120.0, 95.0],
"area_px": 15000
}
],
"image_file": "迈皋桥-L1-drawing.png",
"l1_base_file": null
},
"L2-el_003": {
"L2-el_001": {
"world_size_m": [80, 60],
"elements": [
{ "id": "el_yyy", "type": "building", "name": "家", "height_m": 3, "connections": [], "has_detail": false }
{ "id": "el_010", "type": "building", "name": "棚户1", "height_m": 3 }
],
"image_file": "迈皋桥-L2-el_001-drawing.png",
"l1_base_file": "迈皋桥-L2-el_001-l1base.png",
"parent_level": "L1",
"parent_element": "el_003"
"parent_element": "el_001"
}
}
}
```
### 高度字段语义
| 层级 | 元素类型 | `height_m` 含义 | 用途 |
|------|---------|----------------|------|
| L1 | zone(区域) | **地形标高** | 地面海拔相对高度,0=基准面,负值=洼地 |
| L2 | building(建筑) | **建筑高度** | 建筑物垂直高度 |
| L1/L2 | blocked(不可通行) | **障碍高度** | 围墙/山体等垂直尺度 |
---
## 本地服务器
`server.py` 提供三个 HTTP 端点,供 `sketch.html` 直接读写 `output/` 目录:
| 端点 | 方法 | 说明 |
|------|------|------|
| `/list` | GET | 列出 `output/` 下所有 `.json` / `.zip` 文件 |
| `/load/<filename>` | GET | 从 `output/` 读取指定文件 |
| `/save` | POST (multipart) | 接收文件并保存到 `output/` |
服务器运行时,sketch.html 自动检测并使用服务器模式;未运行时自动回退到浏览器下载/文件选择。
手动启动:
```bash
python server.py # 默认端口 8765
python server.py 9000 # 自定义端口
```
---
## 工作流集成
@@ -145,20 +218,21 @@ AI 布局解读文档 (场景名-layout-vXX.md)
### AI 解读流程
1. 用户在工具中画好布局,命名区域,设置高度和连通关系,保存 ZIP
2. AI 读取 JSON 布局数据(elements + regions + connections + height_m)
1. 用户在工具中画好布局,命名区域,设置标高/高度和连通关系,保存
2. AI 读取 JSON 中的 elements + regions + connections + height_m
3. AI 交叉参考场景设计案的氛围描述,将 JSON 数据与场景概念建立映射
4. AI 产出结构化布局解读 Markdown(区域表、连通关系、高度、待确认问题)
4. AI 产出结构化布局解读 Markdown(区域表、连通关系、标高、待确认问题)
5. 用户确认后,策划基于解读文档产出关卡执行案
> **说明**:PNG 文件保存在 ZIP 中用于画布的保存/加载,AI 分析以 JSON 结构化数据为准,不依赖 PNG 像素分析。
> PNG 文件用于画布的保存/加载和人工预览。AI 分析以 JSON 结构化数据为准,不依赖 PNG 像素分析。
---
## 设计原则
- **强制使用预定义画笔**:无自由拾色器,保证每个像素都映射到已知地形类型
- **结构化数据优先**:高度和连通关系由用户直接输入,AI 无需从图像推断
- **预定义画笔**:无自由拾色器,保证每个像素都映射到已知地形类型
- **结构化数据优先**:标高、高度和连通关系由用户直接输入,AI 无需从图像推断
- **层次化设计**:L1 定大局,L2 定细节,L3 预留
- **人机共读**:PNG 供人视觉确认,JSON 供 AI 和程序化解析
- **与 Level Builder 一致**:画笔颜色直接映射 MI_Dev* 标识色材质
- **材质一致**:画笔颜色直接映射 UE5 的 MI_Dev* 标识色材质
- **离线优先**:单 HTML 文件即可使用,服务器仅为便利增强