UrealEngine-5.2.1源码-AttributeSet.h

// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "Templates/SubclassOf.h"
#include "UObject/UnrealType.h"
#include "Engine/DataTable.h"
#include "AttributeSet.generated.h"

class UAbilitySystemComponent;
class UAttributeSet;
class UCurveTable;
struct FGameplayAbilityActorInfo;
struct FAggregator;

/** 放置在AttributeSet中,创建可以使用FGameplayAttribute访问的属性。强烈建议使用此方法而不是原始浮点数属性 */
USTRUCT(BlueprintType)
struct GAMEPLAYABILITIES_API FGameplayAttributeData
{
	GENERATED_BODY()
	FGameplayAttributeData()
		: BaseValue(0.f)
		, CurrentValue(0.f)
	{}

	FGameplayAttributeData(float DefaultValue)
		: BaseValue(DefaultValue)
		, CurrentValue(DefaultValue)
	{}

	virtual ~FGameplayAttributeData()
	{}

	/** 返回当前值,包括临时增益效果 */
	float GetCurrentValue() const;

	/** 修改当前值,通常仅由能力系统或在初始化期间调用 */
	virtual void SetCurrentValue(float NewValue);

	/** 返回基础值,仅包括永久性更改 */
	float GetBaseValue() const;

	/** 修改永久性基础值,通常仅由能力系统或在初始化期间调用 */
	virtual void SetBaseValue(float NewValue);

protected:
	UPROPERTY(BlueprintReadOnly, Category = "Attribute")
	float BaseValue;

	UPROPERTY(BlueprintReadOnly, Category = "Attribute")
	float CurrentValue;
};

/** 描述AttributeSet内的FGameplayAttributeData或float属性。使用此结构提供编辑器UI和辅助函数 */
USTRUCT(BlueprintType)
struct GAMEPLAYABILITIES_API FGameplayAttribute
{
	GENERATED_USTRUCT_BODY()

	FGameplayAttribute()
		: Attribute(nullptr)
		, AttributeOwner(nullptr)
	{
	}

	FGameplayAttribute(FProperty *NewProperty);

	bool IsValid() const
	{
		return Attribute != nullptr;
	}

	/** 从集合内的FProperty设置 */
	void SetUProperty(FProperty *NewProperty)
	{
		Attribute = NewProperty;
		if (NewProperty)
		{
			AttributeOwner = Attribute->GetOwnerStruct();
			Attribute->GetName(AttributeName);
		}
		else
		{
			AttributeOwner = nullptr;
			AttributeName.Empty();
		}
	}

	/** 返回原始属性 */
	FProperty* GetUProperty() const
	{
		return Attribute.Get();
	}

	/** 返回持有此属性的AttributeSet子类 */
	UClass* GetAttributeSetClass() const
	{
		check(Attribute.Get());
		return CastChecked<UClass>(Attribute->GetOwner<UObject>());
	}

	/** 如果这是在AbilitySystemComponent本身上定义的特殊属性之一,则返回true */
	bool IsSystemAttribute() const;

	/** 如果与Property关联的变量是FGameplayAttributeData类型或其子类,则返回true */
	static bool IsGameplayAttributeDataProperty(const FProperty* Property);

	/** 修改属性的当前值,如果支持的话不会修改基础值 */
	void SetNumericValueChecked(float& NewValue, class UAttributeSet* Dest) const;

	/** 返回属性的当前值 */
	float GetNumericValue(const UAttributeSet* Src) const;
	float GetNumericValueChecked(const UAttributeSet* Src) const;

	/** 返回AttributeData,如果这是float属性则会失败 */
	const FGameplayAttributeData* GetGameplayAttributeData(const UAttributeSet* Src) const;
	const FGameplayAttributeData* GetGameplayAttributeDataChecked(const UAttributeSet* Src) const;
	FGameplayAttributeData* GetGameplayAttributeData(UAttributeSet* Src) const;
	FGameplayAttributeData* GetGameplayAttributeDataChecked(UAttributeSet* Src) const;
	
