当前位置: 首页 > article >正文

13. UE5 RPG限制Attribute的值的范围以及生成结构体

前面几章,我们实现了通过GameplayEffect对Attribute值的修改,比如血量和蓝量,我们都是有一个最大血量和最大蓝量去限制它的最大值,而且血量和蓝量最小值不会小于零。之前我们是没有实现相关限制的,接下来,我们需要在AttributeSet函数里面实现一下对实际值的范围限制。

实现

首先覆盖父类函数,在PreAttributeChange()函数,这个函数会在AttributeSet里的监听的值发生改变前触发回调

virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;

回调有返回两个参数,一个是Attribute,我们可以通过此值判断哪个属性被修改掉了,另一个是将要修改成的值,接下来,我们打印一下值,看一下结果。

	if(Attribute == GetHealthAttribute())
	{
		UE_LOG(LogTemp, Warning, TEXT("Health: %f"), NewValue);
	}

	if(Attribute == GetMaxHealthAttribute())
	{
		UE_LOG(LogTemp, Warning, TEXT("MaxHealth: %f"), NewValue);
	}

	if(Attribute == GetManaAttribute())
	{
		UE_LOG(LogTemp, Warning, TEXT("Mana: %f"), NewValue);
	}

	if(Attribute == GetMaxManaAttribute())
	{
		UE_LOG(LogTemp, Warning, TEXT("MaxMana: %f"), NewValue);
	}

编译打开UE,点击场景左下角的输出日志
在这里插入图片描述
选择停靠在布局中
在这里插入图片描述
然后让角色去吃药瓶,水晶,以及去踩火堆,看看属性变化,我们会发现所有属性变化,都能够如实的反应在打印上面
在这里插入图片描述
接着使用clamp函数将血量和蓝量数值限制在0到最大血量和蓝量的范围内

NewValue = FMath::Clamp(NewValue, 0.f, GetMaxHealth());

运行UE,再查看,发现数值都被限制在了范围内
在这里插入图片描述

PostGameplayEffectExecute

PostGameplayEffectExecute()函数是在数值变化后触发的,一般只会在Instant类型的GameplayEffect才可以触发(Duration和Infinite类的GameplayEffect如果设置Period也可以触发)。
这个函数的应用场景很多,我们可以做一些逻辑操作,比如死亡,无敌不扣血等等。
使用它我们需要先覆盖父类

virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override;

它只有一个返回参数就是Data,但是Data里面包含的内容很多

	if(Data.EvaluatedData.Attribute == GetHealthAttribute())
	{
		UE_LOG(LogTemp, Warning, TEXT("Health: %f"), GetHealth());
		UE_LOG(LogTemp, Warning, TEXT("Magnitude: %f"), Data.EvaluatedData.Magnitude);
	}

打开UE可以查看到,当前的血量,以及这次Effect造成的伤害数值。
在这里插入图片描述
我们打一个断点
在这里插入图片描述
可以看到Data里面有三项数据
在这里插入图片描述
EffectSpec就是效果的实例里面包含很多的数据,我们可以通过它获取到时哪个Actor将此GE应用到目标身上的
EvaluatedData就是修改的数据相关的内容,当前值,修改了多少值,修改的什么属性等等
Target就是目标的ASC
在这里插入图片描述
接下来,我们将从Data中获取到需要的然后封装成一个结构体,方便后续使用。

首先创建一个FEffectProperties的结构体,用于存储施放GE的相关对象和目标的相关对象。这个结构体,将GE的上下文,并将施放者和目标的ASC AvatarActor Controller Character都保存了下来

USTRUCT()
struct FEffectProperties
{
	GENERATED_BODY()

	FEffectProperties(){}

	FGameplayEffectContextHandle EffectContextHandle;

	UPROPERTY()
	UAbilitySystemComponent* SourceASC = nullptr;

	UPROPERTY()
	AActor* SourceAvatarActor = nullptr;

	UPROPERTY()
	AController* SourceController = nullptr;

	UPROPERTY()
	ACharacter* SourceCharacter = nullptr;

	UPROPERTY()
	UAbilitySystemComponent* TargetASC = nullptr;

	UPROPERTY()
	AActor* TargetAvatarActor = nullptr;

	UPROPERTY()
	AController* TargetController = nullptr;

	UPROPERTY()
	ACharacter* TargetCharacter = nullptr;
};

接下来,创建一个私有函数,我们在这个函数里面去处理生成结构体属性的值。函数接收两个值,一个是PostGameplayEffectExecute()函数返回的Data,另一个是需要填充的结构体。

static void SetEffectProperties(const FGameplayEffectModCallbackData& Data, FEffectProperties& Props);

接着,实现函数,在函数内设置属性,前面将了,可以通过Data获取到相关的属性

