上一篇,我们实现了玩家角色和敌人的等级的获取,使用MMC的提前工作已经准备完成,那么,这一篇讲一下,如何使用MMC,通过角色等级和体力值设置角色的最大血量。
MMC 全称 Mod Magnitude Calculation,为自定义计算数值的类,可以使用蓝图继承,也可以使用c++继承,在这个项目里面我们使用的c++实现。
它需要我们覆写父类的函数CalculateBaseMagnitude_Implementation(),在内部实现计算并返回,接下来,我们将先实现对等级属性和在AS里的体力属性的获取,然后计算并返回。
创建MMC
打开UE,创建C++类,选择所有类,在里面找到GameplayModeMagnitudeCalculation类作为基类
创建类,放到一个单独的文件夹,后续我们可能需要制作很多的MMC
添加完成后,就可以将UE关闭,在编辑器内编写MMC代码了。
打开首先添加一个构造函数,我们需要在构造函数内去设置需要获取的AS属性
public:UMMC_MaxHealth();
我们需要覆盖父类的CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec),这个函数返回了一个应用此MMC的GE的实例,我们可以通过Spec去获取需要的内容,然后计算数值。
virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
创建一个FGameplayEffectAttributeCaptureDefinition 属性,我们会在cpp文件内,使用它去获取AS内的体力的值。
private:FGameplayEffectAttributeCaptureDefinition VigorDef;
以下为.h文件的代码
// 版权归暮志未晚所有。#pragma once#include "CoreMinimal.h"
#include "GameplayModMagnitudeCalculation.h"
#include "MMC_MaxHealth.generated.h"/*** */
UCLASS()
class AURA_API UMMC_MaxHealth : public UGameplayModMagnitudeCalculation
{GENERATED_BODY()public:UMMC_MaxHealth();virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;private:FGameplayEffectAttributeCaptureDefinition VigorDef;
};
接着转到cpp文件内,编写构造函数,设置需要拾取的参数的内容,用于拾取目标身上的属性的值。
UMMC_MaxHealth::UMMC_MaxHealth()
{VigorDef.AttributeToCapture = UAttributeSetBase::GetVigorAttribute(); //设置需要获取的属性对象VigorDef.AttributeSource = EGameplayEffectAttributeCaptureSource::Target; //设置拾取对象为GE的应用目标VigorDef.bSnapshot = false;RelevantAttributesToCapture.Add(VigorDef); //添加到捕获属性数值,只有添加到列表,才会去获取属性值
}
接下来,就是编写CalculateBaseMagnitude_Implementation()函数,我们首先要实现对体力值的获取。获取使用到的函数是GetCapturedAttributeMagnitude(),里面需要FAggregatorEvaluateParameters 参数,那么我们先创建此参数,并设置目标和源的Tag设置给它
// 从 source 和 target 获取 Tagconst FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();FAggregatorEvaluateParameters EvaluateParameters;EvaluateParameters.SourceTags = SourceTags;EvaluateParameters.TargetTags = TargetTags;float Vigor = 0.f;GetCapturedAttributeMagnitude(VigorDef, Spec, EvaluateParameters, Vigor);Vigor = FMath::Max<float>(Vigor, 0.f);
体力获取到了,我们还需要获取它的等级,获取等级需要此GE的应用源,可以通过Spec.GetContext().GetSourceObject()去获取源对象,当然,你不设置是获取不到的,这个需要在应用GE的时候,设置源,设置方法如下
void ACharacterBase::ApplyEffectToSelf(TSubclassOf<UGameplayEffect> GameplayEffectClass, float Level) const
{check(IsValid(GetAbilitySystemComponent()));check(GameplayEffectClass);FGameplayEffectContextHandle ContextHandle = GetAbilitySystemComponent()->MakeEffectContext();ContextHandle.AddSourceObject(this); //设置源对象,可以通过Spec.GetContext().GetSourceObject()去获取源对象const FGameplayEffectSpecHandle SpecHandle = GetAbilitySystemComponent()->MakeOutgoingSpec(GameplayEffectClass, Level, ContextHandle);GetAbilitySystemComponent()->ApplyGameplayEffectSpecToTarget(*SpecHandle.Data.Get(), GetAbilitySystemComponent());
}
能获取到应用GE的对象,那么,也就是玩家角色自身,我们就可以从玩家角色身上调用GetPlayerLevel()函数获取等级
//获取等级ICombatInterface* CombatInterface = Cast<ICombatInterface>(Spec.GetContext().GetSourceObject());const int32 Level = CombatInterface->GetPlayerLevel();
体力和等级我们都已经拿到,那么,应用我们自身的公式,得出最大血量结果
//计算最大血量return 80.f + Vigor * 2.5f + Level * 10.f;
以下为cpp的完整代码
// 版权归暮志未晚所有。#include "AbilitySystem/ModMagCalc/MMC_MaxHealth.h"#include "AbilitySystem/AttributeSetBase.h"
#include "Interaction/CombatInterface.h"UMMC_MaxHealth::UMMC_MaxHealth()
{VigorDef.AttributeToCapture = UAttributeSetBase::GetVigorAttribute(); //设置需要获取的属性对象VigorDef.AttributeSource = EGameplayEffectAttributeCaptureSource::Target; //设置拾取对象为GE的应用目标VigorDef.bSnapshot = false;RelevantAttributesToCapture.Add(VigorDef); //添加到捕获属性数值,只有添加到列表,才会去获取属性值
}float UMMC_MaxHealth::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
{// 从 source 和 target 获取 Tagconst FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();FAggregatorEvaluateParameters EvaluateParameters;EvaluateParameters.SourceTags = SourceTags;EvaluateParameters.TargetTags = TargetTags;//获取体力值float Vigor = 0.f;GetCapturedAttributeMagnitude(VigorDef, Spec, EvaluateParameters, Vigor);Vigor = FMath::Max<float>(Vigor, 0.f);//获取等级ICombatInterface* CombatInterface = Cast<ICombatInterface>(Spec.GetContext().GetSourceObject());const int32 Level = CombatInterface->GetPlayerLevel();//计算最大血量return 80.f + Vigor * 2.5f + Level * 10.f;
}
编写完成以后,就可以进行编译,进入UE中设置,type修改为Custom Calculation Class,Class设置上我们刚才制作的MMC
完成以后运行,查看效果,我们等级现在设置的是1, Vigor是23 那么结果就应该是 80 + 23*2.5 + 1 * 10 = 147.5,数值可以对起来
打个断点,看一下值,没有问题
这里,我们就实现了对最大血量的设置。
接下来,按照同样的道理,我们再创建一个MMC_MaxMana用于处理最大蓝量,这里我就不再赘述了,和最大血量一个道理,直接贴代码。
// 版权归暮志未晚所有。#pragma once#include "CoreMinimal.h"
#include "GameplayModMagnitudeCalculation.h"
#include "MMC_MaxMana.generated.h"/*** */
UCLASS()
class AURA_API UMMC_MaxMana : public UGameplayModMagnitudeCalculation
{GENERATED_BODY()public:UMMC_MaxMana();virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;private:FGameplayEffectAttributeCaptureDefinition IntelligenceDef;};
// 版权归暮志未晚所有。#include "AbilitySystem/ModMagCalc/MMC_MaxMana.h"#include "AbilitySystem/AttributeSetBase.h"
#include "Interaction/CombatInterface.h"UMMC_MaxMana::UMMC_MaxMana()
{IntelligenceDef.AttributeToCapture = UAttributeSetBase::GetIntelligenceAttribute(); //设置需要获取的属性对象IntelligenceDef.AttributeSource = EGameplayEffectAttributeCaptureSource::Target; //设置拾取对象为GE的应用目标IntelligenceDef.bSnapshot = false;RelevantAttributesToCapture.Add(IntelligenceDef); //添加到捕获属性数值,只有添加到列表,才会去获取属性值
}float UMMC_MaxMana::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
{// 从 source 和 target 获取 Tagconst FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();FAggregatorEvaluateParameters EvaluateParameters;EvaluateParameters.SourceTags = SourceTags;EvaluateParameters.TargetTags = TargetTags;//获取体力值float Intelligence = 0.f;GetCapturedAttributeMagnitude(IntelligenceDef, Spec, EvaluateParameters, Intelligence);Intelligence = FMath::Max<float>(Intelligence, 0.f);//获取等级ICombatInterface* CombatInterface = Cast<ICombatInterface>(Spec.GetContext().GetSourceObject());const int32 Level = CombatInterface->GetPlayerLevel();//计算最大血量return 50.f + Intelligence * 2.5f + Level * 15.f;
}
智力为21 等级为1 50 + 21 * 2.5f + 15 = 117.5 数值正确。
上面就是MMC的创建。
初始化血量和蓝量
在设置完最大血量和最大蓝量后,我们会发现血量和蓝量的都是不满的,接下来,我们要将药瓶填满。
在角色基类上面,我们已经完成对PrimaryAttribute和SecondaryAttribute的初始化
我们在下面接着增加一个参数配置,可以配置更多一个GE
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category="Attributes")TSubclassOf<UGameplayEffect> DefaultVitalAttributes;
在应用GE时,也应用到自身,注意顺序,不能乱
void ACharacterBase::InitializeDefaultAttributes() const
{ApplyEffectToSelf(DefaultPrimaryAttributes, 1.f);ApplyEffectToSelf(DefaultSecondaryAttributes, 1.f);ApplyEffectToSelf(DefaultVitalAttributes, 1.f);
}
接着创建第三个初始化GE,我们这次使用Instant,在设置完成最大血量和最大蓝量,血量和蓝量填满即可
这里只需要使用Attribute Based 基于最大血量和蓝量一比一填入即可
将GE设置到角色身上
药瓶满了
可以看到最大血量和血量,最大蓝量和蓝量数值相同了