下载 App

【聚光灯21天】DAY 13–16 · H5 版四天:从六个页面到能上线

修改于10/204 浏览 包含 AI 合成内容
TapTap
DAY 12 封存了 Lua 版、开了 H5 的壳。这四天把整个游戏在浏览器里重建,并推到接近可上线。
四天的主线:
天做了什么
DAY 13六个页面 + 罗盘 + 双音频系统 + 四条资产管线 + 七套浏览器验收
DAY 14全量审查 → 修 5 条高危 + 15 条中危;存档语义换代(v2.2.0 轮次存档)
DAY 15存档页重做(自动档 6 条 + 暂存)、设置页改滑杆、剧本内容缩减
DAY 16TapTap 云存档对接 + 青史图鉴 + 朝代预告页 + 封面/分享卡素材
horizontal linehorizontal line

一、为什么能一天做完六个页面

因为规则早就写好了,今天只做"渲染层"。
这个项目的规则真源一直是 JS(prot/engine.js),Lua 是它的逐位移植。所以 H5 版:
ts复制
ts
// h5/vite.config.ts alias: { '@engine': resolve(repoRoot, 'prot/engine.js'), // 规则:原样复用 '@data': resolve(repoRoot, 'prot/lua-data'), // 数据:原样 import }
不拷贝、不转译。 prot/ 的测试因此继续守护同一份代码。
于是活就变成了纯粹的**“把状态渲染成 DOM”**。game/session.ts 933 行,控制流严格对齐已封存的 scripts/qingshi/ui/game.lua。

六个页面 + 罗盘

模块行数说明
ui/menu.ts263主菜单
ui/select.ts286选择页(剧本卡 + 难度 + 入局漩涡)
ui/wheel.ts299朝代罗盘
ui/settings.ts115设置页
ui/savepage.ts107存档页
ui/cast.ts77立绘列
game/session.ts933会话与流程
罗盘是最难的一块。 wheel.ts 头注写明:模型与手感参数与 Lua 完全一致 ——
  • 朝代名固定在自己的方位(第 1 项在正上方),不随盘转
  • 盘面(环带 + 烘焙在顶部的指针)旋转,由指针去指当前选中的那一格
  • 拖拽跟手 → 松手惯性 → 欠阻尼弹簧吸附(冲过再回摆)→ 整体归位
与 Lua 的差异只在渲染层:NanoVG 即时绘制换成 DOM + CSS transform,每帧只写 transform/位置,不重排布局。
这块的手感在 Lua 版调了很多轮(DAY 8 的"罗盘四版"),今天照搬结论,不重新调。
horizontal linehorizontal line

二、DAY 14:一次审查,修掉 5 高 15 中

DAY 13 搭得快,DAY 14 就做了全量审查(三路并行深审 + 主审查人逐条核实)。
基线:tsc --noEmit 通过、8/8 套浏览器测试全绿、数据门禁通过。

报告里最有价值的一句话

最值得警惕的模式是「注释里正确诊断了问题,代码却没跟上自己的诊断」。
两个实例:
  • 懒加载被自检抵消 —— scenarios.ts 精心做了按需加载,但 main.ts 的启动自检串行拉全部五份 JSON(280KB),首屏被迫下载全部数据。注释还自承"这是刻意取舍"。
  • 版本号第三副本 —— vite.config.ts 注入 __APP_VERSION__ 并专门注释警告"版本号漂移事故",而 settings.ts 又手抄了一份 APP_VERSION,注入值全仓零引用。下次升版本必然静默漂移 —— 正是注释警告的那件事。

五条高危

#问题后果
H1「回溯」按钮每开一局累积一个,旧按钮闭包绑定旧对局第二局起顶栏叠出 N 个按钮;点旧的会污染正在进行的对局
H2顶栏返回钮每次开局 addEventListener 且从不移除第 N 局点返回会触发 N 次 goSelect()
H3存档页「存 入」是永远走不通的死功能所有入口都先 session = null;且返回路径硬编码 'normal' 重开,难度被悄悄改写
U-M1罗盘缺 pointercancel 处理移动端切后台 → 相位永久卡在 drag
M1启动自检抵消懒加载首屏多下 280KB
H1 和 H2 是同一个根因:shell.bar 是常驻元素,而每局的 Session 往上面挂东西却从不摘除。

修复:两个"出口"侧的洞

