언리얼 - GAS(Game Ability System)

안영욱·2024년 11월 21일
0

언리얼

목록 보기
8/8
post-thumbnail

언리얼 프로젝트를 진행하고 공부하면서 기억하면 좋을만한 내용들을 메모하였습니다.


[이글은 2024 언리얼패스트 GAS 프로토타입 제작사례 영상을 토대로 작성하였습니다.]

Youtube Link : https://youtu.be/Jd8KjxNe4EU?si=2CwY7jGAvLcTGGzz
ppt링크 : https://epiclounge.co.kr/v3/contents/replay_view.php?idx=1322

GAS(Game Ability System)란?

언리얼 엔진의 Game Ability System 이하 GAS
액터간 상호작용 메커니즘을 구현할수있는 고도로 유연한 프레임워크 입니다.
확장성과 유연성이 뛰어나 게임장르에맞게 수정가능하며 멀티플레이 동기화문제를 최소화 시킬수 있습니다.

1. 고도로 유연한 프레임워크

능력, 상태, 어트리 뷰트를 구축하고 복잡한 상호작용과 시작적인 효과를 유연하게 관리할 수 있는 시스템

2. 손쉬운 구성과 재사용

GAS 는 다양한 능력을 쉽게 구성하고 재사용할 수 있도록 도와주며, 복잡한 능력을 구현할 수 있습니다.

3. 뛰어난 확장성과 유연성

확장성과 유연성 뛰어나 다양한 게임 장르에 맞게 적용 가능하며, 멀티플레이 게임에서의 동기화 문제를 최소화 할 수 있습니다.


GAS를 사용하는 이유

일반적으로 캐릭터의 공격이나 기술을 구현하는것은 캐릭터 클래스에서 구현하는것이 가장 간단하고 쉬울것입니다
하지만 기획이 바뀌거나 수정사항이 발생할때 유지보수의 어려움과 여러가지 사이드 이펙트가 발생할수 있습니다

GAS를 사용하면 이런 부분을 모듈화 하여 유지보수에 체계적이고 안정적으로 사용할수 있고
대부분게임에 필요한 데이터(스텟시스템)를 제공해주어 편리하게 사용할수 있습니다.
포트나이트, 파라곤과같은 AAA게임에 적용하여 안정성또한 검증되었습니다.

1. 복잡한 Ability 관리

게임 내 캐릭터가 사용할 액티브, 패시브 능력을 체계적으로 관리
활성화 및 비활성화, Cooldown 및 Cost를 쉽게 구현

2. 구조화된 Attribute Set

Attribute를 이용해 버프나 디버프를 유연하게 적용가능

3. 체계적이면서 유연한 상호작용

GameplayEffect Class를 활용하여게임 내 액터 간의 상호작용을 전달

4. 성능 최적화

에픽게임즈에서 제작한 프레임워크로 대규모 게임에서도 안정적인 성능을 제공

5. 개발 생산성 향상

모듈화 되어 있어 재사용성과 확장성이 뛰어나 복잡한 게임플레이 메커니즘을 빠르게 프로토타이핑


GAS 구성요소

GamePlayAbilitySystem 구성요소는 크게 5가지로 나뉩니다.

1. Ability System Component

Ability, Attibute Set, GamePlayEffect를 관리하는 컴포넌트
Ability SystemComponent간의 상호작용

2. Ability

캐릭터가 수행할 수 있는 행동

3. GamePlayEffect

캐릭터에 적용되는 상호작용 및 효과 정의

4. Attribute Set

캐릭터 스탯값의 모음

5. GamePlayCue

시작적, 청각적 피드백


GAS 활성화 방법

  • Edit -> Plugins
  • GamePlay카테고리 -> GamePlayAbilities 플러그인 활성화

추가적으로 게임플레이 전반적으로 GamePlayTag도 사용된다.


GamePlayTag

GamePlayTag

  • FName을 가지는 구조체로
  • GamePlayManager에서 관리됨
  • GamePlayTagsManager에의해 관리되는
    GamePlayTagNode를 이용하여 태그일치를 확인함
// GamePlayTag를 가져오는 방법
FGamePlayTag StateBurn = FGameplayTag::RequestGameplayTag(FName(TEXT("State.Charater.Burned")));

GamePlayTag Manage 방법

  • Edit -> Project Setting창열기
  • Project
    -> GamePlayTags
    -> GamePlayList
    -> Manage GameplayTags ...

GamePlayTag 특징

계층적 트리구조로 상태를 유연하게 관리할수 있다는 큰 특징을 가지고
Enum이나 Bool기반이 아닌 Tag를 사용하여 다양한 상태를 표현할 수 있으며,
상태를 사람이 이해하기 쉬운 문자로 나타낼 수 있습니다.

1. 계층적 트리구조

태그는 계층적 트리 구조로 구성됩니다.

  • State.Character.Element.Fire
  • State.Character.Element.Warter

2. 상태 추적 및 관리

캐릭터의 상태를 유연하게 관리 할 수있습니다.

  • State.Character.Alive
  • State.Character.Dead

3. 관리 용이성

다양한 상태를 태그로 관리함으로써 코드의 복잡성을 줄이고, 쉽게 확장할 수 있습니다.


Gameplay Tag Container

Gameplay Tag Container는 여러개의 Gameplay Tag를 가지고있는 구조체로
컨테이너 내의 태그를 추가, 삭제할수 있으며
컨테이너간 태그를 비교할수 있습니다.

FGameplayTagContainer TagContainer;
// TagContainer에 태그를 추가하는 방법
TagContainer.AddTag(FGamePlayTag::RequestGameplayTag(FName"State.Character.Element.Burned")));
TagContainer.AddTag(FGamePlayTag::RequestGameplayTag(FName"State.Character.Element.Wet")));
  • TagContiner는 내부적으로 명시적 태그부모 태그의 모음으로 구성되어 있습니다.
    명시적 태그 : State.Character.Element.Burend, State.Character.Element.Wet
    부모 태그: State.Character.Element, State.Character, State
    2개의 명시적태그, 3개의 부모태그로 구성될수 있습니다.

