UE5多人MOBA+GAS 43、制作激光技能

文章目录

  • 创建激光GA
    • 添加技能标签
    • 初步实现激光技能
    • 给激光技能配置GE
    • 配置动画
    • 添加新的技能对应到角色以及资产的配置
  • 添加新的TA`TargetActor_Line`
  • 将激光TA添加到激光GA上
  • 修改一点杂事
  • 补充初始化金币的GE

创建激光GA

添加新技能激光技能GA_Laser(不用管下图的打错,我也是后知后觉)

添加技能标签

cpp 复制代码
	// 激光
	CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_Laser_Shoot)
	CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_Laser_Cooldown)
cpp 复制代码
	UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_Laser_Shoot, "Ability.Laser.Shoot", "激光技能")
	UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_Laser_Cooldown, "Ability.Laser.Cooldown", "激光技能冷却")

初步实现激光技能

cpp 复制代码
#pragma once

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

/**
 * 激光类技能:
 * - 持续施法技能,消耗法力值维持激光
 * - 对路径上的目标造成持续伤害和击退效果
 */
UCLASS()
class CRUNCH_API UGA_Laser : public UCGameplayAbility
{
	GENERATED_BODY()
public:
	// 技能激活时调用
	virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override;
	
	// 技能结束时调用
	virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override;

private:
	// 目标检测范围(厘米)
	UPROPERTY(EditDefaultsOnly, Category = "Targeting")
	float TargetRange = 4000;

	// 激光检测半径(厘米)
	UPROPERTY(EditDefaultsOnly, Category = "Targeting")
	float DetectionCylinderRadius = 30.f;

	// 目标检测频率(秒)
	UPROPERTY(EditDefaultsOnly, Category = "Targeting")
	float TargetingInterval = 0.3f;

	// 命中时应用的伤害效果
	UPROPERTY(EditDefaultsOnly, Category = "Effects")
	FGenericDamageEffectDef HitDamageEffect;

	// 持续施法时的法力消耗效果
	UPROPERTY(EditDefaultsOnly, Category = "Effects")
	TSubclassOf<UGameplayEffect> OnGoingConsumptionEffect;

	// 持续消耗效果的激活句柄
	FActiveGameplayEffectHandle OnGoingConsumptionEffectHandle;
	
	// 激光技能动画蒙太奇
	UPROPERTY(EditDefaultsOnly, Category = "Anim")
	TObjectPtr<UAnimMontage> LaserMontage;

	// 目标Actor附加的骨骼名称
	UPROPERTY(EditDefaultsOnly, Category = "Targeting")
	FName TargetActorAttachSocketName = "Laser";

	// 动画事件触发的射击回调
	UFUNCTION()
	void ShootLaser(FGameplayEventData Payload);
	
	// 法力值更新回调(用于检测法力不足)
	void ManaUpdated(const FOnAttributeChangeData& ChangeData);
};
cpp 复制代码
#include "GA_Laser.h"

#include "Abilities/Tasks/AbilityTask_PlayMontageAndWait.h"
#include "Abilities/Tasks/AbilityTask_WaitGameplayEvent.h"
#include "Abilities/Tasks/AbilityTask_WaitCancel.h"
#include "Abilities/Tasks/AbilityTask_WaitTargetData.h"
#include "AbilitySystemBlueprintLibrary.h"
#include "AbilitySystemComponent.h"
#include "GAS/Core/CAttributeSet.h"

