下载 App
《变成黑洞》开发日志
为玩家推翻重来设计新玩法,导致游戏全是bug, ai都不听话的那种。以后我慢慢修吧。当前版本可能是最后的版本了。已经将tap tap创造已经开发到极致了。


一、本期主线:一次撞击被当成了无数次
这一期所有问题都指向同一件事:
Impact 被反复调用。
Contact 每帧处理一次接触,但接触记录会被距离判定清掉重建。撞击后两块球贴合在一起(位置重叠、速度相同),PruneContacts 每隔几帧就把记录清掉,下次 broadphase 又发现它们重叠 → Impact 再来一次。
于是出现三个看起来毫无关联的现象,其实是同一个 bug:
| 表面现象 | 真实原因 |
|---|---|
| 高速对撞最后都会融合 | 第二次 Impact 把接近速度从 800 覆盖成 20 |
| 一次对撞喷出上百个碎片 | 每次 Impact 都喷一批碎片 |
| 两块差不多大的球,一方被吃掉 | 反复撞击把一方剥到降阶 |


二、高速对撞必融合
现象
用户反馈「碰撞逻辑有问题」。实测碰撞矩阵:
正心 800 速度 → 融合
正心 1500 速度 → 融合
悬殊 3 撞 6 → 融合
悬殊 3 撞 9 → 融合而融合门槛只有 266(vEsc × 1.25)。800 和 1500 都远超门槛,不该融合。
排查过程
第 1 次尝试:以为是「接触记录被重建,contact.approachSpeed 丢了」。
把接近速度从 contact 搬到天体自身 body.impactSpeed。结果:没用。
第 2 次尝试:加时间窗口,防止 impactSpeed 永久残留。
结果:还是没用。
第 3 次:直接打印 Impact 每次调用的参数。
step=30 speed=813 ← 真撞击
step=53 speed=20 ← 又一次 "撞击"撞击发生了两次。 第二次时两球已经贴合、一起以 400 飞,接触记录重建又触发一次,把 impactSpeed 从 813 覆盖成了 20。
融合判据读到 20 < 255 → 判定「引力束缚」→ 1.5 秒后融合。
修复
只记录更强的撞击,不被低速余震覆盖:
lualocal prevSpeed = a.impactSpeed or 0 if speed >= prevSpeed then a.impactSpeed, b.impactSpeed = speed, speed a.impactSpeedTick, b.impactSpeedTick = step, step end
验证:
step=30 speed=813 采用=true
step=53 speed=20 采用=false
结果:未死亡 ✓

三、一次对撞喷出上百个碎片
现象
用户说「碎石旁边突然冒出一小堆」。实测两块第 5 阶天体(r=161)对撞:
f=3 实体 2 → 42 (新增 40)
f=6 实体 42 → 82 (新增 40)
f=9 实体 82 → 112 (新增 30)一次对撞,实体数从 2 涨到 169。
根因
同第二节:Impact 被反复调用,每次都调用 SprayShards 喷一批碎片。
修复
加撞击节流,同一对天体在 graceTime 内只做一次完整撞击:
luaif (step - a.physicsGraceTick) * fixedStep < graceTime then -- 冷却中:只做速度处理,不剥落、不喷碎片 ShareHeat(a, b, Impulse(a, b, nx, ny, 0, 0)) return end
验证:
f=40 撞击碎片=6 (峰值=6)
f=200 撞击碎片=6 (峰值=6)峰值稳定在 FragmentsPerImpact = 6,不再增长。


四、碎石互撞,一方凭空消失
现象
用户贴图指出「这玩意是碎石与碎石撞出来的碎片」。实测两块等质量自然碎石(各 1728)对撞:
f=22 r1活=false(质量 0) r2活=true(质量 1728 → 3456)3456 = 1728 × 2——一块把另一块吞了,不是碎片。
根因
Collect 的双碎石分支没有质量门槛:
luaelseif aPrey0 and bPrey0 then if b.mass > a.mass then host, food = b, a end -- 大一点点就吃
两块同为 1728 的碎石,浮点差一点点就一方吞另一方。
物理上,两块等大的岩块对撞会各自崩碎或互相弹开,不会凭空合并。
修复
碎石之间也加门槛,复用 BounceRatio:
luaif small.mass / big.mass > rr then contacts[key] = nil return -- 差不多大:互不相吃 end
验证:修复后两块都存活,各保持 1728。
门槛对碎片的影响(确认没误伤):
| 情况 | 质量比 | 结果 |
|---|---|---|
| 两枚自然碎石 | 1.00 | 互不相吃 ✓ |
| 撞击碎片大块(0.40) vs 小块(0.06) | 0.15 | 小块被吸收 ✓ |


