13. UE5 RPG limita a gama de valores de atributos e gera estruturas

Nos capítulos anteriores, implementamos a modificação dos valores dos atributos através do GameplayEffect, como volume de sangue e volume de mana. Todos nós temos um volume máximo de sangue e um volume máximo de mana para limitar seus valores máximos e os valores mínimos O volume de saúde e o volume de mana não são iguais. será menor que zero. Não implementamos restrições relevantes antes, em seguida, precisamos implementar restrições de intervalo nos valores reais na função AttributeSet.

concluir

Primeiro substitua a função da classe pai, na função PreAttributeChange(), esta função irá acionar um retorno de chamada antes que o valor monitorado no AttributeSet seja alterado.

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

O retorno de chamada retorna dois parâmetros. Um é o Atributo. Podemos usar esse valor para determinar qual atributo foi modificado. O outro é o valor a ser modificado. A seguir, imprimimos o valor e vemos o resultado.

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

Compile e abra o UE, clique no registro de saída no canto inferior esquerdo da cena.
Insira a descrição da imagem aqui
Selecione para encaixar no layout
Insira a descrição da imagem aqui
e deixe o personagem comer frascos de remédios, cristais e pisar no fogo para ver as mudanças de atributos. Descobriremos que tudo as alterações nos atributos podem ser refletidas verdadeiramente em Imprimir o acima
Insira a descrição da imagem aqui
e, em seguida, usar a função de fixação para limitar os valores de saúde e mana na faixa de 0 ao máximo de saúde e mana.

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

Execute o UE e verifique novamente e descubra que os valores estão limitados dentro do intervalo.
Insira a descrição da imagem aqui

PostGameplayEffectExecute

A função PostGameplayEffectExecute() é acionada após a mudança de valor, geralmente só pode ser acionada por GameplayEffect do tipo Instant (GameplayEffect das classes Duration e Infinite também pode ser acionada se Period estiver definido).
Existem muitos cenários de aplicação para esta função, podemos realizar algumas operações lógicas, como morte, invencibilidade e nenhuma dedução de sangue, etc.
Para usá-lo, precisamos primeiro substituir a classe pai

virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override;

Ele possui apenas um parâmetro de retorno, Dados, mas Dados contém muito conteúdo.

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

Abra o UE para ver o volume de sangue atual e o valor do dano causado por este efeito.
Insira a descrição da imagem aqui
Quando atingimos um ponto de interrupção, podemos
Insira a descrição da imagem aqui
ver que há três dados em Data.
Insira a descrição da imagem aqui
EffectSpec é a instância do efeito que contém muitos dados. Podemos usá-lo para obter qual ator aplicou esse GE ao alvo.
EvaluatedData é o conteúdo relacionado aos dados modificados. Valor atual, quantos valores foram modificados, quais atributos foram modificados, etc.
Target é o ASC do alvo.
Insira a descrição da imagem aqui
A seguir, obteremos o que precisamos de Data e encapsularemos em uma estrutura para uso posterior.

Primeiro, crie uma estrutura FEffectProperties para armazenar objetos relacionados à conversão de GE e objetos relacionados ao destino. Esta estrutura salva o contexto GE e o personagem controlador ASC AvatarActor do lançador e do alvo.

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

A seguir, criamos uma função privada na qual processamos os valores dos atributos da estrutura gerada. A função recebe dois valores, um é o Data retornado pela função PostGameplayEffectExecute() e o outro é a estrutura que precisa ser preenchida.

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

Em seguida, implemente a função e defina as propriedades dentro da função. Conforme mencionado anteriormente, você pode obter as propriedades relevantes por meio de Dados.

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

Depois, basta criar uma estrutura dentro de PostGameplayEffectExecute() e chamar a função para gerar conteúdo.

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

	FEffectProperties Props;
	SetEffectProperties(Data, Props);

}

Ao usá-lo, podemos obter o conteúdo correspondente através da estrutura, e a lógica fica mais organizada
Insira a descrição da imagem aqui

Código fonte

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

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


Acho que você gosta

Origin blog.csdn.net/qq_30100043/article/details/136058336
Recomendado
Clasificación