下载 App
《变成黑洞》开发日志
日期:2026-10-21 代码规模:13086 行 / 19 个文件


一、本期主线:把「同一个概念的多套实现」逐个收敛
这一期没有新增大玩法,全部工作量都花在消除重复上。 每修一个 bug,根源几乎都是同一句: 同一个概念在多处各写了一遍,谁最后执行谁生效。
| 概念 | 曾经有几套 | 现在 |
|---|---|---|
| 碎石半径 | 4 套(Collision.Sync / CosmicPhysics 碎片 / CosmicPhysics 尘云 / 主角色) | 1 套 |
| 玩家与 NPC 外观 | 2 套(玩家硬编码颜色 + NPC 走样式表) | 1 套 |
| 融合演出形态 | 3 版(两半撕裂 → 串珠 → 连续潮汐) | 1 套 |
| 进度条 | 2 条并排 | 1 条 |
| 进化判定 | 混合(第 1 阶按口数,其余按质量) | 统一按口数 |


二、碎石尺寸:四种大小同时存在
用户贴图指出「同为碎石,为什么大小不一样」。
实测同一颗 1728 质量的碎石,屏幕半径出现了四个值:
| 来源 | 公式 | 半径 |
|---|---|---|
| Collision.Sync 碎片分支 | (5 + 4·ln(1+m/125)) × 8 | 126.3 |
| CosmicPhysics.Sync 碎片分支 | (5 + 3·ln(1+m/125)) × 8 | 106.2 |
| CosmicPhysics.Sync 尘云分支 | (5 + 2·ln(1+m/125)) × 8 | 86.0 |
| 主角色碎石(非碎片) | Collision.Radius 按 visualR | 62.0 |
收敛为一条:Collision.FragmentRadius(mass, refMass)
- 锚定阶段表:质量 = 第一阶门槛时,半径 = 第一阶 visualR(62)。
- 物理层不再自己算,只调 Collision.Sync。
碎屑大小层次(方案 A)
等分质量会让碎屑像「撒豆子」。改为幂律分配:
luaDebrisSizeMix = { 0.40, 0.20, 0.20, 0.07, 0.07, 0.06 }
两个约束同时满足:
- 动量守恒:一对碎屑严格等质量、严格反向。第一版做成「对内不等质量 + 速度反比」, 回归直接测出偏差 1.5e9;第二版改成对等质量、对间不等。
- 半径层次:碎屑质量跨度 3 个数量级(1728 → 46 万),纯三次方根会全部撞上限。 改为同批次归一化——最大块映射到上限,其余按 (m/max)^(1/3) 缩放。
实测:
464359 → 109.3
282194 → 92.6
135871 → 72.6半径比 1.51,质量比开立方 1.51,大小关系真实。


三、玩家与 NPC 外观统一
用户指出「玩家和 NPC 都不一样」。
查完发现两处分裂:
颜色来源
| 玩家 | NPC | |
|---|---|---|
| 岩石色相 | 硬编码 hue=205(蓝) | KIND_STYLE.rock = 30(暖棕) |
| 贴图键 | s01 | rock / gas |
| 球面特征点 | 无 | 有 |
绘制分发
- NPC:DrawEntityBody 按 e.kind
- 玩家:DrawPlayerAvatar 按 stage.form + 硬编码颜色
收敛为 Environment.ApplyStageVisual(e, stage) 唯一入口:
- 玩家在 StartRun 与 CheckEvolution 调用
- NPC 在 NewEntity 调用
- 玩家绘制改为与 NPC 相同的 kind 分发,删掉硬编码颜色
验证:12 个阶段结构参数 0 处不一致;同阶段 200 次采样,边数/色相/贴图完全同分布。


四、融合演出:从「掰饼干」到连续潮汐剥离
用户反馈「融合动画不帅也不真实」。前后改了三版。
第 1 版:两半椭圆(被否)
把被吞方撕成两个并排的椭圆盘——观感像「饼干被掰开」, 而且前一拍 1 个、后一拍突然变 2 个,突兀。
第 2 版:串珠(被否)
用多个球沿轴排开模拟拉伸。当时的理由是「nvgScale 会破坏贴图」—— 这个判断是错的。做对照实验后确认:单球 nvgScale(1.6, 0.7) 完全干净。
那块一直消不掉的白斑,真正来源是物质流的实心宽线画在了球体内部。 我先后怀疑过光照、贴图采样、串珠重叠、裁剪,绕了四次弯路, 最后靠「注释掉一段看现象是否消失」定位。
第 3 版:连续潮汐剥离(当前)
科学依据:被吞方进入宿主洛希极限后,沿连线方向被拉长, 物质从近端溢出(洛希瓣溢流),沿螺旋坠入宿主。本体全程连续,不断成两块。
| 阶段 | 表现 |
|---|---|
| 潮汐拉伸 | 单球 nvgScale,长轴 1.0 → 2.2,短轴 1.0 → 0.45 |
| 物质流 | 碎屑从拉伸后的尖端沿贝塞尔流入宿主(不是球心) |
| 吸积 | 宿主表面涟漪 |
| 剥离 | 最后一截掐断 |
两个关键修正:
- 时间曲线 t^1.6:前慢后快,越接近越剧烈。
- 宿主重画:真实融合时两球中心距 = r_A + r_B(刚好接触),拉长后必然重叠。 重叠是物理正确的,只需在最后重画一次宿主把伸进去的部分盖住。


