提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
虚幻引擎中的 GAS(Gameplay Ability System) 是一个高性能、高扩展性的能力框架。为了支撑其复杂的技能、效果、属性和状态系统,它背后拥有一套精心设计的内存管理机制,包括:
- 结构体池化、对象复用
- 网络序列化内存优化
- 特定对象生命周期托管(Spec、Effect)
- 自定义 allocator、Arena 分配器
- Attribute 缓存机制
子系统注册与 GC 管理
接下来我们分模块详细解析 GAS 是如何进行 内存管理和资源优化 的。
一、整体内存管理思路概览
GAS 的内存管理策略遵循以下原则:
原则 | 举例 |
---|---|
对象复用 | EffectSpecHandle、GameplayCueNotify |
数据结构轻量化 | FGameplayAttributeData 只保存基础值与当前值 |
结构体池化 | 目标数据、ModifierSpec 都来自池化内存 |
避免频繁GC | 能力实例只创建一次,Effect 不主动GC |
引用计数管理 | GE 生命周期由 ASC 完整托管 |
Arena分配器 | 属性聚合使用 FAggregator 特制 allocator |
二、核心对象的生命周期与托管逻辑
UGameplayAbility 的管理
所有 Ability 的实例(C++/BP)在 GiveAbility() 时被构造并缓存
每个 FGameplayAbilitySpec 中持有指针
在技能激活时不会重新创建实例,而是复用
TSharedPtr<UGameplayAbility> AbilityInstance = Spec.GetPrimaryInstance();
内存只在注册或删除时分配一次。
GameplayEffect 的内存管理
所有 GE(UGameplayEffect)作为配置类,只读资源,常驻内存
每次应用 GE 时,并不会直接创建对象,而是构建轻量运行体:
FGameplayEffectSpecHandle SpecHandle = MakeOutgoingSpec(...);
-
FGameplayEffectSpec 是轻量结构体,仅含:
-
指向 GE 定义指针(不复制类)
捕获的属性快照(值类型)
一些标志位和引用类型(如 TargetData)
避免了 UE 常见 UObject 开销,支持池化。
ActiveGameplayEffect 生命周期
FActiveGameplayEffectsContainer ActiveGameplayEffects;
所有激活中的 GE 存放于此容器
GE 的添加/更新/移除由 ASC 内部完成
不暴露给外部直接操作(防止悬挂指针)
生命周期结束自动清除,不走垃圾回收(避免 GC 开销)
三、属性(Attribute)缓存与更新机制
每个属性是 FGameplayAttributeData,它只包含两个 float 值:
float BaseValue;
float CurrentValue;
所有属性更新通过 FAggregator 进行聚合处理:
// 聚合器计算路径
FAggregator → Mod Stack → Base + Add + Mult + Override → Current
特点:
- 聚合器使用轻量结构 + 内嵌 Arena Allocator
- 修改属性不触发对象创建,只更新浮点值
- 属性变化自动触发 NetDeltaSerialize,减少带宽与内存分配
四、TargetData 与 EffectContext 的池化与复用
FGameplayAbilityTargetDataHandle
用于传输目标信息的结构体
包含多个 FGameplayAbilityTargetData 派生类(如 TargetActorArray)
所有派生结构体均支持结构体序列化,无 UObject 开销
通过 TSharedPtr 管理生命周期(引用计数)
FGameplayEffectContextHandle
表示 GE 的施加上下文(来源 Actor、命中位置等)
内部指向 FGameplayEffectContext(非 UObject)
通过 TSharedPtr 管理,可嵌套复制
支持网络复制与再利用
这些都是 轻量非 UObject 结构体,避免 UE GC 开销
五、网络同步中的内存优化
GAS 中有两个重要的结构体用于同步:
结构体 | 用途 |
---|---|
FGameplayAttributeData | 属性同步(NetDeltaSerialize) |
FActiveGameplayEffectHandle | 效果同步(GE ID + 生命周期) |
优化机制:
使用 FastArraySerializer 同步 GE 列表,只同步变化项
所有同步数据支持 NetSerialize(),避免复制整个对象
使用 PackedBitWriter 实现高压缩的浮点数传输
不同步 UObject,只传递指针索引、标签、数值等
六、对象池和自定义 Allocator 的应用
GAS 使用了多种自定义分配器:
- 聚合器分配器 FAggregatorRef:
FGameplayEffectSpec::Captures → 聚合器系统 → ArenaAllocator
用于批量构建临时 Modifier 聚合数据,生命周期绑定到 GE Spec。
GE 缓存:
UGameplayEffect 被作为资源缓存,使用标准 UObject 生命周期,但不频繁加载/卸载。TagContainer:
FGameplayTagContainer 使用值类型 + 显式引用共享,不走 UObject。
七、垃圾回收与非 GC 对象管理
GAS 中大量使用 非 UObject 的结构体 + 智能指针:
类型 | GC 管理方式 |
---|---|
UGameplayAbility / GE | 常驻资源,由 ASC 或系统持有 |
FGameplayEffectSpec / TargetData | 非 GC 对象,TSharedPtr 管理 |
AttributeSet | UObject,会注册到 ASC 的 SubObject 管理器中 |
GameplayCueNotify | 使用 Object Pool 缓存重用(如 FX) |
总结:GAS 尽可能避开 GC,使用结构体和智能指针进行生命周期控制
总结:GAS 内存管理的关键设计点
设计点 | 实现方式 |
---|---|
复用对象,避免频繁创建 | 技能/GE/特效对象仅实例化一次 |
尽量结构体化 | Spec/TargetData 均为结构体(TSharedPtr 管理) |
避免 GC 压力 | 除 Ability 外几乎无 UObject 生命周期 |
内存池化 | TargetData、Aggregator 使用 Arena 分配器 |
高效网络结构 | NetDeltaSerialize + PackedBitWriter |
生命周期集中托管 | ASC 中统一管理技能/GE/属性/标签的状态 |