当前位置: 首页 > ops >正文

UE5多人MOBA+GAS 23、制作一个地面轰炸的技能

文章目录

  • 创建一个新的技能的流程
  • 添加瞄准动画(身体随相机朝向旋转)
    • 角色基类中响应瞄准Tag
    • 动画类中响应Tag
    • 设置瞄准状态下摄像机的位置
  • 添加新的检测通道
  • 瞄准目标用的Actor
  • 让TA_GroundPick可以获取目标
  • 实现击飞多个目标在代码中调用GC生成特效
    • 做击飞
    • 添加GC、蓝耗和冷却的提交
  • 完善TargetActor_GroundPick的外观
  • 完整代码
    • TargetActor_GroundPick的完整代码
    • GA_GroundBlast的完整代码


创建一个新的技能的流程

创建技能的流程
在这里插入图片描述
因为这个技能有两个阶段,一个是瞄准阶段,另一个是射击阶段,技能一开始触发的时候是属于瞄准阶段,不会直接提交消耗以及进入冷却,K2_CommitAbility()的调用将会在发射技能伤害的时候触发提交。

#pragma once#include "CoreMinimal.h"
#include "GAS/Core/CGameplayAbility.h"
#include "GA_GroundBlast.generated.h"/*** */
UCLASS()
class CRUNCH_API UGA_GroundBlast : public UCGameplayAbility
{GENERATED_BODY()public:virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override;
private:// 瞄准动画UPROPERTY(EditDefaultsOnly, Category = "Animation")TObjectPtr<UAnimMontage> TargetingMontage;// 释放动画UPROPERTY(EditDefaultsOnly, Category = "Animation")TObjectPtr<UAnimMontage> CastMontage;
};
#include "GAS/Abilities/GA_GroundBlast.h"#include "Abilities/Tasks/AbilityTask_PlayMontageAndWait.h"void UGA_GroundBlast::ActivateAbility(const FGameplayAbilitySpecHandle Handle,const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo,const FGameplayEventData* TriggerEventData)
{if (!HasAuthorityOrPredictionKey(ActorInfo, &ActivationInfo)) return;UAbilityTask_PlayMontageAndWait* PlayGroundBlasAnimTask = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(this, NAME_None, TargetingMontage);PlayGroundBlasAnimTask->OnBlendOut.AddDynamic(this, &UGA_GroundBlast::K2_EndAbility);PlayGroundBlasAnimTask->OnCancelled.AddDynamic(this, &UGA_GroundBlast::K2_EndAbility);PlayGroundBlasAnimTask->OnCompleted.AddDynamic(this, &UGA_GroundBlast::K2_EndAbility);PlayGroundBlasAnimTask->OnInterrupted.AddDynamic(this, &UGA_GroundBlast::K2_EndAbility);PlayGroundBlasAnimTask->ReadyForActivation();
}

AM_GroundBlast_Targeting
在这里插入图片描述
AM_GroundBlast_Casting
在这里插入图片描述
继承GA创建蓝图并添加蒙太奇进去
在这里插入图片描述
将技能放入DT表中(做的新技能都要放进去让UI显示出来)
在这里插入图片描述
创建对应的按键输入
在这里插入图片描述

在这里插入图片描述
将技能添加到角色中
在这里插入图片描述

添加瞄准动画(身体随相机朝向旋转)

添加新的状态标签

	// 瞄准CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Stats_Aim)
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Stats_Aim, "Stats.Aim", "瞄准")

给技能添加构造函数,在构造函数中添加激活时给角色添加瞄准标签

UGA_GroundBlast::UGA_GroundBlast()
{// 技能激活时给角色添加瞄准标签ActivationOwnedTags.AddTag(TGameplayTags::Stats_Aim);// 阻断基础攻击技能BlockAbilitiesWithTag.AddTag(TGameplayTags::Ability_BasicAttack);
}

角色基类中响应瞄准Tag

	// 瞄准标签变化回调void AimTagUpdated(const FGameplayTag Tag, int32 NewCount);// 设置是否处于瞄准状态void SetIsAiming(bool bIsAiming);// 瞄准状态变化时回调virtual void OnAimStateChanged(bool bIsAiming);
void ACCharacter::BindGASChangeDelegates()
{if (CAbilitySystemComponent){CAbilitySystemComponent->RegisterGameplayTagEvent(TGameplayTags::Stats_Dead).AddUObject(this, &ACCharacter::DeathTagUpdated);CAbilitySystemComponent->RegisterGameplayTagEvent(TGameplayTags::Stats_Stun).AddUObject(this, &ACCharacter::StunTagUpdated);CAbilitySystemComponent->RegisterGameplayTagEvent(TGameplayTags::Stats_Aim).AddUObject(this, &ACCharacter::AimTagUpdated);}
}void ACCharacter::AimTagUpdated(const FGameplayTag Tag, int32 NewCount)
{SetIsAiming(NewCount != 0);
}void ACCharacter::SetIsAiming(bool bIsAiming)
{bUseControllerRotationYaw = bIsAiming;GetCharacterMovement()->bOrientRotationToMovement = !bIsAiming;OnAimStateChanged(bIsAiming);
}void ACCharacter::OnAimStateChanged(bool bIsAiming)
{// 子类中重写
}

动画类中响应Tag

public:// 获取前进方向速度UFUNCTION(BlueprintCallable, meta=(BlueprintThreadSafe))FORCEINLINE float GetFwdSpeed() const { return FwdSpeed; }// 获取横向速度UFUNCTION(BlueprintCallable, meta=(BlueprintThreadSafe))FORCEINLINE float GetRightSpeed() const { return RightSpeed; }// 是否需要执行全身动画UFUNCTION(BlueprintCallable, meta=(BlueprintThreadSafe))bool ShouldDoFullBody() const;
private:// 前进方向速度float FwdSpeed;// 横向速度float RightSpeed;// 是否瞄准bool bIsAiming;