Gameplay Tag 주요함수

출처 : https://youtu.be/Jd8KjxNe4EU?si=2CwY7jGAvLcTGGzz

  • Tag와 Tag 비교함수
  • Tag와 TagContainer 비교함수
  • TagContainer와 TagContainer 비교함수

Has

Tag를 서로 비교하거나 컨테이너에서 찾는 방법들입니다.

HasTag

Tag Container에서 단일 GamePlay Tag의 부분적 포함유무
{"Insect.Ant" ,"Mammal.Cay"}.HasTag("Insect") -> true
{"Insect, Mammal"}.HasTag("Insect.Ant") -> false

컨테이너에 찾고자하는 태그가 부분적으로 존재한다면 true를 반환합니다.

HasTagExact

Tag Container에서 단일 Gameplay Tag의 명시적 포함 유무
{"Insect.Ant", "Mammal.Cat"}.HasTagExact("Insect.Ant") -> true
{"Insect.Ant", "Mammal.Cat"}.HasTagExact("Insect") -> false

컨테이너에 찾고자하는 태그가 명시적으로 존재할때만 true를 반환합니다.

HasAll

Tag Container에 모든 Gameplay Tag의 부분적 포함유무
{"Insect.Ant", "Mammal.Cat"}.HasAll({"Insect","Mammal"})-> true
{"Insect.Ant", "Mammal.Cat"}.HasAll({"Insect","Bird"}) -> false

컨테이너 와 컨테이너를 비교해 모두 부분적으로 일치하는 경우에만 true를 반환합니다.

HasAllExact

Tag Container에 모든 Gameplay Tag의 명시적 포함유무
{"Insect.Ant", "Mammal.Cat"}.HasAllExact({"Insect","Mammal"})-> false
{"Insect.Ant", "Mammal.Cat"}.HasAllExact({"Insect.ant"})-> true
{"Insect.Ant", "Mammal.Cat"}.HasAllExact({"Insect.ant, Bird.Sparrow"})-> false

컨테이너와 컨테이너를 비교해 모두 명시적으로 일치하는 경우만 true를 반환합니다.

HasAny

Tag Container에서 어떤것 하나라도 Gameplay Tag의 부분적 포함 유무
{"Insect.Ant", "Mammal.Cat"}.HasAny({"Insect","Bird"})-> true
{"Insect.Ant", "Mammal.Cat"}.HasAny({"Reptile","Bird"})-> false

컨테이너와 컨테이너를 비교해 어떤것 하나라도 부분적으로 일치하는경우 true를 반환합니다.

HasAnyExact

Tag Container에서 어떤것 하나라도 Gameplay Tag의 명시적 포함 유무
{"Insect.Ant", "Mammal.Cat"}.HasAnyExact({"Insect","Bird"})-> false
{"Insect.Ant", "Mammal.Cat"}.HasAnyExact({"Insect.Ant","Bird.Sparrow"})-> true

컨테이너와 컨테이너를 비교해 어떤것 하나라도 명시적으로 일치하는경우 true를 반환합니다.


GamePlay Tag Query

복잡한 논리적 연산을위해 GamePlay Tag Query 라는것이 존재합니다.
AND, OR, NOT을 이용해 조건을 조합하고 이를이용해 캐릭터의 복잡한 상태를 구현할수 있습니다.

  • 복잡한 논리적 연산을 수행하기위해 태그를 쿼리하는 방법
  • AND, OR, NOT 연산자를 사용하여 조건을 조합하고,
    태그컨테이너가 이 조건에 맞는지 여부를 True또는 False로 반환함
    예) (캐릭터 독중독) 이면서 (캐릭터가 죽지않는상태)를 구현

Query C++예제

FGameplayTag PoisonTag = FGamePlayTag::ReQuestGamePlayTag(FName"State.Character.Poison"));
FGameplayTag DeadTag = FGamePlayTag::ReQuestGamePlayTag(FName"State.Character.Dead"));

FGamePlayTagQuery TagQuery;

TagQuery.Build(FGameplayTagQueryExpression() // 쿼리빌드 
	.AllExprMatch() // 하위조건이 모드 true인지 확인
	.AddExpr(FGameplayTagQueryExpression().AllTagsMatch().AddTag(PoisonTag)) // Posion 태그가 포함되어있어야함
	.AddExpr(FGameplayTagQueryExpression().NoTagsMatch().AddTag(DeadTag))); // Dead 태그가 없어야함

C++ Native GameplayTag추가

C++에서 Native로 태그를 추가할수있습니다.
아래 예제는 Lyra에도 존재한다고합니다

  • 태그 시스템에서 컴파일 타임에 정의된 태그로 등록할수 있습니다.
  • C++에서 정의된 태그를 사용할 수 있습니다.
// .h
UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Mammal_Cat);
// .cpp
UE_DEFINE_GAMEPLAY_TAG(TAG_Mammal_Cat, "Mammal.Cat");

UE_DEFINE_GAMEPLAY_TAG_COMMENT(TAG_Mammal_Dog, "Mammal.Dog", "Dog in the Mammal category");

UE_DEFINE_GAMEPLAY_TAG_STATIC(TAG_Mammal_Elephant, "Mammal.Elephant");

ASC(Ability System Component)란?

ASC(Ability System Component) 는 GAS에서 핵심적인 구성요소입니다
Ability를 보유하고 관리하며 Attribute Set을 가지고있으며
Component간 상호작용을 할 수 있습니다.

  • Gameplay Ability System의 핵심 구성 요소
  • 캐릭터가 보유하는 Ability(기술)를 관리
  • 캐릭터의 Attribute Set(속성모음)을 관리
  • 캐릭터에 적용되는 GameplayEffect(효과)를 관리
  • Gameplay Tag를 이용하여 상태표현
  • Ability System Component간 상호작용