void UGA_Laser::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo,
                                const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
	if (!K2_CommitAbility() || !LaserMontage)
	{
		K2_EndAbility();
		return;
	}

	// 仅在服务器或预测有效时执行
	if (HasAuthorityOrPredictionKey(ActorInfo, &ActivationInfo))
	{
		// 播放激光蒙太奇
		UAbilityTask_PlayMontageAndWait* PlayerLaserMontageTask = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(this, NAME_None, LaserMontage);
		PlayerLaserMontageTask->OnBlendOut.AddDynamic(this, &UGA_Laser::K2_EndAbility);
		PlayerLaserMontageTask->OnCancelled.AddDynamic(this, &UGA_Laser::K2_EndAbility);
		PlayerLaserMontageTask->OnInterrupted.AddDynamic(this, &UGA_Laser::K2_EndAbility);
		PlayerLaserMontageTask->OnCompleted.AddDynamic(this, &UGA_Laser::K2_EndAbility);
		PlayerLaserMontageTask->ReadyForActivation();

		// 等待动画事件触发激光发射
		UAbilityTask_WaitGameplayEvent* WaitShootEvent = UAbilityTask_WaitGameplayEvent::WaitGameplayEvent(this, TGameplayTags::Ability_Laser_Shoot);
		WaitShootEvent->EventReceived.AddDynamic(this, &UGA_Laser::ShootLaser);
		WaitShootEvent->ReadyForActivation();
		
		// 设置技能取消监听
		UAbilityTask_WaitCancel* WaitCancel = UAbilityTask_WaitCancel::WaitCancel(this);
		WaitCancel->OnCancel.AddDynamic(this, &UGA_Laser::K2_EndAbility);
		WaitCancel->ReadyForActivation();
	}
	
	
}

void UGA_Laser::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo,
	const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled)
{
	// 移除持续消耗法力的效果
	UAbilitySystemComponent* OwnerAbilitySystemComponent = GetAbilitySystemComponentFromActorInfo();
	if (OwnerAbilitySystemComponent && OnGoingConsumptionEffectHandle.IsValid())
	{
		OwnerAbilitySystemComponent->RemoveActiveGameplayEffect(OnGoingConsumptionEffectHandle);
	}
	Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled);
}

void UGA_Laser::ShootLaser(FGameplayEventData Payload)
{
	// --- 服务器端逻辑 ---
	if (K2_HasAuthority())
	{
		// 1. 应用持续消耗法力的效果
		OnGoingConsumptionEffectHandle = BP_ApplyGameplayEffectToOwner(OnGoingConsumptionEffect, GetAbilityLevel(CurrentSpecHandle, CurrentActorInfo));
		
		// 2. 注册法力变化回调(检测法力不足)
		UAbilitySystemComponent* OwnerAbilitySystemComponent = GetAbilitySystemComponentFromActorInfo();
		if (OwnerAbilitySystemComponent)
		{
			OwnerAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UCAttributeSet::GetManaAttribute()).AddUObject(this, &UGA_Laser::ManaUpdated);
		}
	}
}

void UGA_Laser::ManaUpdated(const FOnAttributeChangeData& ChangeData)
{
	// 当法力不足时自动结束技能
	UAbilitySystemComponent* OwnerASC = GetAbilitySystemComponentFromActorInfo() ;
	if (OwnerASC && !OwnerASC->CanApplyAttributeModifiers(
		OnGoingConsumptionEffect.GetDefaultObject(),
		GetAbilityLevel(CurrentSpecHandle, CurrentActorInfo), 
		MakeEffectContext(CurrentSpecHandle, CurrentActorInfo)))
	{
		K2_EndAbility();
	}
}

给激光技能配置GE

创建多个GE

伤害GE

眩晕GE

持续射击的蓝耗GE

冷却GE(使用我之前的mmc的话)

不用的话就使用可浮动点数

首次提交的蓝耗

继承激光GA创建蓝图版本

配置动画

蒙太奇中添加激光的动画通知

添加新的技能对应到角色以及资产的配置

创建一个插槽Laser用来释放激光的起点

角色类中添加技能

UI数据中添加技能

添加新的TATargetActor_Line

用于设置激光以及检测敌人发送需要造成伤害的敌人

添加一下奶瓜粒子特效

cpp 复制代码
#pragma once

#include "CoreMinimal.h"
#include "GenericTeamAgentInterface.h"
#include "Abilities/GameplayAbilityTargetActor.h"
#include "Components/SphereComponent.h"
#include "TargetActor_Line.generated.h"

class UNiagaraComponent;
/**
 * 线性目标检测器
 * 用于技能/能力的线性范围目标检测与反馈
 */
