UE5多人MOBA+GAS 27、死亡被动(用于作为击杀奖励,爆金币和是增加经验)

文章目录


添加一个标签用来区分该Actor是否是英雄单位

cpp 复制代码
CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Role_Hero)
cpp 复制代码
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Role_Hero, "Role.Hero", "英雄角色")

添加死亡被动

继承C技能添加一个死亡被动技能

cpp 复制代码
// 幻雨喜欢小猫咪

#pragma once

#include "CoreMinimal.h"
#include "GAS/Core/CGameplayAbility.h"
#include "GAP_Dead.generated.h"

/**
 * 死亡能力类
 * 负责角色死亡时的奖励分配(经验、金币等)
 */
UCLASS()
class CRUNCH_API UGAP_Dead : public UCGameplayAbility
{
	GENERATED_BODY()
public:
	UGAP_Dead();
	virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override;

};
cpp 复制代码
// 幻雨喜欢小猫咪


#include "GAS/Abilities/GAP_Dead.h"

#include "GAS/Core/TGameplayTags.h"

UGAP_Dead::UGAP_Dead()
{
	// 设置网络执行策略为仅在服务器端执行
	NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::ServerOnly;

	// 创建一个新触发数据对象
	FAbilityTriggerData TriggerData;

	// 设置触发数据的触发源为游戏事件
	TriggerData.TriggerSource = EGameplayAbilityTriggerSource::GameplayEvent;
	TriggerData.TriggerTag = TGameplayTags::Stats_Dead;

	AbilityTriggers.Add(TriggerData);

	// 死亡去除眩晕状态
	ActivationBlockedTags.RemoveTag(TGameplayTags::Stats_Stun);
}

void UGAP_Dead::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
	if (K2_HasAuthority())
	{
		AActor* Killer = TriggerEventData->ContextHandle.GetEffectCauser();
		if (!Killer)
		{
			UE_LOG(LogTemp, Warning, TEXT("Killer is null"))
		}else
		{
			UE_LOG(LogTemp, Warning, TEXT("Killer is : %s"), *Killer->GetName())
		}
		
	}
}

激活死亡被动技能

ASC中添加被动技能,以及在死亡的时候激活死亡被动技能(如果使用MMC的话就在这里激活技能使用ECC的话获取不到敌人)

这是直接用GE来计算扣血的操作

ECC的扣血触发的话就会无法获取角色

cpp 复制代码
	// 被动技能
	UPROPERTY(EditDefaultsOnly, Category = "Gameplay Ability")
	TArray<TSubclassOf<UGameplayAbility>> PassiveAbilities;
cpp 复制代码
void UCAbilitySystemComponent::GiveInitialAbilities()
{
	// 检查当前组件是否拥有拥有者,并且拥有者是否具有网络权限(权威性) 
	if (!GetOwner() || !GetOwner()->HasAuthority()) return;

	for (const TPair<ECAbilityInputID,TSubclassOf<UGameplayAbility>>& AbilityPair : BasicAbilities)
	{
		// 赋予技能 等级为 1
		GiveAbility(FGameplayAbilitySpec(AbilityPair.Value, 1, static_cast<int32>(AbilityPair.Key), nullptr));
	}
	
	for (const TPair<ECAbilityInputID,TSubclassOf<UGameplayAbility>>& AbilityPair : Abilities)
	{
		GiveAbility(FGameplayAbilitySpec(AbilityPair.Value, 0, static_cast<int32>(AbilityPair.Key), nullptr));
	}

	for (const TSubclassOf<UGameplayAbility>& PassiveAbility : PassiveAbilities)
	{
		GiveAbility(FGameplayAbilitySpec(PassiveAbility, 1, -1, nullptr));
	}
}

MMC做法

