虚幻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
相关推荐
Seven971 分钟前
剑指offer-19、顺时针打印矩阵
java
SimonKing2 分钟前
PostMan不香了,IDEA因为Apipost插件这一功能轻松取代
java·后端·程序员
_杨瀚博36 分钟前
Maven 构建知识库
java·后端
我叫黑大帅37 分钟前
📝 Java 文件 IO 入门教程
java·前端
.又是新的一天.1 小时前
SpringBoot+SpringMVC常用注解
java·spring boot·后端
三木水1 小时前
Spring-rabbit使用实战六
java·后端·spring·消息队列·java-rabbitmq
录大大i1 小时前
Druid与JdbcTemplate基本使用
java·spring
丶小鱼丶1 小时前
Spring之【详解FactoryBean】
java·spring
Dcs1 小时前
IDEA 2025.2又整大活!AI 助手脱胎换骨,Spring 调试一秒看透,连 Java 25 和虚拟线程都安排上了!
java
Boilermaker19922 小时前
【Java EE】Spring AOP
java·java-ee