	/** 相等/不等运算符 */
	bool operator==(const FGameplayAttribute& Other) const;
	bool operator!=(const FGameplayAttribute& Other) const;

	friend uint32 GetTypeHash( const FGameplayAttribute& InAttribute )
	{
		// FIXME: Use ObjectID or something to get a better, less collision prone hash
		return PointerHash(InAttribute.Attribute.Get());
	}

	/** 返回属性名称,通常与属性相同 */
	FString GetName() const
	{
		return AttributeName.IsEmpty() ? *GetNameSafe(Attribute.Get()) : AttributeName;
	}

#if WITH_EDITORONLY_DATA
	/** 自定义序列化 */
	void PostSerialize(const FArchive& Ar);
#endif

	/** 属性名称,通常与属性名称相同 */
	UPROPERTY(Category = GameplayAttribute, VisibleAnywhere, BlueprintReadOnly)
	FString AttributeName;

	/** 在编辑器中,这将过滤掉具有元标记"HideInDetailsView"或等于FilterMetaStr的属性。在非编辑器中,返回所有属性 */
	static void GetAllAttributeProperties(TArray<FProperty*>& OutProperties, FString FilterMetaStr=FString(), bool UseEditorOnlyData=true);

private:
	friend class FAttributePropertyDetails;

	UPROPERTY(Category = GameplayAttribute, EditAnywhere)
	TFieldPath<FProperty> Attribute;
	//FProperty*	Attribute;

	UPROPERTY(Category = GameplayAttribute, VisibleAnywhere)
	TObjectPtr<UStruct> AttributeOwner;
};

#if WITH_EDITORONLY_DATA
template<>
struct TStructOpsTypeTraits< FGameplayAttribute > : public TStructOpsTypeTraitsBase2< FGameplayAttribute >
{
	enum
	{
		WithPostSerialize = true,
	};
};
#endif

/**
 * 定义游戏中所有GameplayAttributes的集合
 * 游戏应该继承此类并添加FGameplayAttributeData属性来表示诸如生命值、伤害等属性
 * AttributeSet作为子对象添加到actor中,然后在AbilitySystemComponent中注册
 * 通常希望每个项目有多个相互继承的集合
 * 您可以创建一个基础生命值集合,然后有一个从它继承并添加更多属性的玩家集合
 */
UCLASS(DefaultToInstanced, Blueprintable)
class GAMEPLAYABILITIES_API UAttributeSet : public UObject
{
	GENERATED_UCLASS_BODY()

public:

	/** 重写以禁用特定属性的初始化 */
	virtual bool ShouldInitProperty(bool FirstInit, FProperty* PropertyToInit) const { return true; }

	/**
	 * 在修改属性值之前调用。AttributeSet可以在这里进行额外的修改。返回true继续,或false丢弃修改。
	 * 注意这仅在'execute'期间调用。例如,对属性'基础值'的修改。在应用GameplayEffect时不会调用,例如5秒+10移动速度增益。
	 */	
	virtual bool PreGameplayEffectExecute(struct FGameplayEffectModCallbackData &Data) { return true; }
	