在初始化阶段绑定监听,并每帧更新FwdSpeedRightSpeed的值

void UCAnimInstance::NativeInitializeAnimation()
{// 获取Owner转换为角色OwnerCharacter = Cast<ACharacter>(TryGetPawnOwner());if (OwnerCharacter){// 获取Owner的移动组件OwnerMovementComponent = OwnerCharacter->GetCharacterMovement();}UAbilitySystemComponent* OwnerASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(TryGetPawnOwner());if (OwnerASC){// 绑定标签监听OwnerASC->RegisterGameplayTagEvent(TGameplayTags::Stats_Aim).AddUObject(this, &UCAnimInstance::OwnerAimTagChanged);}
}void UCAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{if (OwnerCharacter){FVector Velocity = OwnerCharacter->GetVelocity();Speed = Velocity.Length();// 获取当前身体旋转角度与上一帧的差值,用于计算旋转变化率FRotator BodyRotator = OwnerCharacter->GetActorRotation();FRotator BodyRotatorDelta = UKismetMathLibrary::NormalizedDeltaRotator(BodyRotator, BodyPrevRotator);BodyPrevRotator = BodyRotator;// 计算 yaw 轴旋转速度(每秒旋转角度)YawSpeed = BodyRotatorDelta.Yaw / DeltaSeconds;// 如果偏航角速度为0,则使用特定的速度进行平滑插值float YawLerpSpeed = YawSpeedSmoothLerpSpeed;if (YawSpeed == 0){YawLerpSpeed = YawSpeedLerpToZeroSpeed;}// 使用线性插值(FInterpTo)对 yaw 速度进行平滑处理,提高动画过渡质量SmoothedYawSpeed = UKismetMathLibrary::FInterpTo(SmoothedYawSpeed, YawSpeed, DeltaSeconds, YawLerpSpeed);// 获取拥有者角色的基础瞄准旋转FRotator ControlRotator = OwnerCharacter->GetBaseAimRotation();// 计算当前帧的旋转变化量,并将其归一化,以便在多个帧之间平滑旋转LookRotatorOffset = UKismetMathLibrary::NormalizedDeltaRotator(ControlRotator, BodyPrevRotator);// 获取当前帧的 fwd/right 速度FwdSpeed = Velocity.Dot(ControlRotator.Vector());RightSpeed = -Velocity.Dot(ControlRotator.Vector().Cross(FVector::UpVector));}if (OwnerMovementComponent){bIsJumping = OwnerMovementComponent->IsFalling();}
}bool UCAnimInstance::ShouldDoFullBody() const
{// 速度为0 并且 不处于瞄准状态return (GetSpeed() <= 0) && !(GetIsAiming());
}void UCAnimInstance::OwnerAimTagChanged(const FGameplayTag Tag, int32 NewCount)
{bIsAiming = NewCount != 0;
}

创建一个混合空间
在这里插入图片描述
到动画蓝图中
在这里插入图片描述
在这里插入图片描述

设置瞄准状态下摄像机的位置

#pragma region Gameplay Ability
private:virtual void OnAimStateChanged(bool bIsAiming) override;
#pragma endregion#pragma region 摄像机视角
private:// 瞄准时相机的本地偏移量UPROPERTY(EditDefaultsOnly, Category = view)FVector CameraAimLocalOffset;// 相机插值速度UPROPERTY(EditDefaultsOnly, Category = view)float CameraLerpSpeed = 20.f;// 相机插值定时器句柄FTimerHandle CameraLerpTimerHandle;// 插值相机到目标本地偏移位置void LerpCameraToLocalOffsetLocation(const FVector& Goal);// 相机插值Tick回调void TickCameraLocalOffsetLerp(FVector Goal);
#pragma endregion
void ACPlayerCharacter::OnAimStateChanged(bool bIsAiming)
{// 瞄准状态变化时,插值相机到瞄准或默认位置LerpCameraToLocalOffsetLocation(bIsAiming ? CameraAimLocalOffset : FVector{0.f});
}void ACPlayerCharacter::LerpCameraToLocalOffsetLocation(const FVector& Goal)
{GetWorldTimerManager().ClearTimer(CameraLerpTimerHandle);// 下一帧执行,采取递归的方式CameraLerpTimerHandle = GetWorldTimerManager().SetTimerForNextTick(FTimerDelegate::CreateUObject(this,&ACPlayerCharacter::TickCameraLocalOffsetLerp,Goal));
}void ACPlayerCharacter::TickCameraLocalOffsetLerp(FVector Goal)
{// 获取相机的位置FVector CurrentLocalOffset = ViewCamera->GetRelativeLocation();// 如果相机位置与目标位置的距离小于1,则直接设置相机位置if (FVector::Dist(CurrentLocalOffset, Goal) < 1.f){ViewCamera->SetRelativeLocation(Goal);return;}// 计算插值系数,保证插值平滑且不超过1float LerpAlpha = FMath::Clamp(GetWorld()->GetDeltaSeconds() * CameraLerpSpeed, 0.f, 1.f);// 执行线性插值计算新位置FVector NewLocalOffset = FMath::Lerp(CurrentLocalOffset, Goal, LerpAlpha);// 设置相机位置ViewCamera->SetRelativeLocation(NewLocalOffset);// 继续下一帧插值,直到到达目标位置CameraLerpTimerHandle = GetWorldTimerManager().SetTimerForNextTick(FTimerDelegate::CreateUObject(this, &ACPlayerCharacter::TickCameraLocalOffsetLerp, Goal));
}

去蓝图中调整进去瞄准状态相机的位置
在这里插入图片描述

添加新的检测通道

在这里插入图片描述
这里可以看见创建的检测通道,可以直接调用划红线的名称
在这里插入图片描述
但还是起个define更能清楚含义
在这里插入图片描述

// Copyright Epic Games, Inc. All Rights Reserved.#pragma once#include "CoreMinimal.h"#define ECC_TARGET ECC_GameTraceChannel1
#define ECC_SPRING_ARM ECC_GameTraceChannel2
ACCharacter::ACCharacter()
{PrimaryActorTick.bCanEverTick = true;// 禁用网格的碰撞功能GetMesh()->SetCollisionEnabled(ECollisionEnabled::NoCollision);// 忽略弹簧臂的碰撞GetCapsuleComponent()->SetCollisionResponseToChannel(ECC_SPRING_ARM, ECR_Ignore);// 忽略目标的碰撞GetCapsuleComponent()->SetCollisionResponseToChannel(ECC_TARGET, ECR_Ignore);
}

设置弹簧臂的碰撞通道

ACPlayerCharacter::ACPlayerCharacter()
{// 创建并设置摄像机弹簧臂组件CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));CameraBoom->SetupAttachment(GetRootComponent()); // 将弹簧臂组件附加到角色的根组件CameraBoom->bUsePawnControlRotation = true; // 使用Pawn控制旋转CameraBoom->ProbeChannel = ECC_SPRING_ARM;
}

瞄准目标用的Actor

创建一个继承GameplayAbilityTargetActorTargetActor_GroundPick

在这里插入图片描述
在这里插入图片描述

在本地客户端中获取摄像机的朝向以及位置,发出射线来确定目标的位置

// 幻雨喜欢小猫咪#pragma once#include "CoreMinimal.h"
#include "Abilities/GameplayAbilityTargetActor.h"
#include "TargetActor_GroundPick.generated.h"/*** */
UCLASS()
class ATargetActor_GroundPick : public AGameplayAbilityTargetActor
{GENERATED_BODY()
public:ATargetActor_GroundPick();
private:virtual void Tick(float DeltaTime) override;// 获取当前目标点(玩家视角射线检测地面)FVector GetTargetPoint() const;// 目标区域半径UPROPERTY(EditDefaultsOnly, Category = "Targeting")float TargetAreaRadius = 300.f;// 目标检测最大距离UPROPERTY(EditDefaultsOnly, Category = "Targeting")float TargetTraceRange = 2000.f;// 是否绘制调试信息bool bShouldDrawDebug = false;
};
// 幻雨喜欢小猫咪#include "GAS/TA/TargetActor_GroundPick.h"#include "Crunch/Crunch.h"ATargetActor_GroundPick::ATargetActor_GroundPick()
{PrimaryActorTick.bCanEverTick = true;
}void ATargetActor_GroundPick::Tick(float DeltaTime)
{Super::Tick(DeltaTime);// 检测是否是本地玩家控制(只在本地客户端中显示)if (PrimaryPC && PrimaryPC->IsLocalPlayerController()){// 设置目标点位置SetActorLocation(GetTargetPoint());}
}FVector ATargetActor_GroundPick::GetTargetPoint() const
{if (!PrimaryPC || !PrimaryPC->IsLocalPlayerController())return GetActorLocation();FHitResult TraceResult;FVector ViewLoc;FRotator ViewRot;// 获取视角位置和朝向PrimaryPC->GetPlayerViewPoint(ViewLoc, ViewRot);// 获取射线终点位置FVector TraceEnd = ViewLoc + ViewRot.Vector() * TargetTraceRange;// 射线检测GetWorld()->LineTraceSingleByChannel(TraceResult,ViewLoc,TraceEnd,ECC_TARGET);// 如果没有命中,向下做一次射线检测,把结果点放地上if (!TraceResult.bBlockingHit){GetWorld()->LineTraceSingleByChannel(TraceResult, TraceEnd, TraceEnd + FVector::DownVector * TNumericLimits<float>::Max(), ECC_TARGET);}// 如果依然没有命中,返回当前位置if (!TraceResult.bBlockingHit){return GetActorLocation();}// 绘制调试球体if (bShouldDrawDebug){DrawDebugSphere(GetWorld(), TraceResult.ImpactPoint, TargetAreaRadius, 32, FColor::Red);}return TraceResult.ImpactPoint;
}

GA_GroundBlast技能添加该能力目标Acotor,在技能触发的时候生成这个actor

private:// 地面选点目标Actor类UPROPERTY(EditDefaultsOnly, Category = "Targeting")TSubclassOf<ATargetActor_GroundPick> TargetActorClass;// 瞄准动画UPROPERTY(EditDefaultsOnly, Category = "Animation")TObjectPtr<UAnimMontage> TargetingMontage;// 释放动画UPROPERTY(EditDefaultsOnly, Category = "Animation")TObjectPtr<UAnimMontage> CastMontage;// 目标确认回调UFUNCTION()void TargetConfirmed(const FGameplayAbilityTargetDataHandle& TargetDataHandle);// 目标取消回调UFUNCTION()void TargetCanceled(const FGameplayAbilityTargetDataHandle& TargetDataHandle);
void UGA_GroundBlast::ActivateAbility(const FGameplayAbilitySpecHandle Handle,const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo,const FGameplayEventData* TriggerEventData)
{if (!HasAuthorityOrPredictionKey(ActorInfo, &ActivationInfo)) return;UAbilityTask_PlayMontageAndWait* PlayGroundBlasAnimTask = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(this, NAME_None, TargetingMontage);PlayGroundBlasAnimTask->OnBlendOut.AddDynamic(this, &UGA_GroundBlast::K2_EndAbility);PlayGroundBlasAnimTask->OnCancelled.AddDynamic(this, &UGA_GroundBlast::K2_EndAbility);PlayGroundBlasAnimTask->OnCompleted.AddDynamic(this, &UGA_GroundBlast::K2_EndAbility);PlayGroundBlasAnimTask->OnInterrupted.AddDynamic(this, &UGA_GroundBlast::K2_EndAbility);PlayGroundBlasAnimTask->ReadyForActivation();// 等待瞄准敌人UAbilityTask_WaitTargetData* WaitTargetDataTask = UAbilityTask_WaitTargetData::WaitTargetData(this, NAME_None, EGameplayTargetingConfirmation::UserConfirmed, TargetActorClass);// 确认技能WaitTargetDataTask->ValidData.AddDynamic(this, &UGA_GroundBlast::TargetConfirmed);// 技能取消WaitTargetDataTask->Cancelled.AddDynamic(this, &UGA_GroundBlast::TargetCanceled);WaitTargetDataTask->ReadyForActivation();// 生成目标ActorAGameplayAbilityTargetActor* TargetActor;WaitTargetDataTask->BeginSpawningActor(this, TargetActorClass, TargetActor);WaitTargetDataTask->FinishSpawningActor(this, TargetActor);
}void UGA_GroundBlast::TargetConfirmed(const FGameplayAbilityTargetDataHandle& TargetDataHandle)
{UE_LOG(LogTemp, Warning, TEXT("技能发射"));K2_EndAbility();
}void UGA_GroundBlast::TargetCanceled(const FGameplayAbilityTargetDataHandle& TargetDataHandle)
{UE_LOG(LogTemp, Warning, TEXT("技能取消"));K2_EndAbility();
}

在这里插入图片描述
创建后 ,没什么需要做的

到技能中设置一下
在这里插入图片描述

到能力组件CAbilitySystemComponent中配置一下确认的输入ID和取消的输入ID

UCAbilitySystemComponent::UCAbilitySystemComponent()
{GetGameplayAttributeValueChangeDelegate(UCAttributeSet::GetHealthAttribute()).AddUObject(this, &UCAbilitySystemComponent::HealthUpdated);GetGameplayAttributeValueChangeDelegate(UCAttributeSet::GetManaAttribute()).AddUObject(this, &UCAbilitySystemComponent::ManaUpdated);GenericConfirmInputID = static_cast<int32>(ECAbilityInputID::Confirm);GenericCancelInputID = static_cast<int32>(ECAbilityInputID::Cancel);
}

在这里插入图片描述
在这里插入图片描述
左键发射,右键取消
在这里插入图片描述

让TA_GroundPick可以获取目标

在玩家控制器发送确认信息的时候会调用ConfirmTargetingAndContinue函数,在该函数中,生成一个球体检测该范围内的所有pawn,把数据通过广播传给GA,在GA中处理处理伤害的传递以及冲击等事情。

public:// 设置目标区域半径void SetTargetAreaRadius(float NewRadius);// 设置目标检测最大距离FORCEINLINE void SetTargetTraceRange(float NewRange) { TargetTraceRange = NewRange; }// 确认目标virtual void ConfirmTargetingAndContinue() override;// 设置目标筛选选项void SetTargetOptions(bool bTargetFriendly, bool bTargetEnenmy = true);// 设置是否绘制调试信息FORCEINLINE void SetShouldDrawDebug(bool bDrawDebug) { bShouldDrawDebug = bDrawDebug; }private:// 是否可选敌方bool bShouldTargetEnemy = true;// 是否可选友方bool bShouldTargetFriendly = false;
// 幻雨喜欢小猫咪#include "GAS/TA/TargetActor_GroundPick.h"#include "AbilitySystemBlueprintLibrary.h"
#include "GenericTeamAgentInterface.h"
#include "Abilities/GameplayAbility.h"
#include "Crunch/Crunch.h"
#include "Engine/OverlapResult.h"ATargetActor_GroundPick::ATargetActor_GroundPick()
{PrimaryActorTick.bCanEverTick = true;
}void ATargetActor_GroundPick::SetTargetAreaRadius(float NewRadius)
{TargetAreaRadius = NewRadius;
}void ATargetActor_GroundPick::ConfirmTargetingAndContinue()
{// 检测目标TArray<FOverlapResult> OverlapResults;// 设置碰撞查询参数,仅检测PawnFCollisionObjectQueryParams ObjectQueryParams;ObjectQueryParams.AddObjectTypesToQuery(ECC_Pawn);// 创建球形碰撞球体,设置半径和位置FCollisionShape CollisionShape;CollisionShape.SetSphere(TargetAreaRadius);GetWorld()->OverlapMultiByObjectType(OverlapResults,GetActorLocation(),FQuat::Identity,ObjectQueryParams,CollisionShape);TSet<AActor*> TargetActors;// 获取能力使用者的团队接口IGenericTeamAgentInterface* OwnerTeamInterface = nullptr;if (OwningAbility){OwnerTeamInterface = Cast<IGenericTeamAgentInterface>(OwningAbility->GetAvatarActorFromActorInfo());}for (const FOverlapResult& OverlapResult : OverlapResults){// 检测到友军,友军为false,不打友军跳过if (OwnerTeamInterface && OwnerTeamInterface->GetTeamAttitudeTowards(*OverlapResult.GetActor()) == ETeamAttitude::Friendly && !bShouldTargetFriendly)continue;// 检测到敌军,敌军为false,不打敌军跳过if (OwnerTeamInterface && OwnerTeamInterface->GetTeamAttitudeTowards(*OverlapResult.GetActor()) == ETeamAttitude::Hostile && !bShouldTargetEnemy)continue;// 添加目标TargetActors.Add(OverlapResult.GetActor());}// 创建目标数据FGameplayAbilityTargetDataHandle TargetData = UAbilitySystemBlueprintLibrary::AbilityTargetDataFromActorArray(TargetActors.Array(), false);// 触发目标数据已就绪委托TargetDataReadyDelegate.Broadcast(TargetData);
}void ATargetActor_GroundPick::SetTargetOptions(bool bTargetFriendly, bool bTargetEnemy)
{bShouldTargetFriendly = bTargetFriendly;bShouldTargetEnemy = bTargetEnemy;
}

这里的委托
在这里插入图片描述
传到这里
在这里插入图片描述

private:// 技能目标区域半径UPROPERTY(EditDefaultsOnly, Category = "Targeting")float TargetAreaRadius = 300.f;UPROPERTY(EditDefaultsOnly, Category = "Damage")FGenericDamageEffectDef DamageEffectDef;// 目标检测最大距离UPROPERTY(EditDefaultsOnly, Category = "Targeting")float TargetTraceRange = 2000.f;
// 配置一下参数
void UGA_GroundBlast::ActivateAbility(const FGameplayAbilitySpecHandle Handle,const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo,const FGameplayEventData* TriggerEventData)
{// 生成目标ActorAGameplayAbilityTargetActor* TargetActor;WaitTargetDataTask->BeginSpawningActor(this, TargetActorClass, TargetActor);// 设置目标Actor参数ATargetActor_GroundPick* GroundPickActor = Cast<ATargetActor_GroundPick>(TargetActor);if (GroundPickActor){GroundPickActor->SetShouldDrawDebug(ShouldDrawDebug());GroundPickActor->SetTargetAreaRadius(TargetAreaRadius);GroundPickActor->SetTargetTraceRange(TargetTraceRange);}WaitTargetDataTask->FinishSpawningActor(this, TargetActor);
}
void UGA_GroundBlast::TargetConfirmed(const FGameplayAbilityTargetDataHandle& TargetDataHandle)
{// 仅在服务器上执行伤害和击退if (K2_HasAuthority()){// 对目标应用伤害效果BP_ApplyGameplayEffectToTarget(TargetDataHandle, DamageEffectDef.DamageEffect, GetAbilityLevel(CurrentSpecHandle, CurrentActorInfo));}UE_LOG(LogTemp, Warning, TEXT("技能发射"));K2_EndAbility();
}

把升龙的伤害GE复制过来用一下
在这里插入图片描述
在这里插入图片描述

实现击飞多个目标在代码中调用GC生成特效

做击飞

CGameplayAbility中实现方便每个子类调用,传进来的循环遍历一下调用推目标的技能就好了

    // 推动多个目标void PushTargets(const TArray<AActor*>& Targets, const FVector& PushVel);// 推动TargetData中的所有目标void PushTargets(const FGameplayAbilityTargetDataHandle& TargetDataHandle, const FVector& PushVel);
void UCGameplayAbility::PushTargets(const TArray<AActor*>& Targets, const FVector& PushVel)
{for(AActor* Target : Targets){PushTarget(Target, PushVel);}
}void UCGameplayAbility::PushTargets(const FGameplayAbilityTargetDataHandle& TargetDataHandle, const FVector& PushVel)
{TArray<AActor*> Targets = UAbilitySystemBlueprintLibrary::GetAllActorsFromTargetData(TargetDataHandle);PushTargets(Targets, PushVel);
}

然后再伤害传递的地方调用一下

void UGA_GroundBlast::TargetConfirmed(const FGameplayAbilityTargetDataHandle& TargetDataHandle)
{// 仅在服务器上执行伤害和击退if (K2_HasAuthority()){// 对目标应用伤害效果BP_ApplyGameplayEffectToTarget(TargetDataHandle, DamageEffectDef.DamageEffect, GetAbilityLevel(CurrentSpecHandle, CurrentActorInfo));// 对目标施加推力PushTargets(TargetDataHandle, DamageEffectDef.PushVelocity);}UE_LOG(LogTemp, Warning, TEXT("技能发射"));K2_EndAbility();
}

添加GC、蓝耗和冷却的提交

添加cdtag

	// 大地爆炸cdCRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_GroundBlast_Cooldown)
	UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_GroundBlast_Cooldown, "Ability.GroundBlast.Cooldown", "大地爆炸技能冷却")

