다양한 기록

[Unreal GAS] GAS의 시작 본문

카테고리 없음

[Unreal GAS] GAS의 시작

라구넹 2025. 1. 11. 22:04

어빌리티 시스템으로 회전하는 분수대 구현하기 

1. 액터에 넣기

2. GAS로 구현

3. GAS에 게임플레이 태그를 부여해 구현

 

1. 액터에 넣기

// Fill out your copyright notice in the Description page of Project Settings.


#include "Prop/ABGASFountain.h"
#include "GameFramework/RotatingMovementComponent.h"
#include "ArenaBattleGAS.h"

AABGASFountain::AABGASFountain()
{
	RotatingMovement = CreateDefaultSubobject<URotatingMovementComponent>(TEXT("RotatingMovement"));
	ActionPeriod = 3.0f;
}

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->SetActive(true);
	}
	else
	{
		RotatingMovement->Deactivate();
	}
}

RotatingComponent 추가하고

타이머 설정해주면 알아서 돌아감

 

 

 

2. GAS로 구현

* 어빌리티 시스템 컴포넌트

- 줄여서 ASC(Ability System Component)

- 게임플레이 어빌리티 시스템을 관리하는 핵심 컴포넌트

- 게임플레이 어빌리티 및 다양한 작업을 관리하고 처리하는 중앙 처리 장치

- 액터에 단 하나만 부착 가능

- 액터는 부착된 ASC를 통해 게임플레이 어빌리티를 발동 가능

- ASC를 부착한 액터 사이에 GAS 시스템의 상호작용이 가능해짐

 

* 게임플레이 어빌리티

- 줄여서 GA(Gameplay Ability)

- ASC에 등록되어 발동시킬 수 있는 액션 명령

- GA의 발동 과정

    - ASC에 어빌리티를 등록(ASC의 GiveAbility 함수에 발동할 GA의 타입을 전달)

         - 발동할 GA 타입 정보를 게임플레이 어빌리티 스펙(GameplayAbilitySpec)이라고 함

    - ASC에 어빌리티를 발동하라고 명령(ASC의 TryActivateAbility 함수에 발동할 GA의 타입을 전달)

         - ASC에 등록된 타입이면 GA의 인스턴스가 생성

    - 발동된 GA에는 발동한 액터와 실행 정보가 기록됨

         - SpecHandle : 발동된 어빌리티에 대한 핸들

         - ActorInfo : 어빌리티의 소유자와 아바타 정보

         - ActivationInfo : 발동 방식에 대한 정보

- GA의 주요 함수

    - CanActivateAbility : 어빌리티가 발동될 수 있는지 파악

    - ActivateAbility : 어빌리티가 발동될 때 호출

    - CancleAbility : 어빌리티가 취소될 때 호출

    - EndAbility : 스스로 어빌리티를 마무리할 때 호출

 

* 타입 == GA의 클래스 정보를 Spec 구조체에 넣은 것

 

소유하는 액터는 AbilitySystemInterface를 구현하도록 함

GetAbilitySystemComponent랑 ASC 변수 선언 

오너는 실제 작업이 일어나는 액터,

아바타는 실제 액션을 수행하진 않지만 비주얼만 수행해주는 액터

GA 만들기

// Fill out your copyright notice in the Description page of Project Settings.


#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("RotatingMovement"));
	ActionPeriod = 3.0f;
}

UAbilitySystemComponent* AABGASFountain::GetAbilitySystemComponent() const
{
	return ASC;
}

void AABGASFountain::PostInitializeComponents()
{
	Super::PostInitializeComponents();

	RotatingMovement->bAutoActivate = false;
	RotatingMovement->Deactivate();
	ASC->InitAbilityActorInfo(this, this);

	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, Log, TEXT("No Rotate Spec Found!"));
		return;
	}

	if ( !RotateGASpec->IsActive() )
	{
		ASC->TryActivateAbility(RotateGASpec->Handle);
	}
	else
	{
		ASC->CancelAbilityHandle(RotateGASpec->Handle);
	}

	/*if ( !RotatingMovement->IsActive() )
	{
		RotatingMovement->SetActive(true);
	}
	else
	{
		RotatingMovement->Deactivate();
	}*/


}
// Fill out your copyright notice in the Description page of Project Settings.


#include "GA/ABGA_Rotate.h"
#include "GameFramework/RotatingMovementComponent.h"

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* RotatingComponent = Cast<URotatingMovementComponent>(AvatarActor->GetComponentByClass(URotatingMovementComponent::StaticClass()));

		if ( RotatingComponent )
		{
			RotatingComponent->Deactivate();
		}
	}
}

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* RotatingComponent = Cast<URotatingMovementComponent>(AvatarActor->GetComponentByClass(URotatingMovementComponent::StaticClass()));

		if (RotatingComponent)
		{
			RotatingComponent->Activate(true);
		}
	}
}

GA를 부여해주는 코드

똑같이 작동함

 

3. 게임플레이 태그 활용

* 게임플레이 태그

- FName으로 관리되는 경량의 표식 데이터 (액터나 컴포넌트에 지정했던 태그와 다른 데이터)

- 프로젝트 설정에서 별도로 게임플레이 태그를 생성 및 관리 가능 (DefaultGameplayTags.ini에 저장됨)

- 계층 구조로 구성되어 있어 체계적인 관리 가능

ex) Actor.Action.Rotate : 행동에 대한 태그 / Actor.State.IsRotating : 상태에 대한 태그

- 게임플레이 태그들의 저장소 : GameplayTagContainer

    -  계층 구조를 지원하는 검색 가능 제공

    - 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

- 게임플레이 어빌리티 시스템과 독립적으로 사용 가능

 

중요한 거

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

어빌리티 실행 시 태그 설정 (ActivationOwnedTags 태그 컨테이너)

프로젝트 세팅에서 태그 설정 가능

계층구조

ini 파일에도 잘 들어가 있음

 

태그 가져오기

정의해둬서 편하게 쓸 것임

 

어빌리티 가서 태그 설정

대표 태그 설정하고, 액티베이션 될 때 생기는 태그 설정

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);
	}

}

어빌리티 주고 뺐고는 IsRotating 태그 가지는지로 체크

대표 태그로 어빌리티 액티베이트 확인 가능

 

클래스 정보 담을 어레이 만들고

* 어차피 GA들은 다 저거 상속받아야 하니까 저렇게만 해두면 됨

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);
	}
}

초기화할 때 스펙 만들어서 넣어주면 됨

이러면 의존성을 떨어뜨릴 수 있음

저기 StartAbilities 넣어주는 건 에디터에서 해주면 됨

 

블루프린트 만들어주고

 

거기서 설정

 

이제 실행하면 정상작동 확인 가능