游戏引擎技术分享
一、问题背景:为什么需要AOI
在大型多人在线游戏中,一个场景可能同时存在数百个NPC和交互实体。传统的全量同步模型面临严峻的性能挑战:
在这个模型下,服务端每个entity的变化都要广播给所有在线玩家;客户端需要处理所有entity的逻辑运算、网络消息解析和渲染开销。当场景entity数量达到数百个时,网络带宽、CPU Tick、GPU渲染三方面同时承压。
AOI(Area of Interest,感兴趣区域)正是解决这一问题的核心方案。
二、AOI核心原理
2.1 基本概念
AOI的核心思路:以玩家为中心,划定一个圆形区域,只同步该区域内的entity数据。圈外的entity对该玩家"不存在"。
图1:AOI基本原理 — 左:静态视角;右:玩家移动时entity进出AOI
关键术语:
- AOI半径:以玩家为圆心的同步范围,单位为米
- 进AOI(Enter):entity进入玩家的AOI范围,服务端开始同步该entity数据到客户端,客户端创建entity及其关联的system、模型等
- 出AOI(Leave):entity离开玩家的AOI范围,服务端停止同步,客户端销毁entity及相关资源
2.2 不同玩家的AOI互相独立
每个玩家都有自己独立的AOI区域,所能"看到"的entity集合各不相同:
图2:玩家1和玩家2各自的AOI区域独立,可见entity集合不同
三、性能提升效果分析
AOI从根本上减少了客户端需要处理的entity数量,带来了网络同步、逻辑运算、渲染开销三个层面的性能收益:
图3:AOI优化从网络、CPU、GPU三个维度同时降低开销
实测数据
数据表明,AOI可以将客户端可见NPC数量降低60%~90%。对于大场景(如开放世界、多人战场),性能收益尤其显著。
四、AOI系统架构设计
4.1 Entity类型分层策略
核心设计决策:只有NPC受AOI影响,其余entity类型(Door、HiddenPoint、玩家、可交互物件等)不受AOI影响,始终全量同步。
这一决策基于以下考量:
- NPC数量占比最高:场景中NPC通常占entity总数的80%以上,是性能瓶颈的主要来源
- 玩家之间不能有AOI:多人协作/对抗场景中,玩家之间必须始终互相可见
- 关键交互物件必须可见:门、交互点等功能性entity不能因AOI而消失,否则影响玩法
图4:Entity类型分层 — NPC受AOI管控,其余类型全量同步
4.2 服务端-客户端AOI交互架构
图5:AOI系统完整架构 — 从服务端计算到客户端entity生命周期管理
五、适配AOI:核心编程规则
Entity在进出AOI时需要在代码层面做相应处理来保证状态正确恢复,这些处理统称为"适配AOI"。整个适配工作围绕两条核心规则展开:
规则一:客户端逻辑独立
客户端entity的逻辑和表现不能依赖客户端其他entity的状态,必须依赖服务端下发的数据。
因为AOI机制下,其他entity可能随时不在客户端上存在。如果A的逻辑依赖B的状态,而B不在AOI内(客户端没有B的entity),A的逻辑就会出错。
以扫描技能为例:客户端拿本地NPC数据来计算玩家朝向
问题:目标NPC不在AOI内时,客户端没有该NPC数据,朝向计算失败
服务端计算朝向,Yaw值通过基础移动方式同步到客户端
客户端只依赖服务端数据,不依赖本地其他entity状态
规则二:适配on_reconnect ≈ 适配AOI
绝大多数情况下,适配好断线重连(on_reconnect)就等同于适配了AOI。
原因:on_reconnect时会创建新entity并全量更新property到客户端——entity进入AOI也走同样的流程。两者的客户端处理高度一致:
| 处理步骤 | 断线重连 | 进入AOI |
|---|---|---|
| 创建新Entity | 是 | 是 |
| Property全量更新 | 是 | 是 |
| 状态机恢复 | 需要 | 需要 |
| Graph恢复 | 需要 | 需要 |
因此,已有的on_reconnect适配代码通常可以直接覆盖AOI场景。
六、适配实例详解
6.1 成功适配案例
NPC表演动画
服务端通过RPC通知客户端push/pop graph
RPC是一次性消息,entity重新进入AOI时不会重放 → 动画丢失
服务端通过Property控制客户端push/pop graph
Property在entity创建时全量同步 → 客户端可恢复,数据不丢失
电击手机(可交互物件)
手机的逻辑全部运行在服务端,客户端只有动画表现。NPC的动画和逻辑都由服务端数据控制,天然符合"逻辑独立"原则——无需额外适配。
QTE交互
QTE是较为复杂的适配案例,因为涉及两个entity的交互:
- UI相关操作和客户端动画/逻辑都写在状态机内,已适配on_reconnect
- 交互双方不依赖彼此,只受服务端数据控制,符合逻辑独立
QTE的额外问题 — loop动画对齐
QTE交互双方的loop动画在进出AOI后可能失去同步。两种解决方案:
1. 服务端记录QTE开始时间,客户端对动画跳帧以对齐时间线
2. 双方都pop旧loop动画再重新push,自然恢复对齐
6.2 未适配的反面案例
BirthPoint(出生点)
客户端UI通过获取所有BirthPoint entity来显示出生点标记。开启AOI后,不在AOI范围内的BirthPoint在客户端不存在,导致出生点UI显示不全。
这是典型的违反"逻辑独立"原则的案例——客户端UI逻辑依赖于"所有同类entity都存在"的假设,AOI下该假设不成立。
七、客户端新增API
为支持AOI机制,客户端Python层新增了以下接口,方便业务逻辑感知和响应AOI事件:
# ClientEntity 和 SystemBase 新增通知函数
class ClientEntity:
def on_aoi_enter(self):
"""entity进入玩家AOI时被调用"""
# 在此恢复表现、启动逻辑
pass
def on_aoi_leave(self):
"""entity离开玩家AOI时被调用"""
# 在此清理状态、停止逻辑
pass
# Area 新增成员变量
class Area:
is_aoi_enter: bool
# True → 当前area在自己的AOI范围内
# False → 当前area不在自己的AOI范围内
通过这组接口,业务层可以精确控制entity在进出AOI时的行为——比如进入时恢复动画、加载特效,离开时暂停不必要的计算。
八、引入AOI后的实战问题与解决方案
问题1:Entity创建卡顿
现象:entity进入AOI时需要创建entity对象及其关联的system、模型、特效等,这个创建过程造成明显卡顿。
图6:预加载 + 对象复用机制 — 用Loading阶段的内存换运行时的流畅度
问题2:高速移动时entity涌入卡顿
现象:被勾中、冲撞等高速移动场景下,短时间内大量entity进入AOI,即使有对象复用,集中创建仍然造成卡顿。
图7:限流策略将entity创建分散到时间轴上,避免瞬时卡顿
限流规则:
- 服务端每100ms只允许下发1个entity的进AOI数据
- 按距离排序作为优先级,优先下发最近的entity
- 玩家感知上是entity从近到远逐步出现,而不是瞬间全部弹出
问题3:技能状态不可恢复
现象:部分技能在断线重连时客户端不会恢复,还会打断当前技能。进入AOI触发entity的on_reconnect,同样存在不恢复问题。
解决:将客户端实现改为可恢复方案——使用状态机或Property驱动。参照QTE案例的做法,确保:
- 技能状态由Property持久化,不依赖一次性RPC
- 状态机可以从任意状态正确恢复
- 客户端不依赖"技能是从头开始播放"的假设
九、AOI扩展机制
除了基础的圆形AOI筛选外,引擎还提供了一系列高级机制,用于应对更复杂的业务场景:
图8:基础AOI之上的五种扩展机制
| 机制 | 核心能力 | 典型场景 |
|---|---|---|
aoi_exclusive |
修改ID列表,精确控制哪些avatar的客户端能接收到特定entity的信息 | Boss战中只让特定队伍看到特定机关 |
attention |
标记entity忽略AOI,始终同步给所有玩家 | 全局事件NPC、剧情关键角色 |
dist_aoi |
客户端自主控制AOI内entity的显示/隐藏 | 低配设备进一步裁剪显示量 |
| 分层AOI (Layer) | 将entity分配到不同层,每层可单独设置AOI距离 | 重要NPC大AOI半径,路人NPC小AOI半径 |
| 被动AOI (Passive) | 进场景前设置passive_aoi_radius,其他entity进入该范围时自动同步 | 隐藏陷阱 — 靠近才显现 |
十、已知遗留问题
| # | 问题 | 影响 |
|---|---|---|
| 1 | 进入AOI时部分动画恢复不正确 | NPC表现异常,需要逐一排查动画状态机 |
| 2 | NPC出AOI后胶囊体阴影仍在渲染 | 无用的渲染开销,影响GPU性能 |
| 3 | entity反复进出AOI时 area_impl 数量持续增长 | 潜在内存泄漏,长时间战斗可能导致crash |
十一、未来优化方向
AOI算法升级:2D → 3D空间筛选
当前基于2D平面距离筛选,无法区分上下层的entity。升级为3D空间筛选后,可更精确地过滤不同楼层/高度的NPC。甚至可以支持策划在场景中通过标注区域的方式进行辅助筛选。
Entity创建性能深度优化
复用entity对象(而不仅仅是关联资源)、差量化同步进AOI entity的property数据(只发增量而非全量)。优化到位后,限流策略可以适当放宽,提升entity出现速度。
内存精细化管理
当前Entity的system、model等都预加载在客户端内存中。随着场景规模增长,存在内存不足引发crash的风险。未被预加载的entity在进入AOI后加载时还是会卡顿,需要建立更精细的加载/卸载策略。
AOI与模型显隐机制一致性
AOI机制需与模型显示/隐藏机制保持一致,否则可能出现暴露攻方位置等玩法漏洞——因为当前玩家之间没有AOI,但如果NPC的可见性暴露了阵营分布信息,就会被利用。
Graph恢复增强
进入AOI时graph的恢复应考虑数据驱动、动画快进等更健壮的方式,避免动画从头播放导致的不自然表现。
扩大AOI覆盖范围
目前只有NPC受AOI影响。后续可将更多entity类型(如垃圾桶等可交互物件)纳入AOI管理,进一步减少客户端entity数量,压缩不必要的性能开销。