Start / End AbilityState

chan·2025년 4월 12일

Unreal Engine5

목록 보기
3/3
post-thumbnail

GameplayAbility에서 AbilityTask_StartAbilityTask를 종종 쓰는데, 어떤 로직으로 구성돼있는지 알아보자.

주요 코드를 먼저 보자

UAbilityTask_StartAbilityState::UAbilityTask_StartAbilityState(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
	bEndCurrentState = true;
	bWasEnded = false;
	bWasInterrupted = false;
}

UAbilityTask_StartAbilityState* UAbilityTask_StartAbilityState::StartAbilityState(UGameplayAbility* OwningAbility, FName StateName, bool bEndCurrentState)
{
	UAbilityTask_StartAbilityState* Task = NewAbilityTask<UAbilityTask_StartAbilityState>(OwningAbility, StateName);
	Task->bEndCurrentState = bEndCurrentState;
	return Task;
}

void UAbilityTask_StartAbilityState::Activate()
{
	if (Ability)
	{
		if (bEndCurrentState && Ability->OnGameplayAbilityStateEnded.IsBound())
		{
			Ability->OnGameplayAbilityStateEnded.Broadcast(NAME_None);
		}

		EndStateHandle = Ability->OnGameplayAbilityStateEnded.AddUObject(this, &UAbilityTask_StartAbilityState::OnEndState);
		InterruptStateHandle = Ability->OnGameplayAbilityCancelled.AddUObject(this, &UAbilityTask_StartAbilityState::OnInterruptState);
	}
}

void UAbilityTask_StartAbilityState::OnDestroy(bool AbilityEnded)
{
	using namespace UE::AbilitySystem::AbilityTask_StartAbilityState;

	// Unbind delegates so this doesn't get recursively called
	if (Ability)
	{
		Ability->OnGameplayAbilityCancelled.Remove(InterruptStateHandle);
		Ability->OnGameplayAbilityStateEnded.Remove(EndStateHandle);
	}

	if (bWasInterrupted && OnStateInterrupted.IsBound())
	{
		if (CustomShouldBroadcastAbilityTaskDelegates(Ability))
		{
			OnStateInterrupted.Broadcast();
		}
	}
	else if ((bWasEnded || AbilityEnded) && OnStateEnded.IsBound())
	{
		if (CustomShouldBroadcastAbilityTaskDelegates(Ability))
		{
			OnStateEnded.Broadcast();
		}
	}

	// This will invalidate the task so needs to happen after callbacks
	Super::OnDestroy(AbilityEnded);
}

void UAbilityTask_StartAbilityState::OnEndState(FName StateNameToEnd)
{
	// All states end if 'NAME_None' is passed to this delegate
	if (StateNameToEnd.IsNone() || StateNameToEnd == InstanceName)
	{
		bWasEnded = true;

		EndTask();
	}
}

void UAbilityTask_StartAbilityState::OnInterruptState()
{
	bWasInterrupted = true;
}

void UAbilityTask_StartAbilityState::ExternalCancel()
{
	bWasInterrupted = true;

	Super::ExternalCancel();
}

분석해보면
1. AbilityTask_StartAbilityState를 통해 AbilityTask를 동적 생성하여 반환한다.
2. State Name을 Instance Name에 설정.
3. Activate에서 NAME_None을 Broadcast하는 이유는 기본적으로 동작 방식이 한번에 하나의 AbilityState만 존재하는 구조로 설계하였기 때문.
4. 만약 여러 개의 AbilityState를 쓰고 싶다면 bEndCurrentState를 false로 설정해야 함.
(반드시 고유 State Name으로 설정해야 각각의 AbilityState를 관리 할 수 있음.)
5. End Ability State 함수를 호출하여 AbilityTask를 끝냄.
6. Optional State Name to End와 StartAblityState AbilityTask의 Instance Name이 일치하거나 NAME_None인 경우 EndTask.
7. 각 Task가 자신을 해당 GameplayAbility에 Delegate로 바인딩하고 있기 때문에, 동일 Ability 내에서 중복된 InstanceName을 가진 Task가 여러 개 있다면 같은 StateName을 기준으로 동시에 EndTask 된다.

profile
게임을 사랑하는 개발자 / Unreal Engine5 Client Programmer

0개의 댓글