cpp 复制代码
void UCAbilitySystemComponent::HealthUpdated(const FOnAttributeChangeData& ChangeData)
{
	if (!GetOwner() || !GetOwner()->HasAuthority()) return;

	// 获取当前最大生命值
	bool bFound = false;
	float MaxHealth = GetGameplayAttributeValue(UCAttributeSet::GetMaxHealthAttribute(), bFound);
    
	// 如果生命值达到最大值,添加生命值已满标签
	if (bFound && ChangeData.NewValue >= MaxHealth)
	{
		if (!HasMatchingGameplayTag(TGameplayTags::Stats_Health_Full))
		{
			// 仅本地会添加标签
			AddLooseGameplayTag(TGameplayTags::Stats_Health_Full);
		}
	}
	else
	{
		// 移除生命值已满标签
		RemoveLooseGameplayTag(TGameplayTags::Stats_Health_Full);
	}
	if (ChangeData.NewValue <= 0.0f)
	{
		if (!HasMatchingGameplayTag(TGameplayTags::Stats_Health_Empty))
		{
			// 本地添加生命值清零标签
			AddLooseGameplayTag(TGameplayTags::Stats_Health_Empty);
			// 角色死亡
			if (DeathEffect)
			{
				AuthApplyGameplayEffect(DeathEffect);
			}
			// 创建需要传给死亡被动技能的事件数据
			FGameplayEventData DeadAbilityEventData;
			if (ChangeData.GEModData)
			{
				DeadAbilityEventData.ContextHandle = ChangeData.GEModData->EffectSpec.GetContext();
			}else
			{
				UE_LOG(LogTemp, Error, TEXT("ChangeData.GEModData is null"))
			}
			UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(GetOwner(), 
				TGameplayTags::Stats_Dead, 
				DeadAbilityEventData);
		}
	}else
	{
		RemoveLooseGameplayTag(TGameplayTags::Stats_Health_Empty);
	}
}

ECC做法

MMC那种在ASC里面获取的GEModData和属性里获取的const FGameplayEffectModCallbackData& Data是同一种数据类型

在属性里伤害接收这里判断生命是否小于等于0

两种方法都可以

cpp 复制代码
void UCAttributeSet::OnDeadAbility(const FGameplayEffectModCallbackData& Data)
{
	FGameplayEventData DeadAbilityEventData;
	if (AActor* TargetActor = Data.EffectSpec.GetContext().GetOriginalInstigatorAbilitySystemComponent()->AbilityActorInfo->AvatarActor.Get())
	{
		UE_LOG(LogTemp, Warning, TEXT("Dead:%s"), *GetOwningActor()->GetName())
		DeadAbilityEventData.Target = TargetActor;
		DeadAbilityEventData.ContextHandle = Data.EffectSpec.GetContext();
	}
	
	UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(GetOwningActor(), 
		TGameplayTags::Stats_Dead, 
		DeadAbilityEventData);
}
cpp 复制代码
void UGAP_Dead::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
	if (K2_HasAuthority())
	{
		// MMC 获取
		// AActor* Killer = TriggerEventData->ContextHandle.GetEffectCauser();
		// ECC 获取
		AActor* Killer = nullptr;
		if (TriggerEventData->ContextHandle.GetOriginalInstigatorAbilitySystemComponent()->AbilityActorInfo->AvatarActor.Get())
		{
			Killer = TriggerEventData->ContextHandle.GetOriginalInstigatorAbilitySystemComponent()->AbilityActorInfo->AvatarActor.Get();
			UE_LOG(LogTemp, Warning, TEXT("TriggerEventData->ContextHandle.GetOriginalInstigatorAbilitySystemComponent()->AbilityActorInfo->AvatarActor.Get(): %s"), *Killer->GetName())
		}
		if (TriggerEventData->Target)
		{
			Killer = static_cast<AActor*>(TriggerEventData->Target);
			UE_LOG(LogTemp, Warning, TEXT("TriggerEventData->Target: %s"), *Killer->GetName())
		}
		if (!Killer)
		{
			UE_LOG(LogTemp, Warning, TEXT("Killer is null"))
		}else
		{
			UE_LOG(LogTemp, Warning, TEXT("Killer is : %s"), *Killer->GetName())
		}
		
	}
}


寻找奖励目标

设置一个范围的检测,寻找击杀者的阵容的英雄单位,因此在CAbilitySystemStatics中添加一个函数来判断是否为英雄

cpp 复制代码
	// 判断是否为英雄
	static bool IsHero(const AActor* ActorToCheck);
