这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
《音视频与媒体:video_player、音频播放与相机集成》
从 video_player 的播放器生命周期、just_audio 的音频焦点与后台播放,到 camera 插件的预览与拍照录像,系统梳理 Flutter 音视频与媒体能力的选型、平台配置、性能优化与常见踩坑,附可直接复用的 Dart 代码与 pubspec 依赖片段。
posts
这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
从 video_player 的播放器生命周期、just_audio 的音频焦点与后台播放,到 camera 插件的预览与拍照录像,系统梳理 Flutter 音视频与媒体能力的选型、平台配置、性能优化与常见踩坑,附可直接复用的 Dart 代码与 pubspec 依赖片段。
《游戏引擎实现原理》卷一附录 D:汇总跟读本卷时最容易踩的坑与高频疑问。覆盖 erasable syntax 的写法替代、句柄失效的排查、内存布局的对齐陷阱、基准数字为何波动、Job System 在 Node 与浏览器的差异、反射为什么不用装饰器元数据,以及「怎么调试」这类实操问题,每条给出原因与解决方向。
《游戏引擎实现原理》卷一附录 B:跟读本卷所需的环境准备与代码复现方法。列出实测的 Node / TypeScript / vitest 版本,给出从零搭建 playground 的完整步骤、推荐的 tsconfig 与 package.json 并逐个解释编译选项、各层代码(L1 运行、L2 无头浏览器、L3 类型检查、B 基准)的跑通命令,以及常见环境问题的排查清单与目录结构约定。
《游戏引擎实现原理》卷一《引擎内核》全书导航页:说明本卷定位、读者画像与前置知识、学习节奏与三条阅读路径,交代贯穿项目《微光岛》与从零引擎 helio,讲清 L1–L4 可运行性口径,并按五个部分列出 18 章 54 节的完整章节索引,另含序、前言、附录 A–D 与常见问题速查表,方便读者按需定位、跳读与随时查阅。
《游戏引擎实现原理》卷一序言:在现成引擎随手可得的今天,为什么还要从零写一个引擎内核。文章用三个真实痛点说明「自己写一遍」换来的判断力,讲清本套书选择 TypeScript 的理由、这一卷写什么不写什么、它和站内既有专题的分工,并给读者一段开始前的鼓励。
《游戏引擎实现原理》卷一前言:交代本套书的技术选型理由(为何是 TypeScript 而非 C++、为何以 WebGPU 为渲染目标)、L1–L4 可运行性口径的含义与验证方式、基准数字的诚实声明、代码块标注约定,以及跟读本卷所需的环境准备与目录篇、附录的配合读法。
稀疏集用 sparse 数组把稀疏 ID 映射到 dense 下标、dense 数组紧凑存值,换来 O(1) 增删查和一段连续内存上的遍历。本节实现可编译的 SparseSet,用 swap-remove 做句柄回收,并断言删除之后 dense 依然无空洞、遍历次数恒等于元素个数。
把句柄编码成「低 20 位槽位索引 + 高 12 位世代」的 32 位整数,槽位复用时世代自增,旧句柄的世代对不上就立刻失效。本节给出 helio 内核里可编译的 SlotMap 完整实现与 vitest 用例,并用「删除后再复用」的断言证明旧句柄再也取不到值。
对象被回收或移动之后,旧引用要么读到 undefined,要么更糟——读到恰好复用了同一块内存的另一个对象。本节用 TypeScript 复现裸索引句柄的三种失效形态,说明内核为什么必须用「索引 + 世代」的句柄替代裸指针,并给出可运行的验证用例。
本节给每笔分配挂上 Rendering/Audio/Physics/Gameplay 等内存标签,用 MemoryTracker 统计各标签的当前与峰值占用并按预算告警;再用分配计数快照做帧级泄漏检测,真跑制造一次忘记 release 的泄漏并当场抓出。
本节在固定 ArrayBuffer 上实现按 2 的幂分大小类的块分配器 PoolAllocator:16/32/…/1024 每类一条空闲链表,支持任意顺序 free;断言分配-释放-再分配复用同一块,并统计内部碎片率与各类占用,是对象池在字节层的对应物。
本节用空闲链表实现定长对象池 ObjectPool<T>,支持 acquire/release 与耗尽时抛错或扩容两种策略;并在本机真跑 10 万次构造,对照 new/GC 给出耗时与堆增量,诚实讨论池化反而更慢的场景,最后总结何时才该池化。
本节把顶点结构 [pos.xyz][pad][uv.uv][color.rgba] 手工布局到一块 ArrayBuffer 上,讲清 std140 的 16 字节对齐规则、padding 从哪来、以及「CPU 紧打包」为什么会让偏移错位;并用可运行代码算出结构体大小与步长,再补上 WebGPU 的 bytesPerRow 必须是 256 倍数这一约束。
本节从零实现 helio 的线性 arena 分配器:一块 ArrayBuffer + 一个只前进的偏移量,讲清 bump 分配、按 2 的幂对齐、highWaterMark 与 reset;再在其上搭出 ScratchArena 的 mark/release,为「帧内临时内存」提供一次性回收。全部代码经 tsc 与 vitest 真跑验证。
本节把「为什么要自己管内存」从「避开 GC」纠正为「控制分配节奏、让布局可预测」,并用一个真跑过的基准说明差异:1M 个四字段对象在 JS 堆上占约 61 MB,而 6 条 1M 元素的 TypedArray 堆增量是 0.0 MB——因为 TypedArray 的 buffer 内容不走 JS 堆。
内核要能在出错时立刻喊停、在发布后按需静音、在启动时被命令行改参数。本节用 TypeScript 从零实现 helio 的三件基础设施:分 debug/release 两档的 assert 与永远生效的 check、五级加分类过滤的 Logger、带类型化 get/set 与命令行解析的 CVarSystem,并在 Node 里真跑验证。
内核的其余部分不应该知道 node:fs 或 window 的存在。本节定义 helio 的 IPlatform 接口(时间、文件、线程、日志四个能力面),给出 NodePlatform 的真实实现,说明浏览器 WebPlatform 的差异,并真跑一段「同一份 measureMillis 代码在 PAL 上读时间戳」的用例。
引擎启动时几十个子系统谁先谁后不是拍脑袋定的。本节用 TypeScript 从零实现 helio 内核的 ISubsystem 接口与 SubsystemRegistry,用 DFS 三色标记做依赖拓扑排序、检测循环依赖,并保证关闭顺序严格是启动顺序的逆序,最后在 Node 里真跑断言把顺序验出来。
固定步长让物理与渲染解耦,但游戏逻辑时间本身还需要被缩放。本节实现 TimeService:用 timeScale 做慢动作、paused 做暂停,并把逻辑 delta 与 UI delta 拆成两套,真跑出「暂停时逻辑步为 0 而 UI 动画仍推进」的结果。
上一节量出的帧时间抖得厉害,直接喂给物理会跑出不确定的结果。本节实现 FixedStepLoop:用累加器把不均匀的 dt 变成均匀的物理步,再用 alpha 做渲染插值;并真实复现死亡螺旋,用 maxStepsPerFrame 把它钳制住。