ASC추가 방법

ASC를 사용하기위해서는 Actor에 IAbilitySystemInterface를 구현해야합니다

// .h
#include "AbilitySystemInterface.h" // 인터페이스 헤더추가

class AMyActor : Public AActor, Public IAbilitySystemInterface
{
	UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Abilities")
    UAbilitySystemComponent* AbilitySystemComponent;
    virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
// .cpp
UAbilitySystemComponent* AMyActor::GetAbilitySystemComponent() const
{
	return AbilitySystemComponent;
}

GA(Gameplay Ability)란?

어빌리티는 행동과 동작을 구현하는데 유용합니다
게임플레이 어빌리티 클래스를 상속받아 구현하며 Enhanced Input과 연동하여 사용할수 있습니다.
내부적으로 활성화시 Tag를 부여할수 있으며
CanActivateAbility함수를 통해 실행가능 여부도 알수있습니다.

  • 캐릭터가 수행할 수 있는 특정 행동이나 동작 정의
  • Gameplay Ability 클래스를 상속하여 구현
  • Enhanced Input과 커스텀하여 연동
  • 활성화 시 태그 부여
  • Can Activate Ability함수를 통한 실행가능 판단
  • Ability Task를 활용한 지연되는 로직 제작 용이

GA를 이용한 구현가능 능력

  • 점프
  • 포션 사용
  • 검을 휘두르는 공격
  • 총을 이용한 총알 발사
  • 탄창 재장전
  • 마법 사용
  • 액터와 상호작용

GA생성 방법

  • Content Browser
    -> Gameplay
    -> GameplyAbility Blueprint

  • C++에서는 GameplayAbility class를 상속하여 제작할수 있습니다.

UCLASS()
class ABILITYSAMPLE_API UMyFameplayAbility : public UGameplayAbility
{
	GENERATED_BODY()
};

간단한 Ability 생성 예제

출처 : https://youtu.be/Jd8KjxNe4EU?si=2CwY7jGAvLcTGGzz

  • Event Activate Ability
    Ability가 활성화될 때 호출

  • CommitAbility
    Ability를 Commit하여 필요한 자원(Attribute - 마나, 기력 등)이 소비되고 Cooldown이 시작됩니다.
    정상적으로 실행되면 내부적으로 Event CommitExcute가 실행됩니다.

  • PlayMontage And Wait
    몽타주를 재생하고 완료될 때까지기다립니다.

  • EndAbility
    Ability를 종료합니다.


Ability 부여방법

출처 : https://youtu.be/Jd8KjxNe4EU?si=2CwY7jGAvLcTGGzz

  • GiveAbility
    ASC(Ability System Component)에 Ability Class능력을 부여합니다.
    추가된 Ability는 GameplayAbilitySpecHandle을 반환합니다.

  • GiveAbility And ActiveOnce
    Ability를 부여한 후 바로 실행합니다.
    만약 실행 조건을 충족하지 못하면 Ability는 자동으로 제거됩니다.


Ability 제거방법

출처 : https://youtu.be/Jd8KjxNe4EU?si=2CwY7jGAvLcTGGzz

  • Clear Ability
    부여시 반환된 Gameplay Ability Spec Handle을 사용하여 지정된 Ability를
    AbilityComponent에서 제거합니다.

  • Clear All Abilites
    ASC에 등록된 모든 Ability를 제거합니다.

  • Clear All Abilities with InputID
    입력받은 InputID를 사용하는 모든 Ability를 제거합니다.


Ability 활성화

출처 : https://youtu.be/Jd8KjxNe4EU?si=2CwY7jGAvLcTGGzz

  • Try Activate Ability
    Give후 받은 Ability Spec Handle을 사용하여
    Ability를 황성화 합니다.

  • Try Activate Abilites By Tag
    지정된 태그를 사용하여 Ability를 활성화합니다.

  • Try Activate Ability By Class
    지정된 Ability클래스를 사용하여 Ability를 활성화 합니다.


Ability 실행 흐름도

출처 : https://youtu.be/Jd8KjxNe4EU?si=2CwY7jGAvLcTGGzz

일반적인 어빌리티가 실행되고 종료되기 까지의 과정입니다.


* Enhanced Input연동방법

Ability를 Give할때 InputID를 명시하여, 해당 Ability가 어떤 InputAction에 결합될지 미리 정의한 후,
Enhanced Input의 BindActoin콜백을 사용하여 Ability를 실행합니다.
Lyra는 Tag를 이용해 연동되며 각방법엔 장단점이 있습니다.

void UMyAbilitySystemComponent::BindAbilityActivatoinToEnhancedInputComponent(
	UEnhancedInputComponent* EnhancedInputComponent, TMap<UInputActoin*, int34> IndexByInputAction)
{
	if(EnhancedInputComponent)
    {
    	for(const auto& Item : indexByInputAction)
        {
        	// Triggerd event
            EnhancedInputComponent->BindAction(
            	Item.Key, ETriggerEvent::Triggered, this, &UAbilitySystemComponent::AbilityLocalInputPressed, Item.Value);
                
			// Cancel event
            EnhancedInputComponent->BindAction(
            	Item.Key, ETriggerEvent::Canceled, this, &UAbilitySystemComponent::AbilityLocalInputReleased, Item.Value);
                
            // Complete event
            EnhancedInputComponent->BindAction(
            	Item.Key, ETriggerEvent::Completed, this, &UAbilitySystemComponent::AbilityLocalInputReleased, Item.Value);
		}
	}
}
            

Ability Instancing Policy

Ability의 내부변수들 입니다
어빌리티를 인스턴싱을 하는 3가지방법입니다

1. Non Instanced

출처 : https://youtu.be/Jd8KjxNe4EU?si=2CwY7jGAvLcTGGzz

어빌리티가 Instancing 되지않고 COD를 사용합니다.
즉, 어빌리티의 모든 실행이 동일한 클래스 인스턴스를 공유합니다
주로 상태를 저장할 필요가 없는 간단한 어빌리티에서 사용됩니다.

2. Instanced Per Actor

출처 : https://youtu.be/Jd8KjxNe4EU?si=2CwY7jGAvLcTGGzz

액터마다 하나의 어빌리티 인스턴스를 생성합니다.
즉, 동일한 액터가 어빌리티를 실행하더라고 동일한 인스턴스를 사용합니다.
어빌리티 내에서 변수를 관리해야 하는 어빌리티에 적합합니다.

3. Instanced Per Execution

출처 : https://youtu.be/Jd8KjxNe4EU?si=2CwY7jGAvLcTGGzz

어빌리티가 실행된 때마다 새로운 인스턴스를 생성합니다.
즉, 동일한 액터가 여러번 같은 어빌리티를 실행하더라도 각 실행마다 별도의 인스턴스를 생성되고 사용합니다.
각 실행간의 상태를 독립적으로 유지해야 하는 어빌리티에 적합합니다.


Ability 상세항목

1. Ability Tags

간단하게 어빌리티의 명칭, 별칭으로 생각하면 됩니다.

2. Cancel Abilities With Tag

출처 : https://youtu.be/Jd8KjxNe4EU?si=2CwY7jGAvLcTGGzz

해당 Ability가 활성화 될 때, 지정된 태그를 가진 모든 활성화 된 Ability를 취소합니다.
예시) "Ability.Dash" 태그를 가진 Ability가 활성화될 때, "Ability.Attack" 태그를 가진 모든 Ability를 취소

