🌻 设计文档小农场 · 野花笔记
← 全部文档/夜市/美术

美术风格

现行 #美术#风格#色板#验收 docs/夜市/美术风格.md
一句话

深蓝的夜,暖黄的灯,圆脑袋的小人。 所有素材都从这份取;色板之外的颜色要先加进第二节再用。

两个口径的验收判据在第四节,改完素材必须跑一遍: python3 tools/art/night_check.py <文件...>

夜市摊位与排队的小人 · 概念稿
夜市摊位与排队的小人 · 概念稿
画风不许和另两个项目混用

小农场 是日本清新风(暖白、清新绿),野花笔记 是灰蓝低饱和水彩。 夜市比这两个都更有颜色——它本来就是灯笼照着的一条夜街。 实测对照:野花笔记基准图的色度 P50 是 11,夜市概念稿是 46,差 4 倍。 跨项目复用 CSS / 调色板时,必须逐色重跑一次判据, 否则就会像野花笔记那次一样,把 小农场 的 --gold:#C9A45C(饱和 58.6%)带过去,在灰蓝页面上扎眼。

一、画风基准#

维度 定调 禁止
光 一个光源:灯笼。 所有暖色都从灯笼的高度往下走 多个方向不明的光;全局均匀打光(会变成白天)
色 深蓝夜色打底,暖黄灯芯点睛 荧光色;纯黑;纯白
形 圆、胖、短。小人两头身到三头身 尖角、硬折线、写实比例
边 细描边或不描边,靠色块和明暗分界 粗黑描边
质感 哑光平涂 + 一点柔光晕 写实材质、金属高光、3D 渲染感
空 夜要留得下来。街上要能看见空地 画面塞满

二、色板(22 色,全部实测)#

色号全部用 tools/art/night_palette.py --check-hex '...' 验过, 色度、饱和度、明度三列都是脚本读数,不是估计值。

2.1 夜色(背景)#

用途 色号 色度 明度
夜空最上 #131C33 32 0.20
夜空主色 #1E2A44 38 0.27
远处街道 #2B3A5C 49 0.36
街面石板 #3A4358 30 0.35

2.2 暖光(灯笼到摊台)#

用途 色号 色度 明度
灯笼纸 #F2C879 121 0.95
灯笼芯(最亮) #F0D5A0 80 0.94
摊灯暖橙 #E2A968 122 0.89
木台(亮面) #8A6244 70 0.54
木台(暗面) #5E4433 43 0.37

2.3 界面#

用途 色号 色度 明度
面板底(纸米) #F4E9D6 30 0.96
面板描边 #C9A87C 77 0.79
主文字 #2A2F3E 20 0.24
次文字 #6B6F80 21 0.50
金币 #E8C36E 122 0.91

2.4 小人#

用途 色号 色度 明度
圆脑袋(无脸) #F2DCC6 44 0.95
头发 / 配饰 #4A4258 22 0.35
衣 ① 珊瑚 #D98F7A 95 0.85
衣 ② 芥黄 #D9B267 114 0.85
衣 ③ 雾青 #7FAAA5 43 0.67
衣 ④ 藕紫 #A98CB5 41 0.71
衣 ⑤ 米灰 #C8C2A8 32 0.78
衣 ⑥ 淡蓝 #8FA7C9 58 0.79
小人衣服为什么是低饱和的六色

灯笼和金币已经占了画面里全部的高饱和配额。 小人再艳,画面就没有主次了——光是主角,人是群众。 六色色相拉开(16° / 40° / 174° / 285° / 60° / 213°), 排成一队时一眼能数出"来了 6 个人",这是排队游戏最重要的可读性。

三、小人:无脸的圆脑袋#

无脸小人的动作与拆件规范
无脸小人的动作与拆件规范

3.1 为什么不做五官#

没有五官,情绪就只能靠动作表达。这看着像妥协,实际是三条好处:

  1. 省素材:一张头、一张身体、两手两脚,六个零件演完所有情绪。
  2. 好动:没有眼睛要跟着转,木偶怎么摆都不"死"。
  3. 可爱:观众会自动脑补表情——这是无脸角色比有脸角色更好用的地方。

代价是:动作必须做到位。 一个姿势僵硬的无脸小人,就是一根会走的柱子。