cpp 复制代码
bool UCAbilitySystemStatics::IsHero(const AActor* ActorToCheck)
{
	const IAbilitySystemInterface* ActorISA = Cast<IAbilitySystemInterface>(ActorToCheck);
	if (ActorISA)
	{
		UAbilitySystemComponent* ActorASC = ActorISA->GetAbilitySystemComponent();
		if (ActorASC)
		{
			return ActorASC->HasMatchingGameplayTag(TGameplayTags::Role_Hero);
		}
	}
	return false;
}
cpp 复制代码
private:
	// 奖励分配的范围,在该范围内的队友可获得奖励
	UPROPERTY(EditDefaultsOnly, Category = "Reward")
	float RewardRange = 1000.f;

	// 获取奖励目标(如附近的队友等)
	TArray<AActor*> GetRewardTargets() const;
cpp 复制代码
// 幻雨喜欢小猫咪


#include "GAS/Abilities/GAP_Dead.h"

#include "AbilitySystemComponent.h"
#include "Engine/OverlapResult.h"
#include "GAS/Core/CAbilitySystemStatics.h"
#include "GAS/Core/TGameplayTags.h"

UGAP_Dead::UGAP_Dead()
{
	// 设置网络执行策略为仅在服务器端执行
	NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::ServerOnly;

	// 创建一个新触发数据对象
	FAbilityTriggerData TriggerData;

	// 设置触发数据的触发源为游戏事件
	TriggerData.TriggerSource = EGameplayAbilityTriggerSource::GameplayEvent;
	TriggerData.TriggerTag = TGameplayTags::Stats_Dead;

	AbilityTriggers.Add(TriggerData);

	// 死亡去除眩晕状态
	ActivationBlockedTags.RemoveTag(TGameplayTags::Stats_Stun);
}

void UGAP_Dead::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
	if (K2_HasAuthority())
	{
		// MMC 获取
		// AActor* Killer = TriggerEventData->ContextHandle.GetEffectCauser();
		// ECC 获取
		AActor* Killer = nullptr;
		if (TriggerEventData->Target)
		{
			Killer = static_cast<AActor*>(TriggerEventData->Target);
			UE_LOG(LogTemp, Warning, TEXT("TriggerEventData->Target: %s"), *Killer->GetName())
		}
		if (!Killer)
		{
			UE_LOG(LogTemp, Warning, TEXT("Killer is null"))
			K2_EndAbility();
			return;
		}
		TArray<AActor*> RewardTargets = GetRewardTargets();
		for (const AActor* RewardTarget : RewardTargets)
		{
			UE_LOG(LogTemp, Warning, TEXT("Find Reward Target: %s"), *RewardTarget->GetName());
		}
		K2_EndAbility();
	}
}

TArray<AActor*> UGAP_Dead::GetRewardTargets() const
{
	TSet<AActor*> OutActors;
	AActor* AvatarActor = GetAvatarActorFromActorInfo();
	if (!AvatarActor || !GetWorld())
	{
		return OutActors.Array();
	}

	// 配置碰撞检测参数
	FCollisionObjectQueryParams ObjectQueryParams;
	ObjectQueryParams.AddObjectTypesToQuery(ECC_Pawn);
	FCollisionShape CollisionShape;
	CollisionShape.SetSphere(RewardRange);  // 使用奖励范围作为检测半径

	TArray<FOverlapResult> OverlapResults;
	// 检测碰撞
	if (GetWorld()->OverlapMultiByObjectType(OverlapResults, AvatarActor->GetActorLocation(), FQuat::Identity, ObjectQueryParams, CollisionShape))
	{
		for (const FOverlapResult& OverlapResult : OverlapResults)
		{
			// 获取团队接口,给死亡目标的敌对单位提供奖励
			const IGenericTeamAgentInterface* TeamInterface =  Cast<IGenericTeamAgentInterface>(OverlapResult.GetActor());
			// 过滤非敌对单位
			if (!TeamInterface || TeamInterface->GetTeamAttitudeTowards(*AvatarActor) != ETeamAttitude::Hostile)
			{
				continue;
			}

			// 判断是否为英雄单位
			if (!UCAbilitySystemStatics::IsHero(OverlapResult.GetActor()))
			{
				continue;
			}
			
			// 添加到奖励目标集合
			OutActors.Add(OverlapResult.GetActor());
		}
	}
	return OutActors.Array();
}