3. Block Abilities With Tag

출처 : https://youtu.be/Jd8KjxNe4EU?si=2CwY7jGAvLcTGGzz

해당 Ability가 활성화 되어있는 중에는 지정된 태그를 가진 Ability는 실행할 수 없습니다.
예시) "Ability.Interaction"태그를 가진 Ability가 활성화 된 동안 "Ability.Attack"태그를 가진 Ability 사용차단

4. Activation Owned Tags

출처 : https://youtu.be/Jd8KjxNe4EU?si=2CwY7jGAvLcTGGzz

Ability가 활성화 될때 소유하는 태그입니다. 이 태그는 Ability가 활성화되어 있는 동안 ASC에 부여됩니다
예시) 어빌리티를 실행하는 동안에는 힘 강화가 부여된다면 State.Buff.Strength태그를 ASC에 추가합니다.

5. Acrivation Required Tags

출처 : https://youtu.be/Jd8KjxNe4EU?si=2CwY7jGAvLcTGGzz

Ability를 활성화 하기위해 요구되는 태그입니다. 캐릭터가 이 태그를 가지고 있어야 해당 Ability를 사용할 수 있습니다.
예시) "State.Buff.Strength"태그가 요구되는 Ability.Mine은 힘버프 상태에서만 활성화 할수 있습니다.

6. Acrivation Blocked Tags

출처 : https://youtu.be/Jd8KjxNe4EU?si=2CwY7jGAvLcTGGzz

Ability를 활성화 하는것을 차단하는 태그입니다. 캐릭터가 이 태그를 가지고 있으면 해당 Ability를 활성화 할 수 없습니다.
예시) "State.Debuff" 태그가 있는 동안에는 Ability.UsePotion을 사용할 수 없습니다.


7. Source Required Tags

Event에 의한 트리거가 될때만 유효합니다.
Event의 Instigator Tags가 모든 태그를 가지고 있을 경우에만 Ability를 활성화할 수 있습니다.

8. Source Blocked Tags

Event에 의한 트리거가 될때만 유효합니다.
Event의 Instigator Tags가 지정된 태그중 하나라도 가지고 있을 경우에만 Ability를 활성화할 수 있습니다.

9. Retrigger Instanced Ability

Instanced Per Actor를 사용할 경우, Ability가 활성화 되어 있으면 종료 시키고 다시 활성화 시킬수 있습니다,
동일한 어빌리티는 다시사용할때 유용하게 사용합니다.


10. Cost Gameplay Effect Class

"Instant" GE를 적용하여 Commit Cost호출시 Attribute가 감소되도록 할 수 있습니다.
예시) 파이어 볼 사용 시 마나 30감소

11. Cooldown Gameplay Effect Class

"Has Duration" GE를 적용하여 CommitCooldown 호출시 태그를 부여합니다.
예시 ) 파이어 볼 사용 시 "Cooldown.FireBall"태그를 ASC에 부여하여 5초 동안 사용하지 못하도록 합니다.


게임플레이 이벤트로 트리거 (데이터를 포함하여)

출처 : https://youtu.be/Jd8KjxNe4EU?si=2CwY7jGAvLcTGGzz

  • Send GameplayEvent To Actor를 사용하여 DataPayLoad를 포함하여 Ability를 활성화 할수 있습니다.
  • Event로 트리거 되는 경우에는 Activate Ability FromEvent를 재구현해야합니다.
  • Gameplay Ability에 Trigger Tag가 EventTag와 일치하는 태그를 포함해야 합니다.
  • WaitGameplayEvent를 사용하여 이벤트를 받을 수 있습니다.

SendGameEvent 노드를 사용하여 데이터를 넘기면Ability를 활성화 할수있습니다


어빌리티 제작 과정

출처 : https://youtu.be/Jd8KjxNe4EU?si=2CwY7jGAvLcTGGzz

개발사마다 다를수 있지만 위의 과정을 따르며
어빌리티 제작과정중 기획이 변경될경우 어빌리티를 부여하지않는 방식으로 체계적으로 관리할수 있습니다.


AT(Ability Task)란?

Ability Task는 게임플레이를 제어하고 블루프린트 통해 콜백 이벤트를 받을수 있는 노드입니다.

  • 게임플레이 흐름제어
  • 코드 구조 간결화
  • 유연한 게임플레이 구현

--

