做一间「会过日子的房间」——小鲸鱼房间制作攻略
一句话:一张等距插画,加上时钟、天气和几个能点的地方,就能让人物「住」进去。 这份攻略按我们实际做了一遍的顺序写,照着走就能做出同款;不需要建模,也不需要美术基础。
0. 先看效果(30 秒)
打开房间页,你会看到:
- 房间里有一张等距插画(床、书桌、书架、地毯都在)
- 窗外天空会随时间变化:清晨偏冷、中午透亮、下午偏暖、晚上发蓝
- 天气会自己变:晴天 / 雨天 / 雪天(也可手动切)
- 灯可以开关,关灯房间会暗一档
- 鼠标移到床、书架、桌子上会浮出提示,点一下弹出小抽屉:「最近写日记 10-31」「正在读《我是猫》11%」
这些都不需要 3D,也不需要一张图一张图地做——一张底图 + 一层光 + 几个透明层就够了。
1. 需要准备什么
| 东西 | 说明 | 从哪来 |
|---|---|---|
| 一张等距底图 | 建议 3:2(我们用 1536×1024),画面里家具按 2:1 等距摆放 | 自己画 / AI 生成 / 找画师(关键:光和影要中性一点,别把夕照画死) |
| 一个能放静态文件的地方 | 本地就行:任何 http 服务、或者你已有的桥接/后端 | python -m http.server 都可以 |
| 一点点时间概念 | 4 个时段:清晨 6–11 / 中午 11–14 / 下午 14–18 / 晚间 18–6 | 攻略里给了现成代码 |
| 可选:人物的几个姿态 | 坐、读书、睡觉,透明 PNG | 没有也行,先做静态房间 |
别做的事:一开始就想要 12 张图(4 时段 × 3 天气)。做完你会发现不需要——天色是叠上去的,底图只要一张。
2. 三步做出最小版
第一步:把底图放进页面(5 分钟)
到这一步你已经有「一张静态房间」了。先把构图调满意,后面所有坐标都基于这张图,改构图等于全部重标。
第二步:加天色和天气(30 分钟)
新建 environment.js,核心就两张表:
然后不要直接去调底图的颜色,而是:
天气就三层 SVG 元素叠上去:
- 雨:一堆
<line>加@keyframes往下落 - 雪:一堆
<circle>慢慢飘 - 雪天再加屋顶和树枝的白色覆盖层
关键一点:box-shadow、blur、mix-blend-mode 在大多数浏览器里都不贵,但粒子数量别超过 150 个,手机上也才稳。
第三步:能点的地方(30 分钟)
热区不写在代码里,写成一个 JSON:
坐标是底图 viewBox 坐标(1536×1024),不是屏幕坐标。屏幕坐标会被缩放,用 svg.getScreenCTM().inverse() 反算回去。
鼠标移上去显示文字提示,点一下拉一个接口把内容取回来:
内容从哪来随你:日记、待办、书、天气——只要接口能返回 {title, lines[]},抽屉就能显示。
3. 想做得更好:三个可选升级
A. 手动标注热区(强烈建议)
别用眼睛估坐标。加一个 ?calib=1 模式:
- 显示虚线框,可以拖、可以拖角缩放
- 可以「手绘新区域」描一圈不规则形状(斜着的床脚、圆形地毯)
- 按方向键微调 1 像素,Shift + 方向键 10 像素
- 点「保存」写回
hotspots.json,旧版自动备份
手绘多边形用 Douglas–Peucker 抽稀(阈值 5 就够),一个圈通常剩 8–12 个点。 数据结构:{ id, label, rect, poly: [[x, y], ...] },rect 是 poly 的外接框,判断用 poly。
我们踩过的坑:重新标注后id变成了area1、area2,后端还在按bed、shelf查,于是所有点都显示「这里还空着」。 解法:让后端留一张「名字 → 语义 id」的反查表(床/书架/窗户/桌子/椅子/软垫/绿植/台灯/抽屉),id不认识时用label找。
B. 灯和夜
- 开关状态挂在
body[data-lamp="on|off"],别挂在 SVG 上——页面重绘会把它覆盖掉 - 开灯:一团柔光(
radialGradient的椭圆,mix-blend-mode: screen) - 关灯:
#roomBase { filter: brightness(calc(var(--brightness) * .84)) },实测暗一档刚好(14% 左右)
C. 让人物有状态
如果这个房间是给某个 AI 角色住的,可以做一个 /api/room/her:
判断逻辑随便你定,我们用的是:潜水模式 + 安静超过 12 分钟 → 睡觉;45 分钟内看过书写过笔记 → 趴着看书;否则坐着。 前端根据 pose 叠不同的透明 PNG,锚点对齐到床或地毯。
注意:底图里已经画好的人物,要么单独出一张「空床」底图,要么在叠之前把原人物遮住——否则会出现两个她。
4. 接进桌面客户端(可选)
如果房间要放进 Electron / DSH 这类客户端,最省事的做法是:
1. 房间页保持纯静态(index.html + assets/ + 未压缩的 app.js,双击能开、不依赖 CDN) 2. 客户端里用一个全屏 iframe 指向它 3. 需要传参数(比如 ?calib=1)就改 iframe.src,不要试图跨域操作 iframe 内部
我们踩过的坑:<img src="http://127.0.0.1:3100/...">发不出自定义鉴权头,图加载失败、页面全白。 解法:要么给静态站点免鉴权(只监听 127.0.0.1 时可接受),要么用fetch取 blob 再URL.createObjectURL。
5. 常见坑(都是真踩过的)
| 坑 | 现象 | 解法 |
|---|---|---|
| 改了文件像没改 | 页面还是旧样式 | 脚本加版本号 app.js?v=2,服务端注入的脚本也要同步 bump |
| 坐标对不上 | 点窗户点到墙 | 用 getScreenCTM() 反算,别按屏幕像素写死 |
| 叠素材叠歪 | 出现两盏灯、两个人 | 底图里画过的东西别再用 PNG 叠;要叠就得按底图量尺寸 |
| 缓存 | 换了图片不变 | 图片用新文件名,别原地覆盖(max-age 会记住) |
| 中文文件被写坏 | 一堆 锟斤拷 | 改 UTF-8 文件用 Node 读写,别用 PowerShell 的 Get-Content -Raw + Set-Content |
| 面板挡住点击 | 抽屉关了又像没关 | 抽屉别贴着鼠标放,按左右半屏放另一侧 |
6. 速通清单(照着勾)
- ☐ 一张 3:2 等距底图,光中性
- ☐
index.html+app.js+environment.js,双击能打开 - ☐ 4 个时段 + 3 种天气,时钟自动走
- ☐ 热区 JSON,能点出抽屉
- ☐
?calib=1能标注、能保存 - ☐ 灯开关 + 夜晚压暗
- ☐ 可选:人物姿态、真实天气接口
做完大概 1 天(不含画图)。最花时间的是底图和热区坐标,代码部分其实很少。
7. 我们这版的文件结构(可以参考)
配套:
- 后端两个只读接口:
/room/inspect?slot=、/room/state - 一个保存接口:
POST /room/hotspots(只接受本机请求)
这份攻略来自「小鲸鱼房间」的真实制作过程:同一张等距图,靠几层叠加和一点点状态切换,做出白天夜晚、晴雨雪天,没有一张图是为某个天气单独画的。 想直接抄:随便挑一个能跑静态页的地方(本地开个服务、GitHub Pages、任意静态托管都行),从第 2 节的第一步开始。