创建一个GE用来将英雄的Tag赋值给英雄


奖励的计算与发放

添加两个标签用于GE调用数值

cpp 复制代码
	// 经验
	CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(AttributeSet_Experience)
	// 金币
	CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(AttributeSet_Gold)
cpp 复制代码
	UE_DEFINE_GAMEPLAY_TAG_COMMENT(AttributeSet_Experience, "AttributeSet.Experience", "经验值")
	UE_DEFINE_GAMEPLAY_TAG_COMMENT(AttributeSet_Gold, "AttributeSet.Gold", "金币")

给死亡被动添加一些属性用于分配奖励以及发放奖励的GE

cpp 复制代码
private:
	// 奖励分配的范围,在该范围内的队友可获得奖励
	UPROPERTY(EditDefaultsOnly, Category = "Reward", meta=(DisplayName="奖励范围"))
	float RewardRange = 1000.f;
	// 基础经验奖励
	UPROPERTY(EditDefaultsOnly, Category = "Reward", meta=(DisplayName="基础经验奖励"))
	float BaseExperienceReward = 200.f;

	// 基础金币奖励
	UPROPERTY(EditDefaultsOnly, Category = "Reward", meta=(DisplayName="基础金币奖励"))
	float BaseGoldReward = 200.f;

	// 额外经验奖励系数(根据目标经验值加成)
	UPROPERTY(EditDefaultsOnly, Category = "Reward", meta=(DisplayName="经验奖励系数(每单位经验)"))
	float ExperienceRewardPerExperience = 0.1f;

	// 额外金币奖励系数(根据目标经验值加成)
	UPROPERTY(EditDefaultsOnly, Category = "Reward", meta=(DisplayName="金币奖励系数(每单位经验)"))
	float GoldRewardPerExperience = 0.05f;

	// 击杀者奖励占比(击杀者获得的奖励比例,其余分配给队友)
	UPROPERTY(EditDefaultsOnly, Category = "Reward", meta=(DisplayName="击杀者奖励比例(0-1)"))
	float KillerRewardPortion = 0.5f;
	
	// 获取奖励目标(如附近的队友等)
	TArray<AActor*> GetRewardTargets() const;

	// 奖励GE(用于发放经验、金币等)
	UPROPERTY(EditDefaultsOnly, Category = "Reward", meta=(DisplayName="奖励效果(GE类)"))
	TSubclassOf<UGameplayEffect> RewardEffect;
