这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
《游戏引擎实现原理》5.3 Web Audio 的限制与解锁
前两节的混音图与音效池最终要落到 Web Audio 上,而它有两条硬限制:自动播放策略会挂起 AudioContext,必须靠用户手势 resume;时间精度与延迟又受渲染量子与设备缓冲支配。自动播放解锁与真实设备只能在浏览器里手动验证,但秒帧换算、量子量化、过去时间钳位、延迟补偿这些算术可以抽成纯 TS 真跑。
posts
这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
前两节的混音图与音效池最终要落到 Web Audio 上,而它有两条硬限制:自动播放策略会挂起 AudioContext,必须靠用户手势 resume;时间精度与延迟又受渲染量子与设备缓冲支配。自动播放解锁与真实设备只能在浏览器里手动验证,但秒帧换算、量子量化、过去时间钳位、延迟补偿这些算术可以抽成纯 TS 真跑。
混音图解决了「怎么求和」,这一节解决「同时能响几个」。用固定容量 voice 池做音效管理,配优先级抢占与线性淡入淡出包络避免爆音;BGM 则走流式:解码器按源采样率吐 PCM 分块,混音器按设备采样率拉帧,中间的线性插值重采样与欠载检测都抽成纯 TS 真跑并配 vitest。
把「音源 → 总线 → 主输出」建模成一张有向图:节点带线性增益,边是数据流,渲染一个量子时按拓扑序累加、逐节点乘增益。先用纯 TS 真跑分贝换算、等功率声像、混音求和与总线路由,再把同一张图放进 headless Chromium 的 OfflineAudioContext 复算,逐样本比对 Web Audio 与 L1 的输出。
边界上最后一块职责是回答引擎的提问:射线先打到谁、这个区域内有什么、A 和 B 是刚开始接触还是早就碰着。本节用位掩码做碰撞分层,实现 raycast 与 overlap,再把物理库给的接触列表差分成 enter / exit / stay 三态事件,全部可在 Node 直跑。
物理对步长极其敏感,所以引擎侧必须做三件事:用固定步长累加器推进逻辑,用上一态与当前态之间的插值喂给渲染,用扫掠 AABB 在位移超过自身体积时兜住高速穿透。本节复用内核的 FixedStepLoop,把这三件事写成可跑的 TypeScript,并用 vitest 真跑过。
本节不写求解器,只把引擎与物理库之间那条线画出来:用判别联合描述碰撞体形状,用一个 PhysicsBackend 接口把 Jolt、Rapier 或自研后端挡在下面,再用 ColliderSync 只把版本号变过的实体推给物理侧。全部纯 TypeScript,可在 Node 直跑并跑通 vitest。
把姿态接到骨骼上:由绑定逆矩阵算蒙皮矩阵,实现 CPU 线性混合蒙皮,再把这套数学搬进 WGSL 顶点着色器,在 headless Chromium 里与 CPU 逐顶点比对。最后本机实测蒙皮相对静态网格的代价——4 影响蒙皮约为静态变换的 6.3 倍。
状态机决定「现在放哪段动画」,混合树决定「两段动画之间怎么过渡」。从零实现参数驱动的状态转移(条件、exitTime、any-state、trigger 消费)、两段姿态的交叉淡化与一维混合树权重,全部 L1 纯 TypeScript 并带 10 条单测。
把一段动画拆成剪辑、轨道与关键帧,从零写出 helio 的关键帧求值器:时间轴钳制/循环/乒乓三种环绕、关键帧二分定位、step/linear/cubic 三种插值与旋转 slerp。全部为 L1 纯 TypeScript,可在 Node 直跑并带 14 条单测。
《微光岛》的岛不是一整块资源:本节用 TypeScript 把它切成 4x4 个区块,用相机距离排加载优先级、用每帧字节预算限流、用加载与卸载双半径做滞回防抖,写出一个可逐帧推进的流式调度器,并实测单块字节数、峰值驻留与单帧最大派发量,全部来自本机实跑。
构建系统凭什么知道该重导谁:本节用 TypeScript 实现内容哈希(FNV-1a 64)、导入器版本与依赖键沿 DAG 的传播,让增量构建只重导真正脏掉的子树;再加产物哈希校验与热重载级联派发,全部用真实构建日志与缓存字节数验证,无一估算。
美术交给引擎的是源资源,运行时却不能直接吃:本节用 TypeScript 从零实现 helio 的资源导入器与离线处理——注册表按扩展名分派、版本号驱动失效、PPM 解码、BC1 块压缩、mip 链生成、自描述二进制产物与元数据,并实测真实压缩比与 BC1 往返误差。
资源加载进来之后,谁拥有它、什么时候可以扔?本节从零实现资源级的引用计数表:acquire 加引用、release 减引用、归零不立刻释放而是挂进待释放队列、帧末 collect 统一释放。用一个「材质释放时级联释放它持有的 mesh」的场景证明延迟释放如何避开 re-entrant 释放,并量出本机约 2100 万次 acquire/release 每秒。
依赖图告诉引擎「先加载谁」,但真正的加载是异步并发的,还要让 UI 看到进度。本节从零实现带并发上限、优先级队列、in-flight 去重与字节进度回调的异步加载器,用注入的假 IO 在 Node 直跑,量出并发从 1 到 8 时加载 64 个资源的墙钟耗时几乎线性下降,并解释为什么进度条要按字节而不是按个数。
资源管线的第一课是把「资源」本身建模清楚:一个 GUID、一个类型、一条路径、一张依赖表。本节从零实现 helio 的资源模型与依赖图,用 Kahn 算法算出「依赖在前」的拓扑加载序,用 DFS 三色标记检测循环依赖,再给出反向边供后续热重载定位受影响资源。全部代码在 Node 直跑,11 条用例锁住行为。
讲迁移的验证与回滚:本机 4.1.1 主线与 3.5.16 对照线并行的意义、编译到灰度的分批验证判据、数据格式与 API 契约这类不可回滚的改动、特性开关与双跑降险、回滚演练与工程化护栏,以及反射配置、序列化与时区默认值等最易漏的问题和放弃迁移的判断标准。
离线收益与体力是同一类问题的两面:玩家下线后时间还在走,服务端要在他回来时把这段时间补上。本节从零写 internal/attr 的 Stamina 与 SettleOffline,用整数周期推进避免短会话丢点,并处理离线封顶、时钟回拨与满体力不攒点三类边界,全部 go test 验收。
等级是成长的核心状态机:一次经验结算可能连升多级、可能撞到满级、还要把溢出经验正确结转。本节从零写 internal/attr 的 Curve 与 Progression,用相位 idle/levelup/max 约束迁移,并用 go test 钉住连升、结转、封顶与非法迁移四类边界。
属性是玩法系统的地基,装备、buff、称号都会改数值,聚合顺序写错就会算错伤害。本节从零写 internal/attr 包,用万分比定点做「平加 → 加算百分比 → 乘算百分比」三层聚合,实现同类取最强与缓存失效,并复用卷一 CmdAttrSync 0x0103 做增量同步,附 go test 验收。
装备是背包里唯一同时带「位置」和「个体属性」的东西。本节实现装备位与换装(旧装备换下回背包),给出按等级配置成功率、材料消耗与保底等级的强化表;强化在同一事务里扣材料、失败掉级、材料不足整笔回滚,随机源由调用方注入以保证可复现,产出的强化等级作为第 9 章属性计算的输入。