ts复制
ts
// Session.dispose():对局离场时必须调 // bar/main 都是常驻壳节点,挂上去的回溯按钮与 pointerdown 监听 // 不摘掉的话,每开一局就叠一层 —— 旧按钮的闭包还绑着旧 Game, // 点了会污染正在进行的对局。
wireBack 改成幂等:用 Symbol 属性记住旧回调,先 removeEventListener 再挂新。
修完后测试从 8 套扩到 11 套 —— 新增 ending / settings / hardcore 三套,专补审查指出的"测试盲测面"。
horizontal linehorizontal line

三、DAY 14 重头:存档从「回到章首」改成「回到那一刻」

旧语义的问题

原来的存档是章首档:读档 = 回到本章开头。
问题是:一章里有 3–5 轮经营,每轮 3 张牌。 玩家玩到第 4 轮想存档去吃饭 —— 回来发现前 3 轮白打了。

新语义:两件套(v2.2.0)

snapshot  章首快照(供章内回溯用)
live      当前时刻的全量状态(发牌/相位/随机轨迹)

最硬的一块:随机轨迹也要存

原设计有条原则写得明明白白:
回溯恢复历史状态,不恢复随机命运(不存 rng 状态)
理由是 SL 问题:随机数能存能读,玩家就能"穷举读穿骰子"。
但跨会话存档是另一回事 —— 你存了档关掉游戏,明天打开,总不能让世界重新掷骰子。
所以取舍是分开对待:
场景随机数理由
章内回溯不存,重新掷防 SL:可以试错,不可重播
跨会话读档存,原样续接存档是"暂停",不是"重开"
实现上让 rng 可序列化:
js复制
js
f._getState = function () { return a | 0; }; // 取出内部状态 f._setState = function (s) { a = s | 0; }; // 写回
主 rng 与已派生的流(risk / event / corr)都随档存取。

四类相位的 round-trip 测试

新语义最容易错的是"相位"—— 存档时可能停在四种不同相位:
测试存/读的位置
Around —— 第 2 轮已发牌时存;读档后相位/手牌/倒计时/属性/计次一致
B随机轨迹 —— 同一存档读进两个新对局,后续同操作逐位一致
Cchoice —— 抉择中存档;读档仍在抉择页
Dsummary —— 章末结算中存档;读档可推进下一章
B 是最关键的一条:它证明随机轨迹真的随档恢复了。

顺带补的两道校验

① 剧本身份绑定(scenKey) —— 五剧本属性维度同名,原校验只看难度,于是跨剧本的档能导入他本对局(带着别人的 flags 进不可达内容)。
② 脏档拒收 —— 存档存在 localStorage,是不可信输入:
js复制
js
if (!this.d.ATTR_NAMES[ak2]) return false; // 未知属性键 = 跨剧本脏档 if (!numRange(s.attrs[ak2], 0, this.attrMax(ak2))) return false; // 范围须落在本剧本上限内
horizontal linehorizontal line

四、DAY 15:存档页重做 + 设置页改滑杆

存档结构(用户二次拍板)

类型键数量写入时机
自动档qingshi.autos.v1每剧本最多 6 条只有一个:对局页点「返回」时玩家确认
暂存qingshi.stash.v1每剧本只留最近 1 条关页/刷新(pagehide)自动落,防丢兜底
原来的 6 个手动槽位,改成展示这 6 条自动档 —— 不再有手动存。从"玩家自己管理存档槽"变成"系统自动管,玩家只管要不要留"。
槽位还会显示轮次标识(经营过才显示)—— 因为存档现在能精确到轮次了。

设置页:开关+三档 → 横向滑杆

音频三项(音乐/音效/朗读)各一条横向滑杆:拖到最左即静音,
没有独立开关键(2026-10-15 用户拍板,参照常见音量滑杆的交互)。
滑杆左侧的喇叭图标随音量档位变化:0 打叉 / 低无波 / 中一波 / 高两波。
一个实现细节值得记:
拖动期间不重画整行(重画会销毁滑杆、打断拖拽),只就地更新图标与填充。
老存档的三档值(0.3/0.6/0.9)直接映射为滑杆位置 30/60/90,无需迁移。

剧本内容缩减

建了 备份_缩减前_2026-10-15,对五本剧本做了一轮压缩:
剧本行数变化
李斯5,896 → 5,793
荆轲4,838 → 4,803
韩信5,135 → 5,086
项羽4,899 → 4,875
陈胜4,668 → 4,629
horizontal linehorizontal line

五、DAY 16:TapTap 云存档 + 图鉴 + 朝代预告