	/**
	 * 在执行GameplayEffect以修改属性的基础值之前调用。不能再进行更改。
	 * 注意这仅在'execute'期间调用。例如,对属性'基础值'的修改。在应用GameplayEffect时不会调用,例如5秒+10移动速度增益。
	 */
	virtual void PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData &Data) { }

	/**
	 * "On Aggregator Change"类型的事件可以放在这里,当主动游戏效果被添加或移除到属性聚合器时调用。
	 * 不过在这些情况下很难提供所有信息 - 聚合器可能因多种原因改变:被添加、被移除、被修改、修饰符改变、免疫、堆叠规则等。
	 */

	/**
	 * 在对属性进行任何修改之前调用。这比PreAttributeModify/PostAttributeModify更低级。
	 * 这里没有提供额外的上下文,因为任何东西都可能触发此调用。执行的效果、基于持续时间的效应、被移除的效果、应用的免疫、堆叠规则改变等。
	 * 此函数旨在强制执行诸如"Health = Clamp(Health, 0, MaxHealth)"之类的事情,而不是"如果应用了伤害则触发这个额外的事情"。
	 * 
	 * NewValue是一个可变引用,因此您也可以钳制新应用的值。
	 */
	virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) { }

	/** 在对属性进行任何修改之后调用。 */
	virtual void PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue) { }

	/**
	 * 当属性聚合器存在时,在对属性的基础值进行任何修改之前调用。
	 * 此函数应强制执行钳制(假设您希望在PreAttributeChange中钳制基础值和最终值)
	 * 此函数不应调用游戏玩法相关的事件或回调。在PreAttributeChange()中进行这些操作,该函数将在
	 * 属性的最终值实际改变之前调用。
	 */
	virtual void PreAttributeBaseChange(const FGameplayAttribute& Attribute, float& NewValue) const { }

	/** 当属性聚合器存在时,在对属性的基础值进行任何修改之后调用。 */
	virtual void PostAttributeBaseChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue) const { }

	/** 当为此集合中的属性创建FAggregator时的回调。允许自定义设置FAggregator::EvaluationMetaData */
	virtual void OnAttributeAggregatorCreated(const FGameplayAttribute& Attribute, FAggregator* NewAggregator) const { }

	/** 这表示属性集可以通过网络按名称进行ID识别。 */
	void SetNetAddressable();

	/** 从元数据DataTable初始化属性数据 */
	virtual void InitFromMetaDataTable(const UDataTable* DataTable);

	/** 获取关于所属actor的信息 */
	AActor* GetOwningActor() const;
	UAbilitySystemComponent* GetOwningAbilitySystemComponent() const;
	UAbilitySystemComponent* GetOwningAbilitySystemComponentChecked() const;
	FGameplayAbilityActorInfo* GetActorInfo() const;

	/** 打印调试信息到日志 */
	virtual void PrintDebug();

	// Overrides
	virtual bool IsNameStableForNetworking() const override;
	virtual bool IsSupportedForNetworking() const override;
	virtual void PreNetReceive() override;
	virtual void PostNetReceive() override;

#if UE_WITH_IRIS
	/** 注册所有复制片段 */
	virtual void RegisterReplicationFragments(UE::Net::FFragmentRegistrationContext& Context, UE::Net::EFragmentRegistrationFlags RegistrationFlags) override;
#endif // UE_WITH_IRIS
protected:
	/** 这个属性集是否可以通过网络按名称安全地进行ID识别? */
	uint32 bNetAddressable : 1;
};

/**
 *	DataTable,允许我们定义关于属性的元数据。仍在进行中。
 */
USTRUCT(BlueprintType)
struct GAMEPLAYABILITIES_API FAttributeMetaData : public FTableRowBase
{
	GENERATED_USTRUCT_BODY()

public:

	FAttributeMetaData();

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Gameplay Attribute")
	float		BaseValue;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Gameplay Attribute")
	float		MinValue;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Gameplay Attribute")
	float		MaxValue;

	UPROPERTY()
	FString		DerivedAttributeInfo;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Gameplay Attribute")
	bool		bCanStack;
};

