GameplayAbility Tick 구현 C++

250x250

GameplayAbility Tick

기본적으로 GameplayAbility자체는 Tick을 제공하지 않기 때문에 GameplayAbility에서 Tick을 사용하기 위해서는 추가적인 설정이 필요합니다.

Tick구현 - Ability Task.

GameplayAbility에서 Tick을 사용하기 위해 Ability Task를 이용해 Tick을 구현해서 사용했습니다.

C++로 구현했지만 조금만 수정하면 블루프린트에서도 사용가능합니다.

Ability Task.h

#pragma once

#include "CoreMinimal.h"
#include "Abilities/Tasks/AbilityTask.h"
#include "AbilityTask_ExecuteTaskOnTick.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAbilityTaskTickDelegate, float, DeltaTime);
/**
 * 
 */
UCLASS()
class BLADE_API UAbilityTask_ExecuteTaskOnTick : public UAbilityTask
{
	GENERATED_BODY()
public:
	UAbilityTask_ExecuteTaskOnTick();
    
	static UAbilityTask_ExecuteTaskOnTick* ExecuteTaskTick(UGameplayAbility* OwningAbility);
	
    //Gameplaytask Interface
	virtual void TickTask(float DeltaTime) override;

	FOnAbilityTaskTickDelegate OnAbilityTaskTick;
};

AbilityTask.cpp

#include "AbilitySystem/AbilityTasks/AbilityTask_ExecuteTaskOnTick.h"

UAbilityTask_ExecuteTaskOnTick::UAbilityTask_ExecuteTaskOnTick()
{
    bTickingTask = true;
}
UAbilityTask_ExecuteTaskOnTick* UAbilityTask_ExecuteTaskOnTick::ExecuteTaskTick(UGameplayAbility* OwningAbility)
{
    UAbilityTask_ExecuteTaskOnTick* Node = NewAbilityTask<UAbilityTask_ExecuteTaskOnTick>(OwningAbility);
    return Node;
}
void UAbilityTask_ExecuteTaskOnTick::TickTask(float DeltaTime)
{
    Super::TickTask(DeltaTime);
    if (ShouldBroadcastAbilityTaskDelegates()) {
        OnAbilityTaskTick.Broadcast(DeltaTime);
    }
    else {
        EndTask();
    }
}

 

Tick을 필요로 하는 GameplayAbility

위 코드를 보면 아시겠지만 Delegate를 이용한 방법이라 델리게이트를 바인딩하여 사용할 수 있습니다.

TargetLock을 구현하고있는 GameplayAbility를 예시로 보여드리겠습니다.

GameplayAbility.h

#pragma once

#include "CoreMinimal.h"
#include "AbilitySystem/Abilites/BladePlayerGameplayAbility.h"
#include "PlayerTargetLockAbility.generated.h"

class UTargetLockWidget;

UCLASS()
class BLADE_API UPlayerTargetLockAbility : public UBladePlayerGameplayAbility
{
	GENERATED_BODY()
protected:
	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:
	void TryLockOnTarget();
	void GetAvailableActorsToTarget();
	void CancelTargetLockAbility();
	void CleanUp();
	void DrawTargetLockWidget();
	void SetTargetLockWidgetLoc();
	/*Tick을 사용하기 위해 바인딩해야하는 함수. UFUNCTION 필수*/
	UFUNCTION()
	void OnTargetLockTick(float DeltaTime);
	/*여기까지*/
	AActor* GetNearestTargetFromAvailableActors(const TArray<AActor*>& InAvailableActors);

	UPROPERTY(EditDefaultsOnly,Category="Target Lock")
	float TraceDistance = 3000.f;
	UPROPERTY(EditDefaultsOnly,Category="Target Lock")
	FVector TraceBoxSize = FVector(3000.f, 3000.f, 300.f);
	UPROPERTY(EditDefaultsOnly,Category="Target Lock")
	TArray<TEnumAsByte<	EObjectTypeQuery> > BoxTraceChannel;
	UPROPERTY(EditDefaultsOnly,Category="Target Lock")
	bool bIsDebugging;
	UPROPERTY()
	TArray<AActor* > AvailableActorsToLock;
	UPROPERTY()
	AActor* CurrentLockActor;
	UPROPERTY(EditDefaultsOnly,Category="Target Lock")
	TSubclassOf<UTargetLockWidget> TargetLockWidgetClass;

	UPROPERTY()
	FVector2D TargetLockBoxSIze = FVector2D::ZeroVector;
	UPROPERTY()
	UTargetLockWidget* TargetLockWidget;
};

GameplayAbility.cpp

ActivateAbility()와 OnTargetLockTick()부분만 보시면 됩니다.

// YunMinSeong. All right reserved. 


#include "AbilitySystem/Abilites/PlayerTargetLockAbility.h"
#include "Characters\BladePlayerCharacter.h"
#include "Kismet\KismetSystemLibrary.h"
#include "Kismet\GameplayStatics.h"
#include "Controllers/BladeController.h"
//Widget
#include "Widgets/TargetLockWidget.h"
#include "Blueprint/WidgetLayoutLibrary.h"
#include "Components/SizeBox.h"

