虚幻GAS底层原理解剖三 (GA)

文章目录


前言

现在深入分析 GAS 中 Attribute System 的底层实现原理,包括:

  1. AttributeSet 的作用和结构

  2. FGameplayAttribute 的底层机制

  3. 属性是如何被修改的

  4. 多人同步的技术原理

  5. GAS 如何保证属性安全性、灵活性与效率

一、Attribute System 架构概览

Attribute System 是 GAS 的核心基础,用于统一管理角色的数值属性(如血量、攻击力、护甲、速度等)。

核心类关系:

cpp 复制代码
UAttributeSet           // 属性集合类,定义具体属性
    ↑
FGameplayAttribute      // 属性引用结构,用于泛型操作
    ↑
UAbilitySystemComponent // 管理所有 AttributeSet + 与 GE 交互

它并不直接处理逻辑,而是 通过 ASC、GE、Tag 等统一修改和同步数值属性。

二、UAttributeSet:属性类本体

UAttributeSet 是一个继承自 UObject 的类,开发者可自定义子类,在其中定义属性字段:

cpp 复制代码
UCLASS()
class UMyAttributeSet : public UAttributeSet
{
    GENERATED_BODY()

public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Health", ReplicatedUsing = OnRep_Health)
    FGameplayAttributeData Health;

    UFUNCTION()
    void OnRep_Health(const FGameplayAttributeData& OldHealth);

    static FGameplayAttribute GetHealthAttribute();
};

:
每个属性都是 FGameplayAttributeData 类型

复制代码
该类型封装了 float 值,支持缓冲、同步、变动检测等   

可通过 OnRep_XXX() 实现属性同步的回调

三、FGameplayAttributeData 的作用

它是 GAS 中实际存储属性值的类型:

cpp 复制代码
struct FGameplayAttributeData
{
    float BaseValue;
    float CurrentValue;
};

:
BaseValue:基础值,不随 BUFF 等效果改变

复制代码
CurrentValue:实际数值,会受 GE 动态影响   

内建了属性变更检测与 NetDeltaSerialize 支持

🧬 四、FGameplayAttribute:属性引用包装器

为了让 GE、GA 以"泛型方式"修改属性(而不是硬编码字段名),GAS 提供了 FGameplayAttribute:

cpp 复制代码
FGameplayAttribute MyAttribute = UMyAttributeSet::GetHealthAttribute();
ASC->SetNumericAttributeBase(MyAttribute, 100.0f);

:
所属 UClass*(哪个 AttributeSet)

复制代码
属性名 FName   

C++ 成员指针

并通过反射方式获取对应的 FGameplayAttributeData& 实例。

五、属性变更流程

属性变更路径(以 GE 加血为例):

  1. GE 的 FModifierSpec 指定修改 Health
  2. GE 应用到目标 ASC → 调用 ApplyModToAttribute()
  3. 找到 AttributeSet → 定位到 FGameplayAttributeData
  4. 修改 CurrentValue → 触发 PostGameplayEffectExecute()

若是网络游戏,调用 OnRep_Health() 同步客户端

cpp 复制代码
void UAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
    if (Data.Attribute == GetHealthAttribute()) {
        if (Health.GetCurrentValue() <= 0.f) {
            // 死亡逻辑
        }
    }
}

六、属性计算流程

GAS 支持多个层级的属性修改:

层级 修改函数 特性
Base SetBaseAttributeValue 基础值
Current ApplyModToAttribute 当前值
额外运算 Execution Calculation 动态伤害、暴击等

在执行 GE 时,会通过 FAggregator 系统完成对属性的加权合并运算:

cpp 复制代码
float NewValue = BaseValue
               + AdditiveModifiers
               + MultiplicativeModifiers
               + Override (if any);

这些 Modifier 都存储在 ASC 的内部结构 FAggregatorMap 中,并会在任意值变动时自动重新计算属性。

七、属性网络同步原理

GAS 的属性同步机制非常高效:

  1. 使用 NetDeltaSerialize + FGameplayAttributeData
    每个属性结构都注册了 Delta 序列化:

cpp

复制

编辑

void FGameplayAttributeData::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)

只有当属性值变动时,才会序列化发送

自动触发 OnRep_属性名() 回调

  1. GAS 会为所有 AttributeSet 注册为子对象并同步
    在 ASC 初始化时会自动注册属性集合并设置 RepNotify:

cpp

复制

编辑

ASC->AddReplicatedSubObject(AttributeSetInstance);

八、属性保护机制(只通过 GE 修改)

:
不要在游戏逻辑中直接 Health = xxx

复制代码
必须通过 ASC 和 GE 修改属性!

:
不会触发 OnRep

复制代码
不会触发 PostGameplayEffectExecute   

不参与网络同步

推荐方法:

cpp 复制代码
FGameplayEffectSpecHandle Spec = MakeOutgoingSpec(HealthRestoreGE, 1.0f, Context);
ASC->ApplyGameplayEffectSpecToSelf(*Spec.Data.Get());

九、案例:实现护甲减伤

  1. 定义两个属性:Health, Armor
  2. 创建一个 Execution Calculation 类,逻辑如下:
cpp 复制代码
float Damage = InDamage;
float Armor = 0.f;
ExecParams.AttemptCalculateCapturedAttributeMagnitude(ArmorDef, EvalParams, Armor);
Damage *= (1 - Armor * 0.01f);  // 减伤公式
  1. 设置 OutSpec.AddOutputModifier() 作用到 Health 属性上

总结

模块 作用 说明
UAttributeSet 属性定义类 包含各个 FGameplayAttributeData
FGameplayAttributeData 属性值结构 有 Base 和 Current 值,支持序列化
FGameplayAttribute 泛型引用器 可用于在运行时访问属性
ASC 执行属性修改 持有属性容器、同步、处理 GE
相关推荐
我命由我12345几秒前
Kotlin 运算符 - == 运算符与 === 运算符
android·java·开发语言·java-ee·kotlin·android studio·android-studio
小途软件7 分钟前
ssm327校园二手交易平台的设计与实现+vue
java·人工智能·pytorch·python·深度学习·语言模型
alonewolf_9910 分钟前
Java类加载机制深度解析:从双亲委派到热加载实战
java·开发语言
追梦者12312 分钟前
springboot整合minio
java·spring boot·后端
云游15 分钟前
Jaspersoft Studio community edition 7.0.3的应用
java·报表
帅气的你20 分钟前
Spring Boot 集成 AOP 实现日志记录与接口权限校验
java·spring boot
zhglhy40 分钟前
Spring Data Slice使用指南
java·spring
win x1 小时前
Redis 主从复制
java·数据库·redis
weixin_423995001 小时前
unity 处理图片:截图,下载,保存
java·unity·游戏引擎
帅气的你1 小时前
从零封装一个通用的 API 接口返回类:统一前后端交互格式
java·设计模式