void UAttributeSetBase::SetEffectProperties(const FGameplayEffectModCallbackData& Data, FEffectProperties& Props)
{
	//Source 效果的所有者   Target 效果应用的目标

	Props.EffectContextHandle = Data.EffectSpec.GetContext();
	Props.SourceASC = Props.EffectContextHandle.GetOriginalInstigatorAbilitySystemComponent(); //获取效果所有者的ASC

	//获取效果所有者的相关对象
	if(IsValid(Props.SourceASC) && Props.SourceASC->AbilityActorInfo.IsValid() && Props.SourceASC->AbilityActorInfo->AvatarActor.IsValid())
	{
		Props.SourceAvatarActor = Props.SourceASC->AbilityActorInfo->AvatarActor.Get(); //获取Actor
		Props.SourceController = Props.SourceASC->AbilityActorInfo->PlayerController.Get(); //获取PlayerController
		if(Props.SourceController == nullptr && Props.SourceAvatarActor != nullptr)
		{
			if(const APawn* Pawn = Cast<APawn>(Props.SourceAvatarActor))
			{
				Props.SourceController = Pawn->GetController();
			}
		}

		if(Props.SourceController)
		{
			Props.SourceCharacter = Cast<ACharacter>(Props.SourceController->GetPawn());
		}
	}

	if(Data.Target.AbilityActorInfo.IsValid() && Data.Target.AbilityActorInfo->AvatarActor.IsValid())
	{
		Props.TargetAvatarActor = Data.Target.AbilityActorInfo->AvatarActor.Get();
		Props.TargetController = Data.Target.AbilityActorInfo->PlayerController.Get();
		Props.TargetCharacter = Cast<ACharacter>(Props.TargetAvatarActor);
		Props.TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(Props.TargetAvatarActor);
	}
}

接着,只需要在PostGameplayEffectExecute()内创建一个结构体,并调用函数生成内容即可。

void UAttributeSetBase::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
	Super::PostGameplayEffectExecute(Data);

	FEffectProperties Props;
	SetEffectProperties(Data, Props);

}

在使用时,我们就可以通过结构体去获取相应的内容,逻辑更加整洁
在这里插入图片描述

源代码

AttributeSetBase.h

// 版权归暮志未晚所有。

#pragma once

#include "CoreMinimal.h"
#include "AttributeSet.h"
#include "AbilitySystemComponent.h"
#include "AttributeSetBase.generated.h"

// Uses macros from AttributeSet.h
#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)

USTRUCT()
struct FEffectProperties
{
	GENERATED_BODY()

	FEffectProperties(){}

	FGameplayEffectContextHandle EffectContextHandle;

	UPROPERTY()
	UAbilitySystemComponent* SourceASC = nullptr;

	UPROPERTY()
	AActor* SourceAvatarActor = nullptr;

	UPROPERTY()
	AController* SourceController = nullptr;

	UPROPERTY()
	ACharacter* SourceCharacter = nullptr;

	UPROPERTY()
	UAbilitySystemComponent* TargetASC = nullptr;

	UPROPERTY()
	AActor* TargetAvatarActor = nullptr;

	UPROPERTY()
	AController* TargetController = nullptr;

	UPROPERTY()
	ACharacter* TargetCharacter = nullptr;
};

/**
 * 技能系统属性集
 */
UCLASS()
class AURA_API UAttributeSetBase : public UAttributeSet
{
	GENERATED_BODY()

public:
	UAttributeSetBase();
	virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;

	virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;
	virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override;

	UPROPERTY(BlueprintReadOnly,ReplicatedUsing = OnRep_Health, Category="Vital Attributes")
	FGameplayAttributeData Health;
	ATTRIBUTE_ACCESSORS(UAttributeSetBase, Health);

	UPROPERTY(BlueprintReadOnly,ReplicatedUsing = OnRep_MaxHealth, Category="Vital Attributes")
	FGameplayAttributeData MaxHealth;
	ATTRIBUTE_ACCESSORS(UAttributeSetBase, MaxHealth);

	UPROPERTY(BlueprintReadOnly,ReplicatedUsing = OnRep_Mana, Category="Vital Attributes")
	FGameplayAttributeData Mana;
	ATTRIBUTE_ACCESSORS(UAttributeSetBase, Mana);

	UPROPERTY(BlueprintReadOnly,ReplicatedUsing = OnRep_MaxMana, Category="Vital Attributes")
	FGameplayAttributeData MaxMana;
	ATTRIBUTE_ACCESSORS(UAttributeSetBase, MaxMana);

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

	UFUNCTION()
	void OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth) const;

	UFUNCTION()
	void OnRep_Mana(const FGameplayAttributeData& OldMana) const;

	UFUNCTION()
	void OnRep_MaxMana(const FGameplayAttributeData& OldMaxMana) const;

private:
	static void SetEffectProperties(const FGameplayEffectModCallbackData& Data, FEffectProperties& Props);
};

AttributeSetBase.cpp

// 版权归暮志未晚所有。


#include "AbilitySystem/AttributeSetBase.h"

#include "AbilitySystemBlueprintLibrary.h"
#include "GameplayEffectExtension.h"
#include "GameFramework/Character.h"
#include "Net/UnrealNetwork.h"