3.2 情绪只能靠这四件事#

情绪 怎么做 出现在
等待 身体左右轻晃(±3°),重心轻微上下 队伍里所有人,永远在动
开心 蹦一下(离地 6px)+ 头顶冒爱心 拿到吃的之后
好奇 歪头(头旋转 ±12°),身体不动 新菜品第一次出现时
累了 原地坐下(腿收起来,整体矮 1/4) 等太久的客人。不是生气,更不会走
"等太久"的表现要克制

坐下可以,不能出现皱眉、跺脚、转身走。见核心玩法铁律一: 零失败,不存在"客人被气跑"。孩子看到一个小人生气走掉,第一反应是"我做错了"。

3.3 拆件规范(六片)#

零件必须头身分离、四肢分开出图,关节处留 5% 重叠边,并标出轴心。

零件 说明 轴心标记
head 圆脑袋 + 头饰(头饰单独一层,方便换) 颈点
body 胶囊身体 颈点、双髋
arm-l / arm-r 短圆手臂 肩点
leg-l / leg-r 短圆腿 髋点
零件要**从定稿原图切**,不许重新生图

小农场 那一轮踩过:重出零件看着省事,实际是偷偷换了角色—— 把正面小鸡(两只眼睛、喙在正中)换成了侧面(一只眼睛、喙偏一侧)。 无脸小人更危险,因为没有五官当参照物,换了配色或比例根本看不出来。 切完必须能原样拼回去,和原图做像素差,差 0 才算过。

3.4 衣服颜色用染色,不用六套图#

零件出带明暗的灰度图,颜色在代码里乘算染上去。 这样 6 种衣服只要 6 张灰度零件,之后加配色不用重出图。 唯一的例外是头饰——它是形状差异,必须单独出图。

3.5 尺寸#

角色 屏上高度(750×1334 设计稿) 站位
街上路人 40~52 px 沿街走,越远越小(纵深 1.6~3.0)
排队的客人 56~68 px 沿摊位前沿排成一列
贵客 72~84 px 摊位侧面,比常人高一档
摊主(你) 76 px 摊位内侧,不动

同屏小人的上限按 40 个做——超出的人数用幂曲线压成"街上更多的路人", 不做成真的 40 个物体(参考旧夜市那次的教训:人流量不能 1:1 换算成场上人数)。

四、验收判据(两个口径,缺一个都会漏)#

python3 tools/art/night_check.py <文件...>          # 成品图(像素口径)
python3 tools/art/night_palette.py --check-hex '#...'  # 色板(色号口径)
python3 tools/art/night_check.py --self-test        # 验判据本身有没有区分度

4.1 色号口径(管色板)#

# 判据 拦什么
① 色度 ≥ 18 太灰
② 色度 ≤ 130 太艳
③ 明度 ≥ 0.10 纯黑(夜市允许深,不许死黑)
④ 明度 ≤ 0.96 纯白(夜里的最亮处也只是暖白)
⑤ 亮色(明度 ≥ 0.5)饱和 ≤ 55% 亮色刺眼
判「有没有颜色」用色度,判「艳不艳」才用饱和度

HSV 饱和 = (max−min)/max,max 越小分母越小,暗色会自动虚高。 实测 #131C33 明度只有 0.20,HSV 饱和 62.7% 看着像超标一倍, 而色度只有 32,落在色板正中间。 差点为了迁就一条错判据,把本来就对的深蓝夜色改坏。 (同样的坑野花笔记那一轮记过一次,这是第二次。)

4.2 像素口径(管成品图,阈值拿概念稿反推)#

# 判据 实测(概念稿)
⑥ 色度 P50 ≥ 25 46.0 ✓
⑦ 饱和均值 ≤ 45% 42.1% ✓
⑧ 饱和 P99.9 ≤ 82% 80.0% ✓
⑨ 明度均值 0.38~0.60 0.488 ✓
⑩ 暗部(V<0.25)2%~18% 5.5% ✓
⑪ 亮部(V>0.85)5%~25% 9.0% ✓

阈值必须低于基准图的实测值,否则会出现"基准图自己不合格"。

4.3 判据有效性:拿已知样本跑,必须有一个被拦下#

--self-test 会跑三个样本,实跑结果:

