惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - sun_dust_shadow

(翻译) 延迟补偿方法的协议设计与优化 Latency Compensating Methods in Client/Server In-game Protocol Design and Optimization (翻译) Dota2 Random Distribution Dota2中的随机分布 (个人思考)游戏技能的实现 - sun_dust_shadow (读书笔记)平衡掌控者 [瞄准辅助] 实现一种柔和平滑的瞄准辅助 (个人思考)实现游戏GAS系统中的Tag (翻译) Efficiency Tips on Switching Spaces and Transformation Matrices in Unity (翻译) V Rising's Animation Layering in Unity (翻译 unity2020.1) Understanding the managed heap (UnityEditor Tool) 语音控制unity editor暂停和播放 (翻译 ue gas) Gameplay Ability (翻译 ) Source Multiplayer Networking Source引擎的多人网路系统 (翻译 gafferongames) Networked Physics in Virtual Reality VR中的网络物理 Topdown游戏中Input朝向的转化 (翻译 gafferongames) Client Server Connection 客户端服务器连接 (翻译 gafferongames)Reliable Ordered Messages 可靠有序消息 (翻译 gafferongames) Sending Large Blocks of Data 发送大块数据 (翻译 gafferongames) Packet Fragmentation and Reassembly 数据包分片与重组 游戏中Shotgun(喷子)发射子弹的实现
总结 Overwatch Gameplay Architecture and Netcode 守望先锋的游戏架构与网络代码
sun_dust_sha · 2025-04-09 · via 博客园 - sun_dust_shadow

原视频

https://www.youtube.com/watch?v=W3aieHjyNvw&list=PLG9sbQS_QV1-6MnaNaN-uO5fUkaTocN58&index=4&t=2901s

翻译链接

https://www.lfzxb.top/ow-gdc-gameplay-architecture-and-netcode/

https://www.sohu.com/a/148848770_466876

还是看视频和中文解析吧。我自己翻译起来太麻烦了,主要是截图浪费时间, 上面的翻译链接已经足够好了。

这里我截图并且总结一下关键点。

选择ECS 

和传统的Actor model和最近的 Component model不同,ECS是一个独立的结构 Entity是一个ID,类似数据库查询的一个ID,可以找到这个ID对应的所有Component数据。

ECS 架构能够在快速增长的代码库上管理复杂性

ECS的结构解释

Component是一个纯数据(State状态),无任何逻辑(Behaviour行为)

System是一个纯逻辑,无任何数据

在一个World中包含N个System和M个Entity

多个System的Tick有Order执行

一个System可能需要多个Component,去组成一个ArchType

System并不关心Entity,System只关注Entity上的部分Component Tuple

 下图代码举例,去解释上面的System和Component关系,System Behaviour如何与Component data关联起来

下图:World  Entity System 三者之间的关系

EntityAdmin(World)里有

1.System数组

2.EntityId的映射,并且通过这个ID可以找到Entity包含的所有Component数。同时我们要管理不同Component的生命周期

3.System里会关联多个Component,如下代码图

举反例说明使用System Behaviour执行AFK判断的优点:

下面代码图是ECS实现的Connection State的判断

 如果使用OOP或者ComponentModel,我们要把Connection State的更新逻辑放到哪个模块呢?这么多和Connection有耦合的模块。

ConnectionState不是Behaviour,只是State。

OOP的每个Component是Behaviour+State

制作ECS遇到了2个问题,1.system里存储数据了  2.system里耦合其他system,并且需要读取system的数据

创建了一个 global Entity admin,然后可以获取system。

这造成了扩展困难。

当在实现回放的功能时,这个问题暴露出来了。

两个world: liveGame和replayGame

replayGame: server发送一个8-12秒的网络数据到client,然后像正常游戏一样工作。细节需要看另外一个分享。这里没有展开

取消了之前的做法:创建一个Shared EntityAdmin

新的规则:只有一个Admin (world).

这个Admin里可以有 Singleion Components。这些Singleion Components存在一个匿名的single Entity上。然后Admin可以直接访问这个AnonymousSingleEntiy,去获取上面的SignleComponent

举例 Input的数据作为一个SingleComponent,其他System读取这个InputComponent数据,去同步给Server 去执行本地的移动等。

SingleComponent大改占有了40%

在使用了ECSingletonInput数据后,我们就不存在System之间的耦合了

多个system Behaviour去共享static function。

这里的 side effects,我觉得可以理解为“写数据、发送网络请求”之类的意思,可能会影响这个Component数据的值。

举例:服务器和client都会调用相同的 static movement funciton,去修改entity的 position

