게임플레이 어빌리티 시스템(GAS) 기본 설정 방법의 학습

  • 어빌리티 시스템 컴포넌트의 생성과 초기화
  • 게임플레이 어빌리티의 제작과 활용
  • 게임플레이 태그의 활용

다양한 구현 방법이 가진 장단점의 이해

 

 

목차

     

     


     

     

    ASC와 Gameplay Tag의 활용


     

    AbilitySystemComponent와 Gameplay Tag의 사용

     


     

     

    분수대 액터의 기획

     

    3초 마다 회전과 정지를 무한 반복하면서 동작하는 분수대

    회전 기능은 RotatingMovementComponent

     

    이를 어떻게 구현할 것인가?

    1.  전통적인 방법:  액터에 해당 기능을 구현
    2.  GAS를 사용한 방법: 게임플레이 어빌리티 시스템으로 구현
    3.  GAS + Gameplay Tag:  게임플레이 어빌리티 시스템에 게임플레이 태그를 부여해 구현

     

     

    #1 전통적인 방법:  액터에 해당 기능을 구현

     

     

    ABGASFountain.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Prop/ABFountain.h"
    #include "ABGASFountain.generated.h"
    
    UCLASS()
    class ARENABATTLEGAS_API AABGASFountain : public AABFountain, public IAbilitySystemInterface
    {
    	GENERATED_BODY()
    	
    public:
    	AABGASFountain();
    
    protected:
    	virtual void PostInitializeComponents() override;
    	virtual void BeginPlay() override;
    
    	virtual void TimerAction();
    
    protected:
    	UPROPERTY(VisibleAnywhere, Category=Movement)
    	TObjectPtr<class URotatingMovementComponent> RotatingMovement;
    	
    	UPROPERTY(EditAnywhere, Category=Timer)
    	float ActionPeriod;
    
    	FTimerHandle ActionTimer;
    };

     

     

    ABGASFountain.cpp

    더보기
    #include "Prop/ABGASFountain.h"
    #include "GameFramework/RotatingMovementComponent.h"
    #include "ArenaBattleGAS.h"
    
    AABGASFountain::AABGASFountain()
    {
    	RotatingMovement = CreateDefaultSubobject<URotatingMovementComponent>(TEXT("RotateMovement"));
    	ActionPeriod = 3.0f;
    }
    
    UAbilitySystemComponent* AABGASFountain::GetAbilitySystemComponent() const
    {
    	return ASC;
    }
    
    void AABGASFountain::PostInitializeComponents()
    {
    	Super::PostInitializeComponents();
    
    	RotatingMovement->bAutoActivate = false;
    	RotatingMovement->Deactivate();
    }
    
    void AABGASFountain::BeginPlay()
    {
    	Super::BeginPlay();
    
    	GetWorld()->GetTimerManager().SetTimer(ActionTimer, this, &AABGASFountain::TimerAction, ActionPeriod, true, 0.0f);
    }
    
    void AABGASFountain::TimerAction()
    {
    	ABGAS_LOG(LogABGAS, Log, TEXT("Begin"));
    
    	if (!RotatingMovement->IsActive())
    	{
    		RotatingMovement->Activate(true);
    	}
    	else
    	{
    		RotatingMovement->Deactivate();
    	}
    }

     

    실행화면

     

     

     

     


     

     

     

     

    Ability System Component(ASC) & Gameplay Ability(GA)

     


     

     

     

    어빌리티 시스템 컴포넌트 Ability System Component(ASC)

     

    Ability System Component (ASC)

    • 게임플레이 어빌리티 시스템을 관리하는 핵심 컴포넌트
    • 게임플레이 어빌리티 및 다양한 작업을 관리하고 처리하는 중앙 처리 장치
    • 액터에 단 하나만 부착할 수 있음
    • 액터는 부착된 AbilitySystemComponent(ASC)를 통해 게임플레이 어빌리티를 발동시킬 수 있음
    • AbilitySystemComponent(ASC)를 부착한 액터 사이에 GAS 시스템의 상호 작용이 가능해짐

     

     

    게임플레이 어빌리티 Gameplay Ability

     

    Gameplay Ability (GA)

    • AbilitySystemComponent(ASC)에 등록되어 발동시킬 수 있는 액션 명령
      • 공격, 마법, 특수 공격 등등
      • 간단한 액션 뿐만 아니라 상황에 따른 복잡한 액션 수행 가능
    • GA의 발동 과정
      • ASC에 어빌리티를 등록:  ASCGiveAbility 함수에 발동할 ' GA의 타입 '을 전달
        • 발동할 GA 타입 정보를 게임플레이 어빌리티 스펙(GameplayAbilitySpec)이라고 함
      • ASC에게 어빌리티를 발동하라고 명령:  ASCTryActivateAbility 함수에 발동할 ' GA의 타입 '을 전달
        • ASC에 등록된 타입이면 GA의 인스턴스가 생성
      • 발동된 GA에는 발동한 액터와 실행 정보가 기록됨
        • SpecHandle:  발동한 어빌리티에 대한 핸들
        • ActorInfo:  어빌리티의 소유자와 아바타 정보
        • ActivationInfo:  발동 방식에 대한 정보
    • GA의 주요 함수
    GA의 주요 함수  
      CanActivateAbility   어빌리티가 발동될 수 있는지 파악
      ActivateAbility   어빌리티가 발동될 때 호출
      CancelAbility   어빌리티가 취소될 때 호출
      EndAbility   스스로 어빌리티를 마무리할 때 호출

     


     

     

    #2 GAS를 사용한 방법: 게임플레이 어빌리티 시스템으로 구현

     

     

    ABGASFountain.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Prop/ABFountain.h"
    #include "AbilitySystemInterface.h"
    #include "ABGASFountain.generated.h"
    
    UCLASS()
    class ARENABATTLEGAS_API AABGASFountain : public AABFountain, public IAbilitySystemInterface
    {
    	GENERATED_BODY()
    	
    public:
    	AABGASFountain();
    
    	virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
    
    protected:
    	virtual void PostInitializeComponents() override;
    	virtual void BeginPlay() override;
    
    	virtual void TimerAction();
    
    protected:
    	UPROPERTY(VisibleAnywhere, Category=Movement)
    	TObjectPtr<class URotatingMovementComponent> RotatingMovement;
    	
    	UPROPERTY(EditAnywhere, Category=Timer)
    	float ActionPeriod;
    
    	UPROPERTY(EditAnywhere, Category=GAS)
    	TObjectPtr<class UAbilitySystemComponent> ASC;
    
    	FTimerHandle ActionTimer;
    };

    함수 추가

    • virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;

     

    변수 추가

    • UPROPERTY(EditAnywhere, Category=GAS)
      TObjectPtr<class UAbilitySystemComponent> ASC;

     

     

    ABGASFountain.cpp

    더보기
    #include "Prop/ABGASFountain.h"
    #include "GameFramework/RotatingMovementComponent.h"
    #include "ArenaBattleGAS.h"
    #include "AbilitySystemComponent.h"
    #include "GameplayAbilitySpec.h"
    #include "GA/ABGA_Rotate.h"
    
    AABGASFountain::AABGASFountain()
    {
    	ASC = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("ASC"));
    	RotatingMovement = CreateDefaultSubobject<URotatingMovementComponent>(TEXT("RotateMovement"));
    	ActionPeriod = 3.0f;
    }
    
    UAbilitySystemComponent* AABGASFountain::GetAbilitySystemComponent() const
    {
    	return ASC;
    }
    
    void AABGASFountain::PostInitializeComponents()
    {
    	Super::PostInitializeComponents();
    
    	RotatingMovement->bAutoActivate = false;
    	RotatingMovement->Deactivate();
    
    	ASC->InitAbilityActorInfo(this, this); // ASC 초기화
    
    	FGameplayAbilitySpec RotateSkillSpec(UABGA_Rotate::StaticClass());
    	ASC->GiveAbility(RotateSkillSpec);
    }
    
    void AABGASFountain::BeginPlay()
    {
    	Super::BeginPlay();
    
    	GetWorld()->GetTimerManager().SetTimer(ActionTimer, this, &AABGASFountain::TimerAction, ActionPeriod, true, 0.0f);
    }
    
    void AABGASFountain::TimerAction()
    {
    	ABGAS_LOG(LogABGAS, Log, TEXT("Begin"));
    
    	FGameplayAbilitySpec* RotateGASpec = ASC->FindAbilitySpecFromClass(UABGA_Rotate::StaticClass());
        if (!RotateGASpec)
        {
            ABGAS_LOG(LogABGAS, Error, TEXT("No Rotate Spec Found!"));
            return;
        }
    
        if (!RotateGASpec->IsActive()) // 활성화되어 있지 않다면
        {
            ASC->TryActivateAbility(RotateGASpec->Handle); // Handle를 지정하여활성화
        }
        else
        {
            ASC->CancelAbilityHandle(RotateGASpec->Handle); // 비활성화하여 취소
        }
    }

    헤더추가

    • #include "AbilitySystemComponent.h"
    • #include "GameplayAbilitySpec.h"
    • #include "GA/ABGA_Rotate.h"

     

    함수 구현 변경

    • void AABGASFountain::PostInitializeComponents()
      • ASC->InitAbilityActorInfo(this, this); 
        FGameplayAbilitySpec RotateSkillSpec(UABGA_Rotate::StaticClass());
        ASC->GiveAbility(RotateSkillSpec);
    • void AABGASFountain::TimerAction()

     

     

     

    새 C++ 클래스 - GameplayAbility - ABGA_Rotate 생성

     

     

    ABGA_Rotate.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Abilities/GameplayAbility.h"
    #include "ABGA_Rotate.generated.h"
    
    UCLASS()
    class ARENABATTLEGAS_API UABGA_Rotate : public UGameplayAbility
    {
    	GENERATED_BODY()
    	
    public:
    	virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override;
    
    	virtual void CancelAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateCancelAbility) override;
    };

     

     

    ABGA_Rotate.cpp

    더보기
    #include "GA/ABGA_Rotate.h"
    #include "GameFramework/RotatingMovementComponent.h"
    
    void UABGA_Rotate::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
    {
    	Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);
    
    	AActor* AvatarActor = ActorInfo->AvatarActor.Get();
    	if (AvatarActor)
    	{
    		URotatingMovementComponent* RotatingMovement = Cast<URotatingMovementComponent>(AvatarActor->GetComponentByClass(URotatingMovementComponent::StaticClass()));
    		if (RotatingMovement)
    		{
    			RotatingMovement->Activate(true);
    		}
    	}
    }
    
    void UABGA_Rotate::CancelAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateCancelAbility)
    {
    	Super::CancelAbility(Handle, ActorInfo, ActivationInfo, bReplicateCancelAbility);
    
    	AActor* AvatarActor = ActorInfo->AvatarActor.Get();
    	if (AvatarActor)
    	{
    		URotatingMovementComponent* RotatingMovement = Cast<URotatingMovementComponent>(AvatarActor->GetComponentByClass(URotatingMovementComponent::StaticClass()));
    		if (RotatingMovement)
    		{
    			RotatingMovement->Deactivate();
    		}
    	}
    }

     

     

    실행화면

     

     

     

     

     

     


     

     

     

    Gameplay Tag

     


     

     

    게임플레이 태그 Gameplay Tag

     

    Gameplay Tag

    • 게임플레이 어빌리티 시스템과 독립적으로 사용 능
    • FName으로 관리되는 경량의 표식 데이터
      • 액터나 컴포넌트에 지정했던 태그와 다른 데이터
    • 프로젝트 설정에서 별도로 게임플레이 태그를 생성하고 관리할 수 있음
      • 결과는 DefaultGameplayTags.ini 파일에 저장
    • 계층 구조로 구성되어 있어 체계적인 관리 가능
      • Actor.Action.Rotate: 행동에 대한 태그
      • Actor.State.IsRotating: 상태에 대한 태그
    • 게임플레이 태그들의 저장소: GampleyTagContainer
      • 계층 구조를 지원하는 검색 기능 제공
       
      HasTagExact   컨테이너에 A.1태그가 있는 상황에서 A로 찾으면 false
      HasAny   컨테이너에 A.1태그가 있는 상황에서 A와 B로 찾으면 true
      HasAnyExact   컨테이너에 A.1태그가 있는 상황에서 A와 B로 찾으면 false
      HasAll   컨테이너에 A.1태그와 B.1태그가 있는 상황에서 A와 B로 찾으면 true
      HasAllExact   컨테이너에 A.1태그와 B.1태그가 있는 상황에서 A와 B로 찾으면 false

     


     

     

     

    게임플레이 어빌리티(Gameplay Ability)와 게임플레이 태그(Gameplay Tag)

     

    게임플레이 어빌리티(Gameplay Ability)에 부착한 태그

    • 어빌리티에 지정한 태그 ( AbiltiyTags 태그 컨테이너 )

     

    게임플레이 어빌리티(Gameplay Ability)에 대해 다양한 실행 조건의 설정

       
      CancelAbilitiesWithTag  태그 컨테이너  Tag로 어빌리티 취소
      BlockAbilitiesWithTag    태그 컨테이너  Tag로 어빌리티 차단
      ActivationOwnedTags    태그 컨테이너  어빌리티 실행시 태그 설정 
      ActivationRequiredTags 태그 컨테이너  Tag가 있어야만 어빌리티 실행
      ActivationBlockedTags 태그 컨테이너  Tag가 있으면 어빌리티 실행 차단
      SourceRequiredTags     태그 컨테이너  시전자가 Tag가 있어야 어빌리티 실행 
      SourceBlockedTags      태그 컨테이너  시전자에 Tag가 있으면 어빌리티 차단
      TargetRequiredTags      태그 컨테이너  시전 대상에 Tag가 있어야 어빌리티 실행 
      TargetBlockedTags       태그 컨테이너  시전 대상에 태그가 있으면 어빌리티 차단 

     

     

    블루프린트와 조합하여 특정 게임플레이 어빌리티에 대한 의존성을 없애고

    게임플레이 태그를 중심으로 게임 로직을 전개 가능

     


     

     

     

    #3 GAS + Gameplay Tag:  게임플레이 어빌리티 시스템에 게임플레이 태그를 부여해 구현

     

    프로젝트 세팅에 Gameplay Tag 추가

     

     

     

     

    ABGASFountain.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Prop/ABFountain.h"
    #include "AbilitySystemInterface.h"
    #include "ABGASFountain.generated.h"
    
    UCLASS()
    class ARENABATTLEGAS_API AABGASFountain : public AABFountain, public IAbilitySystemInterface
    {
    	GENERATED_BODY()
    	
    public:
    	AABGASFountain();
    
    	virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
    
    protected:
    	virtual void PostInitializeComponents() override;
    	virtual void BeginPlay() override;
    
    	virtual void TimerAction();
    
    protected:
    	UPROPERTY(VisibleAnywhere, Category=Movement)
    	TObjectPtr<class URotatingMovementComponent> RotatingMovement;
    	
    	UPROPERTY(EditAnywhere, Category=Timer)
    	float ActionPeriod;
    
    	UPROPERTY(EditAnywhere, Category=GAS)
    	TObjectPtr<class UAbilitySystemComponent> ASC;
    
    	UPROPERTY(EditAnywhere, Category=GAS)
    	TArray<TSubclassOf<class UGameplayAbility>> StartAbilities;
    
    	FTimerHandle ActionTimer;
    };

     

     

    ABGASFountain.cpp

    더보기
    #include "Prop/ABGASFountain.h"
    #include "GameFramework/RotatingMovementComponent.h"
    #include "ArenaBattleGAS.h"
    #include "AbilitySystemComponent.h"
    #include "GameplayAbilitySpec.h"
    #include "Tag/ABGameplayTag.h"
    #include "Abilities/GameplayAbility.h"
    //#include "GA/ABGA_Rotate.h"
    
    AABGASFountain::AABGASFountain()
    {
    	ASC = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("ASC"));
    	RotatingMovement = CreateDefaultSubobject<URotatingMovementComponent>(TEXT("RotateMovement"));
    	ActionPeriod = 3.0f;
    }
    
    UAbilitySystemComponent* AABGASFountain::GetAbilitySystemComponent() const
    {
    	return ASC;
    }
    
    void AABGASFountain::PostInitializeComponents()
    {
    	Super::PostInitializeComponents();
    
    	RotatingMovement->bAutoActivate = false;
    	RotatingMovement->Deactivate();
    
    	ASC->InitAbilityActorInfo(this, this);
    
    	for (const auto& StartAbility : StartAbilities)
    	{
    		FGameplayAbilitySpec StartSpec(StartAbility);
    		ASC->GiveAbility(StartSpec);
    	}
    }
    
    void AABGASFountain::BeginPlay()
    {
    	Super::BeginPlay();
    
    	GetWorld()->GetTimerManager().SetTimer(ActionTimer, this, &AABGASFountain::TimerAction, ActionPeriod, true, 0.0f);
    }
    
    void AABGASFountain::TimerAction()
    {
    	ABGAS_LOG(LogABGAS, Log, TEXT("Begin"));
    
    	FGameplayTagContainer TargetTag(ABTAG_ACTOR_ROTATE);
    
    	if(!ASC->HasMatchingGameplayTag(ABTAG_ACTOR_ISROTATING))
    	{
    		ASC->TryActivateAbilitiesByTag(TargetTag);
    	}
    	else
    	{
    		ASC->CancelAbilities(&TargetTag);
    	}
    }

     

     

     

     

    ABGA_Rotate.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Abilities/GameplayAbility.h"
    #include "ABGA_Rotate.generated.h"
    
    UCLASS()
    class ARENABATTLEGAS_API UABGA_Rotate : public UGameplayAbility
    {
    	GENERATED_BODY()
    	
    public:
    	UABGA_Rotate();
    	
    public:
    	virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override;
    
    	virtual void CancelAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateCancelAbility) override;
    };

     

    생성자 추가

    • UABGA_Rotate();

     

     

    ABGA_Rotate.cpp

    더보기
    #include "GA/ABGA_Rotate.h"
    #include "GameFramework/RotatingMovementComponent.h"
    #include "Tag/ABGameplayTag.h"
    
    UABGA_Rotate::UABGA_Rotate()
    {
    	AbilityTags.AddTag(ABTAG_ACTOR_ROTATE);
    	ActivationOwnedTags.AddTag(ABTAG_ACTOR_ISROTATING);
    }
    
    void UABGA_Rotate::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
    {
    	Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);
    
    	AActor* AvatarActor = ActorInfo->AvatarActor.Get();
    	if (AvatarActor)
    	{
    		URotatingMovementComponent* RotatingMovement = Cast<URotatingMovementComponent>(AvatarActor->GetComponentByClass(URotatingMovementComponent::StaticClass()));
    		if (RotatingMovement)
    		{
    			RotatingMovement->Activate(true);
    		}
    	}
    }
    
    void UABGA_Rotate::CancelAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateCancelAbility)
    {
    	Super::CancelAbility(Handle, ActorInfo, ActivationInfo, bReplicateCancelAbility);
    
    	AActor* AvatarActor = ActorInfo->AvatarActor.Get();
    	if (AvatarActor)
    	{
    		URotatingMovementComponent* RotatingMovement = Cast<URotatingMovementComponent>(AvatarActor->GetComponentByClass(URotatingMovementComponent::StaticClass()));
    		if (RotatingMovement)
    		{
    			RotatingMovement->Deactivate();
    		}
    	}
    }

     

    헤더 추가

    • #include "Tag/ABGameplayTag.h"

    생성자 정의

    • UABGA_Rotate::UABGA_Rotate()

     


     

     

    실행화면