这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
《MMO 服务器开发》11.3 好友关系与关系一致性
好友关系是 MMO 里最容易不一致的共享状态:它是双向的,一次只写一半就会留下「半边关系」。本节把关系图建成有向边、把双向一致写死成不变量、用全局唯一的 opID 幂等窗口挡住重发,并给出半边检测与补偿的实现,呼应卷一的幂等窗口与顺序保证,让关系一致从口头约定变成机器可验证的断言。
posts
这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
好友关系是 MMO 里最容易不一致的共享状态:它是双向的,一次只写一半就会留下「半边关系」。本节把关系图建成有向边、把双向一致写死成不变量、用全局唯一的 opID 幂等窗口挡住重发,并给出半边检测与补偿的实现,呼应卷一的幂等窗口与顺序保证,让关系一致从口头约定变成机器可验证的断言。
敏感词与限速是聊天管线的两级治理:前者挡内容,后者挡频率,两者都必须便宜到能挂在每条消息的同步路径上。本节用 AC 自动机实现一次扫描命中所有敏感词、用每玩家每频道的令牌桶限速,再把禁言、限速、过滤串成一条顺序固定、可 go test 的三级管线,并给出本机实测的匹配开销对照。
聊天是 MMO 里最容易被低估的系统:频道种类多、投递范围各异、发言资格还会随等级、队伍与活动变化。本节把频道建模成数据、把投递范围收敛成一份最小世界视图、把权限判断写成纯函数,用三个能 go test 通过的 L1 实现讲清频道模型与权限的边界。
每日任务几点重置,是整条时间链上最容易被时区坑掉的地方:主机在哪个时区、夏令时怎么切、时钟被 NTP 往回拨一下会不会重置两次。本节把「今天」钉在一个固定时区上,用「日键」而不是时间减法判断跨天,并让重置跟踪器只向前触发,最后用真跑的测试覆盖边界、周界与时钟回拨。
战斗里杀一只怪、背包里捡一件东西,凭什么变成任务进度条上的 +1?本节把玩法事件收敛成一个统一的 Event 结构,让进度服务只认「类型、目标、数量」三元组,再演示计数封顶、就绪转换,以及用请求序号做重传去重,保证同一条上报不会把进度加两次。
任务表里一条前置关系画错,整条任务线就会锁死:玩家接了 A 却发现要先完成 B,而 B 又要求先完成 A。本节把任务配置建成有向图,用 Kahn 算法做拓扑排序,用三色 DFS 把设计者画错的那一圈环精确指出来,再用一组真跑的 Go 测试证明正常表能排出合法顺序、环表能被定位到具体节点。
《游戏引擎实现原理》卷三《子系统与工具》导航页:在卷一《引擎内核》与卷二《渲染引擎》之上,用 TypeScript 从零实现让《微光岛》活起来的资源管线与流式、动画与蒙皮、物理集成、音频、输入、脚本绑定与热重载,以及编辑器四章(数据模型、命令栈、属性面板、视口与资产)。讲清本卷定位、与卷一卷二的关系、L1–L4 可运行性口径,按三个部分列出 33 节索引,另含阅读路径、周计划与常见问题速查。
跳跃输入总被『吃掉』?问题多半不在玩家手速,而在手感逻辑。本节实现跳跃缓冲、土狼时间与输入延迟补偿三件套,三者全部是纯时间逻辑,可在 Node 里逐帧喂入真跑,并说明真正的『手感』数值调参仍需在浏览器中手动验证、命令行不可复现,只有逻辑部分可自动化测试。
游戏逻辑不该认识 Space 或 Pad0:ButtonA。本节把动作名与物理控制解耦成一张数据绑定表,支持多设备并行绑定、运行时重绑定、同键冲突检测与版本化持久化,并用 vitest 在本机真跑重绑定、冲突检测与按键配置往返序列化的完整用例。
键盘、鼠标、手柄、触摸四种设备产生的原始事件形状完全不同,游戏逻辑不该为每种设备各写一遍分支。本节把四种设备归一化成统一的 InputEvent,实现帧内事件队列,并区分边沿与电平两种语义,最后用一组真值用例把归一化与队列逻辑在本机真跑一遍。
前两节的混音图与音效池最终要落到 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 个区块,用相机距离排加载优先级、用每帧字节预算限流、用加载与卸载双半径做滞回防抖,写出一个可逐帧推进的流式调度器,并实测单块字节数、峰值驻留与单帧最大派发量,全部来自本机实跑。