AT를 사용하는 이유

  • Ability 관리 측면에서의 효율성
    Ability가 종료될 때, 해당 Ability 내부의 모든 Ability Task를 종료함으로써 안정적으로 관리 효율성을 높입니다.

  • 게임 플레이 로직의 단순화
    게임 플레이 로직에서 SetTimer를 사용한다면 중간에 끊경우 Timer를 Reset해야 하는 번거로움을 해결 해줍니다.
    Wait Delay(AbilityTask)를 대체 사용가능합니다.

  • 기능 모듈화 및 재사용성 향상
    기능이 모듈화되어 처음에는 제작에 시간이 걸릴 수 있지만, 적절한 Ability Task를 제작해 둔다면 재사용성이 높아져
    제작 효율성이 높아질수 있습니다.


AT 제작방법

엔진에 포함된 AT를참고하여 C++에서 새로운 AT를 제작할 수 있습니다.

  • 엔진에 이미 제작된 다양한 AbilityTask가 포함되어있습니다.

  • C++에서 Ability Task클래스를 상속받아 새로운 Task를 제작할 수 있습니다.

  • Should Broadcast Ability Task Delegates() 함수를 적절히 확인하고 구현해야합니다.
    Task가 이벤트를 브로드 캐스트 가능한 상태인지를 결정하는 함수입니다.


Attribute Set이란?

캐릭터의 스탯(체력, 마나, 기력)의 모음
스킬이나 아이템으로 인한 Atribute의 변화를 쉽게 관리할수 있습니다.

  • 캐릭터의 스탯모음
  • 게임 플레이와 관련된 float값을 계산 및 수정에 용이
  • 아이템이나 스킬로 인한 능력치 변화를 쉽게 관리
  • FGameplay Attribute Data의모음
  • Attribute변화 감지 및 처리를 위한 함수 제공

Attribute Set정의 및 구성

  • FGameplay Attribute Data 구조체를 사용하여 간단하게 필요한 Attribute 추가가능

  • 보편적으로 MaxHealth와 같은 최대 상한 Attribute를 사용하는 것이 필수는 아니지만 권장됩니다.

  • 하나의 큰 Attribute Set을 만들수도 있지만, 대표 그룹을 나누어 여러개의 모듈 형태로 구성할 수 있습니다.

  • 하나의 Ability System Component가 다수의 Attribute Set을 가질수 있지만, 각 Attribute Set의 클래스는 모두 달라야합니다.

구현예시

#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
	GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
	GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
	GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
	GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName) \
    
UCLASS()
class ABILITYSAMPLE_API UMyAttributeSet : public UAttributeSet
{
	GENERATED_BODY()
    
protected:
	UPROPERTY(BlueprintReadOnly, Category = "Health")
    FGameplayAttributeData Health;

	UPROPERTY(BlueprintReadOnly, Category = "Health")
    FGameplayAttributeData MaxHealth;
    
public:
	ATTRIBUTE_ACCESSORS(UMyAttributeSet, Health)
	ATTRIBUTE_ACCESSORS(UMyAttributeSet, MaxHealth)
};   

Attribute 초기 데이터 등록방법

  1. Contents Brower
    -> Miscellanceous
    -> DataTable
  2. Attribute MetaData 선택
  3. Attribute Set에서 추가한 Attribute의 값 추가
    예시) MyAttribute.Health
  4. Ability System Component의 Default Starting Data에추가

Attribute Set C++ 추가방법

  • 액터의 블루프린트에서 Attribute Set과 초기값 데이터 테이블을 추가하였다면 코드로 추가할 필요는 없음

  • 블루프린트에서 Attribute Set을 추가한 경우에는 ASC의 OnRegister함수에서 자동으로 추가

  • 생정자에 추가된 Attribute Set은 ASC의 InitializeComponent함수에서 추가

  • AddSet함수를 사용하여 메뉴얼하게 추가할 수 있습니다.