UAttributeSetBase::UAttributeSetBase()
{
	InitHealth(30.f);
	InitMaxHealth(100.f);
	InitMana(30.f);
	InitMaxMana(100.f);
}

void UAttributeSetBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);

	DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, Health, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, MaxHealth, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, Mana, COND_None, REPNOTIFY_Always);
	DOREPLIFETIME_CONDITION_NOTIFY(UAttributeSetBase, MaxMana, COND_None, REPNOTIFY_Always);
}

void UAttributeSetBase::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
{
	Super::PreAttributeChange(Attribute, NewValue);

	if(Attribute == GetHealthAttribute())
	{
		NewValue = FMath::Clamp(NewValue, 0.f, GetMaxHealth());
		// UE_LOG(LogTemp, Warning, TEXT("Health: %f"), NewValue);
	}

	if(Attribute == GetManaAttribute())
	{
		NewValue = FMath::Clamp(NewValue, 0.f, GetMaxMana());
	}
}

void UAttributeSetBase::SetEffectProperties(const FGameplayEffectModCallbackData& Data, FEffectProperties& Props)
{
	//Source 效果的所有者   Target 效果应用的目标

	Props.EffectContextHandle = Data.EffectSpec.GetContext();
	Props.SourceASC = Props.EffectContextHandle.GetOriginalInstigatorAbilitySystemComponent(); //获取效果所有者的ASC

	//获取效果所有者的相关对象
	if(IsValid(Props.SourceASC) && Props.SourceASC->AbilityActorInfo.IsValid() && Props.SourceASC->AbilityActorInfo->AvatarActor.IsValid())
	{
		Props.SourceAvatarActor = Props.SourceASC->AbilityActorInfo->AvatarActor.Get(); //获取Actor
		Props.SourceController = Props.SourceASC->AbilityActorInfo->PlayerController.Get(); //获取PlayerController
		if(Props.SourceController == nullptr && Props.SourceAvatarActor != nullptr)
		{
			if(const APawn* Pawn = Cast<APawn>(Props.SourceAvatarActor))
			{
				Props.SourceController = Pawn->GetController();
			}
		}

		if(Props.SourceController)
		{
			Props.SourceCharacter = Cast<ACharacter>(Props.SourceController->GetPawn());
		}
	}

	if(Data.Target.AbilityActorInfo.IsValid() && Data.Target.AbilityActorInfo->AvatarActor.IsValid())
	{
		Props.TargetAvatarActor = Data.Target.AbilityActorInfo->AvatarActor.Get();
		Props.TargetController = Data.Target.AbilityActorInfo->PlayerController.Get();
		Props.TargetCharacter = Cast<ACharacter>(Props.TargetAvatarActor);
		Props.TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(Props.TargetAvatarActor);
	}
}

void UAttributeSetBase::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
	Super::PostGameplayEffectExecute(Data);

	FEffectProperties Props;
	SetEffectProperties(Data, Props);
}

void UAttributeSetBase::OnRep_Health(const FGameplayAttributeData& OldHealth) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAttributeSetBase, Health, OldHealth);
}

void UAttributeSetBase::OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAttributeSetBase, MaxHealth, OldMaxHealth);
}

void UAttributeSetBase::OnRep_Mana(const FGameplayAttributeData& OldMana) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAttributeSetBase, MaxHealth, OldMana);
}

void UAttributeSetBase::OnRep_MaxMana(const FGameplayAttributeData& OldMaxMana) const
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UAttributeSetBase, MaxHealth, OldMaxMana);
}



http://www.kler.cn/a/232084.html

相关文章:

  • vue3中pinia状态管理库使用以及持久化配置方法总结
  • 小版本大不同 | Navicat 17 新增 TiDB 功能
  • EXCEL 或 WPS 列下划线转驼峰
  • Spring Boot框架:电商系统的技术革新
  • 前后端分离练习(云客项目)
  • Ubuntu 18 EDK2 环境编译
  • 3D Line Mapping Revisited论文阅读
  • POPPPPPJ勒索病毒:了解最新POPPPPPJ,以及如何保护您的数据
  • 自定义指令实现图片懒加载
  • 【数据结构】链表OJ面试题4(题库+解析)
  • sqli-labs-master靶场训练笔记(38-53|boss战)
  • STM32——FLASH(1)简单介绍、分类、读写流程及注意事项
  • 百面嵌入式专栏(面试题)C语言面试题22道
  • 命令行任务管理器的at命令
  • 瑞_力扣LeetCode_二叉树相关题
  • Win10系统备份的几种方案,以后不重装系统,备份系统恢复Backup,系统映像备份
  • Python(21)正则表达式中的“元字符”
  • 05 06 Verilog基础语法与应用讲解
  • 【Django-ninja】在django ninja中处理异常
  • Java代码实现基数排序算法(附带源码)
  • 计算机设计大赛 深度学习 机器视觉 人脸识别系统 - opencv python
  • TCP 传输控制协议
  • JenkinsGitLab完成自动化构建部署
  • 2024.2.6
  • 二叉树中的最大路径和
  • mysql学习打卡day22