13. UE5 RPG는 속성 값의 범위를 제한하고 구조를 생성합니다.

이전 장에서는 GameplayEffect를 통해 혈액량, 마나량 등 속성 값의 수정을 구현했습니다. 우리 모두는 최대 혈액량과 최대 마나량을 가지고 있어 최대값을 제한하고, 최소값을 제한합니다. ​체력량과 마나량은 동일하지 않으며 0보다 작습니다. 이전에는 관련 제한을 구현하지 않았는데 다음으로 AttributeSet 함수에서 실제 값에 대한 범위 제한을 구현해야 합니다.

성취하다

먼저 PreAttributeChange() 함수에서 상위 클래스 함수를 재정의합니다. 이 함수는 AttributeSet의 모니터링된 값이 변경되기 전에 콜백을 트리거합니다.

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

콜백은 두 개의 매개변수를 반환합니다. 하나는 속성입니다. 이 값을 사용하여 어떤 속성이 수정되었는지 확인할 수 있습니다. 다른 하나는 수정할 값입니다. 다음으로 값을 인쇄하고 결과를 확인합니다.

	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를 컴파일하고 열고 장면의 왼쪽 하단에 있는 출력 로그를 클릭합니다.
여기에 이미지 설명을 삽입하세요.
레이아웃에 도킹하도록 선택
여기에 이미지 설명을 삽입하세요.
하고 캐릭터가 약병, 수정, 불을 밟으면 속성이 변경되는 것을 확인할 수 있습니다. 속성 변경 사항은 위 내용을 인쇄
여기에 이미지 설명을 삽입하세요.
한 후 클램프 기능을 사용하여 체력 및 마나 값을 0부터 최대 체력 및 마나까지의 범위로 제한하여 진실되게 반영할 수 있습니다 .

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

UE를 실행해서 다시 확인해 보니 값이 범위 내에서 제한되어 있는 것을 확인합니다.
여기에 이미지 설명을 삽입하세요.

포스트게임플레이효과실행

PostGameplayEffectExecute() 함수는 값이 변경된 후에 실행되며, 일반적으로 Instant 유형의 GameplayEffect에 의해서만 실행될 수 있습니다(Period가 설정된 경우 Duration 및 Infinite 클래스의 GameplayEffect도 실행 가능).
이 기능에 대한 많은 응용 시나리오가 있으며 사망, 무적, 혈액 공제 없음 등과 같은 몇 가지 논리적 작업을 수행할 수 있습니다.
이를 사용하려면 먼저 상위 클래스를 재정의해야 합니다.

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를 열어 현재 혈액량과 이 효과로 인한 손상 값을 확인하세요.
여기에 이미지 설명을 삽입하세요.
중단점에 도달하면
여기에 이미지 설명을 삽입하세요.
Data에 세 가지 데이터 조각이 있음을 알 수 있습니다.
여기에 이미지 설명을 삽입하세요.
EffectSpec은 많은 데이터가 포함된 효과의 인스턴스입니다. 이를 사용하여 어떤 Actor가 이 GE를 대상에 적용했는지 확인할 수 있습니다.
EvaluatedData는 수정된 데이터와 관련된 내용 현재 값, 수정된 값 수, 수정된 속성 등
Target은 대상의 ASC입니다.
여기에 이미지 설명을 삽입하세요.
다음으로 Data에서 필요한 것을 가져와서 나중에 사용하기 위한 구조입니다.

먼저 FEffectProperties 구조를 만들어 GE 캐스팅과 관련된 개체와 대상과 관련된 개체를 저장합니다. 이 구조는 시전자와 대상의 GE 컨텍스트와 ASC AvatarActor 컨트롤러 캐릭터를 저장합니다.

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() 함수에 의해 반환된 데이터이고, 다른 하나는 채워야 하는 구조입니다.

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);
}


추천

출처blog.csdn.net/qq_30100043/article/details/136058336