cpp 复制代码
void UGAP_Dead::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
	if (K2_HasAuthority())
	{
		// MMC 获取
		// AActor* Killer = TriggerEventData->ContextHandle.GetEffectCauser();
		// ECC 获取
		AActor* Killer = nullptr;
		if (TriggerEventData->Target)
		{
			Killer = static_cast<AActor*>(TriggerEventData->Target);
			UE_LOG(LogTemp, Warning, TEXT("TriggerEventData->Target: %s"), *Killer->GetName())
		}

		
		// 击杀者不存在或者击杀者为非英雄单位
		if (!Killer || !UCAbilitySystemStatics::IsHero(Killer))
		{
			Killer = nullptr;
		}
		// 获取需要奖励的目标
		TArray<AActor*> RewardTargets = GetRewardTargets();
		// 如果没有奖励目标又没有击杀者直接结束技能不需要奖励
		if (RewardTargets.Num() == 0 && !Killer)
		{
			K2_EndAbility();
			return;
		}

		// 击杀者存在并且不在奖励目标中,将击杀者添加进去
		if (Killer && !RewardTargets.Contains(Killer))
		{
			RewardTargets.Add(Killer);
		}
		bool bFound = false;
		// 获取角色当前经验属性值
		float SelfExperience = GetAbilitySystemComponentFromActorInfo_Ensured()->GetGameplayAttributeValue(UCHeroAttributeSet::GetExperienceAttribute(), bFound);

		// 计算总奖励(基础奖励+基于经验的奖励)
		float TotalExperienceReward = BaseExperienceReward + ExperienceRewardPerExperience * SelfExperience;
		float TotalGoldReward = BaseGoldReward + GoldRewardPerExperience * SelfExperience;

		// 判断是否有击杀的英雄单位,给他分大头
		if (Killer)
		{
			float KillerExperienceReward = TotalExperienceReward * KillerRewardPortion;
			float KillerGoldReward = TotalGoldReward * KillerRewardPortion;

			// 创建击杀者奖励效果
			FGameplayEffectSpecHandle EffectSpecHandle = MakeOutgoingGameplayEffectSpec(RewardEffect);
			// 设置GE上面的属性值
			EffectSpecHandle.Data->SetSetByCallerMagnitude(TGameplayTags::AttributeSet_Experience, KillerExperienceReward);
			EffectSpecHandle.Data->SetSetByCallerMagnitude(TGameplayTags::AttributeSet_Gold, KillerGoldReward);

			// 对击杀者应用奖励效果
			K2_ApplyGameplayEffectSpecToTarget(EffectSpecHandle, UAbilitySystemBlueprintLibrary::AbilityTargetDataFromActor(Killer));

			// 从总奖励中扣除击杀者部分
			TotalExperienceReward -= KillerExperienceReward;
			TotalGoldReward -= KillerGoldReward;
		}
		// 把剩余的奖励平均分配给没有k到头的英雄单位(k到的也能多吃一口)
		float ExperiencePerTarget = TotalExperienceReward / RewardTargets.Num();
		float GoldPerTarget = TotalGoldReward / RewardTargets.Num();

		// 创建群体奖励效果
		FGameplayEffectSpecHandle EffectSpecHandle = MakeOutgoingGameplayEffectSpec(RewardEffect);
		// 设置GE上面的属性值
		EffectSpecHandle.Data->SetSetByCallerMagnitude(TGameplayTags::AttributeSet_Experience, ExperiencePerTarget);
		EffectSpecHandle.Data->SetSetByCallerMagnitude(TGameplayTags::AttributeSet_Gold, GoldPerTarget);
		
		// 对所有奖励目标应用奖励
		K2_ApplyGameplayEffectSpecToTarget(EffectSpecHandle, UAbilitySystemBlueprintLibrary::AbilityTargetDataFromActorArray(RewardTargets, true));
		K2_EndAbility();
	}
}

创建一个GE用于给奖励

配置到死亡被动中

添加两个属性监听看一下效果

击杀者拿一半,然后再两个人再平分剩下的一半,没错

完整死亡被动

cpp 复制代码
// 幻雨喜欢小猫咪

#pragma once

#include "CoreMinimal.h"
#include "GAS/Core/CGameplayAbility.h"
#include "GAP_Dead.generated.h"

/**
 * 死亡能力类
 * 负责角色死亡时的奖励分配(经验、金币等)
 */
UCLASS()
class CRUNCH_API UGAP_Dead : public UCGameplayAbility
{
	GENERATED_BODY()
public:
	UGAP_Dead();
	virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override;

private:
	// 奖励分配的范围,在该范围内的队友可获得奖励
	UPROPERTY(EditDefaultsOnly, Category = "Reward", meta=(DisplayName="奖励范围"))
	float RewardRange = 1000.f;
	// 基础经验奖励
	UPROPERTY(EditDefaultsOnly, Category = "Reward", meta=(DisplayName="基础经验奖励"))
	float BaseExperienceReward = 200.f;

	// 基础金币奖励
	UPROPERTY(EditDefaultsOnly, Category = "Reward", meta=(DisplayName="基础金币奖励"))
	float BaseGoldReward = 200.f;

	// 额外经验奖励系数(根据目标经验值加成)
	UPROPERTY(EditDefaultsOnly, Category = "Reward", meta=(DisplayName="经验奖励系数(每单位经验)"))
	float ExperienceRewardPerExperience = 0.1f;

	// 额外金币奖励系数(根据目标经验值加成)
	UPROPERTY(EditDefaultsOnly, Category = "Reward", meta=(DisplayName="金币奖励系数(每单位经验)"))
	float GoldRewardPerExperience = 0.05f;

	// 击杀者奖励占比(击杀者获得的奖励比例,其余分配给队友)
	UPROPERTY(EditDefaultsOnly, Category = "Reward", meta=(DisplayName="击杀者奖励比例(0-1)"))
	float KillerRewardPortion = 0.5f;
	
