DESIGN DOC · 03 场景与地图 · #s4e

★ 场景视觉样张 · 城南的十五个横截面

城南 17 格 · 光照 · 天气 · 回城规则 — 《热血骑手·物语》设计案按节成页:一个 section 一个文件,左侧目录全站可跳。

04E

★ 场景视觉样张 · 城南的十五个横截面

程序化 SVG 分层 · 同一套图层换光照与天气

§04C 定下了"全作只有 17 个手工格子、同一条街来回走三十遍"这条纪律,但纪律本身不能证明风格成立。本节把全部 15 个常规格子直接画出来(含 ③B 辣条妹的御膳房 与 ⑭A 珍珠奶茶铺 两个"只能路过、永不进入室内"的地标,以及本轮新增的 ⑯A 野生狗奶店、⑯B 飞哥农庄、⑰ 电动车竞速·第一人称),用来一次性回答四件事:① 城南到底长什么样、和 Q 版角色是不是同一个世界;② 分层视差(天空 / 天际线 / 中景 / 道具 / 地面 / 前景)在矢量下能做到什么程度;③ 同一张图的多套光照与状态(时段 × 天气 × 营业态/欠账/货架档位)如何只靠参数切换 —— 这是 §04C "每次回去必须有一处可见变化"能否兑现的技术前提;④ 每个格子的信息密度该按到访次数配多少(骑手站 25+ 次 → 道具最多;江堤 5~8 次 → 故意留白最少)。

全部由 design/scenes.js 的 HB_SCENE(key, w, h, opt) 运行时生成,无外部图片、无位图、可无损放大到 4K;opt 支持 stage(时段)、weather(雨/雪)、以及若干剧情状态开关(如卷帘门上的红漆、黑板上的欠款数字、是否已拥有白蜡杆)。下面每张图右下角标注了它对应的 §14.3 场景曲风/BPM/调式,音乐表与美术表现在这里是同一份数据的两种投影。

04E.0 ★ 游戏调色盘 ≠ 本文档调色盘(本轮最重要的修正)

用户原话:"你设计的游戏的色彩风格和你给我看到的网页文档风格是一样的,所以估计会受到影响。实际上我希望的是更青春活泼、色彩冲击感更强的风格,至少给玩家轻松快乐的感觉。"
这句话点中了一个真实存在的污染路径。本文件是一份设计文档:纸白底 + 低饱和土色 + 大量灰阶,为的是长时间阅读不刺眼 —— 这套审美是对的,但只对文档成立。写 scenes.js 的时候它被顺手带进了游戏:柏油路写成 #8a929a 钢灰、旧砖写成 #a05f5e 脏红、木梁写成 #4a4038 褐。结果就是"写实街景 + 漫画人物"两张皮。
两套盘子必须分开

文档求耐读,游戏求兴奋

文档盘:纸白 #fbf8f3、正文墨黑、强调色只用一支品牌红,饱和度 ≤ 0.55,看两小时不累。
游戏盘:糖果高彩、同色相提纯、阴影禁止用灰,平均彩度目标 ≥ 0.70,一眼就要"想进去跑两单"。
本页正文仍然按文档盘排版 —— 这是刻意的:设计稿要给你读两小时不累。
但样张和首页 UI 已经并到游戏盘了:homepages.js 的 CSS 在注入前过一遍 HB_POPCOL()(同一个 candy()),所以你在本页看到的微信/Steam 壳子和游戏里跑的是同一套数,不再需要你自己在脑子里做色彩换算。

病根一:压暗必然掉彩

shadeColor 是元凶

老写法 shadeColor(col,-t) 按比例压缩三个通道 → 深色一侧的彩度必然塌。#bfbced(薰衣草紫,s=.58)压暗 12% 之后变成 s=.32 的灰紫。所有阴影、室内、路面都从这里开始发闷。
修法:shadeC() = 压暗之后立刻用 satfix() 把彩度补回同色相,而不是让它掉。

病根二:中间调漏网

candy() 在出口设总闸

约 370 处写死的"写实中间调"落在 s=.25~.34 这一档 —— 早先只捞 s<.22 的阈值整批漏掉了它们,这就是前两轮改完还是发闷的直接原因。
本版不再逐处改字面量,而是在 rect / line / circle / ellipse / poly / path / text / 渐变 stop 全部图元的出口统一过 POPCOL() → candy():色相不变、感知明度近似不变、彩度抬到天花板以下(ceiling=.62,要糖果不要霓虹)。需要保持写实的参照面可用 data-pop="0" 单独豁免。
三层同一个闸:scenes.js 背景、manga.js 立绘、homepages.js 首页 UI 都调同一支 HB_POPCOL;以后再加配色只改这一处,不会再出现"场景很艳、UI 很灰"的三层脱节。

