美文网首页
五、RPGAttributeSet.h/cpp & Attrib

五、RPGAttributeSet.h/cpp & Attrib

作者: 珏_Gray | 来源:发表于2019-06-12 11:17 被阅读0次

Attibute Set

/**
 * Defines the set of all GameplayAttributes for your game
 * Games should subclass this and add FGameplayAttributeData properties to represent attributes like health, damage, etc
 * AttributeSets are added to the actors as subobjects, and then registered with the AbilitySystemComponent
 * It often desired to have several sets per project that inherit from each other
 * You could make a base health set, then have a player set that inherits from it and adds more attributes
 */

Attribute Set中定义的属性就是RPG游戏中的人物属性,如血量、魔法值。数据类型为FGameplayAttributeData (对float的封装)。
之前写过一篇关于AttributeSet的文章,已经讲的和详细了,这里就不复述了。
GAS - Gameplay Attributes


RPGAttributeSet.cpp

首先,因为AttributeSet是支持Replication的,不要漏了GetLifetimeReplicatedProps

void URPGAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const

RPGAction添加了函数 AdjustAttributeForMaxChange , 其作用是,当属性最大值发生改变时,按比例改变当前值。

void URPGAttributeSet::AdjustAttributeForMaxChange(FGameplayAttributeData& AffectedAttribute, const FGameplayAttributeData& MaxAttribute, float NewMaxValue, const FGameplayAttribute& AffectedAttributeProperty)
{
//获得UAbilitySystemComponent实例
    UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent();、
//Getter方法获取的都是current value而不是base value
    const float CurrentMaxValue = MaxAttribute.GetCurrentValue();
    if (!FMath::IsNearlyEqual(CurrentMaxValue, NewMaxValue) && AbilityComp)
    {
        // Change current value to maintain the current Val / Max percent
        const float CurrentValue = AffectedAttribute.GetCurrentValue();
        float NewDelta = (CurrentMaxValue > 0.f) ? (CurrentValue * NewMaxValue / CurrentMaxValue) - CurrentValue : NewMaxValue;
                //      通过UAbilitySystemComponent实例
        AbilityComp->ApplyModToAttributeUnsafe(AffectedAttributeProperty, EGameplayModOp::Additive, NewDelta);
    }
}

我们要注意到对属性值的修改是通过调用GameplayAbilityComponent的ApplyModToAttributeUnsafe方法实现的。
接下来是两个利用GASt提供的回调函数的例子。

  • PreAttributeChange : 任意属性发生变化前都会调用,通常用于强加规则给数值
void URPGAttributeSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
{
    // This is called whenever attributes change, so for max health/mana we want to scale the current totals to match
    Super::PreAttributeChange(Attribute, NewValue);

//如果将要改变的属性是最大血量
    if (Attribute == GetMaxHealthAttribute())
    {
        AdjustAttributeForMaxChange(Health, MaxHealth, NewValue, GetHealthAttribute());
    }
//如果将要改变的属性是最大魔法值
    else if (Attribute == GetMaxManaAttribute())
    {
        AdjustAttributeForMaxChange(Mana, MaxMana, NewValue, GetManaAttribute());
    }
}
  • PostGameplayEffectExecute: 任意影响Attribute的GameplayEffect执行之后都会调用,通常用于clamp数值,触发游戏中的事件
void URPGAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
    Super::PostGameplayEffectExecute(Data);