// 생성자에서 Attribute Set추가
AAbilitySampleCharacter::AAbilitySampleCharacter()
{
	UMyAttributeSet* Attributes = CreateDefaultSubobject<UMyAttributeSet>(TEXT("AttributesSet"));
// BeginPlay함수에서 추가
void AAbilitySampleCharacter::BeginPlay()
{
	// Call the base class
    Super::BeginPlay();
    
    const UMyAttributeSet* AttributeSet = AbilitySystemComponent->AddSet<UMyAttributeSet>();

Base Value 와 Current Value

Attribute에서 헷갈릴수 있는 두가지 개념으로
Base ValueCurrent Value입니다.

Base Value

  • 값이 일시적으로 변경되어도 유지되는 원본값
  • 버프나 디버프와 같은 효과에 의해 값이 변경되지 않고 유지됩니다.
  • 보편적으로 "Instant" GameplayEffec에의해 변경됩니다.

Current Value

  • Base Value에 효과로 인한 임시 변경이 적용된 현재값
  • 게임 플레이 중 버프나 디버프 등의 효과가 적용되어 변경됩니다.
  • 보편적으로 "Has Duration", "Infinite" GameplayEffect에의해 변경됩니다.
  • 실제 게임 로직에 사용되는 값

Attribute Set의 주요함수

자주사용하는 함수2개

  • PreAttributeChange
  • PostAttributeExecuteChange
  • PreGamePlayEffectExecute
    "Instant"GameplayEffect가 적용되기 직전에 호출됩니다
    재구현하여 값 변경을 취소하고 무효화 시킬 수 있습니다.

  • PreAttributeBaseChange
    Attribute의 Base Value값이 변경되기 전에 호출됩니다
    Base Value값을 제한할 때 유용합니다.

  • PreAttributeChange
    Attribute의 Current Value변경되기 전에 호출됩니다
    값을 제한할 때 유용합니다.

  • PostAttributeChange
    Attribute의 Current Value변경된 후 호출됩니다.

  • PostAttributeBaseChange
    Attribute의 Base Value변경된 후 호출됩니다.

  • PostAttributeExecuteChange
    "Instant"GamePlayEffect가 호출된 후 호출됩니다
    Attribute변화에 따른 추가적인 로직 제작에 유용한함수입니다
    Meta Attribute(임계값)를 활용한 Attribute계산


GE(GamePlayEffect)란?

GE(GamePlayEffect) 캐릭터에게 버프나 디버프 상태를 부여하고
도트대미지나 대미지 공식을 사용해 다양한 방식으로 대미지 주는등으로 사용이 가능합니다

  • Attribute 변경
  • 캐릭터의 버프, 디버프상태 부여
  • 일정 시간 동안 지속적인 피해
  • Damage Calc를 이용한 대미지 적용
  • 동일한 효과를 Stack으로 관리

기본 대미지 로직 & GAS 대미지 로직

기본 대미지 로직

  • Actor클래스의 TakeDamege를 구현하여 대미지 처리 로직 구현
  • ApplyDamage, ApplyPointDamage이벤트를 통해전달
  • 단순한 대미지 처리 로직을 제공하여 빠르게 적용
  • 고도로 커스터마이징된 대미지 계싼이나 복잡한 상태효과 적용에 어려움
  • DamageType 클래스 사용

GAS 대미지 로직

  • Ability System Componenet를 사용하여 대미지 처리
  • Apply Gameplay Effect To Self함수를 사용하여 대미지 전달
  • 다소 높은 학습 곡선
  • 커스터마이징 가능하여 복잡한 대미지 계산을 보다 쉽게 제작 가능
  • GameplayEffect클래스 사용

Gameplay Effect 생성방법

  • ContentsBrowser -> Blueprint Class
  • Gameplay Effect 선택

Gameplay Effect 5.3~ 변경점

언리얼 엔진 5.3부터 Gameplay Effect의 변경점입니다.

  • 디자인 방식변경
    모놀리식 클래스 디자인 -> 컴포넌트 디자인 방식

  • Gameplay Effect Componenet
    GEComponent를 상속하여 원하는 기능을 모듈 방식으로 구현 가능

  • UX개선
    불필요한 정보를 덜 볼수 있어서 작업에 용이


Gameplay Effect 적용 및 제거함수

적용

  • Apply Gamepaly Effect Spec To Self
  • Apply Gamepaly Effect To Self

제거

  • Remove Active Gameplay Effect

Gameplay Effect의 주요변수

Duration Policy

  • Instant
    효과가 즉시 적용, 지속시간이 없는 단발성 효과
    예) 즉시 회복

  • Has Duration
    효과가 일정 시간동안 지속, 지속시간을 설정할수 있으며 이후 자동종료
    예) 3초동안 공격력 증가

  • Infinite
    효과가 무한히 지속, 한번 적용되면 명시적으로 제거되지않는 한 영원히 유지
    예) 장판위에있을 경우 공격력 증가유지

  • Period

    • Has DurationInfinite 정책에서 활성화됩니다.
    • 0.0f 초과인경우에는 Periodic 이펙트가 되며 정해진 시간동안 주기적으로 이펙트를 실행합니다.
    • 정해진 시간동안 주기적으로 대미지를 주는 메커니즘을 제작할 수 있습니다.
      예) 3초동안 1초마다 체력회복

Modifier Operation

Effect의 계산식으로 단순히 덧셈, 나눗셈, 곱셈 등의 연산을하지만
보다 복잡한 연산을위해선 코드기반의 이해가 필요할수 있습니다.

  • Add
    예) 체력 20 증가

  • Multiply
    예) 체력 3 나누기

  • Divide
    예) 체력 2배 증가

  • Override
    예) 체력 50으로 변경


Magnitude Calculation

  • Scalable Float

    • 가장 간단한 방법
    • 단일 Float값을 사용
    • Curve사용시 지정해둔 Level에따라 Curve Value사용
      Float Magnitude * Curve[Level].Value
      예) Effect Level : 2
      20 * 1.5 = 30
  • Attribute Based

    • 캐릭터의 Attribute를 기반으로 동적으로 계산

    • 간단한 대미지 공식 제작 가능

    • Coefficient * (Attribute + Pre Value) + Post Value

    • Backing Attribute 공식에 사용될 Attribute

    • Attribute Source
      Source : 발동한 자신의 Attribute 사용
      Target : 목표물의 Attribute 사용

    • Snapshot
      Backing Attribute 값캡처
      예) Strength 10 - 2* (10+ 3) + 10 = -16

    • Attribute Curve
      LookUp Table처럼 사용, 계산된 Attribute의 값을 조정할때 사용

    • Attrivute Calculation Type
      BackingAttribute의 BaseValue나 버프에의해 증폭된값을 사용

  • Custom Calculation Class

  • Set By Caller


Mod Magnitude Calculation

블루프린트에서 MMC클래스를 상속받아 복잡한 계산공식을만들고 float값을 반환하도록 사용할수 있습니다.

  • Modifiers에서 사용되는 강력한 클래스

  • GameplayEffect Execution Calculation과 유사하지만 덜 강력함

  • 복잡한 대미지 공식을 만들 수 있음

  • Lookup Table과 같이 사용되며 계산된 Attribute의 값을 조정할때 사용

예) Strength 5, Agility 10

대미지 공식
(Strength 2) + Agility((5 2) + 10) * -1 = -20


Set by Caller

런타임에서 값을 설정할수 있습니다.

  • GameplayEffect Spec에 Data Tag를 활용하여 값을 바인딩 할 수 있습니다.

  • 런타임에서 값을 설정할 수 있습니다.

  • Gameplay Effect에서 미리 정해진 값이 아닌 게임안에서 가공된 값을 설정할 수 있습니다.

  • Set By Caller이라는 FName버전이 있지만 Tag버전 사용을 권장


Gameplay Effect Execution Calculation

