@lark-apaas/coding-steering 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +13 -8
- package/steering/html/skills/rich-interactive-design/SKILL.md +125 -0
- package/steering/html/skills/rich-interactive-design/references/common/color-system.md +123 -0
- package/steering/html/skills/rich-interactive-design/references/common/image-generation.md +43 -0
- package/steering/html/skills/rich-interactive-design/references/tasks/game.md +208 -0
- package/steering/html/skills/rich-interactive-design/references/tasks/interactive-scene.md +273 -0
- package/steering/html/skills/rich-interactive-design/references/tasks/simulation.md +238 -0
package/package.json
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/coding-steering",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Stack-specific steering content for miaoda-coding templates",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"files": [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
},
|
|
6
|
+
"files": [
|
|
7
|
+
"steering"
|
|
8
|
+
],
|
|
10
9
|
"devDependencies": {
|
|
11
10
|
"markdownlint-cli": "^0.47.0"
|
|
12
11
|
},
|
|
@@ -14,6 +13,12 @@
|
|
|
14
13
|
"access": "public",
|
|
15
14
|
"registry": "https://registry.npmjs.org/"
|
|
16
15
|
},
|
|
17
|
-
"keywords": [
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
"keywords": [
|
|
17
|
+
"miaoda",
|
|
18
|
+
"coding-steering"
|
|
19
|
+
],
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"scripts": {
|
|
22
|
+
"lint:md": "markdownlint 'steering/**/*.md'"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rich-interactive-design
|
|
3
|
+
description: Use when generating HTML single-file artifacts for rich 2D/3D interactive experiences—games, real-time simulations, or 3D immersive scenes. 触发词:game, 游戏, 互动玩法, simulation, 模拟, 仿真, 沙盒, 流体, 布料, 粒子, 物理模拟, 元胞自动机, boid, 生态, interactive scene, 3D 场景, 3D 漫游, 沉浸, 虚拟展厅, 产品 3D 展示, Three.js, 虚拟现实, 数字孪生, 富交互.
|
|
4
|
+
hook: SessionStart
|
|
5
|
+
available-agents:
|
|
6
|
+
- Html
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Rich Interactive Design
|
|
10
|
+
|
|
11
|
+
3 类富交互前端任务的统一入口:**游戏 / 实时模拟 / 3D 沉浸场景**。承载任务分类路由、跨任务设计原则底座,以及到 `references/` 子模块的渐进式披露。
|
|
12
|
+
|
|
13
|
+
非富交互类的普通 HTML 任务(落地页、仪表盘、管理后台等)**不在本 skill 范围**——按通用前端设计原则执行,不要硬套本 skill 的方法论。
|
|
14
|
+
|
|
15
|
+
## §1 任务分类
|
|
16
|
+
|
|
17
|
+
动手前先判断任务类型。每个新任务归到下列一类,对应到任务文件。
|
|
18
|
+
|
|
19
|
+
| 分类 | 关键特征 | 对应任务文件 |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| `game` | 游戏 / 互动玩法(**有胜负 / 挑战 / 玩法循环**) | `references/tasks/game.md` |
|
|
22
|
+
| `simulation` | 实时模拟 / 沙盒 / 物理演示(**无胜负,乐趣来自系统行为本身**:流体、布料、粒子、生态、化学、社会行为等) | `references/tasks/simulation.md` |
|
|
23
|
+
| `interactive-scene` | 3D / 沉浸式场景(自然景观、产品展示、虚拟漫游、建筑、3D 数据可视化) | `references/tasks/interactive-scene.md` |
|
|
24
|
+
| `out-of-scope` | 不属于以上 3 类的 HTML 任务(落地页、仪表盘、报告、PPT、管理后台等) | 退出本 skill,按通用前端设计原则执行 |
|
|
25
|
+
|
|
26
|
+
## §2 判定优先级
|
|
27
|
+
|
|
28
|
+
按以下规则自上而下匹配:
|
|
29
|
+
|
|
30
|
+
1. **玩法机制优先**:涉及胜负 / 挑战 / 玩法循环 → `game`(即使是 3D 游戏也归 game,不归 interactive-scene)
|
|
31
|
+
2. **关键词匹配**:
|
|
32
|
+
- "模拟 / 仿真 / 沙盒 / 流体 / 布料 / 粒子 / 物理 / 元胞自动机 / boid / 生态" → `simulation`
|
|
33
|
+
- "3D 场景 / 沉浸 / 漫游 / 展厅 / 产品 3D 展示 / Three.js 场景 / 虚拟现实 / 数字孪生" → `interactive-scene`
|
|
34
|
+
3. **系统行为 vs 视觉空间**:核心是"系统怎么演化" → `simulation`;核心是"场景多美 / 怎么探索" → `interactive-scene`
|
|
35
|
+
4. **都不匹配** → `out-of-scope`,**退出本 skill**,不要给非富交互任务套用本 skill 的反模式与维度框架
|
|
36
|
+
|
|
37
|
+
## §3 跨类任务
|
|
38
|
+
|
|
39
|
+
按主要篇幅/价值选一个主类,主任务 `references/tasks/<task>.md` 必读。次类元素(如游戏里嵌一个 3D 关卡 / 模拟器里嵌挑战目标)在生成对应模块时读对应类任务文件的相关章节。
|
|
40
|
+
|
|
41
|
+
plan 中说明「主类 X,包含 Y 模块」即可。
|
|
42
|
+
|
|
43
|
+
示例:
|
|
44
|
+
|
|
45
|
+
- **3D 山脉场景 + 隐藏地标找寻挑战**:主类 = `interactive-scene`(整体光照、相机、性能按 scene 规范);次类元素 = 计分 / 通关 / 反馈按 `game` §6 难度反馈持久化章节
|
|
46
|
+
- **物理沙盒 + 关卡式挑战目标**(如愤怒的小鸟):主类 = `simulation`(物理积分、时间步长按 sim 规范);次类元素 = 关卡 / 计分按 `game` §2 状态机 + §6 难度章节
|
|
47
|
+
- **3D 流体效果展示**(咖啡拉花、wet paint):主类 = `simulation`(流体算法是核心);次类元素 = 3D 渲染按 `interactive-scene` §4 渲染性能、§5 光照章节
|
|
48
|
+
|
|
49
|
+
## §4 易混类目判定
|
|
50
|
+
|
|
51
|
+
- `game` vs `simulation`:**有胜负 / 挑战目标** → `game`;**无胜负,乐趣来自系统行为本身的可观察 / 可调 / 可创作** → `simulation`。Conway / falling sand 这种无胜负沙盒明确归 `simulation`,不归 `game`
|
|
52
|
+
- `simulation` vs `interactive-scene`:**系统行为本身是核心**(流体演化 / 粒子相互作用 / 物理模拟)→ `simulation`;**视觉与空间体验是核心、系统简单或仅作氛围**(3D 山脉的昼夜变化属于氛围演化)→ `interactive-scene`
|
|
53
|
+
- `interactive-scene` vs `game`(3D 漫游 vs 3D 游戏):纯探索 / 观赏 / 信息展示 → `interactive-scene`;有目标 / 障碍 / 胜负 → `game`
|
|
54
|
+
|
|
55
|
+
判断完后,用人话写进 plan 的任务理解部分(不必报内部标签名)。
|
|
56
|
+
|
|
57
|
+
## §5 设计原则(跨任务底座)
|
|
58
|
+
|
|
59
|
+
通用、跨富交互分类的视觉与交互设计底线。具体物理参数、相机配置、性能预算等下沉到任务分类文件。
|
|
60
|
+
|
|
61
|
+
### 5.1 禁用反模式
|
|
62
|
+
|
|
63
|
+
富交互场景常翻车的"AI 味"模式:
|
|
64
|
+
|
|
65
|
+
- emoji 充当游戏精灵 / 角色 / 敌人 / 食物(🎮🐍🍎),或当 UI 功能图标
|
|
66
|
+
- `alert()` / `confirm()` / `prompt()` 当反馈或弹窗 —— 破坏沉浸,必须用画布内 UI
|
|
67
|
+
- 黑底白字 + Times New Roman 默认样式 —— 一眼"未做设计"
|
|
68
|
+
- 占位图走 placeholder.com / unsplash 等不稳定外链
|
|
69
|
+
- 七彩 / 多主色(一个作品用 > 2 个主色调),缺乏视觉收敛
|
|
70
|
+
- 默认 60fps 假设、`setInterval` 当主循环、不处理 `visibilitychange` 暂停
|
|
71
|
+
- 不区分桌面 / 移动端输入(PC 只 keydown / 移动端只 click)
|
|
72
|
+
- 加载完成无 fade-in 过渡,首帧突现
|
|
73
|
+
- 模拟 / 3D 场景里大面积装饰性紫蓝渐变背景(科幻味的廉价 AI 审美)
|
|
74
|
+
|
|
75
|
+
### 5.2 默认设计倾向
|
|
76
|
+
|
|
77
|
+
无明确指令时按以下方向走:
|
|
78
|
+
|
|
79
|
+
- **配色**:4-6 色 palette 前置定义 + 全程引用常量,禁止散落 magic color;数值场可视化用 viridis / inferno 等色盲友好梯度
|
|
80
|
+
- **字号阶梯**:UI 文字(分数、提示、按钮)字号阶梯清晰,避免 h1/h2/h3 大小近似
|
|
81
|
+
- **间距**:8px grid,禁混用任意值(如 `p-[13px]`)
|
|
82
|
+
- **留白**:UI 元素之间宁多勿少,密集排不出质感
|
|
83
|
+
- **克制**:每个视觉装饰要解决具体问题(反馈玩家行为 / 标识系统状态),不为装饰而装饰
|
|
84
|
+
- **文案**:UI 文字根据场景写真实文案("开始游戏"、"再来一局"、"重置场景"),不要 Lorem ipsum / "Sample Text" / "Button 1"
|
|
85
|
+
|
|
86
|
+
### 5.3 资产策略
|
|
87
|
+
|
|
88
|
+
- **程序化优先**:能用 SVG / Canvas / Three.js Geometry 画的就画,能用 Web Audio API 合成的就合成,不依赖外部资源
|
|
89
|
+
- **GenerateImage 兑底**:复杂角色 sprite、场景背景、风格定调封面才用,产出以 base64 data URL 嵌入;详见各 task 文件
|
|
90
|
+
- **图标**:禁止 emoji 充当功能性图标(导航 / 按钮 / 状态指示),用 Lucide 风格 inline SVG
|
|
91
|
+
- **`<img>` alt**:每个 `<img>` 的 `alt` 必须有意义(描述图片内容),不要空 alt 或填文件名
|
|
92
|
+
- **占位图**:禁用 placeholder.com 等外链占位服务;需要时用纯 CSS 占位框或程序化生成
|
|
93
|
+
|
|
94
|
+
### 5.4 排版纪律(UI 覆盖层适用)
|
|
95
|
+
|
|
96
|
+
UI 覆盖层(HUD、菜单、参数面板)翻车的常见点:
|
|
97
|
+
|
|
98
|
+
- **对齐统一**:HUD 各元素左右内边距走同一基线,避免分数 `px-4`、暂停按钮 `px-2`、生命槽 `px-6` 这种混搭
|
|
99
|
+
- **间距分层**:元素内 padding < 元素间 gap < 区块间 section gap,三层至少 1.5-2 倍差异,UI 才不会糊
|
|
100
|
+
- **主从对比**:关键信息(当前分数 / 主操作按钮 / 关键提示)必须靠尺寸、字重、颜色任一维度突出
|
|
101
|
+
|
|
102
|
+
## §6 渐进式披露指令
|
|
103
|
+
|
|
104
|
+
识别任务类后,按以下顺序加载文档。**必读组合在同一轮 tool call 中并行 Read**,避免串行等待拉长首响。
|
|
105
|
+
|
|
106
|
+
### 6.1 已加载
|
|
107
|
+
|
|
108
|
+
本 SKILL.md §1-§5。
|
|
109
|
+
|
|
110
|
+
### 6.2 必读组合(同一轮 tool call 并行 Read)
|
|
111
|
+
|
|
112
|
+
每类任务的 `references/tasks/<task>.md` 都是必读首步。下表列出还要与之**并行加载**的 common 模块("—" 表示无额外 common,但任务文档本身仍必读):
|
|
113
|
+
|
|
114
|
+
| 任务 | 并行加载的 common 模块 |
|
|
115
|
+
|---|---|
|
|
116
|
+
| `game` | `image-generation.md` |
|
|
117
|
+
| `simulation` | `color-system.md`(数值场色彩映射 / 科学可视化配色) |
|
|
118
|
+
| `interactive-scene` | `color-system.md` + `image-generation.md`(环境/纹理素材按需) |
|
|
119
|
+
|
|
120
|
+
common 模块路径前缀统一为 `references/common/`。
|
|
121
|
+
|
|
122
|
+
### 6.3 按需补读(出现非典型决策时再发起 Read)
|
|
123
|
+
|
|
124
|
+
- 涉及生图 prompt(如 `simulation` 需要科学可视化封面图)→ `references/common/image-generation.md`
|
|
125
|
+
- 涉及配色方法论(如 `game` 走色彩心理学)→ `references/common/color-system.md`
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Color System
|
|
2
|
+
|
|
3
|
+
跨富交互任务(`game` / `simulation` / `interactive-scene`)通用的 HSL 配色方法论:主色推导 / 灰阶派生 / 对比度 / 状态色 / 科学可视化数值映射。各 task 文件 §视觉风格 章节只列**风格特化收紧值**,方法论统一引用本文。
|
|
4
|
+
|
|
5
|
+
## 主色选择
|
|
6
|
+
|
|
7
|
+
**用户已指定主色时,跳过下面的主色推导直接用——本节方法只用于派生灰阶 / accent / 其他 token**。
|
|
8
|
+
|
|
9
|
+
- 用户已指定 → 直接用,按下面方法派生其他 token
|
|
10
|
+
- 没指定 → 按以下三步生成:
|
|
11
|
+
|
|
12
|
+
**1. 色相 H**:从任务推出的调性词匹配下表
|
|
13
|
+
|
|
14
|
+
| 调性方向 | H 范围 |
|
|
15
|
+
|---|---|
|
|
16
|
+
| 信任 / 专业 / 冷静(科学可视化、产品展示、严肃模拟)| 200-230°(蓝)|
|
|
17
|
+
| 自然 / 健康 / 治愈(自然场景、生态模拟、休闲解谜)| 120-160°(绿)|
|
|
18
|
+
| 活力 / 食欲 / 热情(休闲游戏、儿童类、家庭娱乐)| 10-30°(橙红)|
|
|
19
|
+
| 高端 / 神秘 / 文化(沉浸艺术、暗调场景、深度解谜)| 260-300°(紫)|
|
|
20
|
+
| 警示 / 紧迫 / 激情(动作游戏、危机感、危险模拟)| 0-15°(红)|
|
|
21
|
+
| 友好 / 阳光 / 温暖(家庭娱乐、教育模拟、儿童类)| 35-55°(黄橙)|
|
|
22
|
+
|
|
23
|
+
用法:
|
|
24
|
+
|
|
25
|
+
- 用推出的**调性词**匹配最贴近的一行
|
|
26
|
+
- 介于两个调性之间时,H 在两范围之间取值
|
|
27
|
+
- 例:"温暖治愈" → 35-55° 与 120-160° 之间 → 取黄绿 80-110°
|
|
28
|
+
- 表外调性按色彩心理学推导
|
|
29
|
+
- 例:"硬核极简 / 工业极客" → 不限 H,S < 10%(中性单色),靠 L 阶梯做层次
|
|
30
|
+
- 例:"霓虹赛博" → H 280-320°(紫粉)+ S > 80%(高饱和)
|
|
31
|
+
|
|
32
|
+
**2. 饱和度 S** / **3. 亮度 L**:见下方"按风格的 S/L 速查"
|
|
33
|
+
|
|
34
|
+
## 中性灰阶派生
|
|
35
|
+
|
|
36
|
+
**从主色派生灰阶**,避免"主色与灰阶不和谐":
|
|
37
|
+
|
|
38
|
+
- 取主色色相 H,饱和度降到 **5-12%**(带主色相的"暖灰/冷灰")
|
|
39
|
+
- 明度阶梯(亮主题):95% / 90% / 80% / 45% / 15%(对应 bg / surface / border / text-muted / text)
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
主色 hsl(215 65% 48%) → 灰阶 hsl(215 8% 95%) / 90% / 80% / 45% / 15%
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**禁用纯灰**(饱和度 0%)—— 跟有色主色一起使用会显得不和谐。
|
|
46
|
+
|
|
47
|
+
**暗主题**(沉浸场景 / 科学可视化 / 暗调动作游戏常用)灰阶翻转:bg / surface / border / text-muted / text = 12% / 18% / 28% / 65% / 92%,灰阶 S = 5-10% 同样从主色派生。
|
|
48
|
+
|
|
49
|
+
## Accent 选择
|
|
50
|
+
|
|
51
|
+
- **默认**:主色明亮版(同 H、L 提高 15-20%),用于 hover / 高亮 / 关键反馈(命中提示 / 得分 toast)
|
|
52
|
+
- **罕见**:主色补色做次强调(如蓝主色配橙 accent),需刻意设计才用,**避免七彩**
|
|
53
|
+
|
|
54
|
+
## 对比度约束
|
|
55
|
+
|
|
56
|
+
富交互场景的关键文字常常叠在动态背景上,对比要求**普遍高于普通 UI**:
|
|
57
|
+
|
|
58
|
+
- 关键 UI 文字 on bg ≥ **4.5:1**
|
|
59
|
+
- 主操作按钮(白字按钮)≥ **4.5:1**
|
|
60
|
+
- **HUD 数字**(分数 / 生命 / 计时 / FPS)≥ **7:1** —— 动态背景下扫读必须快
|
|
61
|
+
- **关键提示文字 on 动态背景**(游戏内 toast / 警告 / 模拟参数 readout)≥ **7:1**,必要时加 text-shadow / 半透明底纹保证可读性
|
|
62
|
+
- 暗主题下尤其注意:浅灰文字 on 暗灰背景容易掉到 3:1 以下,要主动拉对比
|
|
63
|
+
|
|
64
|
+
## 按风格的 S/L 速查
|
|
65
|
+
|
|
66
|
+
富交互作品的 S/L 范围**更取决于美术风格而非任务类型**。常见风格收紧值:
|
|
67
|
+
|
|
68
|
+
| 风格倾向 | S 范围 | L 范围 | 主题 | 典型适用 |
|
|
69
|
+
|---|---|---|---|---|
|
|
70
|
+
| **写实 PBR**(自然场景、产品展示、写实模拟)| 20-50% | 25-65% | 亮 / 暗 | `interactive-scene` 自然 / 产品;`simulation` 流体 / 写实拟物 |
|
|
71
|
+
| **像素 8-bit / 16-bit** | 60-90% | 40-70% | 亮 | `game` 怀旧 / 街机 / 复古解谜 |
|
|
72
|
+
| **卡通扁平** | 50-75% | 50-70% | 亮 | `game` 休闲 / 家庭娱乐;轻量教学模拟 |
|
|
73
|
+
| **极简几何** | 30-60%(或 < 10% 中性)| 40-60% | 亮 / 暗 | `game` 益智解谜;`simulation` 抽象演化(boid / Conway)|
|
|
74
|
+
| **霓虹赛博** | 80-100% | 45-65% | 暗 | `game` 节奏类 / 跑酷;`interactive-scene` 暗调氛围 |
|
|
75
|
+
| **科学可视化** | 30-50% + 渐变梯度 | 25-55% | 暗 | `simulation` 数值场(温度 / 流速 / 密度)|
|
|
76
|
+
| **抽象艺术 / 生成艺术** | 大胆,无限制 | 大胆,无限制 | 暗为主 | `interactive-scene` 生成艺术;`simulation` 抽象演化 |
|
|
77
|
+
|
|
78
|
+
风格在各 task 文件 §视觉风格 / §视觉表达 章节决定,本表只给收紧基线。
|
|
79
|
+
|
|
80
|
+
## 五类语义色(状态色)
|
|
81
|
+
|
|
82
|
+
主要用于 **UI 覆盖层**(HUD、暂停菜单、参数面板、设置、通知 toast)。主体的成功 / 失败 / 警告反馈**优先用游戏化手段**(震屏、粒子、音效、hitstop、屏闪),状态色 token 只作辅助。
|
|
83
|
+
|
|
84
|
+
| 语义 | 推荐色相 H | 用途(富交互场景)|
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| **success**(通关 / 达标 / 模型稳态)| 绿 130-150° | 通关画面、达标徽章、模拟数值正常区间 |
|
|
87
|
+
| **danger**(失败 / 错误 / 危险)| 红 0-10° | 失败画面、模拟即将发散、危险参数 |
|
|
88
|
+
| **warning**(警示 / 临界)| 橙黄 35-50° | 性能下降、资源即将耗尽、参数靠近临界 |
|
|
89
|
+
| **info**(处理中 / 提示)| 蓝 200-220°(或沿用主色)| 加载中、loading、教程提示 |
|
|
90
|
+
| **muted**(已暂停 / 不可用)| 灰阶 text-muted | 暂停状态、不可用按钮、归档 |
|
|
91
|
+
|
|
92
|
+
S / L 按本文 §主色选择 方法 HSL 化,注册到 `tailwind.config` 的 `success / danger / warning / info / muted` token。
|
|
93
|
+
|
|
94
|
+
### 状态色纪律
|
|
95
|
+
|
|
96
|
+
- **禁**把状态色挪用作普通分类色("敌人类型一绿一红一橙")—— 误导语义
|
|
97
|
+
- **禁**同一状态用两种颜色 —— 全作品状态色一致映射
|
|
98
|
+
- **禁**装饰性渐变 / 阴影叠在状态徽章上 —— 徽章应纯色 + 文字,扫读快
|
|
99
|
+
- 徽章风格择一统一(填充 / outline),**同一作品不混用**
|
|
100
|
+
|
|
101
|
+
## 科学可视化数值映射(`simulation` 专用)
|
|
102
|
+
|
|
103
|
+
模拟类常需把**数值场**(温度 / 速度 / 密度 / 高度 / 浓度)映射到颜色:
|
|
104
|
+
|
|
105
|
+
| 映射类型 | 推荐 |
|
|
106
|
+
|---|---|
|
|
107
|
+
| 单调数值(温度 / 高度 / 密度 / 速度大小)| **viridis** / **inferno** / **magma**(色盲友好,科学可视化标配)|
|
|
108
|
+
| 方向 / 相位(旋度场 / 流向 / 朝向)| **HSL 色相环**(H 0-360° 映射方向,S/L 固定)|
|
|
109
|
+
| 二极对比(电荷 / 引力符号 / 偏离均值)| 蓝-白-红 diverging(冷 / 中性 / 暖)|
|
|
110
|
+
| 离散类别(粒子类型 / 物种 / 元素种类)| 5 色定性色板(Tableau 10 / Set2)|
|
|
111
|
+
|
|
112
|
+
**禁用红绿映射数值**(色盲不友好 + 审美差)。
|
|
113
|
+
|
|
114
|
+
viridis / inferno 等可在 fragment shader 内手写多项式近似(搜 "viridis polynomial GLSL"),不必引入额外库。
|
|
115
|
+
|
|
116
|
+
## Token 集
|
|
117
|
+
|
|
118
|
+
主任务通常需要的 token 集(注入 `tailwind.config`):
|
|
119
|
+
|
|
120
|
+
- **基础**:`bg / surface / text / text-muted / primary / accent / border`
|
|
121
|
+
- **状态**:`success / danger / warning / info / muted`(仅 UI 覆盖层用)
|
|
122
|
+
|
|
123
|
+
具体任务的 token 取舍详见各 task 文件。
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Image Generation
|
|
2
|
+
|
|
3
|
+
游戏 / 模拟 / 沉浸场景的 `GenerateImage` 用法。**优先程序化生成**(SVG / Canvas / Three.js Geometry / shader 等,详见各 task 文件),本文聚焦确实需要调 GenerateImage 时的 prompt 公式与开关。
|
|
4
|
+
|
|
5
|
+
## prompt 构造公式
|
|
6
|
+
|
|
7
|
+
按以下公式分段构造,每段从本任务推导:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
[主体描述] + [风格词] + [构图] + [配色/光线] + 通用尾部
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| 部分 | 推导依据 | 示例 |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| 主体 | 任务的"内容"维度 | "8-bit pixel game character"、"cyberpunk neon city skyline"、"3D mountain landscape at dawn"、"abstract fluid simulation cover" |
|
|
16
|
+
| 风格词 | 各 task 文件 §视觉风格 已推 | "pixel 16-bit"、"flat cartoon"、"minimalist geometric"、"neon cyber"、"photorealistic PBR" |
|
|
17
|
+
| 构图 | 用途决定 | sprite → "centered, transparent background";场景 → "wide cinematic angle";封面 → "16:9 hero composition";seamless 纹理 → "seamless tileable" |
|
|
18
|
+
| 配色 / 光线 | 主色 + 风格派生 | "warm earth tones"、"high contrast neon"、"soft natural lighting"、"dramatic rim light"、"HDR studio lighting" |
|
|
19
|
+
|
|
20
|
+
通用尾部**按用途选一种**:
|
|
21
|
+
|
|
22
|
+
- Sprite / 角色 / 物件:`flat icon, transparent background, game asset, no text overlay, no UI mockup`
|
|
23
|
+
- 场景 / 背景 / 封面:`16:9, high resolution, no text overlay, no UI mockup, no AI watermark`
|
|
24
|
+
- Seamless 纹理:`seamless tileable pattern, no obvious seams, high resolution`
|
|
25
|
+
|
|
26
|
+
## 各任务生图开关
|
|
27
|
+
|
|
28
|
+
| 任务 | 默认 GenerateImage | 适用场景 | 风格倾向 |
|
|
29
|
+
|---|---|---|---|
|
|
30
|
+
| `game` | **主动调** | sprite / 角色 / 敌人 / 道具 / 关卡背景 / 封面 | 跟随 game.md §5 推出的风格词,使用 game asset 通用尾部;详细公式见 game.md §5 |
|
|
31
|
+
| `interactive-scene` | **按需** | 环境 HDR 风格图 / 关键背景 / 产品场景照 / 抽象艺术封面 | 跟随 interactive-scene.md §7 推出的风格词;写实场景倾向 PBR / studio / 自然光关键词 |
|
|
32
|
+
| `simulation` | **极少**(默认全程式化)| 仅"科学可视化的封面图" / 抽象艺术化模拟的视觉调性参考 | 抽象克制,**禁营销词**("震撼"/"未来感"/"活力"),用 "scientific visualization"、"abstract data art"、"minimal generative" 等 |
|
|
33
|
+
|
|
34
|
+
## 风格一致性纪律
|
|
35
|
+
|
|
36
|
+
同一作品多次调用 GenerateImage 时(如游戏的多个角色、场景的多张参考图),prompt 内**必须包含相同的风格描述词**(如统一带 "pixel 16-bit, warm earth tones"),避免画风分裂。
|
|
37
|
+
|
|
38
|
+
## 通用禁令
|
|
39
|
+
|
|
40
|
+
- `<img>` alt 必须有意义,描述图片内容,禁空 alt / 文件名
|
|
41
|
+
- 禁用 placeholder.com / unsplash 等外链占位服务
|
|
42
|
+
- 生成结果以 base64 data URL 嵌入 HTML,**禁外链**(详见各 task 文件资产策略)
|
|
43
|
+
- 不要在生成尾部加 "epic / cinematic / masterpiece" 等大词,产出反而失控(详见 game.md "生图 prompt 写'epic game hero cinematic scene'——产出电影海报而非可用素材" 反模式)
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Game Design
|
|
2
|
+
|
|
3
|
+
HTML 单文件游戏 / 互动玩法的专属设计决策。
|
|
4
|
+
|
|
5
|
+
## Quick Reference
|
|
6
|
+
|
|
7
|
+
| 决策 | 默认 | 例外 |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| 类型识别 | 见 §1 三维度推导 | 用户已指定具体玩法 → 直接尊重 |
|
|
10
|
+
| 渲染层 | 见 §3 决策树 | 用户指定 Canvas / DOM → 尊重 |
|
|
11
|
+
| 状态机 | 4 态必有:menu / playing / paused / over | 回合驱动游戏可省 paused |
|
|
12
|
+
| "再来一局" | **必须 reset 全部状态**,禁 `location.reload()` | — |
|
|
13
|
+
| 输入支持 | 桌面键鼠 + 移动 touch **必须同时** | 用户明确单端时尊重 |
|
|
14
|
+
| 关键反馈延迟 | ≤ 100ms | — |
|
|
15
|
+
| 最高分 | 持久化到 localStorage | 用户明确不需要 |
|
|
16
|
+
| 素材 | 主动调 `GenerateImage` 产精灵 / 背景 / UI | 用户已提供 / 玩法不需要素材 |
|
|
17
|
+
|
|
18
|
+
## 1. 游戏类型识别
|
|
19
|
+
|
|
20
|
+
从用户描述推三个维度,每次得出**一句话定位**写进 plan:
|
|
21
|
+
|
|
22
|
+
| 维度 | 取值 |
|
|
23
|
+
|---|---|
|
|
24
|
+
| 实时性 | 实时连续帧(每秒重绘)/ 回合驱动(仅在用户输入后更新) |
|
|
25
|
+
| 状态离散度 | 离散格子(棋盘/网格/卡牌)/ 连续物理(位置 / 速度浮点)/ 表单选择(题目 / 选项) |
|
|
26
|
+
| 主输入通道 | 方向键 WASD / 单击 / 拖拽 / 滑动手势 / 鼠标移动追踪 / 选择题点击 |
|
|
27
|
+
|
|
28
|
+
输出例:
|
|
29
|
+
|
|
30
|
+
- "回合驱动 + 离散网格 + 滑动手势 → 合并解谜类(2048)"
|
|
31
|
+
- "实时连续 + 连续物理 + 方向键 → 横版动作 / 跑酷"
|
|
32
|
+
- "回合驱动 + 表单选择 + 点击 → 多步答题 / 剧情分支"
|
|
33
|
+
- "实时连续 + 离散网格 + 单击 → 打地鼠 / 反应速度类"
|
|
34
|
+
|
|
35
|
+
这三个维度直接决定 §3 渲染层、§4 输入、§5 视觉。
|
|
36
|
+
|
|
37
|
+
**禁止组合**(违反认知):益智解谜 × 抖屏闪光 / 儿童益智 × 暗黑血腥 / 慢节奏阅读 × 高刺激粒子 / 单功能反应训练 × 复杂剧情。
|
|
38
|
+
|
|
39
|
+
## 2. 核心循环与状态机
|
|
40
|
+
|
|
41
|
+
### 4 态状态机(强制)
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
menu ──start──▶ playing ──pause──▶ paused
|
|
45
|
+
│ ◀──resume──┘
|
|
46
|
+
│
|
|
47
|
+
└──lose/win──▶ over ──restart──▶ playing
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
每个状态都要有清晰的回退路径,禁"卡死"无出口的中间态。回合驱动游戏可省 paused(玩家自然停手即是暂停)。
|
|
51
|
+
|
|
52
|
+
### 核心循环契约
|
|
53
|
+
|
|
54
|
+
- **反馈窗口**:用户输入到屏幕产生**任何**视觉变化 ≤ 100ms。粒子 / 数字弹出 / 缩放 / 颜色脉冲任选一种作为"打击感"
|
|
55
|
+
- **"再来一局"必须 reset 整局状态**:分数清零、棋盘清空、计时器重置。**禁用** `location.reload()` 当重启——交互契约要求按钮真实工作,而非粗暴刷新页面
|
|
56
|
+
- **暂停态**:实时类游戏必须暂停时停止主循环(`cancelAnimationFrame`),不是把 dt 设 0 继续空转;回合类不需要暂停
|
|
57
|
+
- **结束态**:必须有 Game Over 面板(本局分数 + 最高分 + 再来一局按钮),**禁用** `alert()` / `confirm()` 报分或问"是否重玩"
|
|
58
|
+
|
|
59
|
+
### 主循环写法
|
|
60
|
+
|
|
61
|
+
实时类用 `requestAnimationFrame`,**禁** `setInterval` 当主循环(与刷新率失同步 / Tab 失焦累积)。回合类无需主循环,事件驱动即可——不要为了"像游戏"硬塞 rAF。
|
|
62
|
+
|
|
63
|
+
## 3. 渲染选择决策树
|
|
64
|
+
|
|
65
|
+
按三问决定渲染层,**不要默认 Canvas**:
|
|
66
|
+
|
|
67
|
+
1. **状态是否离散网格?** 是 → DOM
|
|
68
|
+
2. **是否需要每帧重绘连续动画?** 是 → Canvas
|
|
69
|
+
3. **是否纯静态形状 + 简单过渡?** 是 → SVG / CSS 动画即可
|
|
70
|
+
|
|
71
|
+
| 类型推导(§1) | 推荐层 |
|
|
72
|
+
|---|---|
|
|
73
|
+
| 回合 + 离散网格(2048 / 三消 / 推箱子 / 卡牌) | **DOM 网格** |
|
|
74
|
+
| 实时 + 连续物理(跑酷 / 打砖块 / 弹幕) | **Canvas 2D** |
|
|
75
|
+
| 实时 + 离散网格(打地鼠) | **DOM**;动画密集才 Canvas |
|
|
76
|
+
| 回合 + 表单选择(答题 / 剧情) | **DOM 表单 + 状态** |
|
|
77
|
+
| 静态几何 + 过渡(拼图慢动作) | **SVG** 或 CSS |
|
|
78
|
+
|
|
79
|
+
DOM 网格常见误区:用 `position: absolute` 配像素 left/top 排格子。改用 **CSS Grid**(`grid-template-columns: repeat(N, 1fr)`),每格是 grid item,合并/移动用 `transform` + `transition`。
|
|
80
|
+
|
|
81
|
+
## 4. 双端输入设计
|
|
82
|
+
|
|
83
|
+
HTML 单文件不能预设设备,桌面键鼠 + 移动 touch **必须同时**支持,**除非**用户明确说"只做桌面 / 只做移动"。
|
|
84
|
+
|
|
85
|
+
### 输入通道映射
|
|
86
|
+
|
|
87
|
+
| 主输入(§1 推出) | 桌面绑定 | 移动绑定 |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| 方向键 / WASD | `keydown` | 屏幕方向虚拟键 **或** 画布 swipe 手势 |
|
|
90
|
+
| 单击 / 选择题 | `click` | `click`(浏览器自动合成) |
|
|
91
|
+
| 拖拽 | `mousedown/move/up` | `touchstart/move/end`(或 Pointer Events 统一) |
|
|
92
|
+
| 滑动手势(2048 类) | 方向键 `keydown` + 画布 swipe | 画布 swipe |
|
|
93
|
+
| 鼠标移动追踪 | `mousemove` | 退化为 `touchmove` 拖拽 |
|
|
94
|
+
|
|
95
|
+
优先 **Pointer Events** 统一两端,减少分支。
|
|
96
|
+
|
|
97
|
+
### 移动端额外约束
|
|
98
|
+
|
|
99
|
+
- 游戏容器加 `user-select: none; touch-action: none;`——防止长按选中、防止 swipe 触发页面滚动
|
|
100
|
+
- 顶部留出 safe area:`padding-top: env(safe-area-inset-top)`
|
|
101
|
+
- 移动端字号 ≥ 14px,按钮触控区 ≥ 44 × 44px
|
|
102
|
+
|
|
103
|
+
## 5. 视觉风格与游戏素材生图
|
|
104
|
+
|
|
105
|
+
### 风格选择(从 §1 类型推一句话风格词)
|
|
106
|
+
|
|
107
|
+
常见方向(任选其一,**不要混风格**):
|
|
108
|
+
|
|
109
|
+
- 像素 8-bit / 16-bit(怀旧街机 / 复古解谜)
|
|
110
|
+
- 卡通扁平(休闲 / 家庭娱乐)
|
|
111
|
+
- 极简几何(益智解谜 / 抽象类)
|
|
112
|
+
- 霓虹未来(赛博跑酷 / 节奏类)
|
|
113
|
+
- 手绘水彩 / 涂鸦(绘本 / 儿童 / 剧情)
|
|
114
|
+
|
|
115
|
+
风格词写进 plan 一行,后续配色、字体、生图、动效都依据这条决策。
|
|
116
|
+
|
|
117
|
+
### 游戏素材 prompt 公式(本任务特有,与 `references/common/image-generation.md` 通用公式不同)
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
[游戏元素] + [素材类型] + [风格词] + [构图/视角] + [配色] + flat icon, transparent background, game asset, no text overlay
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
游戏要的是 **game sprite / tile / character icon**,构图是"中央元素 + 透明背景",**不是**电影级场景。
|
|
124
|
+
|
|
125
|
+
| 部分 | 推导依据 | 示例 |
|
|
126
|
+
|---|---|---|
|
|
127
|
+
| 游戏元素 | 玩法本身 | "数字方块 2/4/8"、"卡通蛇头"、"打砖块块"、"答题角色头像" |
|
|
128
|
+
| 素材类型 | 用途 | "game tile sprite"、"character avatar"、"background tile"、"icon button" |
|
|
129
|
+
| 风格词 | 上面已推 | "pixel 16-bit"、"flat cartoon"、"minimalist geometric"、"neon cyber" |
|
|
130
|
+
| 构图 | 素材常用中央透明 | "centered icon, transparent background" / "seamless pattern"(背景平铺) |
|
|
131
|
+
| 配色 | 跟主题色一致 | "warm earth tones"、"high contrast neon" |
|
|
132
|
+
|
|
133
|
+
通用尾部固定追加:`flat icon, transparent background, game asset, no text overlay, no UI mockup`。
|
|
134
|
+
|
|
135
|
+
仅需要场景背景大图(非精灵)时才用 16:9 横构图。
|
|
136
|
+
|
|
137
|
+
## 6. 难度 / 反馈 / 持久化
|
|
138
|
+
|
|
139
|
+
### 难度曲线
|
|
140
|
+
|
|
141
|
+
- **第一局别 3 秒死**:起手难度必须让玩家拿到 ≥ 1 次反馈循环(≥ 1 个合并、≥ 1 次得分、≥ 1 关通过)才"开始有挑战"
|
|
142
|
+
- **进度感**至少满足一种:得分递增 / 速度递增 / 关卡推进 / 等级提升 / 棋盘扩大
|
|
143
|
+
- 无限模式:动态调难度(每 N 分加速 / 每 N 关增加敌人)。有限关卡:内嵌数据,关卡 ≥ 5
|
|
144
|
+
|
|
145
|
+
### 反馈强度(HTML 单文件无音频默认 → 视觉是核心)
|
|
146
|
+
|
|
147
|
+
每次有效操作至少触发一种视觉反馈:
|
|
148
|
+
|
|
149
|
+
- 数字/分数变化用**动画**(弹出、缩放、上浮消失)而非瞬移
|
|
150
|
+
- 关键状态变更(合并 / 命中 / 击破 / 答对)用粒子或闪光,1-2 帧高亮
|
|
151
|
+
- 错误反馈用**抖动**(CSS keyframes `translate: ±4px`),不要全屏颜色闪烁
|
|
152
|
+
- 屏幕震动只对**游戏容器**做 `transform: translate()` 关键帧,**不要**改 body margin / scroll 位置
|
|
153
|
+
|
|
154
|
+
`alert()` / `confirm()` / `prompt()` **全程禁用**——破坏沉浸。所有提示用游戏内 UI 面板。
|
|
155
|
+
|
|
156
|
+
### 持久化
|
|
157
|
+
|
|
158
|
+
- 最高分 `localStorage.setItem('<game>_best', score)`,开局读出来在 HUD 显示
|
|
159
|
+
- 当前局进度不必持久化(关 Tab = 重玩)
|
|
160
|
+
- localStorage 读写包 `try/catch`(隐私模式可能抛错)
|
|
161
|
+
|
|
162
|
+
## 游戏特有反模式
|
|
163
|
+
|
|
164
|
+
- Game Over 后只能 `location.reload()`,"再来一局"按钮实际是刷新页面
|
|
165
|
+
- 用 `alert("得分:" + score)` 报分 / 用 `confirm()` 问"是否再来一局"
|
|
166
|
+
- 移动端长按游戏元素弹出系统菜单(缺 `user-select: none` / `touch-action: none`)
|
|
167
|
+
- 实时类游戏用 `setInterval(loop, 16)` 当主循环
|
|
168
|
+
- emoji(🎮🐍🍎🍪)当游戏精灵 / 角色 / 敌人 / 食物
|
|
169
|
+
- 棋盘 / 格子用 `position: absolute` + 像素 left/top,而非 CSS Grid
|
|
170
|
+
- 暂停按钮存在但无 handler,或暂停后主循环继续空转
|
|
171
|
+
- 单一难度——从开局到第 100 分速度毫无变化
|
|
172
|
+
- 移动端用 `keydown` / `keypress` 检测输入(移动端无键盘)
|
|
173
|
+
- 生图 prompt 写"epic game hero cinematic scene"——产出电影海报而非可用素材
|
|
174
|
+
|
|
175
|
+
## Worked Example
|
|
176
|
+
|
|
177
|
+
> 用户输入:"做一个 2048 风格的合并解谜小游戏,要好看。"
|
|
178
|
+
>
|
|
179
|
+
> **§1 类型识别**:回合驱动 + 离散 4×4 网格 + 滑动手势 → 「回合制 / 离散网格 / 滑动 → 合并解谜类」
|
|
180
|
+
>
|
|
181
|
+
> **§2 状态机**:menu(开始按钮 + 最高分)→ playing(4×4 网格 + 当前分 + 最高分 HUD)→ over(棋盘满且无可合并触发)→ restart 回 playing。无 paused(回合驱动不需要)。
|
|
182
|
+
>
|
|
183
|
+
> **§3 渲染层**:离散网格 + 回合驱动 → **DOM 网格**:
|
|
184
|
+
>
|
|
185
|
+
> - CSS Grid `grid-template-columns: repeat(4, 1fr)`,每数字格是 grid item
|
|
186
|
+
> - 合并动画 `transform: scale(1.15) → scale(1)` + `transition: 120ms`
|
|
187
|
+
> - 新格出现 `scale(0) → scale(1)` 弹出
|
|
188
|
+
> - 不上 Canvas
|
|
189
|
+
>
|
|
190
|
+
> **§4 双端输入**:
|
|
191
|
+
>
|
|
192
|
+
> - 桌面:`keydown` 绑 4 方向键 + WASD
|
|
193
|
+
> - 移动:游戏容器监听 `touchstart/touchend`,按 dx/dy 判 4 方向 swipe
|
|
194
|
+
> - 容器加 `user-select: none; touch-action: none;`
|
|
195
|
+
>
|
|
196
|
+
> **§5 视觉风格**:「极简几何 + 暖色阶」(解谜类合适极简)
|
|
197
|
+
>
|
|
198
|
+
> - 主色按配色规则推:H ≈ 30° 暖橙,S 65% L 55%;灰阶从 30° 派生
|
|
199
|
+
> - HUD 字体:`font-family: 'Press Start 2P', monospace, sans-serif`
|
|
200
|
+
> - **不调 GenerateImage**——2048 没有精灵需求,纯数字 + 色块就够
|
|
201
|
+
>
|
|
202
|
+
> **§6 难度 / 反馈 / 持久化**:
|
|
203
|
+
>
|
|
204
|
+
> - 难度:自然进阶(合并越大越难凑)
|
|
205
|
+
> - 反馈:合并时格子 `scale` 弹动 + 数字上浮 `+N`;新格 `scale` 弹出;首次 2048 出现时全屏闪光 + 提示面板
|
|
206
|
+
> - 持久化:`localStorage` 存 `best`,开局读
|
|
207
|
+
>
|
|
208
|
+
> **不要照抄此例**——同样需求另一次推可能选「卡通扁平 + 蓝绿主色」或「霓虹暗色 + Canvas 走粒子合并」,都是合理的。按 §1-§6 维度从本任务重新推导。
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# Interactive Scene Design
|
|
2
|
+
|
|
3
|
+
HTML 单文件 3D / 沉浸式场景类的专属设计决策。覆盖自然场景(山脉、海洋、星空)、产品展示(360° 旋转预览)、虚拟漫游(建筑、虚拟展厅)、抽象空间(数据结构 3D 可视化、概念演示)等"用户探索 + 美学体验"为主的场景。
|
|
4
|
+
|
|
5
|
+
与 `simulation` 的本质区别:**简单系统下的强视觉/强空间感**,而非"模拟系统行为"。与 `game` 的本质区别:**没有挑战与胜负**。
|
|
6
|
+
|
|
7
|
+
## Quick Reference
|
|
8
|
+
|
|
9
|
+
| 决策 | 默认 | 例外 |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| 技术栈 | **Three.js** | 用户明确要 Babylon / WebGPU 时尊重 |
|
|
12
|
+
| 相机控制 | OrbitControls + 阻尼 + 移动端 touch 支持 | 第一人称漫游 → PointerLockControls;纯展示无交互 → 仅自动旋转 |
|
|
13
|
+
| 加载体验 | **必有 loading 进度**(黑/纯色屏 + 进度数字),禁直接白屏到首帧 | 加载 < 200ms 可省 |
|
|
14
|
+
| 光照基础 | 至少 1 直射 + 1 环境,禁纯 ambient 全亮 | 卡通/扁平风格可调整 |
|
|
15
|
+
| 材质 | **MeshStandardMaterial**(PBR) 默认,不要 MeshBasicMaterial 当主材质 | 程式化/极简风格除外 |
|
|
16
|
+
| 性能预算 | Draw calls ≤ 100;总顶点 ≤ 500k;纹理总和 ≤ 32MB | 移动端各砍半 |
|
|
17
|
+
| 资源释放 | 切换 / 销毁场景必须 `geometry.dispose()` + `material.dispose()` + `texture.dispose()` | — |
|
|
18
|
+
| Pixel Ratio | `min(devicePixelRatio, 2)`,不能直接用 dpr | — |
|
|
19
|
+
| 移动端 | `touch-action: none` + 双指缩放手势 + 减面减分辨率 | — |
|
|
20
|
+
|
|
21
|
+
## 1. 场景类型识别
|
|
22
|
+
|
|
23
|
+
从用户描述推三个维度,得出**一句话定位**写进 plan:
|
|
24
|
+
|
|
25
|
+
| 维度 | 取值 |
|
|
26
|
+
|---|---|
|
|
27
|
+
| 场景内容 | 自然(地形/天空/海/植物)/ 产品(单物体展示/对比)/ 建筑空间(漫游/导览)/ 抽象(数据/概念可视化)/ 抽象艺术(生成几何/参数化) |
|
|
28
|
+
| 用户角色 | 观看(仅看自动巡游)/ 操控相机(轨道旋转、缩放、平移)/ 漫游(第一人称走动)/ 探索(点击热点、模式切换、对比) |
|
|
29
|
+
| 时间维度 | 静态(无演化)/ 周期演化(昼夜、季节、潮汐自动循环)/ 用户驱动演化(拨滑块控制时间) |
|
|
30
|
+
|
|
31
|
+
输出例:
|
|
32
|
+
|
|
33
|
+
- "自然 + 操控相机 + 周期演化 → 3D 自然景观(山脉昼夜、海洋日落)"
|
|
34
|
+
- "产品 + 操控相机 + 静态 → 商品 3D 展示(鞋、车、家具)"
|
|
35
|
+
- "建筑空间 + 漫游 + 静态 → 虚拟展厅 / 房屋导览"
|
|
36
|
+
- "抽象 + 探索 + 静态 → 3D 数据可视化(节点图、力导向)"
|
|
37
|
+
- "抽象艺术 + 观看 + 周期演化 → 生成艺术展示"
|
|
38
|
+
|
|
39
|
+
这三维度直接决定 §3 相机方案、§5 光照与时间、§6 交互层。
|
|
40
|
+
|
|
41
|
+
**禁止组合**(违反认知):商品展示 × 阴森暗光 / 自然景观 × 霓虹紫粉 / 严肃数据可视化 × 卡通材质 / 建筑漫游 × 强烈鱼眼变形。
|
|
42
|
+
|
|
43
|
+
## 2. 资源加载与首屏
|
|
44
|
+
|
|
45
|
+
3D 场景的杀手用户体验 = **白屏 → 突然出现完整场景**。必须有过渡:
|
|
46
|
+
|
|
47
|
+
### 加载状态(必备)
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
const loadingManager = new THREE.LoadingManager(
|
|
51
|
+
() => fadeOutLoadingScreen(), // 全部加载完
|
|
52
|
+
(url, loaded, total) => updateProgress(loaded / total) // 进度更新
|
|
53
|
+
);
|
|
54
|
+
const textureLoader = new THREE.TextureLoader(loadingManager);
|
|
55
|
+
const gltfLoader = new GLTFLoader(loadingManager);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
页面打开 → 全屏纯色 + 进度条/数字 → 加载完 fade out → 场景渐现(0.5s)。**禁止**首帧白屏到画面突现。
|
|
59
|
+
|
|
60
|
+
### 资源策略
|
|
61
|
+
|
|
62
|
+
- 模型:`.glb` 二进制格式(不要 `.gltf` + 分离的 bin),DRACO 压缩可选
|
|
63
|
+
- 纹理:`.webp` 优先,回退 `.jpg`;HDR 环境贴图用 `.hdr` 或 RGBM `.png`
|
|
64
|
+
- 资源体积控制:单纹理 ≤ 4MB,总场景 ≤ 32MB(移动端 ≤ 16MB)
|
|
65
|
+
- CDN 路径:自由选用 jsDelivr / Three.js 官方 examples,禁占位图外链
|
|
66
|
+
|
|
67
|
+
## 3. 相机控制
|
|
68
|
+
|
|
69
|
+
按用户角色(§1)选控制器:
|
|
70
|
+
|
|
71
|
+
| 用户角色 | 控制器 | 关键配置 |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| 观看 | 无控制器 + 自动 `orbit` 缓慢自转 | rotation per frame ≈ 0.001 rad |
|
|
74
|
+
| 操控相机 | **OrbitControls** | `enableDamping = true`、`dampingFactor = 0.05`、限制 polarAngle 防止穿地 |
|
|
75
|
+
| 漫游 | PointerLockControls + WASD + 重力 | 桌面专属;移动端退化为虚拟摇杆 |
|
|
76
|
+
| 探索 | OrbitControls + 点击切视角(tween 过渡) | 视角切换手写 lerp + easing 函数(如 `easeInOutCubic`),禁瞬移; |
|
|
77
|
+
|
|
78
|
+
### 必备约束
|
|
79
|
+
|
|
80
|
+
- **限制角度**:地面场景 polarAngle ∈ [10°, 80°],禁止看到地面以下
|
|
81
|
+
- **限制距离**:min/max distance 防止穿物体或飞到太空
|
|
82
|
+
- **阻尼**:`enableDamping` 让操作有惯性感,不要瞬停
|
|
83
|
+
- **重置视角按钮**:用户飞偏了能一键回到初始视角,tween 0.6s
|
|
84
|
+
|
|
85
|
+
### 移动端
|
|
86
|
+
|
|
87
|
+
- `touch-action: none`(防 swipe 触发页面滚动)
|
|
88
|
+
- 双指缩放 + 单指拖拽旋转 + 双指拖拽平移(OrbitControls 默认支持)
|
|
89
|
+
- 灵敏度移动端略低于桌面(避免误触)
|
|
90
|
+
|
|
91
|
+
## 4. 渲染与性能
|
|
92
|
+
|
|
93
|
+
### Pixel Ratio
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
不要 `setPixelRatio(window.devicePixelRatio)`——4K 屏 dpr = 2 但有些手机 dpr = 3 或 4,纯按 dpr 渲染量爆炸卡死。
|
|
100
|
+
|
|
101
|
+
### Draw Call 预算
|
|
102
|
+
|
|
103
|
+
- 桌面 ≤ 100,移动端 ≤ 50
|
|
104
|
+
- 多个相同物体用 **InstancedMesh** 合并(一次 draw call 渲染 N 个)
|
|
105
|
+
- 静态场景用 **BufferGeometryUtils.mergeBufferGeometries** 合并相邻 mesh
|
|
106
|
+
|
|
107
|
+
### LOD(细节层次)
|
|
108
|
+
|
|
109
|
+
镜头远了切低面模型,近了切高面:
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
const lod = new THREE.LOD();
|
|
113
|
+
lod.addLevel(highMesh, 0);
|
|
114
|
+
lod.addLevel(midMesh, 50);
|
|
115
|
+
lod.addLevel(lowMesh, 200);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
地形/植被类场景必用 LOD,否则远处大片高面物体把性能拖垮。
|
|
119
|
+
|
|
120
|
+
### 移动端优化
|
|
121
|
+
|
|
122
|
+
- 减半渲染分辨率:`renderer.setSize(w * 0.7, h * 0.7)` + CSS 拉伸
|
|
123
|
+
- 关闭阴影或用低分辨率阴影(`shadow.mapSize = 512`)
|
|
124
|
+
- 减少粒子数 / 关闭高级后期(bloom、SSAO)
|
|
125
|
+
|
|
126
|
+
### 资源释放(切场景 / 销毁必做)
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
function disposeScene(scene) {
|
|
130
|
+
scene.traverse((obj) => {
|
|
131
|
+
if (obj.geometry) obj.geometry.dispose();
|
|
132
|
+
if (obj.material) {
|
|
133
|
+
const m = obj.material;
|
|
134
|
+
Object.values(m).forEach(v => v?.isTexture && v.dispose());
|
|
135
|
+
m.dispose();
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
不 dispose 切场景一次内存涨几十 MB,第三次切就 crash。
|
|
142
|
+
|
|
143
|
+
## 5. 光照与时间演化
|
|
144
|
+
|
|
145
|
+
### 光照基础组合
|
|
146
|
+
|
|
147
|
+
| 场景类型 | 推荐光照 |
|
|
148
|
+
|---|---|
|
|
149
|
+
| 室外自然 | DirectionalLight(太阳)+ HemisphereLight(天空 + 地面色) |
|
|
150
|
+
| 室内/产品 | 3 点布光:主光(DirectionalLight)+ 辅光(DirectionalLight 弱)+ 边缘光 |
|
|
151
|
+
| 程式化/极简 | AmbientLight + 1 DirectionalLight |
|
|
152
|
+
| 写实 | EnvironmentMap(HDR)+ DirectionalLight 阳光,PMREMGenerator 预处理 |
|
|
153
|
+
|
|
154
|
+
**禁止**:纯 AmbientLight 全亮(场景扁平无立体);强度全开(>2 的 intensity 让金属材质曝白)
|
|
155
|
+
|
|
156
|
+
### 昼夜 / 周期演化
|
|
157
|
+
|
|
158
|
+
时间演化场景(§1 时间维度 = 周期演化 / 用户驱动)必做:
|
|
159
|
+
|
|
160
|
+
- 太阳位置随时间在天穹上扫弧
|
|
161
|
+
- 太阳颜色:日出/日落 `0xffaa44` 暖橙 → 正午 `0xfff5e0` 暖白 → 夜晚 `0x223366` 冷蓝
|
|
162
|
+
- 天空颜色用 `Sky` shader(Three.js examples 自带)或自实现 fragment shader 上下渐变
|
|
163
|
+
- 环境光强度随昼夜变化(夜晚 0.2,正午 0.6)
|
|
164
|
+
- 用户驱动:滑块 ∈ [0, 24],实时更新太阳与光色
|
|
165
|
+
|
|
166
|
+
## 6. 交互层与模式切换
|
|
167
|
+
|
|
168
|
+
3D 场景"沉浸"之上还得"可探索"。常用交互模式:
|
|
169
|
+
|
|
170
|
+
### 热点 / 信息点
|
|
171
|
+
|
|
172
|
+
- 用 `Sprite` + 透明纹理或 HTML overlay(CSS `position: absolute` + WebGL 坐标 → 屏幕坐标投影)
|
|
173
|
+
- 点击触发面板弹出 / tween 相机到该位置
|
|
174
|
+
- 移动端用 `touchend`,不用 `click`(300ms 延迟会让用户觉得卡)
|
|
175
|
+
|
|
176
|
+
### 模式切换(如等高线 / 线框 / 透明)
|
|
177
|
+
|
|
178
|
+
- 切换按钮浮在右上角,托盘式 UI
|
|
179
|
+
- 等高线:自实现 fragment shader 用 `fract(vWorldPosition.y * lineFreq)` 阈值判定,比加 mesh 高效
|
|
180
|
+
- 线框:`material.wireframe = true`(最简但低质感),或自实现 fragment shader 画边
|
|
181
|
+
- 透明 / X 光:调 `material.opacity` + `transparent: true` + `depthWrite: false`
|
|
182
|
+
|
|
183
|
+
### Raycaster 点击拾取
|
|
184
|
+
|
|
185
|
+
```js
|
|
186
|
+
const raycaster = new THREE.Raycaster();
|
|
187
|
+
raycaster.setFromCamera(pointer, camera);
|
|
188
|
+
const hits = raycaster.intersectObjects(targets, false);
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
`targets` 显式列出可点物体,**不要** `intersectObjects(scene.children, true)`——递归遍历整个场景每帧爆炸。
|
|
192
|
+
|
|
193
|
+
### 自动巡游 / 相机动画
|
|
194
|
+
|
|
195
|
+
观看类场景默认应该有"惊艳首屏"动画:开局相机从远处缓慢推近 + 轻微环绕,2-3 秒后停止让用户接管。
|
|
196
|
+
|
|
197
|
+
## 7. 视觉风格
|
|
198
|
+
|
|
199
|
+
### 风格倾向
|
|
200
|
+
|
|
201
|
+
| 场景类型 | 推荐风格 |
|
|
202
|
+
|---|---|
|
|
203
|
+
| 自然景观 | 写实 PBR + HDR 环境光 + 真实大气散射 |
|
|
204
|
+
| 产品展示 | 干净 studio 灯光(HDR studio.hdr)+ 浅灰/纯色背景 + 柔和阴影 |
|
|
205
|
+
| 建筑漫游 | 写实 PBR + 真实材质(混凝土、玻璃、木)+ 局部强调灯 |
|
|
206
|
+
| 抽象数据 | 暗底 + 高饱和发光(emissive + bloom 后期) |
|
|
207
|
+
| 生成艺术 | 极简几何 + 程式化材质 + 大胆配色 |
|
|
208
|
+
|
|
209
|
+
### 后期处理(按需)
|
|
210
|
+
|
|
211
|
+
`EffectComposer` + `UnrealBloomPass`(发光)/ `SMAAPass`(抗锯齿)/ `SSAOPass`(环境光遮蔽)
|
|
212
|
+
|
|
213
|
+
不要默认全开后期——移动端会爆。只在视觉受益大的场景加(如夜景发光、暗调氛围)。
|
|
214
|
+
|
|
215
|
+
## 场景特有反模式
|
|
216
|
+
|
|
217
|
+
- **白屏到首帧突现**——没有 loading 进度
|
|
218
|
+
- **纯 AmbientLight 全亮**——画面扁平如纸片
|
|
219
|
+
- **MeshBasicMaterial 当主材质**——无光照响应,看着像未渲染
|
|
220
|
+
- **不 dispose**——切场景内存爆炸,几次后 crash
|
|
221
|
+
- **`setPixelRatio(devicePixelRatio)`**——高 dpr 手机直接卡死
|
|
222
|
+
- **OrbitControls 不限制 polarAngle**——用户翻到地下看到悬空模型
|
|
223
|
+
- **Raycaster 每帧 `intersectObjects(scene.children, true)`**——递归全场景 60 次/秒
|
|
224
|
+
- **render loop 里 `new THREE.Vector3()`**——GC 风暴
|
|
225
|
+
- **移动端不 `touch-action: none`**——拖拽场景同时滚动页面
|
|
226
|
+
- **桌面 / 移动同一套灵敏度**——手机操作太灵或太钝
|
|
227
|
+
- **加载完场景突现无过渡**——0 帧 fade in 突兀
|
|
228
|
+
- **太阳颜色一日不变**——黄昏还是白光太阳,假
|
|
229
|
+
- **后期 bloom 强度 > 2**——所有亮处都晕成一团白
|
|
230
|
+
|
|
231
|
+
## Worked Example
|
|
232
|
+
|
|
233
|
+
> 用户输入:"3D HTML 山脉场景,含悬崖、河流、昼夜光照变化。支持拖动缩放、动画过渡、真实感渐变色,可切换等高线显示。"
|
|
234
|
+
>
|
|
235
|
+
> **§1 类型识别**:自然 + 操控相机 + 周期演化 + 探索(等高线切换)→ 「3D 自然景观 + 时间演化 + 模式切换」
|
|
236
|
+
>
|
|
237
|
+
> **§2 加载**:LoadingManager 接 TextureLoader(地形 heightmap、岩石/草地纹理、HDR 天空);纯黑屏 + 进度数字 → 加载完 fade out 0.5s → 场景 fade in
|
|
238
|
+
>
|
|
239
|
+
> **§3 相机**:OrbitControls,dampingFactor 0.05;polarAngle ∈ [10°, 80°] 防穿地;min/max distance 限制;右上角"重置视角"按钮 tween 0.6s
|
|
240
|
+
>
|
|
241
|
+
> **§4 性能**:
|
|
242
|
+
>
|
|
243
|
+
> - 地形:PlaneGeometry 256×256 段,displacement 用 heightmap 纹理(不用 vertex 数组手动算)
|
|
244
|
+
> - LOD:远景地形 64×64 段切换
|
|
245
|
+
> - InstancedMesh:树木 / 岩石散布 500 个,单 draw call
|
|
246
|
+
> - PixelRatio `min(dpr, 2)`;移动端 `setSize(w * 0.7, h * 0.7)` + CSS 拉伸
|
|
247
|
+
> - 阴影:DirectionalLight casts shadow,`shadow.mapSize = 1024`(移动端 512)
|
|
248
|
+
>
|
|
249
|
+
> **§5 光照与时间**:
|
|
250
|
+
>
|
|
251
|
+
> - DirectionalLight 太阳 + HemisphereLight 天空-地面光
|
|
252
|
+
> - Sky shader(Three.js examples)+ Sun position 联动
|
|
253
|
+
> - 滑块 0-24 控制时间:日出 (5h) 暖橙 → 正午 (12h) 暖白 → 黄昏 (18h) 深橙 → 夜 (22h) 冷蓝 + 月光弱直射
|
|
254
|
+
> - 自动循环模式:勾选后时间自动以 1 分钟/天 流逝
|
|
255
|
+
>
|
|
256
|
+
> **§6 交互层**:
|
|
257
|
+
>
|
|
258
|
+
> - 模式切换托盘(右上角):默认渲染 / 等高线 / 线框
|
|
259
|
+
> - 等高线模式:fragment shader 用 `step(0.05, fract(vWorldPosition.y * 0.1))` 画等高线 + 高度颜色映射(viridis)
|
|
260
|
+
> - 河流:PlaneGeometry + 水 shader(动画 uv 偏移 + 法线扰动 + 透明 + 反射环境)
|
|
261
|
+
> - 拖拽 / 缩放:OrbitControls 自动支持桌面 + 移动 touch
|
|
262
|
+
>
|
|
263
|
+
> **§7 视觉**:写实 PBR + HDR 环境光(开源 venice_sunset.hdr 或类似);地形材质用 splat map(草 / 岩 / 雪按高度混合)
|
|
264
|
+
>
|
|
265
|
+
> **反模式提醒**:
|
|
266
|
+
>
|
|
267
|
+
> - 必须 `min(dpr, 2)` 不能直接 dpr
|
|
268
|
+
> - 必须 dispose geometry/material/texture(虽然这是单场景,但树木 InstancedMesh 需要正确释放)
|
|
269
|
+
> - Raycaster 不要每帧全场景遍历——本例没有点击拾取,可省
|
|
270
|
+
> - 太阳颜色不能恒定,必须随时间变
|
|
271
|
+
> - 必须有 loading 进度
|
|
272
|
+
>
|
|
273
|
+
> **不要照抄此例**——同样需求换成"低多边形风格 + 程式化生成地形(noise)"也合理。按 §1-§7 维度从本任务重新推导。
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# Simulation Design
|
|
2
|
+
|
|
3
|
+
HTML 单文件实时模拟 / 沙盒 / 物理演示类的专属设计决策。覆盖流体、布料、粒子、生态、化学、电路、社会行为等"用户观察 + 调参 + 体验系统演化"的场景。
|
|
4
|
+
|
|
5
|
+
与 `game` 的本质区别:**没有胜负目标,乐趣来自系统行为本身的可观察性、可调性、可创作性**。
|
|
6
|
+
|
|
7
|
+
## Quick Reference
|
|
8
|
+
|
|
9
|
+
| 决策 | 默认 | 例外 |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| 类型识别 | 见 §1 三维度推导 | 用户已指定具体模拟物 → 直接尊重 |
|
|
12
|
+
| 时间步长 | **固定 dt(如 1/60s)+ 帧间多步细分**,禁止用真实帧 dt 直接积分 | 纯展示无物理时可放宽 |
|
|
13
|
+
| dt 上限 | **强制夹紧到 ≤ 50ms**,防止 tab 切回时一次性追赶炸毁 | — |
|
|
14
|
+
| 主循环暂停 | `visibilitychange` 切走必须停 `cancelAnimationFrame` | — |
|
|
15
|
+
| 渲染层 | 见 §4 决策树 | 用户指定 Canvas/WebGL/Three → 尊重 |
|
|
16
|
+
| 参数面板 | **必有**,关键参数实时可调 + 预设档位 + 重置按钮 | 演示类极简可省,但至少保留重置 |
|
|
17
|
+
| 性能预算 | Canvas 2D 粒子 ≤ 5000;WebGL/Three ≤ 100k | 用户明确低端机适配则各砍半 |
|
|
18
|
+
| FPS 显示 | 调试期内嵌(角落小字),release 可去 | — |
|
|
19
|
+
| 用户创作 | 若涉及绘制/雕刻,必须支持撤销 / 清空 / 截图保存 | 纯观察类不需要 |
|
|
20
|
+
|
|
21
|
+
## 1. 模拟类型识别
|
|
22
|
+
|
|
23
|
+
从用户描述推三个维度,得出**一句话定位**写进 plan:
|
|
24
|
+
|
|
25
|
+
| 维度 | 取值 |
|
|
26
|
+
|---|---|
|
|
27
|
+
| 系统介质 | 连续场(流体/电磁/温度)/ 离散粒子(沙、雪、雨)/ 刚体(碰撞、堆叠)/ 软体(布料、绳索、果冻)/ 离散网格演化(Conway / 元胞自动机)/ 行为体(boid / 蚁群 / 捕食) |
|
|
28
|
+
| 用户角色 | 旁观(看演化)/ 调参(拨滑块改变行为)/ 创作(拖拽 / 喷射 / 雕刻输入物质)/ 干预(点击施加力 / 删除粒子) |
|
|
29
|
+
| 视觉维度 | 2D 平面(俯视/侧视/截面)/ 3D 场景 / 2D + 伪 3D(等距视图 + 深度排序) |
|
|
30
|
+
|
|
31
|
+
输出例:
|
|
32
|
+
|
|
33
|
+
- "连续场 + 创作 + 3D 场景 → 流体绘画类(咖啡拉花、wet paint)"
|
|
34
|
+
- "离散粒子 + 干预 + 2D 截面 → 沙池模拟(falling sand)"
|
|
35
|
+
- "刚体 + 创作 + 2D 平面 → 物理积木(box2d 演示)"
|
|
36
|
+
- "行为体 + 旁观/调参 + 2D 俯视 → 群体行为(boid 鸟群)"
|
|
37
|
+
- "离散网格演化 + 旁观/调参 + 2D 平面 → Conway / 元胞自动机"
|
|
38
|
+
|
|
39
|
+
这三维度直接决定 §3 时间步长策略、§4 渲染层、§5 交互形态。
|
|
40
|
+
|
|
41
|
+
## 2. 时间步长与数值稳定性
|
|
42
|
+
|
|
43
|
+
**模拟类的头号杀手是 dt 失控**。必须按以下契约写主循环:
|
|
44
|
+
|
|
45
|
+
### 固定 dt + 帧间细分(推荐范式)
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
const FIXED_DT = 1 / 60; // 物理步长,跟显示无关
|
|
49
|
+
let accumulator = 0;
|
|
50
|
+
let lastTime = performance.now();
|
|
51
|
+
|
|
52
|
+
function loop(now) {
|
|
53
|
+
let frameDt = (now - lastTime) / 1000;
|
|
54
|
+
lastTime = now;
|
|
55
|
+
if (frameDt > 0.05) frameDt = 0.05; // 强制夹紧上限,防 tab 切回炸毁
|
|
56
|
+
accumulator += frameDt;
|
|
57
|
+
while (accumulator >= FIXED_DT) {
|
|
58
|
+
simulate(FIXED_DT);
|
|
59
|
+
accumulator -= FIXED_DT;
|
|
60
|
+
}
|
|
61
|
+
render(accumulator / FIXED_DT); // 插值因子供渲染层做平滑
|
|
62
|
+
requestAnimationFrame(loop);
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 数值积分选型
|
|
67
|
+
|
|
68
|
+
| 场景 | 积分方法 |
|
|
69
|
+
|---|---|
|
|
70
|
+
| 简单粒子、boid、低精度 | Explicit Euler(最简单,可能漂移) |
|
|
71
|
+
| 弹簧、布料、软体 | Verlet(位置式,能量保持好) |
|
|
72
|
+
| 高精度刚体 / 轨道 | RK4 或子步细分 Euler |
|
|
73
|
+
| 流体 / Navier-Stokes | Semi-Lagrangian 或 Stable Fluids(Jos Stam 范式) |
|
|
74
|
+
|
|
75
|
+
不要默认 Euler——布料和流体用 Euler 会"炸"(数值发散)。
|
|
76
|
+
|
|
77
|
+
### 暂停 / 失焦
|
|
78
|
+
|
|
79
|
+
- 切 tab:`document.visibilitychange` → 切走 `cancelAnimationFrame` + 记录暂停时刻,切回不要补帧
|
|
80
|
+
- 显式 pause 按钮:与失焦走同一暂停状态,恢复时重置 `lastTime = performance.now()` 防 dt 跳变
|
|
81
|
+
|
|
82
|
+
## 3. 性能预算与优化
|
|
83
|
+
|
|
84
|
+
### 默认上限
|
|
85
|
+
|
|
86
|
+
| 渲染层 | 粒子/单元上限 | 备注 |
|
|
87
|
+
|---|---|---|
|
|
88
|
+
| Canvas 2D `fillRect` 逐粒子 | ~5000 | 用 `ImageData` 直绘像素可到 10w+ |
|
|
89
|
+
| Canvas 2D `arc` 逐粒子 | ~2000 | 圆形开销大于矩形 |
|
|
90
|
+
| WebGL points/instanced | 100k+ | 唯一可行的高粒子方案 |
|
|
91
|
+
| Three.js InstancedMesh | 50k+ | 单 draw call |
|
|
92
|
+
| DOM 网格(CSS Grid) | ~2500(50×50) | 元素数超过 5000 浏览器吃力 |
|
|
93
|
+
|
|
94
|
+
### 必备优化
|
|
95
|
+
|
|
96
|
+
- **Spatial partitioning**:邻域查询走网格哈希或四叉树,禁止 O(n²) 两两遍历(超过 500 粒子直接卡)
|
|
97
|
+
- **对象池**:粒子频繁生成销毁时复用对象,避免 GC 风暴
|
|
98
|
+
- **render loop 不分配**:禁止在每帧 `new Vector3()` / `new Array()`,全程复用 scratch 对象
|
|
99
|
+
- **典型反例**:`particles.forEach(p => particles.forEach(q => { /* 算距离 */ }))` — 200 粒子还行,2000 直接炸
|
|
100
|
+
- **OffscreenCanvas + Worker**:超大粒子或重计算(流体场)跑 worker,主线程只 blit
|
|
101
|
+
|
|
102
|
+
## 4. 渲染层决策树
|
|
103
|
+
|
|
104
|
+
按四问决定渲染层:
|
|
105
|
+
|
|
106
|
+
1. **是否需要 3D 视角 / 光影 / 相机切换?** 是 → **Three.js**
|
|
107
|
+
2. **是否粒子/单元数 > 5000?** 是 → **WebGL(自写 shader 或 Three.js InstancedMesh)**
|
|
108
|
+
3. **是否离散网格 ≤ 50×50 且需要交互单元格?** 是 → **DOM Grid**
|
|
109
|
+
4. **其他情况** → **Canvas 2D**
|
|
110
|
+
|
|
111
|
+
| 类型推导(§1) | 推荐层 |
|
|
112
|
+
|---|---|
|
|
113
|
+
| 连续场流体 + 2D | Canvas 2D(双 buffer 直绘 ImageData) |
|
|
114
|
+
| 连续场流体 + 3D(如咖啡拉花) | Three.js(surface mesh + shader uv 扰动)或 WebGL |
|
|
115
|
+
| 离散粒子(≤ 5k) | Canvas 2D |
|
|
116
|
+
| 离散粒子(> 5k) | WebGL points |
|
|
117
|
+
| 刚体物理 | 简单场景(Pong / Breakout / 弹球 / 单体重力)原生自实现;复杂场景(多体堆叠 / Joint / 约束 / 完整 2D 物理沙盒)→ **Matter.js**;3D 物理需求 → Rapier(需说明理由) |
|
|
118
|
+
| 离散网格演化(Conway) | Canvas 2D 直绘像素 |
|
|
119
|
+
| 行为体(boid) | Canvas 2D |
|
|
120
|
+
|
|
121
|
+
### 常见库
|
|
122
|
+
|
|
123
|
+
- **2D 刚体物理**:
|
|
124
|
+
- 简单场景(AABB 碰撞、弹簧、单体重力、Verlet 粒子)→ **原生 JS 自实现**,更轻、依赖少
|
|
125
|
+
- 复杂场景(多体堆叠、Joint、约束、完整物理沙盒)→ **Matter.js**
|
|
126
|
+
- **3D 物理**:Rapier,**不在默认白名单**,需在 plan 中说明理由后引入
|
|
127
|
+
- **流体**:建议自实现 Stable Fluids(短小);复杂的用 GPGPU + GLSL
|
|
128
|
+
- **3D 渲染**:Three.js
|
|
129
|
+
|
|
130
|
+
## 5. 用户交互与参数面板
|
|
131
|
+
|
|
132
|
+
模拟类的"可玩性"基本全在交互上。**至少满足两件事:参数实时可调 + 用户能产生影响**。
|
|
133
|
+
|
|
134
|
+
### 参数面板(必备)
|
|
135
|
+
|
|
136
|
+
- 关键参数 2-6 个用滑块暴露(如重力、粘度、扩散率、密度、温度)
|
|
137
|
+
- **预设档位**至少 3 个(如"稳态 / 失稳 / 极端"),点击切换给玩家一个"哇"的入口
|
|
138
|
+
- 重置按钮:清空场到初始态
|
|
139
|
+
- 不用 `dat.GUI` 等老库——用原生 `<input type="range">` + Tailwind 自己写,更轻
|
|
140
|
+
- 移动端折叠:默认收起,点击展开抽屉
|
|
141
|
+
|
|
142
|
+
### 用户输入映射
|
|
143
|
+
|
|
144
|
+
| 用户角色(§1) | 输入设计 |
|
|
145
|
+
|---|---|
|
|
146
|
+
| 旁观 | 仅参数面板 + 暂停/重置 |
|
|
147
|
+
| 调参 | 参数面板 + 实时反馈(数值变更立刻可见) |
|
|
148
|
+
| 创作 | Pointer drag 喷射 / 雕刻 + 撤销 / 清空 / 截图保存 |
|
|
149
|
+
| 干预 | 点击施加冲击波 / 删除局部 / 拖拽刚体 |
|
|
150
|
+
|
|
151
|
+
### 创作类必备
|
|
152
|
+
|
|
153
|
+
- **撤销栈**:至少 10 步
|
|
154
|
+
- **清空场景**:与重置区分开(重置回初始预设,清空到空)
|
|
155
|
+
- **截图保存**:`canvas.toBlob()` 触发下载,或 `canvas.toDataURL()` 弹预览
|
|
156
|
+
- **可分享**:序列化关键参数到 URL hash,刷新可复现
|
|
157
|
+
|
|
158
|
+
## 6. 状态指示与可观察性
|
|
159
|
+
|
|
160
|
+
模拟的灵魂是"看见系统在做什么"。隐藏内部状态的模拟是失败的模拟。
|
|
161
|
+
|
|
162
|
+
- **FPS 显示**:开发期角落小字(如 `{ fps, particles, dt }`),release 可隐藏在 `?debug=1`
|
|
163
|
+
- **系统状态读数**:模拟物的关键指标实时显示(如流体的总动量、生态的种群数量、电路的总电流)
|
|
164
|
+
- **暂停按钮 + 时间倍率**:实时类必有暂停;高级模拟可加 0.25× / 1× / 2× 时间倍率方便观察
|
|
165
|
+
- **典型反例**:模拟跑着但屏幕上没任何数字,用户不知道"正常 vs 异常"
|
|
166
|
+
|
|
167
|
+
## 7. 视觉表达
|
|
168
|
+
|
|
169
|
+
### 风格倾向
|
|
170
|
+
|
|
171
|
+
模拟类常用风格:
|
|
172
|
+
|
|
173
|
+
- **科学可视化感**:暗底 + 高对比单色梯度(青蓝→品红常见,温度图)
|
|
174
|
+
- **沙盒玩具感**:亮底 + 高饱和色块(如 falling sand 经典风)
|
|
175
|
+
- **写实拟物**:流体/咖啡/液体走真实材质 + 光照
|
|
176
|
+
- **抽象艺术感**:boid / Conway 类用渐变 + 拖尾长曝光
|
|
177
|
+
|
|
178
|
+
### 拖尾 / 长曝光(粒子类必杀技)
|
|
179
|
+
|
|
180
|
+
每帧不清屏全黑,而是 `ctx.fillStyle = 'rgba(0,0,0,0.05)'` 半透明 fillRect 覆盖——粒子运动轨迹自然形成漂亮拖尾。这是粒子类最便宜的"高级感"开关。
|
|
181
|
+
|
|
182
|
+
### 颜色映射
|
|
183
|
+
|
|
184
|
+
数值场(温度 / 速度 / 密度)可视化时:
|
|
185
|
+
|
|
186
|
+
- **viridis** / **inferno**:科学可视化标配,色盲友好
|
|
187
|
+
- **HSL 色相环映射**:方向/相位类(如旋度场)
|
|
188
|
+
- 禁止用"红绿"映射数值(色盲不友好 + 审美差)
|
|
189
|
+
|
|
190
|
+
## 模拟特有反模式
|
|
191
|
+
|
|
192
|
+
- 用真实帧 dt 直接积分(`x += v * realDt`),切 tab 回来粒子飞到宇宙外
|
|
193
|
+
- O(n²) 邻域查询不上空间分割,500 粒子就卡
|
|
194
|
+
- render loop 里 `new Vector3()` 每帧 GC 风暴
|
|
195
|
+
- 用 `setInterval(simulate, 16)` 当物理循环(与刷新率失同步)
|
|
196
|
+
- 参数面板做了但**没有重置按钮**,调坏了只能刷新页面
|
|
197
|
+
- FPS 不显示,性能问题没法诊断
|
|
198
|
+
- 创作类没有撤销,用户画错一笔只能 clear all
|
|
199
|
+
- 暂停按钮存在但暂停后主循环继续空转
|
|
200
|
+
- 流体 / 布料用 Explicit Euler 积分(数值发散炸开)
|
|
201
|
+
- 移动端不监听 `touchstart/move`,只支持鼠标拖拽
|
|
202
|
+
- 拖动滑块改参数后**不立即反映**(要等下一次重置才生效)—— 失去"调参"的意义
|
|
203
|
+
|
|
204
|
+
## Worked Example
|
|
205
|
+
|
|
206
|
+
> 用户输入:"3D 咖啡拉花模拟器,倾斜咖啡杯液体表面,能实时模拟奶沫注入、扩散、与浓缩咖啡混合,并可通过交互控制奶流来绘制复杂花纹。"
|
|
207
|
+
>
|
|
208
|
+
> **§1 类型识别**:连续场(液体表面颜色场)+ 创作(用户绘制奶流路径)+ 3D 场景(倾斜杯子) → 「流体绘画类」
|
|
209
|
+
>
|
|
210
|
+
> **§2 时间步长**:固定 dt = 1/60,accumulator 多步细分;dt 上限 50ms 防 tab 切回炸毁;流体走 Stable Fluids(Jos Stam)范式,advection + diffusion + projection 三步
|
|
211
|
+
>
|
|
212
|
+
> **§3 性能**:场分辨率 256×256(液面 uv),WebGL fragment shader 跑 advection;CPU 不参与逐像素计算
|
|
213
|
+
>
|
|
214
|
+
> **§4 渲染层**:3D 视角 → **Three.js**:
|
|
215
|
+
>
|
|
216
|
+
> - 咖啡杯:CylinderGeometry 倾斜 15°,外壁陶瓷材质 PBR
|
|
217
|
+
> - 液面:PlaneGeometry,shader 采样颜色场纹理决定颜色(深棕咖啡 → 米白奶沫的渐变)
|
|
218
|
+
> - 奶流:用户 pointer 路径转 (u, v) 坐标,向颜色场注入白色源项 + 速度场注入向下速度(模拟重力扰动)
|
|
219
|
+
>
|
|
220
|
+
> **§5 交互**:
|
|
221
|
+
>
|
|
222
|
+
> - 用户角色:创作
|
|
223
|
+
> - 输入:鼠标/触摸拖拽液面 = 注入奶流路径
|
|
224
|
+
> - 参数面板:奶流粗细(滑块)/ 流速(滑块)/ 杯子倾角(滑块)/ 预设花纹(心形 / 玫瑰 / 树叶 一键演示)
|
|
225
|
+
> - 必备:撤销栈(10 步液面快照)+ 清空(液面重置为纯咖啡)+ 截图(canvas.toBlob 下载)+ URL hash 分享当前花纹的种子
|
|
226
|
+
>
|
|
227
|
+
> **§6 状态显示**:角落小字显示 FPS + 总奶量 + 当前混合度(颜色场方差)
|
|
228
|
+
>
|
|
229
|
+
> **§7 视觉**:写实拟物风 —— 杯子有真实陶瓷反光,咖啡表面有微弱 specular,奶流接触液面有边缘扩散光晕
|
|
230
|
+
>
|
|
231
|
+
> **反模式提醒**:
|
|
232
|
+
>
|
|
233
|
+
> - 禁止用 setInterval 跑流体步进
|
|
234
|
+
> - 禁止每帧 new THREE.Vector3() 在 shader 设置时
|
|
235
|
+
> - 必须在 visibilitychange 切走时停 rAF
|
|
236
|
+
> - 参数面板的滑块改值必须立刻反映到渲染,不要等下次清空
|
|
237
|
+
>
|
|
238
|
+
> **不要照抄此例**——同样需求换成"2D 俯视咖啡杯 + Canvas 2D Stable Fluids"也合理。按 §1-§7 维度从本任务重新推导。
|