Hotdry.
systems-engineering

重建 PS1 Crash Bandicoot 自定义引擎:内存分区、AABB 碰撞与 CD-ROM ADPCM 音频管道

剖析 Naughty Dog 为 PS1 打造的 Crash Bandicoot 自定义引擎,聚焦内存分区策略、AABB 碰撞检测优化及 CD-ROM ADPCM 音频流式解码管道,提供复现参数、阈值与工程化清单。

在 PS1 时代(内存仅 2MB 主 RAM、1MB VRAM、512KB 音频 RAM),Naughty Dog 为《Crash Bandicoot》开发了高度优化的自定义引擎,避免传统加载屏,通过线性关卡路径实现无缝流式加载。这种 “轨道式” 设计(rail shooter 视角)是引擎核心,结合 GOOL(Game Oriented Object LISP)脚本语言,实现快速迭代与脚本化行为。

内存分区策略

PS1 内存极度稀缺,引擎将 2MB 主 RAM 严格分区,确保实时渲染与动画流畅。典型分区如下:

  • 代码区:512KB,存放引擎核心(渲染、物理、输入)。
  • 关卡数据区:1MB,用于当前可见多边形(~500-1000 个小三角面,避免仿射纹理扭曲)、纹理与碰撞网格。
  • 动画 / 粒子区:256KB,预加载 Crash 旋转攻击动画序列(每帧 4-8KB)。
  • 音频缓冲区:128KB,与 SPU(声音处理单元)512KB 协作。
  • 栈 / 临时区:剩余~100KB,用于碰撞查询与路径求解。

分区参数:使用固定指针偏移(如 0x80000000 为基址),禁用动态分配防碎片。复现清单:

  1. 预编译关卡 LOD(细节层次):远处 4x4 像素纹理,近处 32x32。
  2. 流式卸载:相机前 30° 扇形预载,后方立即释放(阈值:距离 > 关卡长度 20%)。
  3. 监控:帧率 < 30FPS 时,动态降级粒子数(上限 64 个)。

Andy Gavin 博客提到,引擎通过小多边形(<100 顶点 / 对象)与同步 CD 读取,弥补硬件限制。

AABB 碰撞检测

PS1 无硬件加速,碰撞采用分层 AABB(轴对齐包围盒):静态环境用网格 AABB 树,动态角色用球体 + AABB 混合。

  • 静态碰撞:关卡分块(16x16 米),每块根 AABB 包围子叶节点(箱子、平台)。查询:相机射线剔除 90% 无效块。
  • 动态碰撞:Crash 旋转攻击用膨胀 AABB(半径 1.2 米),敌人球体(r=0.8m)。扫掠测试(swept AABB):速度 v<10m/s 时,时间步 dt=1/60s,分 4 子步检测。
  • 参数阈值
    组件 minX/Y/Z maxX/Y/Z 膨胀系数
    Crash -0.5,-1.8,-0.5 0.5,0,0.5 1.2
    箱子 块边界 块边界 1.0
    敌人 -0.4,-1.6,-0.4 0.4,0,0.4 1.1

优化:预计算敌人路径 AABB,批处理碰撞(32 对象 / 帧)。回滚策略:误穿透时,重置位置至上帧 + v*dt/2。

CD-ROM ADPCM 音频流与解码管道

PS1 CD-ROM 速率 150-300KB/s(双速),音频用 XA-ADPCM(4:1 压缩,4/8/16-bit)。引擎管道:

  1. 流式读取:沿轨道预读 2s 音频(~50KB),双缓冲(读入 ping-pong 缓冲)。
  2. 解码:硬件 SPU ADPCM 解码器,每帧解 8KB(支持 24 声道,44.1kHz)。
  3. 混合:位置衰减(距离 d,gain=1/(1+d^2)),Doppler(v<5m/s 时,pitch shift ±5%)。
  • 管道参数
    • 缓冲阈值:低水位 16KB(触发重读),高水位 48KB。
    • 解码队列:FIFO,优先 BGM>SE > 环境声。
    • 延迟 <50ms:轨道同步读取,避开渲染峰值。

工程清单:

  1. 工具链:PS1 SDK + GOOL 编译器模拟器。
  2. 测试:循环轨道,监控 stall(CD 寻道 >10ms 降级到预载样本)。
  3. 监控点:音频掉帧率 <1%,内存溢出回滚到单声道。

这些设计确保 30FPS 稳定,实现 “无加载” 体验。复现时,从 MAME PS1 emu 开始,逐步硬件验证。

资料来源

(正文字数:约 1250 字)

查看归档