两种共享utility functions的情况

简化你的行为(SIMPLIFY YOUR BEHAVIORS)

  • 在单一调用点表达行为(Express in a single call site)   把一个行为的逻辑集中在一个函数调用的地方表达出来,而不是在很多地方分散处理。

  • 将主要副作用局部化到该调用点(Localize major side effects to that call site)  副作用是指函数执行时产生的额外影响,比如修改状态、打印日志、发出网络请求等。这里强调的是,**把这些副作用集中在一个地方(调用点)**处理,而不是让副作用“到处散落”

延迟处理 Deferment

如果多个system都要用到一个Component数据

我们可以使用singleComponent, pending后延迟处理这个数据带来的效果

存储并推迟调用:storing the state required to invoke major side

下图是举例说明上面的延迟处理概念:

Singleton Contact 单例Contact效果

作者举例:枪械和贴花在墙面上的特效,为了避免在同一片区域多种贴花效果z fighting然后和TA battle的问题,使用了下图的方式。

1.先有个PendingContact数组

2.有耦合的system点,向pendingContact数组里添加数据

3.在最终的ResolveContactSystem里执行(lod mix等)效果

有些数据流的感觉,前面都是修改数据,最后才归纳所有数据,分析执行,得到最后的效果

优点除了解除耦合外,如果做异步加载也很容易(大量相同的effect延迟分段生成)。

Overwatch左上角的信息

    • FPS: 70
      当前的帧率(Frames Per Second)为 70,表示画面每秒刷新 70 次。数值越高,游戏越流畅。

    • PNG: 248 ms
      表示 Ping 值,即你客户端到服务器之间发送一个数据包并接收到回复的时间。
      248ms 有点偏高,可能会感到延迟。

    • RTT: 266 ms
      Round Trip Time,也就是完整的来回耗时,理论上 RTT ≈ Ping,但有时候 RTT 会包括更多网络开销和排队延迟。

    • IND: 24 ms
      这是 Interpolation Delay,也称为插值延迟,是客户端用来平滑补偿网络抖动的延迟。值越大说明你看到的画面越滞后于真实情况。24ms 属于正常范围。

下图是接下来要讲的网络方面的大纲:

BREADTH(广度)

    • Novel techniques  使用了一些新颖的技术手段

    • Reduced complexity through ECS  通过使用 ECS(实体-组件-系统)架构,来减少复杂性
      ECS 是一种常用于游戏开发的架构模式,有助于提升性能和模块化程度。在网络同步方面,ECS 也能帮助分离数据与逻辑,简化状态同步。

    • 不会讲的内容(Not covering):

      • General replication of entities  不会讲通用的实体同步机制(比如 transform 复制那种标准操作),因为这不是本次主题重点。

      • Remote entity interpolation 不会涉及远程实体插值的细节。这通常用于客户端平滑远程玩家的位置以应对网络延迟。

      • Details of backwards reconciliation 不会讲反向校正的细节,即在客户端预测错了之后,如何回滚并重放输入来修正状态。这属于网络同步中的高级话题。

 Determinism

DETERMINISM(确定性),主要讲的是在网络游戏中如何实现“客户端与服务器状态一致性”的关键策略

Synchronized Clock(同步时钟)

所有客户端和服务器使用一个统一同步的时钟基准
这样才能保证大家在相同的逻辑时间点处理同样的输入,例如第 100 帧时所有人执行的是同一批命令。

    • 在 lockstep 或 deterministic replay(决定性回放)系统中尤为关键;

    • 通常会通过 NTP、Ping 反馈、帧号对齐等方式校准时钟。

Fixed Update(固定更新步长) 使用固定时间步长(如每 16ms 更新一次),而不是帧率驱动逻辑。

Quantization(量化)将浮点值(比如位置、角度)进行离散化处理,通常是为了:

  • 降低浮点误差对状态同步的影响;

  • 在重播/回放或状态同步中保证一致性;

  • 降低网络带宽(压缩为 8bit、16bit 等)。

  • 16ms command frames(16毫秒命令帧)意味着客户端每 16ms 发送一次输入命令,服务器也以同样频率接收和处理这些命令。这个节奏可以协调多个客户端的输入并保持同步。

Overwatch中,虽然它是一个 高动作性非 lockstep 的 FPS 游戏,但为了优化同步效率、减少带宽、提高一致性,他们仍然在某些逻辑层中应用了“确定性设计思想”。

在游戏逻辑中,时间被量化成命令帧