样本 结果 被哪条拦下
夜市概念稿 通过 —
野花笔记基准图 拦下 ⑥ 色度 P50 = 11(太灰)、⑨ 明度 0.626(太亮)
概念稿加饱和 200% 拦下 ⑦ 饱和均值 66%、⑧ 饱和 P99.9 100%

只能"全过"的判据等于没有判据。 三个样本一眼可分,判据才算立住。

五、动画:一律用木偶,不用生图序列帧#

四条硬规矩,缺一条就不像在动:

  1. 腿拆成两根骨头(大腿 + 脚掌),否则一转身脚就翘。
  2. 地面锁定:髋要下沉 L − L·cos(a) 抵消"腿变短",让踩地的脚钉住不动。
  3. 必须有地面投影:没有影子就看不出脚有没有离地。这是可读性,不是装饰。
  4. 层次 + 相位滞后:根 / 腿 / 身体 / 头 / 手各一层, 头比身体晚动一点(follow-through)。只做"整体上下晃"就是僵硬感的来源。

5.1 侧面走路是"迈步",正面是"摇摆"#

什么时候 做法
街上路人(横向移动) 侧面迈步:两条腿几乎同 x(差 ~16px),前后交替
排队客人(面向摊位) 正面原地轻晃,步频降到 0.22 倍,只做重心左右移
正面走路的两个坑(`小农场` 实测)

① 是 waddle 不是 walk:身体左右倾 + 交替抬脚,重心和抬脚必须反相 (身体往右倾 = 左脚抬起)。反了就是在原地抽搐。 ② 脚的旋转要小:转多了会把抬起量吃掉(实测设 26px 抬起只剩 8px)。

5.2 站定之后要降到 0.22 倍速#

排队的小人永远不能完全静止,但也不能像走路那样摆。 实测旧夜市的做法:站定后步频降到 0.22 倍原地踏步, 看起来是"在等",而不是"卡住了"。

新客人按 i % 6 错开出发相位,否则一整批同时迈步,像一群机器人。

六、场景分层#

竖屏 750×1334 设计稿,五层,从后往前:

层 内容 实现
L0 远景 夜空、月亮、星、屋顶剪影 静态图,可做极慢的星闪
L1 中景 街道、两侧建筑、灯笼串 静态图;灯笼可单独做轻微摇曳
L2 近景 你的摊位(棚、灶台、招牌) 静态图 + 代码驱动的热气 / 火苗
L3 角色 排队客人、街上路人、贵客、摊主 Canvas 木偶,唯一每帧重画的一层
L4 前景 金币飘字、爱心、热气、烟火 纯代码,零素材
只有 L3 是每帧重画的

背景是一张图,UI 是 DOM。不要每秒重建整棵树—— 旧夜市那一版就是每秒 innerHTML = ...,直接后果是 8%~15% 的按压会丢 (手指按下的节点被换掉了,click 落到没有 data-act 的祖先上,静默失效)。 骨架只在切换页签时重建,数字和进度条原地改。

七、素材规范#

类别 尺寸 单张上限 命名
背景(L0+L1 合成) 750×1334 180 KB bg/street.webp
摊位 宽 520 60 KB stall/cart.webp
小人零件(灰度) 头 96 / 身 96 / 四肢 64 见方 各 8 KB char/head.webp char/arm-l.webp
头饰 64 见方 各 4 KB char/hat-*.webp
菜品图标 96 见方 各 6 KB food/skewer.webp
灯笼 / 招牌布 128 / 256 宽 各 12 KB prop/lantern.webp
金币 / 爱心 48 见方 各 2 KB fx/coin.webp

首包预算 ≤ 2 MB(微信小游戏 4 MB 上限留一半给代码和音频)。

八、音频(暂时只列,不做)#

时机 声音 说明
递出一份餐 "叮" + 金币声 全局最重要的一个音,前 60 秒全指望它
一声吆喝 短促人声 队伍空的时候点屏幕
招牌凑齐 一小段上行音阶 一天听不到几次,可以做得夸张
贵客出现 铃声 要有辨识度,玩家不看屏幕也知道他来了

不需要背景音乐循环也可以发——但上面四个音效一个都不能省。 旧版本诊断里那句"完全没有声音"就是一条硬伤。