添加GC显示特效,需要在TargetActor处将Actor的位置发送给能力

void ATargetActor_GroundPick::ConfirmTargetingAndContinue()
{// 创建目标数据FGameplayAbilityTargetDataHandle TargetData = UAbilitySystemBlueprintLibrary::AbilityTargetDataFromActorArray(TargetActors.Array(), false);// 添加命中点信息(用于特效等)FGameplayAbilityTargetData_SingleTargetHit* HitLocation = new FGameplayAbilityTargetData_SingleTargetHit;HitLocation->HitResult.ImpactPoint = GetActorLocation();TargetData.Add(HitLocation);// 触发目标数据已就绪委托TargetDataReadyDelegate.Broadcast(TargetData);
}

在技能中创建传GC的Tag,在发生伤害后面生成特效,再生成一下摄像机震动的GC,顺便播放一下技能的动画
CAbilitySystemStatics中写一个静态函数获取震动相机的标签

	// 获取摄像机震动游戏事件标签static FGameplayTag GetCameraShakeGameplayCueTag();
FGameplayTag UCAbilitySystemStatics::GetCameraShakeGameplayCueTag()
{return FGameplayTag::RequestGameplayTag("GameplayCue.CameraShake");
}

回到技能中添加特效并执行特效,顺便把冷却和CD一起提交了