	// 获取奖励目标(如附近的队友等)
	TArray<AActor*> GetRewardTargets() const;

	// 奖励GE(用于发放经验、金币等)
	UPROPERTY(EditDefaultsOnly, Category = "Reward", meta=(DisplayName="奖励效果(GE类)"))
	TSubclassOf<UGameplayEffect> RewardEffect;
};
cpp 复制代码
// 幻雨喜欢小猫咪


#include "GAS/Abilities/GAP_Dead.h"

#include "AbilitySystemBlueprintLibrary.h"
#include "AbilitySystemComponent.h"
#include "Engine/OverlapResult.h"
#include "GAS/Core/CAbilitySystemStatics.h"
#include "GAS/Core/CHeroAttributeSet.h"
#include "GAS/Core/TGameplayTags.h"

UGAP_Dead::UGAP_Dead()
{
	// 设置网络执行策略为仅在服务器端执行
	NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::ServerOnly;

	// 创建一个新触发数据对象
	FAbilityTriggerData TriggerData;

	// 设置触发数据的触发源为游戏事件
	TriggerData.TriggerSource = EGameplayAbilityTriggerSource::GameplayEvent;
	TriggerData.TriggerTag = TGameplayTags::Stats_Dead;

	AbilityTriggers.Add(TriggerData);

	// 死亡去除眩晕状态
	ActivationBlockedTags.RemoveTag(TGameplayTags::Stats_Stun);
}

void UGAP_Dead::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
	if (K2_HasAuthority())
	{
		// MMC 获取
		// AActor* Killer = TriggerEventData->ContextHandle.GetEffectCauser();
		// ECC 获取
		AActor* Killer = nullptr;
		if (TriggerEventData->Target)
		{
			Killer = static_cast<AActor*>(TriggerEventData->Target);
			UE_LOG(LogTemp, Warning, TEXT("TriggerEventData->Target: %s"), *Killer->GetName())
		}

		
		// 击杀者不存在或者击杀者为非英雄单位
		if (!Killer || !UCAbilitySystemStatics::IsHero(Killer))
		{
			Killer = nullptr;
		}
		// 获取需要奖励的目标
		TArray<AActor*> RewardTargets = GetRewardTargets();
		// 如果没有奖励目标又没有击杀者直接结束技能不需要奖励
		if (RewardTargets.Num() == 0 && !Killer)
		{
			K2_EndAbility();
			return;
		}

		// 击杀者存在并且不在奖励目标中,将击杀者添加进去
		if (Killer && !RewardTargets.Contains(Killer))
		{
			RewardTargets.Add(Killer);
		}
		bool bFound = false;
		// 获取角色当前经验属性值
		float SelfExperience = GetAbilitySystemComponentFromActorInfo_Ensured()->GetGameplayAttributeValue(UCHeroAttributeSet::GetExperienceAttribute(), bFound);

		// 计算总奖励(基础奖励+基于经验的奖励)
		float TotalExperienceReward = BaseExperienceReward + ExperienceRewardPerExperience * SelfExperience;
		float TotalGoldReward = BaseGoldReward + GoldRewardPerExperience * SelfExperience;

		// 判断是否有击杀的英雄单位,给他分大头
		if (Killer)
		{
			float KillerExperienceReward = TotalExperienceReward * KillerRewardPortion;
			float KillerGoldReward = TotalGoldReward * KillerRewardPortion;
			UE_LOG(LogTemp, Warning, TEXT("击杀者经验奖励:%f"), KillerExperienceReward)
			// 创建击杀者奖励效果
			FGameplayEffectSpecHandle EffectSpecHandle = MakeOutgoingGameplayEffectSpec(RewardEffect);
			// 设置GE上面的属性值
			EffectSpecHandle.Data->SetSetByCallerMagnitude(TGameplayTags::AttributeSet_Experience, KillerExperienceReward);
			EffectSpecHandle.Data->SetSetByCallerMagnitude(TGameplayTags::AttributeSet_Gold, KillerGoldReward);

			// 对击杀者应用奖励效果
			K2_ApplyGameplayEffectSpecToTarget(EffectSpecHandle, UAbilitySystemBlueprintLibrary::AbilityTargetDataFromActor(Killer));

			// 从总奖励中扣除击杀者部分
			TotalExperienceReward -= KillerExperienceReward;
			TotalGoldReward -= KillerGoldReward;
		}
		// 把剩余的奖励平均分配给没有k到头的英雄单位
		float ExperiencePerTarget = TotalExperienceReward / RewardTargets.Num();
		float GoldPerTarget = TotalGoldReward / RewardTargets.Num();
		UE_LOG(LogTemp, Warning, TEXT("数组里面多少人%d"), RewardTargets.Num())
		UE_LOG(LogTemp, Warning, TEXT("平均经验奖励:%f"), ExperiencePerTarget)

		// 创建群体奖励效果
		FGameplayEffectSpecHandle EffectSpecHandle = MakeOutgoingGameplayEffectSpec(RewardEffect);
		// 设置GE上面的属性值
		EffectSpecHandle.Data->SetSetByCallerMagnitude(TGameplayTags::AttributeSet_Experience, ExperiencePerTarget);
		EffectSpecHandle.Data->SetSetByCallerMagnitude(TGameplayTags::AttributeSet_Gold, GoldPerTarget);
		
		// 对所有奖励目标应用奖励
		K2_ApplyGameplayEffectSpecToTarget(EffectSpecHandle, UAbilitySystemBlueprintLibrary::AbilityTargetDataFromActorArray(RewardTargets, true));
		K2_EndAbility();
	}
}