UCLASS()
class CRUNCH_API ATargetActor_Line : public AGameplayAbilityTargetActor, public IGenericTeamAgentInterface
{
	GENERATED_BODY()

public:
	ATargetActor_Line();
	/**
	 * 配置目标检测参数
	 * @param NewTargetRange 检测距离
	 * @param NewDetectionCylinderRadius 检测半径
	 * @param NewTargetingInterval 检测频率 /s
	 * @param OwnerTeamId 拥有者队伍ID
	 * @param bShouldDrawDebug 是否绘制调试信息
	 */
	void ConfigureTargetSetting(
		float NewTargetRange,
		float NewDetectionCylinderRadius,
		float NewTargetingInterval,
		FGenericTeamId OwnerTeamId,
		bool bShouldDrawDebug
	);

	// 设置队伍ID
	virtual void SetGenericTeamId(const FGenericTeamId& NewTeamID) override;
	// 获取队伍ID
	FORCEINLINE virtual FGenericTeamId GetGenericTeamId() const override { return TeamId; }

	// 网络属性同步
	virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;

	// 开始目标检测
	virtual void StartTargeting(UGameplayAbility* Ability) override;

	// 每帧调用,处理目标检测和特效更新
	virtual void Tick(float DeltaTime) override;

	// 销毁时回调
	virtual void BeginDestroy() override;
private:
	// 检测射程
	UPROPERTY(Replicated)
	float TargetRange;

	// 检测圆柱体半径
	UPROPERTY(Replicated)
	float DetectionCylinderRadius;

	// 检测间隔(秒)
	UPROPERTY()
	float TargetingInterval;

	// 所属队伍ID
	UPROPERTY(Replicated)
	FGenericTeamId TeamId;

	// 是否绘制调试信息
	UPROPERTY()
	bool bDrawDebug;

	// 技能拥有者Actor
	UPROPERTY(Replicated)
	const AActor* AvatarActor;

	// 激光特效长度参数名
	UPROPERTY(EditDefaultsOnly, Category = "VFX")
	FName LaserFXLengthParamName = "Length";
	// 根组件
	UPROPERTY(VisibleDefaultsOnly, Category = "Component")
	TObjectPtr<USceneComponent> RootComp;
	
	// 激光特效组件
	UPROPERTY(VisibleDefaultsOnly, Category = "Component")
	TObjectPtr<UNiagaraComponent> LaserVFX;

	// 目标检测球体组件
	UPROPERTY(VisibleDefaultsOnly, Category = "Component")
	TObjectPtr<USphereComponent> TargetEndDetectionSphere;

	// 定时检测句柄
	FTimerHandle PeriodicalTargetingTimerHandle;

	// 执行目标检测并报告结果
	void DoTargetCheckAndReport();

	// 更新激光特效和检测逻辑
	void UpdateTargetTrace();

	// 判断Actor是否应被报告为目标
	bool ShouldReportActorAsTarget(const AActor* ActorToCheck) const;
};
cpp 复制代码
#include "TargetActor_Line.h"

#include "NiagaraComponent.h"
#include "Abilities/GameplayAbility.h"
#include "Crunch/Crunch.h"
#include "Kismet/KismetMathLibrary.h"
#include "Net/UnrealNetwork.h"


// Sets default values
ATargetActor_Line::ATargetActor_Line()
{
	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
	bReplicates = true;
	ShouldProduceTargetDataOnServer = true;

	AvatarActor = nullptr;

	// 创建根组件
	RootComp = CreateDefaultSubobject<USceneComponent>("Root Comp");
	SetRootComponent(RootComp);

	// 创建目标检测球体组件(用于检测终点附近的目标)
	TargetEndDetectionSphere = CreateDefaultSubobject<USphereComponent>("Target End Detection Sphere");
	TargetEndDetectionSphere->SetupAttachment(GetRootComponent());
	TargetEndDetectionSphere->SetCollisionResponseToChannel(ECC_SPRING_ARM, ECR_Ignore);

	// 创建激光特效组件
	LaserVFX = CreateDefaultSubobject<UNiagaraComponent>("LaserVFX");
	LaserVFX->SetupAttachment(GetRootComponent());
	
}

void ATargetActor_Line::ConfigureTargetSetting(float NewTargetRange, float NewDetectionCylinderRadius,
	float NewTargetingInterval, FGenericTeamId OwnerTeamId, bool bShouldDrawDebug)
{
	TargetRange = NewTargetRange;
	DetectionCylinderRadius = NewDetectionCylinderRadius;
	TargetingInterval = NewTargetingInterval;
	SetGenericTeamId(OwnerTeamId);
	bDrawDebug = bShouldDrawDebug;
}