病根三:运行时算出来的色,闸看不见

popTint() 把颜色搅灰,而所有校验都是绿的

前两轮把源码里的字面量全刷艳了,验收也绿了,可画面还是发闷。真正的原因是 popTint():它把材质色往【时段天空色】线性混合,而混合没有任何彩度下限 —— 暖色/中性色混进偏青的天空之后三通道被拉平,彩度当场掉光:
#ecdcb4 奶油地 s=.60 →(白天)#d9dbc3 s=.25;#bcbad6 混凝土 s=.25 →(清晨)#d0c8c8 s=.08(几乎纯灰)。
更要命的是这些颜色是运行时才出现的字符串:popv6 那道闸只匹配源码里的 #rrggbb 文本,压根不知道它们存在;竞速格的路面 #232a33 混完落在墨水保护带里,连 candy() 也放过 —— 于是那一格 42% 的面积一直是死灰蓝,而校验一路绿灯。
本轮两件事一起修:① 新增 popTintSafe(),混合后把彩度补回「原色的 92%」与「绝对下限 .36」的较大者(真灰/墨线仍豁免,不染色彩);② 新增运行时色彩闸,直接量渲染出来的 SVG,并且按 SCENE_VARIANTS 覆盖每一格真正会被玩家看到的时段/状态,而不是只看默认参数。36 个变体的平均灰面从 2.36% 降到 0.35%,超标的格子从 2 个变成 0 个。

顺手治的 monotone:一整屏只有一个色相也不叫活泼

竞速格从 100% 青蓝改成暖沥青