云存档(game/tapcloud.ts)

这是第一个"平台相关"的模块。运行环境是 TapTap H5 容器注入的全局 tap 对象;普通浏览器没有 tap,本模块全部降级为 no-op。
三个设计决策写在头注里:
① 整包单存档,不打散
本地是「自动档按剧本分桶 + 暂存每剧本一条」的多槽结构,云端不打散成一存档一槽(存档数量有上限,且逐槽上传会撞上「一分钟一次」的频控),而是把两个 store 打包成一个 JSON,对应云端唯一一份名为 qingshi_saves_v1 的存档。
② 冲突取舍:整包新者胜
云端 modifiedTime 比本地最近变动新 → 云端整包覆写本地(覆写前留本地备份);反之上传本地。官方 QA 建议的「逐档对比引导玩家选」对整包方案不适用,多设备同时玩的冲突场景接受这个粒度。
③ 频控:一分钟一次
本地每次写档只标脏(lastChange),由调度器在距上次上传满 60s 后(debounce 1.5s)合并上传;关页 pagehide 时若已满 60s 补一次末班车。
还留了测试挂钩:window.__QS_TAP_UPLOAD_INTERVAL__ 可覆写上传间隔,测试把它缩到 1.5s 级别,不用真等一分钟。

跨局收藏(game/collection.ts)

审查时发现的缺口:成就/图鉴只活在当局内存里,关页面就全丢。
新增跨局收藏,键 qingshi.col.v1.<scenId>,与封存 Lua 版的 col_v1_ 同代不同名(两个平台各自的收藏各自累计)。
脏档收窄不拒收 —— 结局达成记录(endings)是 H5 新增字段,旧档没有它,读取时按空表收窄,不因为缺一个字段就拒收整份。

青史图鉴(ui/gallery.ts)

版式:左栏三 panel(收集总览环 / 印章收集条 / 筛选),右栏按剧本分节的结局卡片(已达成实线卡 / 未达成虚线卡,同名同尺寸不跳动)。
一个取舍:
五个剧本 JSON 在这里会全部按需下载(与首屏瘦身是 trade-off):图鉴页本来就是「我要看全部剧本」的场景,且 loadScenario 有缓存,入过局的剧本不再重复下载。
未达成的结局只留谜面,不剧透名字与赞文。

朝代预告页(ui/teaser.ts)

未开卷朝代(汉/唐/宋/明)在选择页右列展示的内容,替代原先的竖排「准备中」:
① 题头(朝代大字 + 年代 + 英文 + 金线)
② 一句话定位(lede,可带朱色高亮段)
③ 时代关键词(胶囊样式)
④ 开卷进度(考据/编纂/校验/开卷 四节点三态图)
⑤ 即将登场(只显示四字剧名+年代,不显示人物名、无人立绘 —— 用户拍板)
⑥ 底注(回秦提示 + 罗盘提示)

测试扩到 16 套

四天里测试从 7 套扩到 16 套:
chain / viewports / backtrack / save / audio / voice / reveal
ending / settings / hardcore / stash / gallery / tapcloud   ← 新增
scenarios(纯 Node)/ dev-check(专盯 dev 模式)
其中 hardcore.cjs 补的是一个从未被触发过的硬约束:硬核难度不可存档。
tapcloud.cjs 用 Page.addScriptToEvaluateOnNewDocument 在页面脚本前注入一个内存版 tap mock —— 真容器只在 TapTap 客户端里存在。
horizontal linehorizontal line

六、四天的数据

[图片:暂不支持黏贴,请手动导入]
项值
H5 源码6,192 → 约 9,000 行(16 个 .ts 模块)
浏览器验收7 套 → 16 套
engine.js+298 / −64(轮次存档 + 两道校验)
h5 四天改动+3,686 / −1,363
新增引擎方法makeStateRng / makeLiveState / _importLive / _validStateFields
审查三路并行深审 → 5 高危 + 15 中危,当日全部修完
评级5 级 → 10 级(甲乙丙上下中 + 丁)
资产管线4 条(美术 131MB→WebP / BGM 41MB / 配音 249MB / 截图)
horizontal linehorizontal line

七、四天最值钱的三条

① 换载体能一天做完六个页面,不是因为写得快

是因为规则不在渲染层里。 DAY 6 把规则抽成 JS 真源 + Lua 移植,当时只是为了做对拍网。但 DAY 12 换载体、DAY 13 一天重建六个页面 —— 这份"规则与渲染分离"的架构,决定了项目的迁移成本。