#include "AbilitySystem/AbilityTasks/AbilityTask_ExecuteTaskOnTick.h"
#include "BladeFunctionLibrary.h"
#include "BladeGameplayTags.h"
//Debug
#include "CustomDebugHelper.h"
void UPlayerTargetLockAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
	TryLockOnTarget();
	Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);
	
	//UAbilityTask_ExecuteTaskOnTick* TickTask=UAbilityTask_ExecuteTaskOnTick::ExecuteTaskOnTick(this);
	UAbilityTask_ExecuteTaskOnTick* TickTask=UAbilityTask_ExecuteTaskOnTick::ExecuteTaskTick(this);
	TickTask->OnAbilityTaskTick.AddDynamic(this, &ThisClass::OnTargetLockTick);
	TickTask->ReadyForActivation();
}
void UPlayerTargetLockAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled)
{
	CleanUp();
	Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled);
}
void UPlayerTargetLockAbility::TryLockOnTarget()
{
	GetAvailableActorsToTarget();
	if (AvailableActorsToLock.IsEmpty()) {
		CancelTargetLockAbility();
		return;
	}
	CurrentLockActor=GetNearestTargetFromAvailableActors(AvailableActorsToLock);
	if (CurrentLockActor) {
		DrawTargetLockWidget();
		SetTargetLockWidgetLoc();
	}
	else {
		CancelTargetLockAbility();
	}
}
void UPlayerTargetLockAbility::GetAvailableActorsToTarget()
{
	TArray<FHitResult> BoxTraceHits;
	UKismetSystemLibrary::BoxTraceMultiForObjects(
		GetPlayerCharacterFromActorInfo(),
		GetPlayerCharacterFromActorInfo()->GetActorLocation(),
		GetPlayerCharacterFromActorInfo()->GetActorLocation()+GetPlayerCharacterFromActorInfo()->GetActorForwardVector()* TraceDistance,
		TraceBoxSize/2.f,
		GetPlayerCharacterFromActorInfo()->GetActorForwardVector().ToOrientationRotator(),
		BoxTraceChannel, false,
		TArray<AActor*>(),
		bIsDebugging?EDrawDebugTrace::ForDuration:EDrawDebugTrace::None,
		BoxTraceHits,true
	);
	for (const FHitResult& BoxHit : BoxTraceHits) {
		if (AActor* HitActor=BoxHit.GetActor()) {
			if (HitActor != GetPlayerCharacterFromActorInfo()) {
				AvailableActorsToLock.AddUnique(HitActor);
			}
		}
	}
}
void UPlayerTargetLockAbility::CancelTargetLockAbility()
{
	CancelAbility(GetCurrentAbilitySpecHandle(),GetCurrentActorInfo(),GetCurrentActivationInfo(),true);
}
void UPlayerTargetLockAbility::CleanUp()
{
	AvailableActorsToLock.Empty();
	CurrentLockActor = nullptr;
	if (TargetLockWidget) {
		TargetLockWidget->RemoveFromParent();
	}
	TargetLockWidget = nullptr;
	TargetLockBoxSIze = FVector2D::ZeroVector;
}
void UPlayerTargetLockAbility::DrawTargetLockWidget()
{
	if (!TargetLockWidget) {
		checkf(TargetLockWidgetClass, TEXT("TargetLockWidgetClass is none"));
		TargetLockWidget = CreateWidget<UTargetLockWidget>(GetPlayerControllerFromActorInfo(), TargetLockWidgetClass);
		if (TargetLockWidget) {
			TargetLockWidget->AddToViewport();
		}
	}
}
void UPlayerTargetLockAbility::SetTargetLockWidgetLoc()
{
	if (!TargetLockWidget || !CurrentLockActor) {
		CancelTargetLockAbility();
		return;
	}
	FVector2D ScreenPosition;
	UWidgetLayoutLibrary::ProjectWorldLocationToWidgetPosition(
		GetPlayerControllerFromActorInfo(),
		CurrentLockActor->GetActorLocation(),
		ScreenPosition,
		true
	);
	if (TargetLockBoxSIze == FVector2D::ZeroVector) {
		TargetLockBoxSIze.X = TargetLockWidget->TargetLockBox->GetWidthOverride();
		TargetLockBoxSIze.Y = TargetLockWidget->TargetLockBox->GetHeightOverride();
	}
	ScreenPosition -= TargetLockBoxSIze / 2.f;
	TargetLockWidget->SetPositionInViewport(ScreenPosition,false);
}
void UPlayerTargetLockAbility::OnTargetLockTick(float DeltaTime)
{
	if (!CurrentLockActor|| 
		UBladeFunctionLibrary::NativeDoesActorHaveTag(CurrentLockActor,BladeGameplayTags::Shared_Status_Dead)||
		UBladeFunctionLibrary::NativeDoesActorHaveTag(GetPlayerCharacterFromActorInfo(), BladeGameplayTags::Shared_Status_Dead)) {
		CancelTargetLockAbility();
		return;
	}
	SetTargetLockWidgetLoc();
}
AActor* UPlayerTargetLockAbility::GetNearestTargetFromAvailableActors(const TArray<AActor*>& InAvailableActors)
{
	float ClosestDistance=0.f;
	return UGameplayStatics::FindNearestActor(GetPlayerCharacterFromActorInfo()->GetActorLocation(), InAvailableActors, ClosestDistance);
}

Tick구현 결과.

AbilityTask로 구현한 TargetLock입니다.

Tick을 넣어서 위젯의 위치가 계속 갱신되도록 만들었습니다.

Designed by JB FACTORY