void ATargetActor_Line::SetGenericTeamId(const FGenericTeamId& NewTeamID)
{
	TeamId = NewTeamID;
}

void ATargetActor_Line::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME(ATargetActor_Line, TeamId);
	DOREPLIFETIME(ATargetActor_Line, TargetRange);
	DOREPLIFETIME(ATargetActor_Line, DetectionCylinderRadius);
	DOREPLIFETIME(ATargetActor_Line, AvatarActor);
}

void ATargetActor_Line::StartTargeting(UGameplayAbility* Ability)
{
	Super::StartTargeting(Ability);
	if (!OwningAbility)
		return;

	AvatarActor = OwningAbility->GetAvatarActorFromActorInfo();
	// 在服务器上开启定时器周期检测目标
	if (HasAuthority())
	{
		GetWorldTimerManager().SetTimer(PeriodicalTargetingTimerHandle, this, &ATargetActor_Line::DoTargetCheckAndReport, TargetingInterval, true);
	}
}

void ATargetActor_Line::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	// 更新激光
	UpdateTargetTrace();
}

void ATargetActor_Line::BeginDestroy()
{
	// 在去除Actor的时候停止定时器
	if (GetWorld() && PeriodicalTargetingTimerHandle.IsValid())
	{
		GetWorldTimerManager().ClearTimer(PeriodicalTargetingTimerHandle);
	}
	Super::BeginDestroy();
}

void ATargetActor_Line::DoTargetCheckAndReport()
{
	// 服务器中执行
	if (!HasAuthority()) return;

	// 获取重叠的Actor
	TArray<AActor*> OverlappingActorSet;
	TargetEndDetectionSphere->GetOverlappingActors(OverlappingActorSet);

	// 筛选有效目标
	TArray<TWeakObjectPtr<AActor>> OverlappingActors;
	for (AActor* Actor : OverlappingActorSet)
	{
		if (ShouldReportActorAsTarget(Actor))
		{
			OverlappingActors.Add(Actor);
		}
	}

	// 构建目标数据
	FGameplayAbilityTargetDataHandle TargetDataHandle;
	FGameplayAbilityTargetData_ActorArray* ActorArray = new FGameplayAbilityTargetData_ActorArray;
	ActorArray->SetActors(OverlappingActors);
	TargetDataHandle.Add(ActorArray);

	// 广播目标数据就绪事件
	TargetDataReadyDelegate.Broadcast(TargetDataHandle);
}

void ATargetActor_Line::UpdateTargetTrace()
{
	// 设置激光起始点
	FVector ViewLocation = GetActorLocation();
	FRotator ViewRotation = GetActorRotation();
	if (AvatarActor)
	{
		// 获取摄像机位置和瞄准的旋转
		AvatarActor->GetActorEyesViewPoint(ViewLocation, ViewRotation);
	}

	// 计算激光方向和终点
	FVector LookEndPoint = ViewLocation + ViewRotation.Vector() * 100000;
	// 根据角色位置以及相机方向计算出激光方向
	FRotator LookRotation = UKismetMathLibrary::FindLookAtRotation(GetActorLocation(), LookEndPoint);
	SetActorRotation(LookRotation);

	// 获取激光终点 : 由激光发射位置,由角色朝向,和激光长度设置终点位置
	FVector SweepEndLocation = GetActorLocation() + LookRotation.Vector() * TargetRange;


	TArray<FHitResult> HitResults;

	// 配置碰撞参数,忽略自身和拥有者
	FCollisionQueryParams QueryParams;
	QueryParams.AddIgnoredActor(AvatarActor);
	QueryParams.AddIgnoredActor(this);
	FCollisionResponseParams CollisionResponseParams(ECR_Overlap);
	// 球形检测,查找路径上的目标
	GetWorld()->SweepMultiByChannel(HitResults, GetActorLocation(), SweepEndLocation, FQuat::Identity, ECC_WorldDynamic, FCollisionShape::MakeSphere(DetectionCylinderRadius), QueryParams, CollisionResponseParams);
	FVector LineEndLocation = SweepEndLocation;
	float LineLength = TargetRange;
	// 命中第一个非友方目标则截断激光
	for (FHitResult& HitResult : HitResults)
	{
		if (HitResult.GetActor())
		{
			if (GetTeamAttitudeTowards(*HitResult.GetActor()) != ETeamAttitude::Friendly)
			{
				LineEndLocation = HitResult.ImpactPoint;
				LineLength = FVector::Distance(GetActorLocation() , LineEndLocation);
				break;
			}
		}
	}
	
	// 更新终点检测球体和激光长度
	TargetEndDetectionSphere->SetWorldLocation(LineEndLocation);
	if (LaserVFX)
	{
		LaserVFX->SetVariableFloat(LaserFXLengthParamName, LineLength/100.f);
	}
}