② “存/读"不是"保存数据”,是"保存状态机"

做轮次存档时才发现难点不在"把更多字段塞进 JSON",而在:
  • 相位 —— 四种不同相位都要能恢复
  • 随机轨迹 —— 不只是"存几个数",是"让随机流能在另一个时间点续接"
  • 可信边界 —— 存档来自 localStorage,是不可信输入
一个"存档功能"的复杂度,几乎等于它要保存的那个状态机的复杂度。

③ 最危险的 bug,是"注释诊断对了,代码没跟上"

审查报告里最值得记的一条:
最值得警惕的模式是「注释里正确诊断了问题,代码却没跟上自己的诊断」。
懒加载被自检抵消、版本号第三副本 —— 两个都是同一个模式:注释里写着"要注意 X",代码却做了非 X。
这类问题测试抓不到(代码本身没坏,只是没做它声称要做的事),review 才抓得到。
horizontal linehorizontal line

明天

  1. 真机验收 —— 云存档需在 TapTap 容器内实测(普通浏览器只能验降级路径)
  2. 配音全量转码 —— 试点已验证机制,--all 跑全量(约 100MB,谨慎)
  3. 首屏体积 —— 美术已瘦身,再核一遍实际下载量
  4. 成就图标 64 缺失 —— P3 美术批次待补
  5. 审查 backlog 的打磨项(低危清单)
horizontal linehorizontal line
载体:H5(Vite + TypeScript),规则引擎复用 prot/engine.js · 状态:六页面 + 罗盘 + 双音频 + 云存档 + 图鉴 + 朝代预告;16 套浏览器验收 · 存档语义换代(v2.2.0 轮次存档)· 验证:prot 全绿、tsc 通过、浏览器 11/11