下图说了是怎么把time量化成fixed frame id的

    • Loop clock => Fixed frames  循环时钟(真实运行时间)映射为固定帧(逻辑帧)

      • Add loop clock time to previous remainder  将当前循环的时钟时间加入上次未处理完的剩余时间

      • Increment command frame 如果累计时间足够,推进一个逻辑命令帧

      • Roll-over remainder to next frame 多出来的时间保留到下一个循环处理

    • System::UpdateFixed  所有固定步长逻辑在 System::UpdateFixed() 中执行

下图模拟了正常情况下Client发送数据给Server的方式,可以看出Client是领先Sever并且提前执行预测。

下图的Half RTT很好理解

下图Buffer的话是Server Command Buffer。

server这里cache了一帧,server读取buffer里的Command,去权威的执行client的行为,并加入到要发送的快照队列里去

接下来三张图:说明服务器下发的快照和Client预测的运动冲突后,Client需要回滚执行权威的服务器版本,从第17帧开始

图1:服务器计算第17帧的行为,玩家被冰冻了

 当第17帧的快照到达Client,Client的预测和服务器权威快照有冲突

Client从第17帧开始回滚执行

下图:Server没有正常的接收到Client的Command请求,耗尽了CommandBuffer里的数据。

Server简单的使用之前的输入数据进行Fake 模拟,并且回包中标记为当前是LostCommand的,接下来会告诉client

 客户端收到这个输入丢失的标记后,会提高send频率,也许类似unity fixedupdate 修改fixedDeltaTime之类的

Server同时会增大Command buffer

如下面2张图

 经典的发送冗余input, 大大降低输入丢包。如下图

大致提了Ability也是可以预测并且根据时间去回滚的,工作原理和上面的movement差不多,需要看另外一个GDC分享,这里没有细说。

Hit Registeration 命中检测

首先伤害的结算使用延迟处理 Deferment在服务器进行。就像上面提到的effect contact一样。

命中的预测是在client进行的。

服务器收到命中请求后,会在对应的时刻(倒回rewound)效验

 下图举例说明服务器倒回Rewound去检测命中和产生伤害。

首先在0.5s内给每个 敌人目标设置时间段的box,并且检测shooting raycast是否和这个box相交。

如果相交,再rewound指定的 敌人。这也算一种优化吧。并不是rewound所有的entity。只关注可能的部分。

下图也是一个例子。在模拟60%丢包率情况下,

绿色和蓝色是client下的 target和bullet

黄色和紫色是server下的模拟。

当RTT超过220,客户端预测命中开始变得不太有效了。视频的40分钟左右。

不在进行预测命中,直接交给Server去处理。

原因:客户端上的Target外插值太久了,虽然看起来是及时响应的(及时反馈了飙血Effect,但是真正的HP bar和命中点并没有显示),但客户端预测命中实际上没什么效果。 

retrospective 回顾

主要是讲了ecs的一些使用上的优点

1.定义ComponentTuple,清晰的知道要做什么

 2.明确的知道system需要哪些数据。进行并行化。举例:transform组件,明确的知道哪些System只是Read Transform,然后并行安全。

Entity lifeTime 

延迟创建和延迟销毁。

延迟创建带来了麻烦的一帧的问题。

规则:

ECS不是一种强制性设计原则。如果有些代码不适合ECS,不要强制塞入ECS

update system在主线程上

有些独立的功能是在work thread上的。

比如projectile的模拟,比如nav 路径的重建(和ecs无关)

CLOSING(总结)

    • ECS 是“粘合剂”      ECS(Entity Component System)作为一种架构,是用来连接游戏不同子系统(比如输入、动画、物理、技能逻辑、网络等)的一种“中介”结构

    • ECS 可以最小化耦合度   ECS 的好处是能让你写出非常 解耦、模块化、可组合 的代码 

      • 系统之间互不依赖;

      • 网络代码只处理组件的变化;

      • 渲染代码、输入处理、模拟逻辑彼此隔离。

    • 要对你的粘合代码(glue code)加以约束(上面提到的规则)

      “粘合代码”指的是把多个子系统“连起来”的中间层代码(例如:网络→ECS、技能系统→特效、输入→行为控制器)

      这类代码很容易变成“烂泥山”:

      • 逻辑穿插、职责混乱、耦合过高;

      • 很难调试或替换某一模块;

      • 网络变化时,影响波及全系统。

    • 网络代码很复杂,所以一定要把它解耦

      网络代码尤其难写(tricky),原因包括:

      • 要考虑丢包、延迟、乱序、带宽限制;

      • 还要兼容回滚、预测、补偿;

      • 而且所有这些不能污染游戏核心逻辑

      所以最好设计成:

      • 网络模块 → 只处理收发消息、解包、缓冲、回放;

      • 游戏逻辑模块 → 完全通过命令数据驱动(例如 ApplyCommandFrame())