/**
 *	帮助结构,便于从电子表格(UCurveTable)初始化属性集默认值。
 *	项目可以自由地以任何方式初始化其属性集。这只是在某些情况下有用的示例。
 *	
 *	基本思想是以这种形式拥有一个电子表格:
 *	
 *									1	2	3	4	5	6	7	8	9	10	11	12	13	14	15	16	17	18	19	20
 *
 *	Default.Health.MaxHealth		100	200	300	400	500	600	700	800	900	999	999	999	999	999	999	999	999	999	999	999
 *	Default.Health.HealthRegenRate	1	1	1	1	1	1	1	1	1	1	1	1	1	1	1	1	1	1	1	1
 *	Default.Health.AttackRating		10	10	10	10	10	10	10	10	10	10	10	10	10	10	10	10	10	10	10	10
 *	Default.Move.MaxMoveSpeed		500	500	500	500	500	500	500	500	500	500	500	500	500	500	500	500	500	500	500	500
 *	Hero1.Health.MaxHealth			100	100	100	100	100	100	100	100	100	100	100	100	100	100	100	100	100	100	100	100
 *	Hero1.Health.HealthRegenRate	1	1	1	1	1	1	1	1	1	1	1	1	1	1	1	1 	1	1	1	1
 *	Hero1.Health.AttackRating		10	10	10	10	10	10	10	10	10	10	10	10	10	10	10	10	10	10	10	10
 *	Hero1.Move.MaxMoveSpeed			500	500	500	500	500	500	500	500	500	500	500	500	500	500	500	500	500	500	500	500
 *	
 *	
 *	其中行的形式为:[GroupName].[AttributeSetName].[Attribute]
 *	GroupName			- 标识"组"的任意名称
 *	AttributeSetName	- 属性所属的UAttributeSet。(注意这是对UClass名称的简单部分匹配。"Health"匹配"UMyGameHealthSet")。
 *	Attribute			- 实际属性属性的名称(匹配全名)。
 *		
 *	列代表"等级"。
 *	
 *	FAttributeSetInitter::PreloadAttributeSetData(UCurveTable*)
 *	这将CurveTable转换为更高效的运行时读取格式。例如,应该从UAbilitySystemGlobals调用。
 *
 *	FAttributeSetInitter::InitAttributeSetDefaults(UAbilitySystemComponent* AbilitySystemComponent, FName GroupName, int32 Level) const;
 *	这使用指定的GroupName和Level初始化给定AbilitySystemComponent的属性集。游戏代码在生成新Actor或升级actor等时应该调用此函数。
 *	
 *	示例游戏代码用法:
 *	
 *	IGameplayAbilitiesModule::Get().GetAbilitySystemGlobals()->GetAttributeSetInitter()->InitAttributeSetDefaults(MyCharacter->AbilitySystemComponent, "Hero1", MyLevel);
 *	
 *	注意:
 *	- 这允许系统设计者指定属性的任意值。它们可以基于他们想要的任何公式。
 *	- 具有非常高等级上限的项目可能希望采用更简单的"每级获得的属性"方法。
 *	- 以此方法初始化的任何内容都不应通过游戏效果直接修改。例如,如果MaxMoveSpeed随等级缩放,任何其他修改MaxMoveSpeed的东西都应该使用非即时GameplayEffect来完成。
 *	- "Default"当前是硬编码的备用GroupName。如果使用无效的GroupName调用InitAttributeSetDefaults,我们将回退到default。
 *
 */
struct GAMEPLAYABILITIES_API FAttributeSetInitter
{
	virtual ~FAttributeSetInitter() {}

	virtual void PreloadAttributeSetData(const TArray<UCurveTable*>& CurveData) = 0;
	virtual void InitAttributeSetDefaults(UAbilitySystemComponent* AbilitySystemComponent, FName GroupName, int32 Level, bool bInitialInit) const = 0;
	virtual void ApplyAttributeDefault(UAbilitySystemComponent* AbilitySystemComponent, FGameplayAttribute& InAttribute, FName GroupName, int32 Level) const = 0;
	virtual TArray<float> GetAttributeSetValues(UClass* AttributeSetClass, FProperty* AttributeProperty, FName GroupName) const { return TArray<float>(); }
};

/** 属性集初始化器的显式实现,依赖于离散级别的存在和使用进行数据查找(即,不可能使用CurveTable->Eval) */
struct GAMEPLAYABILITIES_API FAttributeSetInitterDiscreteLevels : public FAttributeSetInitter
{
	virtual void PreloadAttributeSetData(const TArray<UCurveTable*>& CurveData) override;

