核心概念 1:Cell
Cell 就是地图格子。
想象俯视图:
+----+----+----+----+
| A1 | A2 | A3 | A4 |
+----+----+----+----+
| B1 | B2 | B3 | B4 |
+----+----+----+----+
| C1 | C2 | C3 | C4 |
+----+----+----+----+
每个格子里保存一批 Actor:
树
石头
房子
路牌
地形片段
小道具
玩家在 B2 附近时,可能加载:
A1 A2 A3
B1 B2 B3
C1 C2 C3
离远的 A4、C4 就不加载。
Cell 和 Grid 是两个层级,区别很重要。
你可以这么理解:
Grid = 切地图的规则
Cell = 按这个规则切出来的格子
就像:
棋盘规则 = Grid
棋盘上的每一个小格子 = Cell
一个 Grid 里,Cell 大小通常是一样的。
比如:
MainGrid
Cell Size = 25600
Loading Range = 76800
那这个 MainGrid 切出来的所有 Cell 大小都是 25600。
MainGrid:
+----+----+----+
| C | C | C |
+----+----+----+
| C | C | C |
+----+----+----+
| C | C | C |
+----+----+----+
每个 C 都是 25600 大小
所以我前面说"每个 Cell 一样",指的是:
同一个 Grid 里的 Cell 一样大。
但 UE 可以有多个 Runtime Grid。
比如:
SmallObjectGrid
Cell Size = 6400
Loading Range = 20000
BuildingGrid
Cell Size = 25600
Loading Range = 80000
LandscapeGrid
Cell Size = 51200
Loading Range = 150000
这时候就变成:
SmallObjectGrid:切得很碎
BuildingGrid:切得中等
LandscapeGrid:切得更大
也就是:
不同 Grid 的 Cell 大小可以不同。
一个 Grid 切出一堆 Cell
另一个 Grid 也切出另一堆 Cell
它们可以覆盖同一片世界区域
但不是父子包含关系

