UE5 World Partition

核心概念 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 就不加载。

CellGrid 是两个层级,区别很重要。

你可以这么理解:

复制代码
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。

问题:

大世界里面狙击枪开高倍镜怎么办?

相关推荐
HAPPY酷16 小时前
【ROS2】 机械臂架构 vs UE5 游戏开发架构 终极对照表
架构·ue5
DoomGT16 小时前
UE5 - 制作游戏中切换窗口后游戏自动暂停功能
游戏·ue5·ue4·虚幻·虚幻引擎·unreal engine
远离UE44 天前
UE5 简单 Mesh Shader 制作流程
ue5
梦帮科技5 天前
UE5 GAS 实战:用 Gameplay Ability System 搭建「赛博修真」境界与技能体系
c++·人工智能·python·ue5·c#
远离UE45 天前
UE5 各类型灯光学习
学习·ue5
四代水门25 天前
UE5实现客户端与服务器时间同步
ue5
智海深蓝1 个月前
海上平行战场:态势模拟三维可视化平台
3d·ue5
_守一1 个月前
UE5 ListView记录
ue5
成都渲染101云渲染66661 个月前
CR15新功能介绍以及CR15云渲染流程
ue5·图形渲染·blender·maya·corona