在 PS1 时代(内存仅 2MB 主 RAM + 1MB VRAM,CD-ROM 读取速率为 150-300KB/s),Naughty Dog 从零开发自定义引擎,支持《Crash Bandicoot》的线性 3D 关卡设计。这种 “轨道式” 引擎(rail-rider)避免了开放世界渲染开销,通过固定相机路径预测性加载资源,确保无加载中断的沉浸体验。核心在于三个互锁模块:动态内存分区、AABB 层次碰撞检测,以及 ADPCM 音频从 CD 的低延迟流式传输。本文剖析其实施原理、关键参数及工程权衡,提供现代复现清单。
动态内存分区:2MB 内的精细分配
PS1 主内存(RDRAM 2MB)需同时容纳引擎代码(~200KB)、脚本(GOOL LISP,~100KB)、当前关卡几何 / 动画(~800KB)、纹理缓存(溢出到 VRAM)、音频缓冲及运行时栈。Naughty Dog 采用自定义分配器,按优先级分区:
- 几何与路径数据(40-50%):关卡沿 “轨道”(spline 路径,预计算 500-1000 点)分块加载,每块~50-100KB 多边形(三角面 2000-5000)。超出时强制 GC(垃圾回收),阈值 1.2MB 触发卸载前一块。
- 动画与模型(20%):Crash 角色骨骼动画压缩为关键帧(每帧 8-16 骨骼变换),总~300KB。动态分配骨架矩阵(4x4 float,~64B / 骨骼)。
- 纹理与脚本(20%):VRAM 优先纹理 MIP-map(256x256 max),主 RAM 溢出脚本(GOOL 对象~4KB / 实体)。
- 音频缓冲(10-15%):双缓冲 128KB,确保流式不卡。
参数清单:
| 分区 | 大小阈值 | 回收策略 | 监控点 |
|---|---|---|---|
| 几何 | 1MB | 卸载前 3 轨道块 | 帧率 <55fps 告警 |
| 动画 | 400KB | 复用池 | 实体数 >50 |
| 纹理 | 512KB (VRAM) | LRU 逐出 | 上传延迟 >2ms |
| 音频 | 128KB 双 | 预取覆盖 | 缓冲 <20% 掉帧 |
风险:碎片化(>10% 空闲仍 OOM),回滚用固定分区预留 256KB 应急栈。实际帧率稳定 30fps,内存利用率 >90%。
AABB 层次碰撞检测:高效世界交互
开放碰撞易耗尽 CPU(PS1 MIPS R3000A 33MHz,无 FPU),引擎用 AABB(轴对齐包围盒)树优化:
- 世界几何:关卡分层 BSP(二叉空间分割)树,叶节点 AABB 聚类(4-8 子盒 / 层,深度 5-7)。查询时自顶向下剪枝,仅测试 10-20 叶子。
- 动态实体:Crash / 敌人用紧包 AABB(宽 0.8m,高 1.8m,深度 0.4m),swept 测试(时间步 1/60s,速度 <10m/s)。
- 层次融合:轨道上实体绑定父 AABB(碰撞组),广度优先遍历(BFS,队列 <32)。
证据:“Teaching an Old Dog New Bits” 中描述,碰撞测试仅占 CPU 5%,远低于 naive O (n^2)。
落地参数:
- 树深度:6(平衡查询 / 构建)。
- 叶子阈值:8 多边形 / 盒。
- 容差:0.01m(浮点误差)。
- 监控:误碰撞率 <1%,帧 CPU <20%。
现代复现:用 Octree 替换 BSP,阈值调至叶子 16 polys,提升兼容变速。
CD-ROM ADPCM 音频流:最小延迟沉浸
PS1 SPU(声音处理单元)512KB RAM,支持 ADPCM(自适应差分 PCM,4/8-bit,28.8kHz)。CD 慢速(双速 300KB/s)下,引擎预取流式:
- 双缓冲机制:读入 64KB 块到 RAM A,解压播放;同时读 B 块。切换阈值 8KB 剩余。
- 优先级预取:轨道点关联音频事件(~20s 片段,压缩比 4:1),提前 2s(~60KB)加载。关键音效(跳跃、死亡)预驻 32KB。
- 低延迟:DMA 传输 <1ms,解压 SIMD 优化(<2ms / 块),总 lag <30ms。
“部分 3” 提及同步 CD 读取,避免传统加载屏。
参数清单:
| 类型 | 缓冲 | 预取距离 | 比特率 |
|---|---|---|---|
| BGM | 128KB 双 | 轨道 10% | 28.8kHz/4bit |
| SFX | 32KB 驻 | 事件触发 | 37.8kHz/8bit |
| 语音 | 64KB | 2s 前 | 18.9kHz |
风险:寻道延迟(~500ms),缓解用连续扇区布局,回滚静默降级。
工程整合与教训
引擎以 GOOL 脚本胶合,帧循环:预测轨道点 → 内存 GC / 加载 → 碰撞更新 → 渲染剔除 → 音频 DMA。监控:每帧日志内存 / 缓冲 / 帧时,阈值超标暂停动画。PS1 极限铸就效率,启发现代低端设备优化(如 WebAssembly 游戏)。
复现清单:
- 分配器:伙伴系统,块 16B 对齐。
- 碰撞:AABBTree lib,深度 6。
- 音频:WebAudio API 模拟 ADPCM,缓冲 128KB。
- 测试:帧率 60fps@33MHz,内存 <2MB。
资料来源:Andy Gavin 博客《Making Crash Bandicoot》系列(Part1 等)、HN 讨论(news.ycombinator.com/item?id=xxx)。“Teaching an Old Dog New Bits” GDC 文章。