언리얼 프로젝트를 진행하고 공부하면서 기억하면 좋을만한 내용들을 메모하였습니다.
Youtube Link : https://youtu.be/Jd8KjxNe4EU?si=2CwY7jGAvLcTGGzz
ppt링크 : https://epiclounge.co.kr/v3/contents/replay_view.php?idx=1322
언리얼 엔진의
Game Ability System
이하 GAS는
액터간 상호작용 메커니즘을 구현할수있는 고도로 유연한 프레임워크 입니다.
확장성과 유연성이 뛰어나 게임장르에맞게 수정가능하며 멀티플레이 동기화문제를 최소화 시킬수 있습니다.
능력, 상태, 어트리 뷰트를 구축하고 복잡한 상호작용과 시작적인 효과를 유연하게 관리할 수 있는 시스템
GAS 는 다양한 능력을 쉽게 구성하고 재사용할 수 있도록 도와주며, 복잡한 능력을 구현할 수 있습니다.
확장성과 유연성 뛰어나 다양한 게임 장르에 맞게 적용 가능하며, 멀티플레이 게임에서의 동기화 문제를 최소화 할 수 있습니다.
일반적으로 캐릭터의 공격이나 기술을 구현하는것은 캐릭터 클래스에서 구현하는것이 가장 간단하고 쉬울것입니다
하지만 기획이 바뀌거나 수정사항이 발생할때 유지보수의 어려움과 여러가지 사이드 이펙트가 발생할수 있습니다GAS를 사용하면 이런 부분을 모듈화 하여 유지보수에 체계적이고 안정적으로 사용할수 있고
대부분게임에 필요한 데이터(스텟시스템)를 제공해주어 편리하게 사용할수 있습니다.
포트나이트, 파라곤과같은 AAA게임에 적용하여 안정성또한 검증되었습니다.
게임 내 캐릭터가 사용할 액티브, 패시브 능력을 체계적으로 관리
활성화 및 비활성화, Cooldown 및 Cost를 쉽게 구현
Attribute를 이용해 버프나 디버프를 유연하게 적용가능
GameplayEffect Class를 활용하여게임 내 액터 간의 상호작용을 전달
에픽게임즈에서 제작한 프레임워크로 대규모 게임에서도 안정적인 성능을 제공
모듈화 되어 있어 재사용성과 확장성이 뛰어나 복잡한 게임플레이 메커니즘을 빠르게 프로토타이핑
GamePlayAbilitySystem 구성요소는 크게 5가지로 나뉩니다.
Ability, Attibute Set, GamePlayEffect를 관리하는 컴포넌트
Ability SystemComponent간의 상호작용
캐릭터가 수행할 수 있는 행동
캐릭터에 적용되는 상호작용 및 효과 정의
캐릭터 스탯값의 모음
시작적, 청각적 피드백
추가적으로 게임플레이 전반적으로 GamePlayTag도 사용된다.
GamePlayTag는
// GamePlayTag를 가져오는 방법
FGamePlayTag StateBurn = FGameplayTag::RequestGameplayTag(FName(TEXT("State.Charater.Burned")));
계층적 트리구조로 상태를 유연하게 관리할수 있다는 큰 특징을 가지고
Enum이나 Bool기반이 아닌 Tag를 사용하여 다양한 상태를 표현할 수 있으며,
상태를 사람이 이해하기 쉬운 문자로 나타낼 수 있습니다.
태그는 계층적 트리 구조로 구성됩니다.
캐릭터의 상태를 유연하게 관리 할 수있습니다.
다양한 상태를 태그로 관리함으로써 코드의 복잡성을 줄이고, 쉽게 확장할 수 있습니다.
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")));
State.Character.Element.Burend
, State.Character.Element.Wet
State.Character.Element
, State.Character
, State
Tag를 서로 비교하거나 컨테이너에서 찾는 방법들입니다.
Tag Container에서 단일 GamePlay Tag의 부분적 포함유무
{"Insect.Ant" ,"Mammal.Cay"}.HasTag("Insect")
-> true
{"Insect, Mammal"}.HasTag("Insect.Ant")
-> false
컨테이너에 찾고자하는 태그가 부분적으로 존재한다면 true를 반환합니다.
Tag Container에서 단일 Gameplay Tag의 명시적 포함 유무
{"Insect.Ant", "Mammal.Cat"}.HasTagExact("Insect.Ant")
-> true
{"Insect.Ant", "Mammal.Cat"}.HasTagExact("Insect")
-> false
컨테이너에 찾고자하는 태그가 명시적으로 존재할때만 true를 반환합니다.
Tag Container에 모든 Gameplay Tag의 부분적 포함유무
{"Insect.Ant", "Mammal.Cat"}.HasAll({"Insect","Mammal"})
-> true
{"Insect.Ant", "Mammal.Cat"}.HasAll({"Insect","Bird"})
-> false
컨테이너 와 컨테이너를 비교해 모두 부분적으로 일치하는 경우에만 true를 반환합니다.
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를 반환합니다.
Tag Container에서 어떤것 하나라도 Gameplay Tag의 부분적 포함 유무
{"Insect.Ant", "Mammal.Cat"}.HasAny({"Insect","Bird"})
-> true
{"Insect.Ant", "Mammal.Cat"}.HasAny({"Reptile","Bird"})
-> false
컨테이너와 컨테이너를 비교해 어떤것 하나라도 부분적으로 일치하는경우 true를 반환합니다.
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 라는것이 존재합니다.
AND, OR, NOT을 이용해 조건을 조합하고 이를이용해 캐릭터의 복잡한 상태를 구현할수 있습니다.
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로 태그를 추가할수있습니다.
아래 예제는 Lyra에도 존재한다고합니다
// .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) 는 GAS에서 핵심적인 구성요소입니다
Ability를 보유하고 관리하며 Attribute Set을 가지고있으며
Component간 상호작용을 할 수 있습니다.
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;
}
어빌리티는 행동과 동작을 구현하는데 유용합니다
게임플레이 어빌리티 클래스를 상속받아 구현하며 Enhanced Input과 연동하여 사용할수 있습니다.
내부적으로 활성화시 Tag를 부여할수 있으며
CanActivateAbility함수를 통해 실행가능 여부도 알수있습니다.
Content Browser
-> Gameplay
-> GameplyAbility Blueprint
C++에서는 GameplayAbility class를 상속하여 제작할수 있습니다.
UCLASS()
class ABILITYSAMPLE_API UMyFameplayAbility : public UGameplayAbility
{
GENERATED_BODY()
};
Event Activate Ability
Ability가 활성화될 때 호출
CommitAbility
Ability를 Commit하여 필요한 자원(Attribute - 마나, 기력 등)이 소비되고 Cooldown이 시작됩니다.
정상적으로 실행되면 내부적으로 Event CommitExcute가 실행됩니다.
PlayMontage And Wait
몽타주를 재생하고 완료될 때까지기다립니다.
EndAbility
Ability를 종료합니다.
GiveAbility
ASC(Ability System Component)에 Ability Class능력을 부여합니다.
추가된 Ability는 GameplayAbilitySpecHandle
을 반환합니다.
GiveAbility And ActiveOnce
Ability를 부여한 후 바로 실행합니다.
만약 실행 조건을 충족하지 못하면 Ability는 자동으로 제거됩니다.
Clear Ability
부여시 반환된 Gameplay Ability Spec Handle을 사용하여 지정된 Ability를
AbilityComponent에서 제거합니다.
Clear All Abilites
ASC에 등록된 모든 Ability를 제거합니다.
Clear All Abilities with InputID
입력받은 InputID를 사용하는 모든 Ability를 제거합니다.
Try Activate Ability
Give후 받은 Ability Spec Handle을 사용하여
Ability를 황성화 합니다.
Try Activate Abilites By Tag
지정된 태그를 사용하여 Ability를 활성화합니다.
Try Activate Ability By Class
지정된 Ability클래스를 사용하여 Ability를 활성화 합니다.
일반적인 어빌리티가 실행되고 종료되기 까지의 과정입니다.
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의 내부변수들 입니다
어빌리티를 인스턴싱을 하는 3가지방법입니다
어빌리티가 Instancing 되지않고 COD를 사용합니다.
즉, 어빌리티의 모든 실행이 동일한 클래스 인스턴스를 공유합니다
주로 상태를 저장할 필요가 없는 간단한 어빌리티에서 사용됩니다.
액터마다 하나의 어빌리티 인스턴스를 생성합니다.
즉, 동일한 액터가 어빌리티를 실행하더라고 동일한 인스턴스를 사용합니다.
어빌리티 내에서 변수를 관리해야 하는 어빌리티에 적합합니다.
어빌리티가 실행된 때마다 새로운 인스턴스를 생성합니다.
즉, 동일한 액터가 여러번 같은 어빌리티를 실행하더라도 각 실행마다 별도의 인스턴스를 생성되고 사용합니다.
각 실행간의 상태를 독립적으로 유지해야 하는 어빌리티에 적합합니다.
간단하게 어빌리티의 명칭, 별칭으로 생각하면 됩니다.
해당 Ability가 활성화 될 때, 지정된 태그를 가진 모든 활성화 된 Ability를 취소합니다.
예시) "Ability.Dash" 태그를 가진 Ability가 활성화될 때, "Ability.Attack" 태그를 가진 모든 Ability를 취소
해당 Ability가 활성화 되어있는 중에는 지정된 태그를 가진 Ability는 실행할 수 없습니다.
예시) "Ability.Interaction"태그를 가진 Ability가 활성화 된 동안 "Ability.Attack"태그를 가진 Ability 사용차단
Ability가 활성화 될때 소유하는 태그입니다. 이 태그는 Ability가 활성화되어 있는 동안 ASC에 부여됩니다
예시) 어빌리티를 실행하는 동안에는 힘 강화가 부여된다면 State.Buff.Strength태그를 ASC에 추가합니다.
Ability를 활성화 하기위해 요구되는 태그입니다. 캐릭터가 이 태그를 가지고 있어야 해당 Ability를 사용할 수 있습니다.
예시) "State.Buff.Strength"태그가 요구되는 Ability.Mine은 힘버프 상태에서만 활성화 할수 있습니다.
Ability를 활성화 하는것을 차단하는 태그입니다. 캐릭터가 이 태그를 가지고 있으면 해당 Ability를 활성화 할 수 없습니다.
예시) "State.Debuff" 태그가 있는 동안에는 Ability.UsePotion을 사용할 수 없습니다.
Event에 의한 트리거가 될때만 유효합니다.
Event의 Instigator Tags가 모든 태그를 가지고 있을 경우에만 Ability를 활성화할 수 있습니다.
Event에 의한 트리거가 될때만 유효합니다.
Event의 Instigator Tags가 지정된 태그중 하나라도 가지고 있을 경우에만 Ability를 활성화할 수 있습니다.
Instanced Per Actor를 사용할 경우, Ability가 활성화 되어 있으면 종료 시키고 다시 활성화 시킬수 있습니다,
동일한 어빌리티는 다시사용할때 유용하게 사용합니다.
"Instant" GE를 적용하여 Commit Cost호출시 Attribute가 감소되도록 할 수 있습니다.
예시) 파이어 볼 사용 시 마나 30감소
"Has Duration" GE를 적용하여 CommitCooldown 호출시 태그를 부여합니다.
예시 ) 파이어 볼 사용 시 "Cooldown.FireBall"태그를 ASC에 부여하여 5초 동안 사용하지 못하도록 합니다.
SendGameEvent 노드를 사용하여 데이터를 넘기면Ability를 활성화 할수있습니다
개발사마다 다를수 있지만 위의 과정을 따르며
어빌리티 제작과정중 기획이 변경될경우 어빌리티를 부여하지않는 방식으로 체계적으로 관리할수 있습니다.
Ability Task는 게임플레이를 제어하고 블루프린트 통해 콜백 이벤트를 받을수 있는 노드입니다.
--
Ability 관리 측면에서의 효율성
Ability가 종료될 때, 해당 Ability 내부의 모든 Ability Task를 종료함으로써 안정적으로 관리 효율성을 높입니다.
게임 플레이 로직의 단순화
게임 플레이 로직에서 SetTimer를 사용한다면 중간에 끊경우 Timer를 Reset해야 하는 번거로움을 해결 해줍니다.
Wait Delay(AbilityTask)를 대체 사용가능합니다.
기능 모듈화 및 재사용성 향상
기능이 모듈화되어 처음에는 제작에 시간이 걸릴 수 있지만, 적절한 Ability Task를 제작해 둔다면 재사용성이 높아져
제작 효율성이 높아질수 있습니다.
엔진에 포함된 AT를참고하여 C++에서 새로운 AT를 제작할 수 있습니다.
엔진에 이미 제작된 다양한 AbilityTask가 포함되어있습니다.
C++에서 Ability Task클래스를 상속받아 새로운 Task를 제작할 수 있습니다.
Should Broadcast Ability Task Delegates()
함수를 적절히 확인하고 구현해야합니다.
Task가 이벤트를 브로드 캐스트 가능한 상태인지를 결정하는 함수입니다.
캐릭터의 스탯(체력, 마나, 기력)의 모음
스킬이나 아이템으로 인한 Atribute의 변화를 쉽게 관리할수 있습니다.
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 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>();
Attribute에서 헷갈릴수 있는 두가지 개념으로
Base Value와 Current Value입니다.
자주사용하는 함수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) 캐릭터에게 버프나 디버프 상태를 부여하고
도트대미지나 대미지 공식을 사용해 다양한 방식으로 대미지 주는등으로 사용이 가능합니다
언리얼 엔진 5.3부터 Gameplay Effect의 변경점입니다.
디자인 방식변경
모놀리식 클래스 디자인 -> 컴포넌트 디자인 방식
Gameplay Effect Componenet
GEComponent를 상속하여 원하는 기능을 모듈 방식으로 구현 가능
UX개선
불필요한 정보를 덜 볼수 있어서 작업에 용이
적용
제거
Instant
효과가 즉시 적용, 지속시간이 없는 단발성 효과
예) 즉시 회복
Has Duration
효과가 일정 시간동안 지속, 지속시간을 설정할수 있으며 이후 자동종료
예) 3초동안 공격력 증가
Infinite
효과가 무한히 지속, 한번 적용되면 명시적으로 제거되지않는 한 영원히 유지
예) 장판위에있을 경우 공격력 증가유지
Period
Has Duration
과 Infinite
정책에서 활성화됩니다.Effect의 계산식으로 단순히 덧셈, 나눗셈, 곱셈 등의 연산을하지만
보다 복잡한 연산을위해선 코드기반의 이해가 필요할수 있습니다.
Add
예) 체력 20 증가
Multiply
예) 체력 3 나누기
Divide
예) 체력 2배 증가
Override
예) 체력 50으로 변경
Scalable Float
Float Magnitude * Curve[Level].Value
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
블루프린트에서 MMC클래스를 상속받아 복잡한 계산공식을만들고 float값을 반환하도록 사용할수 있습니다.
Modifiers에서 사용되는 강력한 클래스
GameplayEffect Execution Calculation과 유사하지만 덜 강력함
복잡한 대미지 공식을 만들 수 있음
Lookup Table과 같이 사용되며 계산된 Attribute의 값을 조정할때 사용
예) Strength 5, Agility 10
대미지 공식
(Strength 2) + Agility((5 2) + 10) * -1 = -20
런타임에서 값을 설정할수 있습니다.
GameplayEffect Spec에 Data Tag를 활용하여 값을 바인딩 할 수 있습니다.
런타임에서 값을 설정할 수 있습니다.
Gameplay Effect에서 미리 정해진 값이 아닌 게임안에서 가공된 값을 설정할 수 있습니다.
Set By Caller이라는 FName버전이 있지만 Tag버전 사용을 권장
데미지 계산에 사용된는 가장 강력한 클래스로 MMC와 유사하지만 여러 Attribute를 동시에 조작가능하다는것이 특징입니다.
Instant
와Period
유형에 적용되며 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));
}
같은 GamePlayEffect가 여러 번 적용 될 때 단일 이펙트로 관리되지 않고 누적되는 형태의 이펙트로 관리 할 수 있도록 합니다.
스택은 보편적으로 Has Duration
, Infinite
기간 정책을 사용합니다.
Overflow Effects를 사용하여 스택이 가득 찬 경우에 추가 효과를 줄 수 있습니다.
시전자와 관련된 데이터를 전달하는 구조체로 사용되며, 서브클래싱하여 다양한 변수를 추가할수 있습니다
GameplayeEffect Spec에 필요한 필수 데이터입니다.
Instigator와 관련된 데이터를 전달하기 위한 구조체
게임에 맞게 서브 클래싱하여 다양한 정보를 추가 가능
GameplayEffect Spec에 필수적인 데이터
GameplayEffect 상호작용에 필요한 일시적인 데이터를 저장
Mod Mag Calc, Execution Calc, Attrivute Set, Gameplay Cue등에서
Effect Context의 데이터를 가져와서 필요한 연산 가능
예) Hit Result, 위치정보, 타깃의 위치정보, Origin위치정보 등
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에서 적용시 캡처 됩니다.
대미지를 받는 경우 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);
}
GamePlayCue는 주로 시각적, 청각적 요소를 포함하며
대표적으로 파티클효과가 있습니다
Cue는 Tag를 기반으로 쉽게 트리거 시킬수 있습니다.
사운드 효과, 파티클, 카메라 연출을 묶어서 표현
태그를 사용하여 연출 효과를 트리거
다양한 효과를 쉽게 관리
OnActive
Gameplay Cue가 처음 활성화될 때 한 번 호출 됩니다.
초기 효과나 설정을 처리합니다
예시) 기름에 불이 붙으면 폭발파티클 생성
WhileActive
Gameplay Cue가 활성상태 일 때 한번 호출됩니다.
이미 활성 상태인 GameplayCue에서 실행이 되어야하는 효과를 처리합니다.
이것은 Tick연산이 아닙니다.
예시) 기름에 불이 붙은후 지속적으로 활활 타오르는 이펙트
Excuted
Gameplay Cue가 실행될 때 호출되며, 즉시 실행되는 효과나 이벤트를 처리합니다.
예시) Hit이펙트의 Slash이펙트
Removed
Gameplay Cue가 제거될 때 한번 호출됩니다. 종료 효과와 정리 작업을 처리합니다.
예시) 활활 타오르는 불 이펙트를 Deactivate
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를 사용해 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;
어빌리티 태스크와 유사하며 일반적인 Actor클래스에서도 사용가능한 함수입니다
Blueprint Latentg 함수를 쉽게 제작 할 수 있는 클래스
액터 클래스에서 Ability Task와 유사하게 사용가능
재사용이 높은 콜백 함수를 블루프린트 노드로 만들어 생산성을 향상 시킬 수 있습니다.
엔진에 이미 구현되어있는 예시 클래스)
AbilityAsync_WaitAttributeChanged
AbilityAsync_WaitGameplayTagAdded
AbilityAsync_WaitGameplayTagRemoved
콘솔커맨드로
Ability가 종료 되었음에도 불구하고 콜백을 받은후 이벤트를 전달하려는 Ability Task를 감지하기 좋습니다
// DefaultEngine.ini
[SystemSettingsEditor]
AbilitySystem.AbilityTaskWarnifBroadcastSuppress=1
[SystemSettings]
AbilitySystem.AbilityTaskWarnifBroadcastSuppress=1
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