这种两个grid也会是存在的
Actor 是否 Spatially Loaded
Actor 有个很重要的属性:
Is Spatially Loaded
如果开启:
这个 Actor 根据它所在 Cell 来加载/卸载
比如树、石头、房子。
如果关闭:
它不跟随空间 Cell,通常一直加载
比如:
GameMode 相关管理器
天气系统
全局音频管理
任务系统
世界规则 Actor
如果一个很远的普通 Actor 被设成 Always Loaded:
它永远占内存
大地图就慢慢爆
比如天空盒
核心概念 5:Data Layer
Data Layer 是另一种维度的开关。
它不是按距离加载,而是按"状态/类别"加载。
比如:
白天版本
夜晚版本
任务前版本
任务后版本
破坏前城市
破坏后城市
开发调试层
例子:
DataLayer_City_Normal
DataLayer_City_Destroyed
任务前加载正常城市,任务后切到破坏城市。
所以:
World Partition 负责空间切分
Data Layer 负责状态切换
相对于浣熊市核弹爆炸前加载正常城市,核弹爆炸后卸载正常城市,加载破坏后的城市
室内 / 地下 / 多层空间会让 WP 不够聪明
World Partition 本质是空间分块,主要按世界坐标划分。
但搜打撤常见结构是:
地上城市
地下室
下水道
楼内多层
地铁
堡垒
它们在 XY 上可能重叠,但玩法上完全不同。
例如:
地面商场
地下停车场
地下实验室
它们 XY 坐标接近,Cell 可能一起被加载。
这时你要考虑:
- Data Layer 区分地下/地上?
- Runtime Grid 是否分开?
- 室内是否用 Level Instance?
- 地下空间是否单独 Streaming Source?
- AI/Navmesh 是否分层管理?
否则会出现:
玩家在地面
地下大量 Actor 也被加载
这个时候需要你手写辅助逻辑。Data Layer 自己不会判断"玩家在地上还是地下"。
更准确地说:
Data Layer = 开关
程序/蓝图 = 判断什么时候开关
比如:
玩家进地下入口 Trigger
↓
你的逻辑判断:玩家要进地下
↓
把 DataLayer_Underground 设为 Loaded / Activated
↓
必要时把部分地表表现卸掉
核心概念 6:HLOD
HLOD 是远景替身。
如果玩家离一个城市很远,不应该加载城市里每栋房子的真实 Actor。
应该加载一个合并后的低成本模型:
远处城市 HLOD Mesh
靠近后再换成真实房子、窗户、门、灯牌。
简单说:
近处:真实 Actor
远处:HLOD 替身
没有 HLOD 的大地图很容易变成:
远处看不清的东西还在按真实物体渲染
非常亏。
World Partition 的运行逻辑可以想成这样:
玩家移动
↓
更新 Streaming Source 位置
↓
计算哪些 Cell 在加载范围内
↓
加载新 Cell
↓
卸载远离的 Cell
↓
远处显示 HLOD
↓
近处显示真实 Actor
真正危险的地方有几个。
第一个:引用把远处资源拉进来。
比如 B2 的一个 Blueprint 引用了 C9 的某个大资源:
B2 Cell 加载
↓
Blueprint 引用远处资源
↓
远处资源也被加载
这样 World Partition 就被你绕过了。
硬引用会绕过 World Partition
这是大地图项目经典坑。
比如 B2 Cell 里的一个 Blueprint 硬引用了 C9 区域的大资源:
BP_QuestDevice
Hard Reference -> BP_BossRoom
Hard Reference -> 大贴图 / 大蓝图 / 大音效 / 大特效
结果:
B2 Cell 加载
↓
硬引用资源一起加载
↓
远处资源被提前拉进内存
World Partition 只能管 Actor 空间加载,管不了你蓝图硬引用把资源拉进来。
TA 要做引用规范:
- 大资源用 SoftObjectPtr / SoftClassPtr
- 任务资源用 AssetManager 管
- UI 图标、音频、特效不要随便硬挂到地图 Actor
- 定期跑 Reference Viewer / Size Map
- 检查 Cell 加载时实际拉了哪些包
第二个:Actor 太碎。
比如一片区域有几万个小 Actor:
石头 Actor
草 Actor
树枝 Actor
垃圾 Actor
Cell 切换时管理成本很高。
玩家移动到新区域,World Partition 要做:
加载 Cell 包
反序列化 Actor
创建 UObject
创建 Component
RegisterComponent
创建 RenderState
加入 Scene
创建 PhysicsState
加入 Collision
更新 Bounds
加入 Octree / Scene 管理结构
可能执行 BeginPlay / Construction
玩家离开区域时又要:
UnregisterComponent
释放 RenderState
移除 Collision
EndPlay
标记销毁
等待 GC
如果一个 Cell 里是 50 个 Actor,还好。
如果一个 Cell 里是 5000 个小 Actor,就会卡
这时候应该考虑:
Foliage Instance
ISM / HISM
合并 Mesh
HLOD
第三个:Cell 太小或太大都不行。
Cell 太小:
加载/卸载太频繁
IO 请求碎
管理成本高
Cell 太大:
一次加载太多东西
内存峰值高
切换卡顿
先用经验值起步:
小物件 / 道具 Grid:128m ~ 256m
建筑 / 中型场景 Grid:256m ~ 512m
地形 / 大结构 Grid:512m ~ 1km
HLOD Grid:1km ~ 2km 或更大
所以我可以地形单独开一个grid不影响场景其他东西吗?
对,可以。地形单独开一个 Grid 是合理的,而且很常见。
它不会天然影响其他场景物体,因为:
Actor 属于哪个 Runtime Grid,就按哪个 Grid 的规则流送
World Partition 只管 Actor 加载,不管"游戏状态"
这是搜打撤最容易踩坑的地方。
比如:
- 箱子被搜过
- 门被打开
- 电闸被拉下
- 撤离点是否开放
- Boss 是否死亡
- 某个房间是否已被玩家触发
- 掉落物是否还在地上
如果这些状态只存在 Actor 身上:
AChestActor::bHasBeenLooted = true;
那 Cell 卸载后 Actor 销毁,再加载时很可能又回到默认状态。
所以 World Partition 下,搜打撤应该这样设计:
Cell Actor:
负责表现、交互、碰撞、动画
服务器 MatchState / Manager:
负责保存真实状态
Actor 加载时:
Actor BeginPlay
↓
用 StableID 找服务器状态
↓
恢复表现
Actor 变化时:
玩家开门 / 搜箱
↓
服务器修改状态表
↓
Actor 播动画
↓
同步给相关客户端
二次元项目特别要小心 HLOD 破坏画风
二次元开放世界很依赖:
- 描边
- 色块分区
- 材质 ID
- 阴影分层
- 法线风格化
- 轮廓线稳定性
- 角色/建筑的剪影
但 World Partition 远处通常会切 HLOD。
问题:
大世界里面狙击枪开高倍镜怎么办?