private:// 冲击特效Cue标签UPROPERTY(EditDefaultsOnly, Category = "Cue")FGameplayTag BlastGameplayCueTag;
void UGA_GroundBlast::TargetConfirmed(const FGameplayAbilityTargetDataHandle& TargetDataHandle)
{if (!K2_CommitAbility()){K2_EndAbility();return;}// 仅在服务器上执行伤害和击退if (K2_HasAuthority()){// 对目标应用伤害效果BP_ApplyGameplayEffectToTarget(TargetDataHandle, DamageEffectDef.DamageEffect, GetAbilityLevel(CurrentSpecHandle, CurrentActorInfo));// 对目标施加推力PushTargets(TargetDataHandle, DamageEffectDef.PushVelocity);}FGameplayCueParameters BlastingGameplayCueParameters;// 设置特效的位置BlastingGameplayCueParameters.Location = UAbilitySystemBlueprintLibrary::GetHitResultFromTargetData(TargetDataHandle, 1).ImpactPoint;// 设置特效的大小BlastingGameplayCueParameters.RawMagnitude = TargetAreaRadius;// 播放冲击特效和摄像机震动GetAbilitySystemComponentFromActorInfo()->ExecuteGameplayCue(BlastGameplayCueTag, BlastingGameplayCueParameters);GetAbilitySystemComponentFromActorInfo()->ExecuteGameplayCue(UCAbilitySystemStatics::GetCameraShakeGameplayCueTag(), BlastingGameplayCueParameters);// 播放释放动画UAnimInstance* OwnerAnimInst = GetOwnerAnimInstance();if (OwnerAnimInst){OwnerAnimInst->Montage_Play(CastMontage);}UE_LOG(LogTemp, Warning, TEXT("技能发射"));K2_EndAbility();
}