bool ATargetActor_Line::ShouldReportActorAsTarget(const AActor* ActorToCheck) const
{
	// 基本无效检查
	if (!ActorToCheck) return false;
	if (ActorToCheck == AvatarActor) return false; // 忽略自身
	if (ActorToCheck == this) return false; // 忽略自身

	// 团队关系检查(只报告敌对目标)
	if (GetTeamAttitudeTowards(*ActorToCheck) != ETeamAttitude::Hostile)
		return false;

	return true;
}

将激光TA添加到激光GA上

到CGameplayAbility中添加一个函数用来应用伤害

cpp 复制代码
	// 将伤害应用到TargetDataHandle中的所有目标
	void ApplyDamageToTargetDataHandle(const FGameplayAbilityTargetDataHandle& TargetDataHandle, const FGenericDamageEffectDef& Damage, int Level = 1);
cpp 复制代码
void UCGameplayAbility::ApplyDamageToTargetDataHandle(const FGameplayAbilityTargetDataHandle& TargetDataHandle,
	const FGenericDamageEffectDef& Damage, int Level)
{
	const UAbilitySystemComponent* ASC = GetAbilitySystemComponentFromActorInfo();
	AActor* AvatarActor				   = GetAvatarActorFromActorInfo();
	// 创建效果上下文, 设置能力 、源对象 和 施加者
	FGameplayEffectContextHandle ContextHandle = ASC->MakeEffectContext();
	ContextHandle.SetAbility(this);
	ContextHandle.AddSourceObject(AvatarActor);
	ContextHandle.AddInstigator(AvatarActor, AvatarActor);

	for (const auto& TypePair : Damage.DamageTypeDefinitions)
	{
		// 创建效果Spec句柄,指定效果类、能力等级和上下文
		FGameplayEffectSpecHandle EffectSpecHandle = ASC->MakeOutgoingSpec(Damage.DamageEffect, Level, ContextHandle);
		float TotalModifier = TypePair.Value.BaseDamage.GetValueAtLevel(Level);
		for (const auto& Modifier : TypePair.Value.AttributeDamageModifiers)
		{
			UAbilitySystemBlueprintLibrary::AssignTagSetByCallerMagnitude(EffectSpecHandle, Modifier.Key, Modifier.Value);
		}
		UAbilitySystemBlueprintLibrary::AssignTagSetByCallerMagnitude(EffectSpecHandle, TypePair.Key, TotalModifier);
		// 在目标上应用游戏效果规范
		K2_ApplyGameplayEffectSpecToTarget(EffectSpecHandle,TargetDataHandle);
	}
}

发射激光技能中添加这个TA

cpp 复制代码
	// 激光目标Actor类(直线检测)
	UPROPERTY(EditDefaultsOnly, Category = "Targeting")
	TSubclassOf<class ATargetActor_Line> LaserTargetActorClass;

	// 目标数据接收回调(处理命中目标)
	UFUNCTION()
	void TargetReceived(const FGameplayAbilityTargetDataHandle& TargetDataHandle);