TArray<AActor*> UGAP_Dead::GetRewardTargets() const
{
	TSet<AActor*> OutActors;
	AActor* AvatarActor = GetAvatarActorFromActorInfo();
	if (!AvatarActor || !GetWorld())
	{
		return OutActors.Array();
	}

	// 配置碰撞检测参数
	FCollisionObjectQueryParams ObjectQueryParams;
	ObjectQueryParams.AddObjectTypesToQuery(ECC_Pawn);
	FCollisionShape CollisionShape;
	CollisionShape.SetSphere(RewardRange);  // 使用奖励范围作为检测半径

	TArray<FOverlapResult> OverlapResults;
	// 检测碰撞
	if (GetWorld()->OverlapMultiByObjectType(OverlapResults, AvatarActor->GetActorLocation(), FQuat::Identity, ObjectQueryParams, CollisionShape))
	{
		for (const FOverlapResult& OverlapResult : OverlapResults)
		{
			// 获取团队接口,给死亡目标的敌对单位提供奖励
			const IGenericTeamAgentInterface* TeamInterface =  Cast<IGenericTeamAgentInterface>(OverlapResult.GetActor());
			// 过滤非敌对单位
			if (!TeamInterface || TeamInterface->GetTeamAttitudeTowards(*AvatarActor) != ETeamAttitude::Hostile)
			{
				continue;
			}

			// 判断是否为英雄单位
			if (!UCAbilitySystemStatics::IsHero(OverlapResult.GetActor()))
			{
				continue;
			}
			
			// 添加到奖励目标集合
			OutActors.Add(OverlapResult.GetActor());
		}
	}
	return OutActors.Array();
}
相关推荐
程序员阿明19 分钟前
netty的编解码器,以及内置的编解码器
java·spring boot
oh,huoyuyan1 小时前
从效率瓶颈到自动化:火语言 RPA 在日常工作中的技术实践
java·前端·rpa
静若繁花_jingjing1 小时前
电商项目_秒杀_初步分析
java
珹洺1 小时前
Linux操作系统从入门到实战(十二)Linux操作系统第一个程序(进度条)
linux·运维·服务器
LZQqqqqo1 小时前
C# 类 封装 属性 练习题
java·前端·c#
用户4472671933101 小时前
Redis + Caffeine 实现高效的两级缓存架构
java
Java水解1 小时前
美团Java面试题、笔试题(含答案)
java·后端·面试
就叫飞六吧1 小时前
本地代理和服务器代理区别
运维·服务器
苇柠1 小时前
Java常用API(1)
java·开发语言
雪碧聊技术1 小时前
HashMap和Hashtable的区别
java·hashmap·hashtable