添加GC
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
创建两个GE,代表CD和蓝耗
在这里插入图片描述
在这里插入图片描述
CD的GE要记得传一下对应的CDTag
在这里插入图片描述
在这里插入图片描述

完善TargetActor_GroundPick的外观

private:// 贴花(用于显示技能范围)UPROPERTY(VisibleDefaultsOnly, Category = "Visual")TObjectPtr<UDecalComponent> DecalComp;
ATargetActor_GroundPick::ATargetActor_GroundPick()
{PrimaryActorTick.bCanEverTick = true;// 创建根组件SetRootComponent(CreateDefaultSubobject<USceneComponent>("Root Comp"));// 创建贴花组件用于显示范围DecalComp = CreateDefaultSubobject<UDecalComponent>("Decal Comp");DecalComp->SetupAttachment(GetRootComponent());
}void ATargetActor_GroundPick::SetTargetAreaRadius(float NewRadius)
{TargetAreaRadius = NewRadius;// 同步贴花的显示大小DecalComp->DecalSize = FVector{NewRadius};
}

创建一个材质,改成贴花还有半透明
在这里插入图片描述

if(gradient == 0)
{return 0;
}
if(gradient < thickness)
{return 1;
}
return 0;

在这里插入图片描述

float2 coord = (float2(uvcoord) - float2(0.5, 0.5)) * 2;if(abs(coord.x) < thickness || abs(coord.y) < thickness)
{return 0;
}return 1;