五、势均力敌时玩家获胜
需求
「差不多大小的就是玩家获胜。」
第一次修改(失败)
只改了融合路径的判定:
lualocal tie = 质量比 >= PlayerTieRatio if tie and (a.isPlayer or b.isPlayer) then -- 玩家当宿主
测试通过。但用户说「你没改好啊」。
根因:两个门槛互相矛盾
BounceRatio = 0.94 和 PlayerTieRatio = 0.9 在 0.90~0.94 区间给出相反结论:
- BounceRatio:0.94 以下才允许吞
- PlayerTieRatio:0.90 以上算平手、玩家赢
判定顺序写成了「先用 BounceRatio 卡门槛,再判玩家优先」,于是 0.90~0.94 区间被第一道门槛拦下,玩家没赢。
修复:调整判定顺序
lua-- ① 势均力敌 + 有玩家 → 玩家获胜(最高优先级) if (a.isPlayer or b.isPlayer) and massRatio >= playerTie then -- 玩家当宿主 else -- ② 其余:按 BounceRatio 门槛 if massRatio > ratio then return end host, food = big, small end
验证
完全隔离测试(场上只剩玩家和 NPC):
| NPC / 玩家 | 结果 |
|---|---|
| 1.00(等大) | 玩家胜 |
| 1.05(NPC 大 5%) | 玩家胜 |
| 1.08(NPC 大 8%) | 玩家胜 |
| 1.10(NPC 大 10%) | 玩家胜 ← 临界 |
| 1.12(NPC 大 12%) | 玩家输 ← 临界 |
| 1.20(NPC 大 20%) | 玩家输 |
| 1.50(NPC 大 50%) | 玩家输 |


六、天体碰撞应该完全非弹性
一次走错方向
修完「两球撞上粘住」后,我加了 BounceRestitution = 0.35,让它们弹开。用户立刻指出:
「角色之间的相撞哪那么容易被弹开啊,大部分都化成了热量和爆炸对不对。」
确实错了。真实天体撞击的恢复系数 e 在 0.1~0.3,尺度越大越低(星球级 e≈0),绝大部分动能变成热、塑性变形、碎裂。
修复
所有冲量调用统一用 e = 0:
lualocal lost = Impulse(a,b,nx,ny,0, ...) local launch = removed > 0 and lost * 0.2 or 0 -- 20% 给碎屑 ShareHeat(a,b,lost-launch) -- 80% 给热量
删掉了 BounceRestitution 配置和所有相关变量。
验证
初始动能 4.879e10
撞后动能 2.962e5 ← 剩 0.0006%
热量 3.518e10 ← 几乎全部转成热

七、本期的方法教训
三次排查都验证了同一件事:
不要猜,打印出来看。
| 问题 | 我猜的原因 | 实际原因 |
|---|---|---|
| 高速对撞融合 | contact 记录被重建 | 撞击发生两次,第二次覆盖了速度 |
| 碎片喷不停 | (没猜到) | 同上,Impact 被调用多次 |
| 玩家没赢 | 改错了路径 | 两个门槛参数互相矛盾 |
第一、二个问题我猜了两轮都没猜对(先搬 contact、再加时间窗口),最后靠打印 Impact 的每次调用参数才看到 step=30 speed=813 和 step=53 speed=20 这两行。
很多轮排查中,我还遇到一个干扰:改了源码但没构建,跑的仍是旧构建产物,导致「改了没效果」的假象。这一期至少遇到两次。


八、验证
- 高速对撞不再融合(800/1500 实测各自保留质量)
- 一次对撞碎片峰值稳定在 6
- 两块碎石互撞都存活,各保持 1728
- 势均力敌玩家获胜:NPC 大 10% 以内玩家赢,大 12% 以上玩家输
- 碰撞完全非弹性:动能 99.999% 转成热
- LSP 0 Error,构建通过


九、已知边界
- PlayerTieRatio = 0.9 意味着玩家能吃下比自己大 10% 的天体。这是游戏性取舍,不是物理结果。
- 撞击节流用 graceTime = 0.75 秒。理论上短时间内多次真实撞击会被合并成一次,但实际游戏里天体碰撞不会这么密集。
- 食物密度与刷新速度的矛盾仍未解决(第 13 期遗留)。视野内 5 块 + 视野外刷新,玩家找目标太慢。
- 手机实机帧率仍未测。