데미지 계산에 사용된는 가장 강력한 클래스로 MMC와 유사하지만 여러 Attribute를 동시에 조작가능하다는것이 특징입니다.
InstantPeriod유형에 적용되며 C++ 에서만 구현이 가능합니다

  • 대미지 계산에 사용되는 가장 강력한 클래스

  • Mod Mag Calc와 유사하며 여러 Attrivute를 변경할 수 있습니다.

  • Instant, Periodic 유형에서 적용

  • C++에서만 구현 가능

  • 복잡한 대미지 공식을 만들 수 있음

    예시)

    void UMyDamageCalc::Execute_Implementation(
    	const FGameplayEffectCustomExecutionParamerers& ExecutionParams,
    	FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const
    {
    	const FGameplayEffecttSpec& Spec = ExecutionParams.GetOwningSpec();
    	FGameplayTagContainer AssetTags;
    	Spec.GetAllassetTags(AssetTags);
       
    	const FGameplayTagContainer* SourceTags =
    		Spec.CapturedSourceTags.GettAggregatedTags();
    	const FGameplayTagContainer* TargetTags =
    		Spec.CapturedTargetTags.GettAggregatedTags();
           
    	FAggregatorEvaluateParameters EvaluationParameters;
    	EvaluationParameters.SourceTags = SourceTags;
    	EvaluationParameters.TargetTags = TargetTags;
       
       // Get the Source and Target attributes
    	float LocalStrength = 0.f;
    	ExcutionParams.AttemptCalculateCapturedAttributeMagnitude(
    		StrengthDef, EvaluationParameters, LocalStrength);
           
    	if (TargetTags->HasTag(FGameplayTag::ReauestGameplayTag(FName("State.Debuff"))))
    	{
       		LocalStrength *= 2.0f;
    	}
       
       // Set the new Health
       OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(
    		UMyAttrivuteSet::GetHealthAttribute(), EGameplayModeOp::Additive, -LocalStrength));
    }

    Stack

  • 같은 GamePlayEffect가 여러 번 적용 될 때 단일 이펙트로 관리되지 않고 누적되는 형태의 이펙트로 관리 할 수 있도록 합니다.

  • 스택은 보편적으로 Has Duration, Infinite 기간 정책을 사용합니다.

  • Overflow Effects를 사용하여 스택이 가득 찬 경우에 추가 효과를 줄 수 있습니다.


    Gameplay Effect Context

    시전자와 관련된 데이터를 전달하는 구조체로 사용되며, 서브클래싱하여 다양한 변수를 추가할수 있습니다
    GameplayeEffect Spec에 필요한 필수 데이터입니다.

  • Instigator와 관련된 데이터를 전달하기 위한 구조체

  • 게임에 맞게 서브 클래싱하여 다양한 정보를 추가 가능

  • GameplayEffect Spec에 필수적인 데이터

  • GameplayEffect 상호작용에 필요한 일시적인 데이터를 저장

  • Mod Mag Calc, Execution Calc, Attrivute Set, Gameplay Cue등에서
    Effect Context의 데이터를 가져와서 필요한 연산 가능

    예) Hit Result, 위치정보, 타깃의 위치정보, Origin위치정보 등


Gameplay Effect Spec

Gameplay Effect Spec은 내부적으로 게임플레이 이펙트에 CDO를담은 상호작용 사양입니다.

  • Gameplay Effect의 COD를 담고 있는 상호작용 사양

  • Gameplay Effect의 Level이나 지속 시간은 기본적으로 같지만 값을 변경하면 달라질 수 있습니다.

  • Gameplay Effect Context Handle을 가지고 있으며 Instigator나 Targey의 정보를 포함합니다.

  • Make Outoing Spec함수로 생성됩니다.

  • Spec이 만들어질 때 Level, Effect Context 등의 데이터가 초기화 됩니다.
    Modifiers, Executions에 사용되는 데이터의 Source Atrribute가 캡처 됩니다.

  • Target Attribute는 Apply Gameplay Effect에서 적용시 캡처 됩니다.


Post Gameplay Effect Execute 활용 사례

대미지를 받는 경우 Mana를 먼저 감소한 후 Health를 감소시키는 코드

if(Data.EvaluatedData.Attribute == UMyAttributeSet::GetDamageAttribute())
{
	const float Damage = Data.EvaluatedData.Magnitude;
	const float CurMana = UMyAttributeSet::GetManaAttribute().GetGameplayAttributeDataChecked(this)->GetCurrentValue();
    const float ManaDamage = FMath::Clamp(Damage, 0.0f, CurMana);
    const float GealthDamage = FMath::Max(damage - ManaDamage, 0.0f);
    if(ManaDamage > 0.0f);
    {
    	SetMana(CurMana - ManaDamage);
	}
    if(HealthDamage > 0.0f)
    {
    	SetHealth(CurHealth - HealthDamage);
	}
    
    SetDamage(0.0f);
}

이외 GameplayEffect로 할수 있는것들

  • Immunity(면역)
  • Chance To Apply This Effect (확률에 의한 이펙트 적용)

GC(GameplayCue)란?

GamePlayCue는 주로 시각적, 청각적 요소를 포함하며
대표적으로 파티클효과가 있습니다
Cue는 Tag를 기반으로 쉽게 트리거 시킬수 있습니다.

  • 사운드 효과, 파티클, 카메라 연출을 묶어서 표현

  • 태그를 사용하여 연출 효과를 트리거

  • 다양한 효과를 쉽게 관리


Gameplay Cue의 종류

  • GameplyCueNotify_Static
    개별 인스턴스가 없는 CDO에서 작동
    타격 이펙트와 같은 일회성 이펙트에 적합
  • GameplayCueNotify_Actor
    인스턴스화되어 제거 될 때까지 계속 동작
    라이프 사이클이 있느 이펙트에 적합
    기본적으로 ActorCue는 재활용될 수 있습니다.

출처 : https://youtu.be/Jd8KjxNe4EU?si=2CwY7jGAvLcTGGzz