在这里插入图片描述
回到TA_GroundPick让红色的箭头朝上,把刚做好的贴花材质弄进去
在这里插入图片描述
在这里插入图片描述

完整代码

TargetActor_GroundPick的完整代码

// 幻雨喜欢小猫咪#pragma once#include "CoreMinimal.h"
#include "Abilities/GameplayAbilityTargetActor.h"
#include "Components/DecalComponent.h"
#include "TargetActor_GroundPick.generated.h"/*** */
UCLASS()
class ATargetActor_GroundPick : public AGameplayAbilityTargetActor
{GENERATED_BODY()
public:ATargetActor_GroundPick();// 设置目标区域半径void SetTargetAreaRadius(float NewRadius);// 设置目标检测最大距离FORCEINLINE void SetTargetTraceRange(float NewRange) { TargetTraceRange = NewRange; }// 确认目标virtual void ConfirmTargetingAndContinue() override;// 设置目标筛选选项void SetTargetOptions(bool bTargetFriendly, bool bTargetEnemy = true);// 设置是否绘制调试信息FORCEINLINE void SetShouldDrawDebug(bool bDrawDebug) { bShouldDrawDebug = bDrawDebug; }
private:// 贴花(用于显示技能范围)UPROPERTY(VisibleDefaultsOnly, Category = "Visual")TObjectPtr<UDecalComponent> DecalComp;// 是否可选敌方bool bShouldTargetEnemy = true;// 是否可选友方bool bShouldTargetFriendly = false;virtual void Tick(float DeltaTime) override;// 获取当前目标点(玩家视角射线检测地面)FVector GetTargetPoint() const;// 目标区域半径UPROPERTY(EditDefaultsOnly, Category = "Targeting")float TargetAreaRadius = 300.f;// 目标检测最大距离UPROPERTY(EditDefaultsOnly, Category = "Targeting")float TargetTraceRange = 2000.f;// 是否绘制调试信息bool bShouldDrawDebug = true;
};
// 幻雨喜欢小猫咪#include "GAS/TA/TargetActor_GroundPick.h"#include "AbilitySystemBlueprintLibrary.h"
#include "GenericTeamAgentInterface.h"
#include "Abilities/GameplayAbility.h"
#include "Crunch/Crunch.h"
#include "Engine/OverlapResult.h"ATargetActor_GroundPick::ATargetActor_GroundPick()
{PrimaryActorTick.bCanEverTick = true;// 创建根组件SetRootComponent(CreateDefaultSubobject<USceneComponent>("Root Comp"));// 创建贴花组件用于显示范围DecalComp = CreateDefaultSubobject<UDecalComponent>("Decal Comp");DecalComp->SetupAttachment(GetRootComponent());
}void ATargetActor_GroundPick::SetTargetAreaRadius(float NewRadius)
{TargetAreaRadius = NewRadius;// 同步贴花的显示大小DecalComp->DecalSize = FVector{NewRadius};
}void ATargetActor_GroundPick::ConfirmTargetingAndContinue()
{// 检测目标TArray<FOverlapResult> OverlapResults;// 设置碰撞查询参数,仅检测PawnFCollisionObjectQueryParams ObjectQueryParams;ObjectQueryParams.AddObjectTypesToQuery(ECC_Pawn);// 创建球形碰撞球体,设置半径和位置FCollisionShape CollisionShape;CollisionShape.SetSphere(TargetAreaRadius);// 检测碰撞GetWorld()->OverlapMultiByObjectType(OverlapResults,GetActorLocation(),FQuat::Identity,ObjectQueryParams,CollisionShape);TSet<AActor*> TargetActors;// 获取能力使用者的团队接口IGenericTeamAgentInterface* OwnerTeamInterface = nullptr;if (OwningAbility){OwnerTeamInterface = Cast<IGenericTeamAgentInterface>(OwningAbility->GetAvatarActorFromActorInfo());}for (const FOverlapResult& OverlapResult : OverlapResults){// 检测到友军,友军为false,不打友军跳过if (OwnerTeamInterface && OwnerTeamInterface->GetTeamAttitudeTowards(*OverlapResult.GetActor()) == ETeamAttitude::Friendly && !bShouldTargetFriendly)continue;// 检测到敌军,敌军为false,不打敌军跳过if (OwnerTeamInterface && OwnerTeamInterface->GetTeamAttitudeTowards(*OverlapResult.GetActor()) == ETeamAttitude::Hostile && !bShouldTargetEnemy)continue;// 添加目标TargetActors.Add(OverlapResult.GetActor());}// 创建目标数据FGameplayAbilityTargetDataHandle TargetData = UAbilitySystemBlueprintLibrary::AbilityTargetDataFromActorArray(TargetActors.Array(), false);// 添加命中点信息(用于特效等)FGameplayAbilityTargetData_SingleTargetHit* HitLocation = new FGameplayAbilityTargetData_SingleTargetHit;HitLocation->HitResult.ImpactPoint = GetActorLocation();TargetData.Add(HitLocation);// 触发目标数据已就绪委托TargetDataReadyDelegate.Broadcast(TargetData);
}void ATargetActor_GroundPick::SetTargetOptions(bool bTargetFriendly, bool bTargetEnemy)
{bShouldTargetFriendly = bTargetFriendly;bShouldTargetEnemy = bTargetEnemy;
}void ATargetActor_GroundPick::Tick(float DeltaTime)
{Super::Tick(DeltaTime);// 检测是否是本地玩家控制(只在本地客户端中显示)if (PrimaryPC && PrimaryPC->IsLocalPlayerController()){// 设置目标点位置SetActorLocation(GetTargetPoint());}
}FVector ATargetActor_GroundPick::GetTargetPoint() const
{if (!PrimaryPC || !PrimaryPC->IsLocalPlayerController())return GetActorLocation();FHitResult TraceResult;FVector ViewLoc;FRotator ViewRot;// 获取视角位置和朝向PrimaryPC->GetPlayerViewPoint(ViewLoc, ViewRot);// 获取射线终点位置FVector TraceEnd = ViewLoc + ViewRot.Vector() * TargetTraceRange;// 射线检测GetWorld()->LineTraceSingleByChannel(TraceResult,ViewLoc,TraceEnd,ECC_TARGET);// 如果没有命中,向下做一次射线检测,把结果点放地上if (!TraceResult.bBlockingHit){GetWorld()->LineTraceSingleByChannel(TraceResult, TraceEnd, TraceEnd + FVector::DownVector * TNumericLimits<float>::Max(), ECC_TARGET);}// 如果依然没有命中,返回当前位置if (!TraceResult.bBlockingHit){return GetActorLocation();}// 绘制调试球体if (bShouldDrawDebug){DrawDebugSphere(GetWorld(), TraceResult.ImpactPoint, TargetAreaRadius, 32, FColor::Red);}return TraceResult.ImpactPoint;
}

