AbilitySystemComponent와 가장 먼저 상호작용하는 GamePlayAbility를 알아보도록 하겠습니다.
먼저 UGamePlayAbility를 상속받아 사용해야 하므로 UGamePlayAbility.h를 보면 중요한 함수들이라고 설명이 써져있는 것을 볼 수 있습니다.
CanActivateAbility(): 어빌리티를 활성화할 수 있는지 확인하는 함수입니다.
TryActivateAbility(): 어빌리티를 활성화하려고 시도합니다. CanActivateAbility()를 호출합니다. 입력 이벤트가 이 함수를 직접 호출할 수 있습니다. 실행 시 인스턴스화 논리 및 복제/예측 호출도 처리합니다.
CallActivateAbility(): 보호된 비가상 함수입니다. 일부 '사전 활성화' 작업을 수행한 다음 ActivateAbility()를 호출합니다.
ActivateAbility(): 어빌리티를 실제로 수행하는 함수입니다.이를 통해 어빌리티가 실행됩니다.
CommitAbility(): 자원/쿨다운 등을 커밋합니다. ActivateAbility()는 이 함수를 호출해야 합니다.
CancelAbility(): 외부 소스에서 어빌리티를 중단합니다.
EndAbility(): 어빌리티를 종료하기 위해 호출되는 것이 의도된 함수입니다.
언리얼 엔진에서는 UGameplayAbility_CharacterJump라는 UGamePlayAbility를 상속받은 함수를 제공해줍니다.
이를 분석하고 UGameplayAbility_CharacterJump를 AbilitySystemComponent에 적용해보도록 하겠습니다.
class GAMEPLAYABILITIES_API UGameplayAbility_CharacterJump : public UGameplayAbility
{
GENERATED_UCLASS_BODY()
public:
virtual bool CanActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags = nullptr, const FGameplayTagContainer* TargetTags = nullptr, OUT FGameplayTagContainer* OptionalRelevantTags = nullptr) const override;
virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* OwnerInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override;
virtual void InputReleased(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) override;
virtual void CancelAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateCancelAbility) override;
};
헤더파일을 확인해보면 위의 중요코드를 사용하는 것을 확인해 볼 수 있습니다.
void UGameplayAbility_CharacterJump::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
if (HasAuthorityOrPredictionKey(ActorInfo, &ActivationInfo))
{
if (!CommitAbility(Handle, ActorInfo, ActivationInfo))
{
return;
}
ACharacter * Character = CastChecked<ACharacter>(ActorInfo->AvatarActor.Get());
Character->Jump();
}
}
이 핸들은 특정 능력 인스턴스를 식별합니다. 게임플레이 어빌리티 시스템은 여러 인스턴스의 능력을 관리할 수 있으며, 이 핸들을 통해 해당 인스턴스를 구별할 수 있습니다.
이 구조체는 능력을 활성화하는 액터에 대한 정보를 담고 있습니다. 여기에는 액터의 컨트롤러, 폰, 스킬 시스템 컴포넌트 등이 포함됩니다. 이를 통해 능력을 사용할 때 관련된 액터의 정보를 쉽게 접근할 수 있습니다.
이 구조체는 능력이 어떻게 활성화되었는지에 대한 정보를 담고 있습니다. 예를 들어, 능력이 인풋에 의해 활성화되었는지, 자동으로 활성화되었는지 등의 정보를 포함합니다.
이 포인터는 능력을 트리거한 이벤트에 대한 추가 데이터를 포함할 수 있습니다. 이는 특정 이벤트가 능력을 트리거할 때 추가적인 컨텍스트 정보를 전달하기 위해 사용됩니다.
이를 통해 다양한 능력을 만들어 줄 수 있습니다.
bool UGameplayAbility::HasAuthorityOrPredictionKey(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo* ActivationInfo) const
{
return ActorInfo->AbilitySystemComponent->HasAuthorityOrPredictionKey(ActivationInfo);
}
bool UAbilitySystemComponent::HasAuthorityOrPredictionKey(const FGameplayAbilityActivationInfo* ActivationInfo) const
{
return ((ActivationInfo->ActivationMode == EGameplayAbilityActivationMode::Authority) || CanPredict());
}
namespace EGameplayAbilityActivationMode
{
enum Type : int
{
클라이언트 또는 서버가 이 능력을 활성화할 권한을 가지고 있는 상태를 나타냅니다.
Authority,
...
};
}
위와 같이 Actorinfo의 어빌리티시스템컴포넌트의 HasAuthorityOrPredictionKey()함수를 통해
ActivationInfo의 ActivationMode 가 Authority이면 true를 반환하는 것을 알 수 있습니다.
bool UGameplayAbility::CommitAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, OUT FGameplayTagContainer* OptionalRelevantTags)
{
// Last chance to fail (maybe we no longer have resources to commit since we after we started this ability activation)
if (!CommitCheck(Handle, ActorInfo, ActivationInfo, OptionalRelevantTags))
{
return false;
}
CommitExecute(Handle, ActorInfo, ActivationInfo);
// Fixme: Should we always call this or only if it is implemented? A noop may not hurt but could be bad for perf (storing a HasBlueprintCommit per instance isn't good either)
K2_CommitExecute();
// Broadcast this commitment
ActorInfo->AbilitySystemComponent->NotifyAbilityCommit(this);
return true;
}
- CommitCheck(): 어빌리티의 커밋 여부 확인
- CommitExecute(): CooldownEffect나 CostEffect가 있으면 이펙트를 Owner에게 적용
- 엑터의 어빌리티시스템컴포넌트에 해당어빌리티를 알림
마지막으로 ActorInfo->AvatarActor.Get()를 통해 Character->Jump()로 캐릭터 점프를 진행해주게 됩니다.
class SOULLIKE_API ASoulLikeCharacter : public ACharacter, public IAbilitySystemInterface
{
...
public:
UPROPERTY(EditAnywhere, Category = GAS)
TMap<int32, TSubclassOf<class UGameplayAbility>> StartInputAbilities;
}
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &ASoulLikeCharacter::GASInputPressed, 0);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ASoulLikeCharacter::GASInputReleased, 0);