#聚光灯gamejam开发者日志
《卡牌工业》开发日志 01|从“涌现”到产线对打截图
《卡牌工业》开发日志 01|从“涌现”到产线对打
大家好,我们正在用 Godot 做一个暂名《卡牌工业》的游戏。目前已经跑通了最小的生产与战斗循环,也做了两轮迭代。这篇先记录一下:面对“涌现”这个题目,我们讨论过什么,又为什么最后收敛到卡牌和工厂。 第一步:头脑风暴,涌现可以是什么? 刚看到“涌现”这个题目时,我们的想法几乎朝每个方向散开了。塔防、物理搭建、探索、解谜、经济系统、叙事,甚至增量游戏,都能让简单规则在玩家的操作中长出新的结果。再想下
1 回复
【童谣镇】聚光灯第二次开发日志截图
【童谣镇】聚光灯第二次开发日志
开发进度: 美术 1、主角人物立绘已细化完成 2、所有NPC的QQ人形象与呼吸动画已设计完成 3、大部分UI界面已设计完成 4、面包房建模与树屋建模以及基础房屋、路灯、树木模型已完成。 5、制作过场动效,制作通用动效材质ing 6、广场资产替换、永续桥和植被的资产替换、房屋资产替换,河流制作 7、学校模型的材质完成 8、石墩子、小道具模型制作ing 9、NPC正比胸像绘制ing 策划 1、任务系统
11 赞
10 回复
《气味之城》开发日志#1|为什么"涌现"和猫是天作之合截图
《气味之城》开发日志#1|为什么"涌现"和猫是天作之合
大家好,我是一个人报名了 2026 聚光灯 21 天游戏创作挑战 的独立开发者。今年主题公布那一刻我就笑出了声——涌现。因为我已经想做这个游戏很久了: 《气味之城》:你不是猫神,你只是一只猫。 先说一个所有养猫人都懂的事实:你永远无法"教会"一只猫任何它自己不想学会的事。但你家猫可能自己学会了——听见冰箱开门就瞬移到厨房;你心情不好时过来踩奶;凌晨三点准时在你身上练马拉松。没人教过它,这些行为是它
3 赞
6 回复
00:07
雨天決行 开发者日志02-人工智能的自我修养截图
雨天決行 开发者日志02-人工智能的自我修养
又和大家见面啦!第二篇日志,说实话一开始不知道该怎么编排,但团队里惊喜的成果越来越多,我们已经迫不及待把更新的内容端上来给大家啦!那么事不宜迟,快来看看都有什么新情报吧ദ്ദി˶ー̀֊ー́ )✧ 一.美术(重磅升级) 以主美小为的生命力作为燃料(bushi)本次美术组取得了大进程!首先是各个ui界面的优化和细化,上次大家看到时还只是初具雏形的草稿,现在大多都完整起来了哦! 同时,随着配色的补全,手
43 赞
36 回复
00:30
《失心》游戏开发者日志01|游戏UI迭代及实机演示截图
《失心》游戏开发者日志01|游戏UI迭代及实机演示
进入《失心》之前,玩家首先看到的是我们工作室 UNLOOK 的标识。我们将原来的标识转成 SVG,并让其中的红色瞳点转动、观察,再逐步显露完整字形,希望这个短暂的开场能带出一点好奇和不安。 这次《失心》的开局 UI,我们围绕小灵通(因为游戏的主要交互道具就是小灵通)做了几轮调整,希望玩家从打开游戏开始,就能感受到故事里的年代和气氛(故事年代背景为2000s)。 早期版本先确定了一个方向:把菜单放进
15 赞
10 回复
00:34
《荒原复苏指南》开发日志④:关卡演示堂堂袭来!!截图
《荒原复苏指南》开发日志④:关卡演示堂堂袭来!!
- 人类曾因掌握元素合成技术而一度辉煌,又因滥用元素能量而逐渐从内里衰败。 - 30世纪80年代。社会文化充斥着激进乐观主义与庞大机械美学,在这里,污染和能源共生,幸福与痛苦并存。官方广播和宣传画里依旧在宣扬“科学将战胜一切”、“元素能量将再造新农田”的口号,而世界主城却已经因为污染过度而濒临崩溃、空间挤压严重。为了寻找新的生存空间与无污染能源,官方启动了“重构拓荒计划”。 - 玩家将作为前线拓展
18 赞
6 回复
00:10
群迹·上取其下,下取其上截图
群迹·上取其下,下取其上
#聚光灯gamejam开发者日志 向大家汇报一下今日进度,目前在和蚁群的默认移动算法进行斗智斗勇。或许是我的AI对蚂蚁有别样的理解,其实最初写出的方案更接近于围绕着恒星公转的行星。 哦我还以为文章里可以插入多支视频视频,想要展示一些其它的运动方式,可惜了。那么就简单在上方传一个比较典型的,有点类似于蟑螂的运行方式。 目前仍在解决蚁群的随机运动,和玩家可操纵性之间的平衡问题。以及如各位所见,目前的
4 赞
1 回复
【2026聚光灯日志01】什么叫你把手牌,骰子和战棋融合了?截图
【2026聚光灯日志01】什么叫你把手牌,骰子和战棋融合了?
大家好啊,我是官方小编,一位还在学校的独自开发的苦逼带学生 这次算是第二次正式参加GameJam,而这回的比赛主题是【涌现】 嗯…老实说,一开始我还真只是从中文语义上来看,理解成“大量的出现”,直到询问ai哦哦哦我这就才想起来还有英文的“整体大于部分之和”意思来着 说起这个主题的意思,立马想起的类 吸血鬼幸存者 ,或者是 文明 系列的各种爆怪,爆兵的设计,即大量的出现敌人或者单位 而从第二个
4 赞
4 回复
开发者日志#01:涌现!! 披着科幻外衣的涌现式增量游戏!!!!截图
开发者日志#01:涌现!! 披着科幻外衣的涌现式增量游戏!!!!
随着Game Jam 主题公示,我们团队立马着手维持了三天的激烈讨论!决定做一个披着科幻外衣的蚂蚁农场以boids算法为灵感的涌现式增量游戏!!! 至此,方案敲定,我们团队将开发一个可升级的自主群体模拟,并且初步规划了玩家游戏时的体验流程: (1)阶段1(开局30s,触发痛点,会有一次提升,但是游戏仍然艰难):一张地图分布着资源点和仓库,玩家此时只能徒手采集资源点的矿石拖入仓库,矿石可以造机器人;
14 赞
7 回复
00:48
Day1 《逆种》截图
Day1 《逆种》
《逆种》 俯视角快节奏近战动作 Roguelite。 你的打法是自然选择,5 层进化的终点,最终 Boss「逆种」是你亲手培育的克星。 一局 8-15 分钟,死亡即收获,你的怪物通过分享码替你和全世界打。 一、九项核心决定 # 决定项 结论 1 战斗手感 近战为主·哈迪斯风(轻三连段/蓄力重击/冲刺割裂/处决) 2 进化透明度 渐进式透明(表型可视化 → 仪式 diff → 扫描仪 DNA 面板)
4 赞
6 回复