提问时间:

在Component上 有没有实double buffer?

答:没有用到,movement和input是一个ring buffer.你说的这个double buffer也好实现的,我们没用到

你说子弹的命中用的发射者去判断。 如果子弹的生命周期中发射者在不停地变或者离开了怎么办?

答:这是个设计问题,我们没用到,具体问题具体对待

游戏是一定60Hz帧的吗?如果只有30hz的模拟怎么办呢?

答:是的,逻辑帧一定是60的。 

如果客户端CPU性能很差,我们可以做一些优化,去避免Death Spiral。

首先 ,本地预测玩家是重点(优先处理),所以本地预测(local player prediction)是最精确、最及时的;这是高优先级的模拟,占用 CPU 比较多是合理的。

### ② 远程角色(其他玩家)使用“预算化模拟”

关键词:Budgeting(设定每帧上限)

“远程角色脚本模拟最多只能花 1.5ms”

解释:

  • 远程玩家的状态,客户端通常是插值+预测出来的,不需要超高精度;

  • 可以设置每帧最多处理几个远程实体、或者限制脚本调用复杂度

  • 如果时间不够,可以“跳过一些非关键计算”或“延后一帧处理”。

常见策略包括:

  • 模拟远程玩家动画、姿态而不是精确动作逻辑;

  • 远程技能只表现结果,不模拟路径;

  • Tick 频率降低(例如远程实体每两帧 Tick 一次)。

### ③ Spike Smoothing(峰值平滑)技术

解释:

  • 有时帧突然很“重”(比如10个角色同时释放技能),你不能一股脑处理完;

  • 可以将部分模拟负载**“分帧处理”**,让逻辑稍微延后,但保持帧率稳定;

  • 平滑掉 CPU usage 的 spike,避免掉帧和“death spiral”。

技术方式可能包括:

  • 逻辑排队,按预算逐帧执行;

  • 异步/分批脚本执行;

  • 行为拆解,拆成多个帧去执行(比如动画分段执行、伤害延后一帧触发);

很慢的导弹也做预测了吗?

答:目前没考虑过不做预测,有趣有趣。

量化空间有多精细?那物理引擎在这个精度下会遇到问题吗?有多少游戏逻辑会受到物理引擎的影响?

答:

  • 空间量化的精度是大约 1 米 / 1024(≈1mm)

    • 也就是一个浮点数表示的位置,会被压缩为大概 10-bit 的离散格。

  • 他们将“视觉物理引擎”和“游戏物理引擎”分离了:

    • 视觉物理(client physics)主要是为了特效、击中效果等视觉表现

    • 游戏模拟物理(simulation physics)才是用于权威状态判断、逻辑碰撞

  • 两个物理引擎用的是同一套底层逻辑代码(同一个人写的):

    • 所以行为一致性高,且跨平台行为一致(AMD/Intel/Linux等)。我们用了很明确的指令/手法控制浮点行为。

    • 客户端和服务器共享关键逻辑代码(或者用统一库编译成 DLL、共享模块)
    • 非常稳定,不会因为平台浮点差异出问题。

  • 他们担心浮点编译器差异,比如 Clang 优化 & 执行乱序

    • 但由于用了量化,很多浮点误差问题被规避了;

    • 如果有更具体的疑问,可以去第二天某位讲者的分享深聊 IEEE 浮点和一致性问题。

你们客户端预测依赖于确定性(determinism),动态导航网格(navmesh)看起来是异步的,那怎么保持一致?

 答:整个模拟系统是基于固定时间步长(fixed timestep),所有系统都在统一节奏下运行,是实现一致性的关键基础。

他们不是 100% 的确定性(不像 StarCraft/Halo 那样)

开发者 Igor 做了一个“预测差异调试器”

  • 可以看到哪一帧服务器和客户端不一致;

  • 可以精确地定位是哪个数学或逻辑步骤出现偏差;

  • 通常是“寻找角色脚部的射线(ray)”导致的问题。

关于 Navmesh:

  • Navmesh 的结果在客户端和服务器给相同输入时会一致;

  • 重建 Navmesh 的耗时可能不一致,这并不会导致严重问题;

  • 因为大多数玩家移动技能不依赖 Navmesh

  • 即使错预测,也会被服务器矫正回来,不影响最终一致状态;

  • 不会为 Navmesh 做完整状态回滚(太大,占 40MB),仅回滚必要内容。