cpp 复制代码
void UGA_Laser::ShootLaser(FGameplayEventData Payload)
{
	// --- 服务器端逻辑 ---
	if (K2_HasAuthority())
	{
		// 应用持续消耗法力的效果
		OnGoingConsumptionEffectHandle = BP_ApplyGameplayEffectToOwner(OnGoingConsumptionEffect, GetAbilityLevel(CurrentSpecHandle, CurrentActorInfo));
		
		// 注册法力变化回调(检测法力不足)
		UAbilitySystemComponent* OwnerAbilitySystemComponent = GetAbilitySystemComponentFromActorInfo();
		if (OwnerAbilitySystemComponent)
		{
			OwnerAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UCAttributeSet::GetManaAttribute()).AddUObject(this, &UGA_Laser::ManaUpdated);
		}
	}

	// 创建目标检测任务
	UAbilityTask_WaitTargetData* WaitDamageTargetTask = UAbilityTask_WaitTargetData::WaitTargetData(
		this, 
		NAME_None, 
		EGameplayTargetingConfirmation::CustomMulti, // 持续检测模式
		LaserTargetActorClass
	);
	// 绑定委托
	WaitDamageTargetTask->ValidData.AddDynamic(this, &UGA_Laser::TargetReceived);
	// 执行任务
	WaitDamageTargetTask->ReadyForActivation();

	// 配置并生成目标检测Actor
	AGameplayAbilityTargetActor* TargetActor;
	WaitDamageTargetTask->BeginSpawningActor(this, LaserTargetActorClass, TargetActor);
	if (ATargetActor_Line* LineTargetActor = Cast<ATargetActor_Line>(TargetActor))
	{
		// 设置检测参数:范围/半径/频率/队伍/调试显示
		LineTargetActor->ConfigureTargetSetting(
			TargetRange, 
			DetectionCylinderRadius, 
			TargetingInterval, 
			GetOwnerTeamId(), 
			ShouldDrawDebug()
		);
	}
	WaitDamageTargetTask->FinishSpawningActor(this, TargetActor);

	// 将Actor附加到骨骼中
	if (ATargetActor_Line* LineTargetActor = Cast<ATargetActor_Line>(TargetActor))
	{
		LineTargetActor->AttachToComponent(
			GetOwningComponentFromActorInfo(),
			FAttachmentTransformRules::SnapToTargetNotIncludingScale,
			TargetActorAttachSocketName
		);
	}
}

void UGA_Laser::TargetReceived(const FGameplayAbilityTargetDataHandle& TargetDataHandle)
{
	// --- 服务器端逻辑 ---
	if (K2_HasAuthority())
	{
		// 1. 对目标应用伤害效果
		// BP_ApplyGameplayEffectToTarget(
		// 	TargetDataHandle, 
		// 	HitDamageEffect, 
		// 	GetAbilityLevel(CurrentSpecHandle, CurrentActorInfo)
		// );
		// K2_ApplyGameplayEffectSpecToTarget(,TargetDataHandle);
		ApplyDamageToTargetDataHandle(TargetDataHandle, HitDamageEffect, GetAbilityLevel(CurrentSpecHandle, CurrentActorInfo));
	}
	
	// 2. 对目标施加击退力
	PushTargets(
		TargetDataHandle, 
		GetAvatarActorFromActorInfo()->GetActorForwardVector() * HitDamageEffect.PushVelocity // 基于发射者方向的击退
	);
}

编译后在蓝图中把这个TA塞进来

再把瞄准偏移移动一下

修改一点杂事

把风暴核心的碰撞修改一下

顺便把风暴核心的胶囊体弄大点,可能太小不好爬楼梯

把地图的两个目标的碰撞类型改为Pawn

补充初始化金币的GE

到英雄固定资产中添加

相关推荐
远离UE44 小时前
UE5 SF_VertexShader 如何使用
ue5
电子云与长程纠缠7 小时前
UE5 Lyra PocketWorld进行3D内容UI预览 - 上
开发语言·学习·3d·ue5·游戏引擎
电子云与长程纠缠10 小时前
UE5 Lyra PocketWorld进行3D内容UI预览 - 下
开发语言·学习·游戏·ui·ue5
远离UE41 天前
UE5 GPU内部异步
ue5
1204157137 肖哥1 天前
UE5 PCG PCGBlueprintElement简介
ue5
dong1326971 天前
UE5FPS游戏开发教程(四)
ue5
dong1326972 天前
UE5FPS游戏开发教程(三)
ue5
日月云棠2 天前
UE5源码分析之Editor——AnimationBlueprintLibrary模块全面分析
ue5
远离UE42 天前
UE5 ECS 原理
ue5
mengzhi啊4 天前
UE5 把剑绑定到骨骼-给要播放的动画及动画蒙太奇添加武器
ue5