五、进化节奏:所有阶段统一 20 口
用户反馈「陨石的进度条走不动」。
根因是质量门槛每阶跨 10~29 倍,而碎石固定 1728: 陨石 → 小行星要吃 986 颗,吃一颗进度只涨 0.10%。
改为所有阶段统一 EatsPerStage = 20:
- 未吃满:阶段钉住不动
- 吃满:质量补齐到下一阶门槛,正常跨阶
验证:
吃19口 → 阶段1 未进化
第20口 → 阶段2 进化进度条每口 +5%。


六、虚拟世界的一条物理定律:吸收 ≠ 碰撞
用户指出「其他阶触碰碎石只触发吸收,不触发碰撞」,并且强调「零碰撞判定」。
排查发现两层问题:
物理层:CCD 命中时会把高速天体拉回接触点再吃——速度被改、位置被修正, 体感就是「撞了一下」。改为在位置回退之前拦截,确认接触后直接吃。
表现层:OnMerge 对每次吞噬都放撕裂环、震屏、撞击音效—— 吃一颗碎石也炸一圈。改为按「被吞方是否食物」分流: 碎石吸收只保留积分和一声轻音效,无撕裂环、无震屏、无融合动画。
过程中回归测试抓到一个严重错误:第一版拦截条件把「stage≤1 的玩家」也判成猎物, 导致玩家反被第 2 阶天体吞掉(质量归 0)。修正为「玩家永远不是猎物」。


七、其他修正
- 碎石阶段 10 口进化:质量口径下 1728 → 60236 要吃 34 颗,前 30 秒全是重复劳动。
- HUD 质量数字删除:改成进度条。质量是内部刻度,写出来既误导又难和真实天文对照。
- 碰撞碎屑位置修正:碎片此前从两体中点 + 较大者半径外推,玩家撞恒星时会浮在大球内部; 方向绕 360° 均分,观感是「环绕爆炸」。改为从接触点崩落、成对反向沿切面甩出。
- 黑洞无敌:任何天体都撞不掉黑洞的质量,除非黑洞打黑洞。


八、验证与已知边界
- 纯 Lua 回归(sparse_food_regression.lua)全部通过: CCD、三档撞击净增、共享预算、储备收支、地标行为、食物铁律、动量/能量闭合。
- LSP 全程 0 Error。
- 实测数据:
- 同阶段玩家/NPC 结构参数 0 处不一致
- 碎屑半径比 = 质量比开立方(1.51 = 1.51)
- 吸收碎石:撞击 0、撕裂环 0、融合动画 0、震屏 0
诚实边界
- 融合动画的物质流在紧凑接触时几乎看不见(两球贴合没有跨度)。 真实对局中被吞方是逐渐靠近的,融合前有间隙,但手感还要实机验证。
- 手机实机帧率仍未测。
- 小行星(第 3 阶)与��行星(第 4 阶)外观区分度不足——两者共用 kind=asteroid, 第 4 阶按设计是「引力压圆」,但画面上容易混淆。


九、两条教训
1. 修共用函数前,先查它的所有调用方
「碎石大小不一」本该只改碎屑路径,我却改了 Collision.Radius—— 那是玩家也在用的公共函数,结果第 1 阶玩家被推进碎屑公式, 长到比第 2 阶还大,进化时半径反而回落。
这和「玩家与 NPC 不一致」是同一类错误:改了共用代码,只想着当前那条分支。
2. 用打补丁做视觉验证,必须带可搜索标记
为了截图,我往正式代码里注入了「锁第 6 阶」的临时代码。 清理时按「含 SHOT 字样」删除,漏掉了两处不含该字样的, 导致玩家一开局就是类地行星。
以后临时注入统一加 -- @TEMP 标记,清理按标记删,不靠记忆。
3. 排障要用二分,不要猜
那块白斑我先后猜了光照、贴图采样、串珠重叠、裁剪四次,全部猜错。 正确做法是早一点做「注释掉一段看现象是否消失」—— 最后就是这么定位的,但花的时间远超必要。