GA_GroundBlast的完整代码

// 幻雨喜欢小猫咪#pragma once#include "CoreMinimal.h"
#include "GAS/Core/CGameplayAbility.h"
#include "GAS/Core/CGameplayAbilityTypes.h"
#include "GAS/TA/TargetActor_GroundPick.h"
#include "GA_GroundBlast.generated.h"/*** */
UCLASS()
class CRUNCH_API UGA_GroundBlast : public UCGameplayAbility
{GENERATED_BODY()public:UGA_GroundBlast();virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override;
private:// 冲击特效Cue标签UPROPERTY(EditDefaultsOnly, Category = "Cue")FGameplayTag BlastGameplayCueTag;// 技能目标区域半径UPROPERTY(EditDefaultsOnly, Category = "Targeting")float TargetAreaRadius = 300.f;UPROPERTY(EditDefaultsOnly, Category = "Damage")FGenericDamageEffectDef DamageEffectDef;// 目标检测最大距离UPROPERTY(EditDefaultsOnly, Category = "Targeting")float TargetTraceRange = 2000.f;// 地面选点目标Actor类UPROPERTY(EditDefaultsOnly, Category = "Targeting")TSubclassOf<ATargetActor_GroundPick> TargetActorClass;// 瞄准动画UPROPERTY(EditDefaultsOnly, Category = "Animation")TObjectPtr<UAnimMontage> TargetingMontage;// 释放动画UPROPERTY(EditDefaultsOnly, Category = "Animation")TObjectPtr<UAnimMontage> CastMontage;// 目标确认回调UFUNCTION()void TargetConfirmed(const FGameplayAbilityTargetDataHandle& TargetDataHandle);// 目标取消回调UFUNCTION()void TargetCanceled(const FGameplayAbilityTargetDataHandle& TargetDataHandle);
};
// 幻雨喜欢小猫咪#include "GAS/Abilities/GA_GroundBlast.h"#include "AbilitySystemBlueprintLibrary.h"
#include "AbilitySystemComponent.h"
#include "Abilities/Tasks/AbilityTask_PlayMontageAndWait.h"
#include "GAS/Core/TGameplayTags.h"
#include "Abilities/Tasks/AbilityTask_WaitTargetData.h"
#include "GAS/Core/CAbilitySystemStatics.h"UGA_GroundBlast::UGA_GroundBlast()
{// 技能激活时给角色添加瞄准标签ActivationOwnedTags.AddTag(TGameplayTags::Stats_Aim);// 阻断基础攻击技能BlockAbilitiesWithTag.AddTag(TGameplayTags::Ability_BasicAttack);
}void UGA_GroundBlast::ActivateAbility(const FGameplayAbilitySpecHandle Handle,const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo,const FGameplayEventData* TriggerEventData)
{if (!HasAuthorityOrPredictionKey(ActorInfo, &ActivationInfo)) return;UAbilityTask_PlayMontageAndWait* PlayGroundBlasAnimTask = UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(this, NAME_None, TargetingMontage);PlayGroundBlasAnimTask->OnBlendOut.AddDynamic(this, &UGA_GroundBlast::K2_EndAbility);PlayGroundBlasAnimTask->OnCancelled.AddDynamic(this, &UGA_GroundBlast::K2_EndAbility);PlayGroundBlasAnimTask->OnCompleted.AddDynamic(this, &UGA_GroundBlast::K2_EndAbility);PlayGroundBlasAnimTask->OnInterrupted.AddDynamic(this, &UGA_GroundBlast::K2_EndAbility);PlayGroundBlasAnimTask->ReadyForActivation();// 等待瞄准敌人UAbilityTask_WaitTargetData* WaitTargetDataTask = UAbilityTask_WaitTargetData::WaitTargetData(this, NAME_None, EGameplayTargetingConfirmation::UserConfirmed, TargetActorClass);// 确认技能WaitTargetDataTask->ValidData.AddDynamic(this, &UGA_GroundBlast::TargetConfirmed);// 技能取消WaitTargetDataTask->Cancelled.AddDynamic(this, &UGA_GroundBlast::TargetCanceled);WaitTargetDataTask->ReadyForActivation();// 生成目标ActorAGameplayAbilityTargetActor* TargetActor;WaitTargetDataTask->BeginSpawningActor(this, TargetActorClass, TargetActor);// 设置目标Actor参数ATargetActor_GroundPick* GroundPickActor = Cast<ATargetActor_GroundPick>(TargetActor);if (GroundPickActor){GroundPickActor->SetShouldDrawDebug(ShouldDrawDebug());GroundPickActor->SetTargetAreaRadius(TargetAreaRadius);GroundPickActor->SetTargetTraceRange(TargetTraceRange);}WaitTargetDataTask->FinishSpawningActor(this, TargetActor);
}void UGA_GroundBlast::TargetConfirmed(const FGameplayAbilityTargetDataHandle& TargetDataHandle)
{if (!K2_CommitAbility()){K2_EndAbility();return;}// 仅在服务器上执行伤害和击退if (K2_HasAuthority()){// 对目标应用伤害效果BP_ApplyGameplayEffectToTarget(TargetDataHandle, DamageEffectDef.DamageEffect, GetAbilityLevel(CurrentSpecHandle, CurrentActorInfo));// 对目标施加推力PushTargets(TargetDataHandle, DamageEffectDef.PushVelocity);}FGameplayCueParameters BlastingGameplayCueParameters;// 设置特效的位置BlastingGameplayCueParameters.Location = UAbilitySystemBlueprintLibrary::GetHitResultFromTargetData(TargetDataHandle, 1).ImpactPoint;// 设置特效的大小BlastingGameplayCueParameters.RawMagnitude = TargetAreaRadius;// 播放冲击特效和摄像机震动GetAbilitySystemComponentFromActorInfo()->ExecuteGameplayCue(BlastGameplayCueTag, BlastingGameplayCueParameters);GetAbilitySystemComponentFromActorInfo()->ExecuteGameplayCue(UCAbilitySystemStatics::GetCameraShakeGameplayCueTag(), BlastingGameplayCueParameters);// 播放释放动画UAnimInstance* OwnerAnimInst = GetOwnerAnimInstance();if (OwnerAnimInst){OwnerAnimInst->Montage_Play(CastMontage);}UE_LOG(LogTemp, Warning, TEXT("技能发射"));K2_EndAbility();
}void UGA_GroundBlast::TargetCanceled(const FGameplayAbilityTargetDataHandle& TargetDataHandle)
{UE_LOG(LogTemp, Warning, TEXT("技能取消"));K2_EndAbility();
}
http://www.xdnf.cn/news/15454.html