彩度补回来之后,运行时闸仍然拦下竞速格:它整个画面压在色相 195~230 这一段(青-蓝),饱和但不暖 —— 那是"写实夜路",不是青春。
卡通/赛博朋克里的夜路从来不是灰的:夜里改走紫褐(路面实际渲出 #391c3b,hue≈296°),黄昏改走玫红(#c72f63,hue≈339°)。实测该格彩度 .567 → .609,青蓝占比从 100% 掉下来,而路面仍比人行道暗,一点透视的层次没有丢。

指标(面积加权)修之前修之后说明
平均彩度(场景)0.6210.73115 格全过闸;最艳 farm .885 / riverside .808 / dogmilk .807
灰面占比(s<.30,场景)19.25%1.14%桥洞 81.5%→0.0%、竞速 81.3%→0.0%、骑手站 40.4%→0.6%
平均彩度(立绘)0.5690.745manga.js 同样中招:49 个形象灰面 10.0% → 1.7%
平均彩度(首页 UI)0.6700.716浏览器实测注入后的样式表:34 个不同色值 / 87 处引用,灰面 3.4% → 1.1%。闸只捞到字面量,剩下发灰的是半透明纸色叠层 —— 见下方 v2 四条手工定调。
感知明度0.5240.542夜戏保留纵深,不做无脑提亮(DARKLIFT 实测为负收益,已归零)

@HB-HOMEPOP-v2 · 首页 UI 第二轮:闸捞不到的四类发灰

candy() 只改 #rrggbb 字面量,所以第一轮之后微信壳子仍然有一层"看起来不脏但也不兴奋"的底子。这一轮处理的是总闸够不着的四种写法,全部在浏览器里复测过:

漏网写法原来是什么现在改成什么 / 为什么这样改
半透明纸色叠层.hbhw-hero-core 用 rgba(243,239,230,.6) —— 这正是本文档页的米白,等于把文档盘又偷偷搬回了游戏 UI换成 rgba(255,240,194,.72)(与屏幕底 #fff0c2、任务卡 #ffe9b8 同一支奶油糖果)。角色脚下的台子从此是游戏盘的颜色,不再是稿纸的颜色。
黑色遮罩压截图关卡卡 rgba(14,18,24,.65)、Steam 缩略图标题条 rgba(0,0,0,.7)改成同色系的紫:rgba(30,11,72,.86) / rgba(43,17,96,.5)。黑遮罩会把下面的糖果场景直接洗成灰(它绕过了总闸,因为闸只看十六进制);换紫之后文字可读性不变,缩略图饱和度保住了。
品牌红上的黑玻璃片LIMITED 标签 rgba(0,0,0,.18)rgba(122,20,10,.34) —— 压在红色上仍然是红的暗部,不发脏。
小字用了灰紫进度数字 / 角色名 #b39cc4(s=.25)、#4a3550(s=.23)统一收到 #3d1163(s=.71,在最浅的卡片上仍有 5.3:1 对比度)。小字号本来最容易显得"没上色",这里提彩几乎零风险。
顺带补的两件小事:① 关掉浏览器默认的蓝色插入光标/选中底色(手机样张里那根蓝色竖线一眼就是 bug),统一走品牌红 caret + #ffcf2d 选中底; ② 加了唯一一个自制动效:吉祥物 3.4s 呼吸起伏 + 主 CTA 的 box-shadow 心跳(都不碰布局,只 transform/box-shadow),并在 prefers-reduced-motion 下整段关闭。除此之外不做满屏乱飞的花活。

五条硬约束(全部写进校验,不许回退):① 品牌七色一个字节都不许漂 —— 全站三色 #e2402b #ffcf2d #ff7ba8 + 辣条妹 IP 四色 #c63a17 #ff6a2a #e85d1a #ffe926(招牌上的头像必须和 latiaomei.infinityfly.xin 对得上号);② 墨线保护用感知亮度 Rec.709,不许用 HSL 的 (max+min)/2 —— #241a3a 的 HSL 明度 .184 能骗过 .16 的门,一旦加彩,所有深色描边会变成亮紫罗兰;③ 压暗阶梯必须严格单调 —— candy() 用的是以 FLOOR=.62 为支点的仿射压缩 l=FLOOR+(l0-FLOOR)*GAIN,不是钳位,所以桥墩/屋瓦的八档明暗差仍然相邻可辨。
④ UI 不许再手写第二套色 —— HB_HOME.popCSS() 已公开,任何新增的界面样式一律写死品牌色/游戏盘色,由同一个闸负责抬彩,不允许再出现rgba(0,0,0,.x) 直接压在截图上当遮罩(那会把糖果色重新洗灰)。
⑤ 只看源码字面量的闸不算验收 —— 凡是在运行时算出来的颜色(popTint()/shadeColor()/渐变插值),hex 扫描永远抓不到。色彩校验必须量渲染之后的 SVG,而且要按 SCENE_VARIANTS 把每一格的时段与状态都跑一遍:默认参数看着艳,不代表玩家真正看到的那张不灰。

04E.1 为什么坚持程序化矢量,而不是一张张手绘背景

省的是回访成本

变化是参数,不是重画

§04C 要求每个格子每次回去都有可见差异:灯光、破损、货架、天气、营业态。手绘方案下 17 格 × 4 光照 × 3 破损 ≈ 100 多张背景;程序化方案下这些全是 opt 里的一个布尔值。骑手站卷帘门上那道红漆(CH.06 之后)在这里是一行代码,在手绘流程里是一次返工。

两端同源

微信 4K 一套输出

矢量 → 运行时按目标分辨率栅格化:小游戏端出 720p 贴图包,Steam 端出 4K。同一份几何数据,不必为高分屏单独重绘,也不必接受像素风在小屏上的细节丢失(这正是 §06 废弃像素稿的同一个理由)。

代价要说清楚

质感上限低于手绘厚涂

程序化矢量拿不到笔触与颗粒,画面会更"干净、平面、设计感强"。这恰好和 Q 版萌系角色是配套的(角色也是大色块 + 暖黑描边),所以整体自洽;但如果日后走 §16 #8 备选 B「手绘厚涂剧情立绘」,背景需要同步升级,否则两者会脱节。这条已写进 §16 #33 待你拍板。

04E.2 光照 / 天气 / 剧情状态矩阵(美术预算的真实去向)

维度取值实现方式影响哪些格子
时段 stagedawn / day / dusk / night换天空渐变两个色标 + 环境光乘数 + 灯类图元的开关(霓虹、车灯、窗光、路灯)① 骑手站(四套全做,最高频)、⑬ 夜市(只有傍晚以后)、⑫ 江堤(凌晨)
天气 weatherclear / rain / snow叠加一层斜线雨丝 + 地面湿反光带(把上方霓虹镜像压扁贴到地上)+ 只降明度、不降彩度(§04E.0:雨天发灰就是上一版的病)⑫ 江堤(CH.10 小雨)、⑤ 桥洞(积水来源)、④ 花店街(倒影)
剧情状态 flags红漆有无 / 欠款数字 / 白蜡杆归属 / 花架档位 / 赛道封街条件图元:opt.painted、opt.debt、opt.pole、opt.tier、opt.mode==='race'① 卷帘门、② 欠账黑板与墙角杆、④ 老周货架、⑬ 周六夜封街赛道
破损态intact / scuffed / wrecked只在前景层加裂纹与掉落碎片,中远景不动(省钱且玩家注意力本来就在前景)群战打过的格子:⑤ ⑧ ⑩ ⑮
验收标准(可直接写进关卡编辑器校验项):① 任意两个不同格子的主色调与轮廓节奏不许重复(桥洞冷、夜市暖、江堤灰蓝、御膳房高饱和);② 高频格子(到访 ≥ 12 次)必须至少支持 4 种可感知状态,低频格子 1 种就够;③ 每一层的图元必须能独立开关,供视差滚动与"打完之后现场变乱"复用;④ 场景里必须至少有一个"活的"细节(蒸汽、水洼反光、晾衣绳被单、补光灯扇叶),静止的背景没有城南的味道。

04E.3 ★ 建筑/场景画风对齐 —— logo 背景里的那座城(2026-10-09)

用户拍板:"其他形象也按这个画风对齐设计,包括建筑、场景。" game-logo.png 的背景就是城南的视觉判例:那不是一个写实城市,是"热血漫画分镜里的落日城市"。下面把它的六个图层逐一拆解(色值均为原图实测),并映射到现有视差分层。本节同样只动文档,scenes.js 与小游戏素材本轮不改。

图层logo 里的判例做法对现有场景生成器的对齐要求
天空落日橙三段渐变(#f04a03 → 橙 → 云缝里的暖米 #f9c78a),云是块状硬边不是柔边,亮部铺 halftone 圆点纹理;画面边缘切黑白放射速度线。黄昏档(dusk)定为母版光照:天空渐变两个色标向实测值收拢;云的图元从椭圆柔边改为硬边块状;新增一层可选的 halftone 点纹叠层(亮部专用,面积加权不超过 8%)。
天际线(远景)楼群是暗酒红剪影(#4d1522 / #a43929),像素阶梯状楼顶(直角台阶,不是斜屋顶),窗是暖黄小方格阵列(#ffd23f 系),整排楼无描边、纯色块。远景层规则改写:① 楼顶轮廓允许并且鼓励"8-bit 阶梯"处理(这是背景对保温箱像素彩蛋的呼应);② 远景楼去描边、只留中近景描边 —— 描边密度随视差层递减,天然拉开纵深。
电线杆 / 城市构件近黑(#010102)纯剪影:横担、斜撑、成排电线、电线上的麻雀 —— 全是细线条构件,占面积极小但是"城"的身份标签。每一格至少安排一处近黑剪影构件(电线杆 / 水塔 / 天桥桁架 / 晾衣钢索),与 §04E.2 验收标准第④条"活的细节"合并执行:麻雀、电线上的衣服都算。
中景建筑立面砖红 / 赭橙大色块 + 粗黑窗洞,受光面统一来自画面右上(落日方向),暗部用同色相加深、不发灰。与现有 shadeC() 补彩规则一致;新增硬约束:黄昏档所有格子的主光源方向锁定为同一侧低角度,保证任意两格并排时像同一条街。
路面暖紫沥青(实测均值 #412f3d,暗部 #2a2e4d)+ 白色速度线条纹 + 奶油色团状烟尘。夜里路面是紫的,从来不是灰的。直接确认 §04E.0 已做的"竞速格暖紫沥青"改造为全格通则:任何时段的路面色相禁止落在 195°–230° 青蓝死区;烟尘/扬尘图元统一用奶油 #f9c78a 系。
画框整幅图装在圆角方形 + 深色描边的 App 图标框里,四角留黑。分享卡 / 标题页 / Steam 胶囊图沿用此框语言(圆角半径 ≈ 短边 18%);游戏内画面不加框。
四条硬对齐

建筑场景版"三问"的扩充

① 蓝橙互补对撞在场吗——画面主体(角色/招牌)与天空至少一组蓝↔橙;② 描边密度随层递减吗——远景无描边、中景细线、近景粗线(与角色 1/90 画布宽同级);③ 暗部是"更深的同色相"还是灰——后者一律打回(§04E.0 病根一在这里同样适用);④ 至少一处近黑剪影构件。

四套光照怎么办

logo 是黄昏,其余三档从它推导

不推翻现有 dawn/day/night 三档,而是以 dusk 为母版做色相旋转:清晨 = 橙转金(彩度不掉)、白天 = 橙转柠檬暖白、夜里 = 橙转紫红 + 窗光/霓虹点亮。检验:任意一档与 logo 并排,仍应认得出是同一座城 —— 换的是时间,不是画风。

施工边界

文档先行,代码后动

本节的"对齐要求"列是给下一次美术迭代的施工单,本轮 scenes.js / manga.js / wxgame / cocos 一律不动。优先级建议(待 §16 #35 拍板):先改男二/女一共用构图 → 再改高频四格(①②⑭)黄昏档 → 最后铺全 15 格。每步都跑现有运行时色彩闸复测。

《热血骑手·物语》设计案 v1.0 — 分册制 · 域名 hot-blood.infinityfly.xin · 目标平台 Steam / WeGame / 微信小游戏 / iOS·Android

本页是 design/index.html 里 一个 <section> 拆出来的独立页面(一节一文件)。改内容请改 index.html 对应 section,再跑 node split-docs.js 重建;也可以直接改 docs/NN-*-sX.html,但那样下次重建会被覆盖 —— 事实源永远是 index.html。

致敬 FC《热血物语》的结构与气质,角色、世界观、美术均为原创。