Gameplay Cue 실행방법

  • Gameplay Ability에서 함수를 사용하여 호출
  • Gameplay Cue Function Library의함수 호출
  • GameplayEffect에서 GameplayCue Tag를 추가

Gameplay Cue Event

  • OnActive
    Gameplay Cue가 처음 활성화될 때 한 번 호출 됩니다.
    초기 효과나 설정을 처리합니다
    예시) 기름에 불이 붙으면 폭발파티클 생성

  • WhileActive
    Gameplay Cue가 활성상태 일 때 한번 호출됩니다.
    이미 활성 상태인 GameplayCue에서 실행이 되어야하는 효과를 처리합니다.
    이것은 Tick연산이 아닙니다.
    예시) 기름에 불이 붙은후 지속적으로 활활 타오르는 이펙트

  • Excuted
    Gameplay Cue가 실행될 때 호출되며, 즉시 실행되는 효과나 이벤트를 처리합니다.
    예시) Hit이펙트의 Slash이펙트

  • Removed
    Gameplay Cue가 제거될 때 한번 호출됩니다. 종료 효과와 정리 작업을 처리합니다.
    예시) 활활 타오르는 불 이펙트를 Deactivate


알아두면 좋은 정보

Gameplay Cue Manager

Gameplay Cue Manager를 서브클래싱 할수있습니다
기본적으로 모든 Gameplay Cue는 게임내에서 비동기 로드가 됩니다 이를변경하여 로딩전략을 변경할수도 있습니다.

  • 게임 플레이 도중 발생하는 다양한 Gameplay Cue이벤트를 효과적으로 관리하고 재구현 할 수있는 매니저

  • Gameplay Cue Manager를 상속하여 Scan및 Load전략을 변경할 수 있습니다
    런타임에서 기본적으로 모든 Gameplay Cue를 비동기로드합니다.

bool GameplayCueManager::ShouldSyncScanRuntimeObjectLibraries() const
{
	// Always sync scan the runtime object library
	return true;
}

bool UGameplayCueManager::ShouldSyncLoadRuntimeObjectLibraries() const
{
	// No real need to sync load it anymore
    return false;
}

bool UGameplayCueManager::ShouldAsyncLoadRuntimeObjectLibraries() const
{
	// Async load the run time library at startup
    return true;
}

Ability System Globals

Ability System Globals를 사용해 Gameplay Effect Context를 커스텀하여 다영한 변수를 추가할수 있습니다

  • Gameplay Effect Context를 재구현 할 수 있습니다.
    Gameplay Effect전달에 파라메터를 다양화 할 수있습니다.

  • 여러 공용 Tag를 설정할 수 있습니다.

  • Ability System에서 공통적으로 사용되는 데이터

  • GameplayCue Path를 지정합니다. Path가 지정되어 있지 않는 경우 /Game/ 을 사용합니다.
    대규모 프로젝트에서 지정되어 있지 않는 경우 느릴 수 있습니다.

virtual FGameplayEffectContext* AllocGameplayEffectContext() const;

/** Global Tag */
UPROPERTY(config)
FName ActivateFailiIsDeadName;

UPROPERTY(config)
FName ActivateFailCooldownName;

UPROPERTY(config)
FName ActivateFailCostName;

UPROPERTY(config)
FName ActivateFailTagsBlockedName;

UPROPERTY(config)
FName ActivateFailTagsMissingName;

UPROPERTY(config)
FName ActivateFailNetWorkingName;

/**Look in these paths for GameplayCueNotifies. */
UPROPERTY(config)
TArray<FString> GameplayCueNotifyPaths;

Ability Async

어빌리티 태스크와 유사하며 일반적인 Actor클래스에서도 사용가능한 함수입니다

  • Blueprint Latentg 함수를 쉽게 제작 할 수 있는 클래스

  • 액터 클래스에서 Ability Task와 유사하게 사용가능

  • 재사용이 높은 콜백 함수를 블루프린트 노드로 만들어 생산성을 향상 시킬 수 있습니다.

  • 엔진에 이미 구현되어있는 예시 클래스)
    AbilityAsync_WaitAttributeChanged
    AbilityAsync_WaitGameplayTagAdded
    AbilityAsync_WaitGameplayTagRemoved

Ability Task 종류 후의 콜백 경고

콘솔커맨드로
Ability가 종료 되었음에도 불구하고 콜백을 받은후 이벤트를 전달하려는 Ability Task를 감지하기 좋습니다

// DefaultEngine.ini

[SystemSettingsEditor]
AbilitySystem.AbilityTaskWarnifBroadcastSuppress=1

[SystemSettings]
AbilitySystem.AbilityTaskWarnifBroadcastSuppress=1

Debugger

  • ShowDebug AbilitySystem
    AbilitySystem.Debug.NextCategory
    AbilitySystem.Debug.PrevCategory

  • AbilitySystem.DebugAbilityTags
    캐릭에 Tag표시

  • AbilitySystem.DebugAttribute
    [Atribute1][Attribute2]

    예시) AbilitySystem.DebugAttribut Health Mana
    어트리 뷰트값 표시

  • ` (어퍼스트로피)


마치며

GAS에대해 정리해보았습니다 위내용은 2024언리얼 페스트의 발표자료를 받아적은것이기 때문에 완벽한 정리는아니지만 많은 도움이 될것같습니다
발표영상을 시청해보는것을 권장드리며 하단링크에서 발표해주신 ppt자료를 받아보실수 있습니다.

끝으로 GAS에대해 샘플을제작해주시며 설명해주신 (주)넥스트스테이지의 강현수/박준수 님께 감사드립니다.

유튜브 영상링크 : https://youtu.be/Jd8KjxNe4EU?si=XY7wHI5D6xJDWr3P
ppt링크 : https://epiclounge.co.kr/v3/contents/replay_view.php?idx=1322

profile
개발자좀 한번해보자

0개의 댓글