// Context !!! 还记得讲GameplayAbility时提到的RPGAbilityType么?
// 我们可以通过引擎提供的FGameplayEffectContextHandle得到许多关联的信息
    FGameplayEffectContextHandle Context = Data.EffectSpec.GetContext();
    UAbilitySystemComponent* Source = Context.GetOriginalInstigatorAbilitySystemComponent();
    const FGameplayTagContainer& SourceTags = *Data.EffectSpec.CapturedSourceTags.GetAggregatedTags();

    // Compute the delta between old and new, if it is available
    float DeltaValue = 0;
    if (Data.EvaluatedData.ModifierOp == EGameplayModOp::Type::Additive)
    {
        // If this was additive, store the raw delta value to be passed along later
        DeltaValue = Data.EvaluatedData.Magnitude;
    }

    // Get the Target actor, which should be our owner
    AActor* TargetActor = nullptr;
    AController* TargetController = nullptr;
    ARPGCharacterBase* TargetCharacter = nullptr;
    if (Data.Target.AbilityActorInfo.IsValid() && Data.Target.AbilityActorInfo->AvatarActor.IsValid())
    {
        TargetActor = Data.Target.AbilityActorInfo->AvatarActor.Get();
        TargetController = Data.Target.AbilityActorInfo->PlayerController.Get();
        TargetCharacter = Cast<ARPGCharacterBase>(TargetActor);
    }

    if (Data.EvaluatedData.Attribute == GetDamageAttribute())
    {
        // Get the Source actor
        AActor* SourceActor = nullptr;
        AController* SourceController = nullptr;
        ARPGCharacterBase* SourceCharacter = nullptr;
        if (Source && Source->AbilityActorInfo.IsValid() && Source->AbilityActorInfo->AvatarActor.IsValid())
        {
            SourceActor = Source->AbilityActorInfo->AvatarActor.Get();
            SourceController = Source->AbilityActorInfo->PlayerController.Get();
            if (SourceController == nullptr && SourceActor != nullptr)
            {
                if (APawn* Pawn = Cast<APawn>(SourceActor))
                {
                    SourceController = Pawn->GetController();
                }
            }

            // Use the controller to find the source pawn
            if (SourceController)
            {
                SourceCharacter = Cast<ARPGCharacterBase>(SourceController->GetPawn());
            }
            else
            {
                SourceCharacter = Cast<ARPGCharacterBase>(SourceActor);
            }

            // Set the causer actor based on context if it's set
            if (Context.GetEffectCauser())
            {
                SourceActor = Context.GetEffectCauser();
            }
        }

        // Try to extract a hit result
        FHitResult HitResult;
        if (Context.GetHitResult())
        {
            HitResult = *Context.GetHitResult();
        }

        // Store a local copy of the amount of damage done and clear the damage attribute
        const float LocalDamageDone = GetDamage();
        SetDamage(0.f);

        if (LocalDamageDone > 0)
        {
            // Apply the health change and then clamp it
            const float OldHealth = GetHealth();
            SetHealth(FMath::Clamp(OldHealth - LocalDamageDone, 0.0f, GetMaxHealth()));

            if (TargetCharacter)
            {
                // This is proper damage
                TargetCharacter->HandleDamage(LocalDamageDone, HitResult, SourceTags, SourceCharacter, SourceActor);

                // Call for all health changes
                TargetCharacter->HandleHealthChanged(-LocalDamageDone, SourceTags);
            }
        }
    }
    else if (Data.EvaluatedData.Attribute == GetHealthAttribute())
    {
        // Handle other health changes such as from healing or direct modifiers
        // First clamp it
        SetHealth(FMath::Clamp(GetHealth(), 0.0f, GetMaxHealth()));

        if (TargetCharacter)
        {
            // Call for all health changes
            TargetCharacter->HandleHealthChanged(DeltaValue, SourceTags);
        }
    }
    else if (Data.EvaluatedData.Attribute == GetManaAttribute())
    {
        // Clamp mana
        SetMana(FMath::Clamp(GetMana(), 0.0f, GetMaxMana()));

        if (TargetCharacter)
        {
            // Call for all mana changes
            TargetCharacter->HandleManaChanged(DeltaValue, SourceTags);
        }
    }
    else if (Data.EvaluatedData.Attribute == GetMoveSpeedAttribute())
    {
        if (TargetCharacter)
        {
            // Call for all movespeed changes
            TargetCharacter->HandleMoveSpeedChanged(DeltaValue, SourceTags);
        }
    }
}

我们需要注意对FGameplayEffectContextHandle的使用。如果引擎默认提供的信息不足,我们可以考虑拓展FGameplayEffectContextHandle类。这里所有的Set函数也都是通过AbilitySystemComponent调用方法改变数值的。

随着AttributeSet中的属性增多,回调函数中的判断也会变得更加复杂。

相关文章

网友评论

      本文标题:五、RPGAttributeSet.h/cpp & Attrib

      本文链接:https://www.haomeiwen.com/subject/pkukfctx.html