相关文章:

  • SHAP 值的数值尺度
  • 梳理Bean的创建流程
  • burpsuite使用中遇到的一些问题(bp启动后浏览器无法连接)/如何导入证书
  • GPIO 输入/输出
  • 2025年睿抗机器人开发者大赛CAIP-编程技能赛-高职组(省赛)解题报告 | 珂学家
  • 在Autodl服务器中使用VNC建立图形界面
  • 快速排序:原理、示例与 C 语言实现详解
  • C语言---自定义类型(下)(枚举和联合类型)
  • 云服务器如何管理数据库(MySQL/MongoDB)?
  • 【html常见页面布局】
  • 50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | DoubleClickHeart(双击爱心)
  • netstat -tlnp | grep 5000
  • Swift实现股票图:从基础到高级
  • python的形成性考核管理系统
  • 并发-原子变量类
  • 【MCU控制 初级手札】1.1 电阻
  • 现代CSS实战:用变量与嵌套重构可维护的前端样式
  • 使用 Java 获取 PDF 页面信息(页数、尺寸、旋转角度、方向、标签与边框)
  • Flink双流实时对账
  • 大语言模型零样本情感分析实战:无需机器学习训练,96%准确率实现指南
  • 云手机隐私保护指南:如何保障账号与数据的云端安全?
  • 虚拟机删除操作
  • IELTS 阅读C15-test1-passage 2 复盘
  • React源码6 三大核心模块之一:commit, finishConcurrentRender函数
  • 24.找到列表中最大或最小值的索引
  • Pitaya 是一个简单、快速、轻量级的游戏服务器框架,它为分布式多人游戏和服务器端应用程序提供了一个基本的开发框架
  • 优雅的Java:01.数据更新如何更优雅
  • Python学习之路(十二)-开发和优化处理大数据量接口
  • 从springcloud-gateway了解同步和异步,webflux webMvc、共享变量
  • S7-200 SMART PLC:不同CPU及数字量 IO 接线全解析