	virtual void InitAttributeSetDefaults(UAbilitySystemComponent* AbilitySystemComponent, FName GroupName, int32 Level, bool bInitialInit) const override;
	virtual void ApplyAttributeDefault(UAbilitySystemComponent* AbilitySystemComponent, FGameplayAttribute& InAttribute, FName GroupName, int32 Level) const override;

	virtual TArray<float> GetAttributeSetValues(UClass* AttributeSetClass, FProperty* AttributeProperty, FName GroupName) const override;
private:

	bool IsSupportedProperty(FProperty* Property) const;

	struct FAttributeDefaultValueList
	{
		void AddPair(FProperty* InProperty, float InValue)
		{
			List.Add(FOffsetValuePair(InProperty, InValue));
		}

		struct FOffsetValuePair
		{
			FOffsetValuePair(FProperty* InProperty, float InValue)
			: Property(InProperty), Value(InValue) { }

			FProperty*	Property;
			float		Value;
		};

		TArray<FOffsetValuePair>	List;
	};

	struct FAttributeSetDefaults
	{
		TMap<TSubclassOf<UAttributeSet>, FAttributeDefaultValueList> DataMap;
	};

	struct FAttributeSetDefaultsCollection
	{
		TArray<FAttributeSetDefaults>		LevelData;
	};

	TMap<FName, FAttributeSetDefaultsCollection>	Defaults;
};

/**
 *	这是一个辅助宏,可用于RepNotify函数中处理将被客户端预测性修改的属性。
 *	
 *	void UMyHealthSet::OnRep_Health(const FGameplayAttributeData& OldValue)
 *	{
 *		GAMEPLAYATTRIBUTE_REPNOTIFY(UMyHealthSet, Health, OldValue);
 *	}
 */

#define GAMEPLAYATTRIBUTE_REPNOTIFY(ClassName, PropertyName, OldValue) \
{ \
	static FProperty* ThisProperty = FindFieldChecked<FProperty>(ClassName::StaticClass(), GET_MEMBER_NAME_CHECKED(ClassName, PropertyName)); \
	GetOwningAbilitySystemComponentChecked()->SetBaseAttributeValueFromReplication(FGameplayAttribute(ThisProperty), PropertyName, OldValue); \
}

/**
 * 这定义了一组用于访问和初始化属性的辅助函数,以避免手动编写这些函数。
 * 它将为属性Health创建以下函数:
 *
 *	static FGameplayAttribute UMyHealthSet::GetHealthAttribute();
 *	FORCEINLINE float UMyHealthSet::GetHealth() const;
 *	FORCEINLINE void UMyHealthSet::SetHealth(float NewVal);
 *	FORCEINLINE void UMyHealthSet::InitHealth(float NewVal);
 *
 * 要在游戏中使用这个,您可以定义类似这样的东西,然后根据需要添加游戏特定函数:
 * 
 *	#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
 *	GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
 *	GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
 *	GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
 *	GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)
 * 
 *	ATTRIBUTE_ACCESSORS(UMyHealthSet, Health)
 */

#define GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
	static FGameplayAttribute Get##PropertyName##Attribute() \
	{ \
		static FProperty* Prop = FindFieldChecked<FProperty>(ClassName::StaticClass(), GET_MEMBER_NAME_CHECKED(ClassName, PropertyName)); \
		return Prop; \
	}

#define GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
	FORCEINLINE float Get##PropertyName() const \
	{ \
		return PropertyName.GetCurrentValue(); \
	}

#define GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
	FORCEINLINE void Set##PropertyName(float NewVal) \
	{ \
		UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent(); \
		if (ensure(AbilityComp)) \
		{ \
			AbilityComp->SetNumericAttributeBase(Get##PropertyName##Attribute(), NewVal); \
		}; \
	}

#define GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName) \
	FORCEINLINE void Init##PropertyName(float NewVal) \
	{ \
		PropertyName.SetBaseValue(NewVal); \
		PropertyName.SetCurrentValue(NewVal); \
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值