본문 바로가기
둥지/Unreal

Unreal GAS(GameplayAbilitySystem) Documentation 번역글 5부

by 까닭 2023. 7. 28.

5. 일반적으로 구현되는 Ability와 Effect

5.1 스턴

일반적으로 스턴을 사용하면 캐릭터의 활성화된 모든 GameplayAbility를 취소하고, 새로운 GameplayAbility가 활성화되지 않도록 하며, 스턴이 지속되는 동안 움직이지 못하도록 합니다. 샘플 프로젝트의 메테오 GameplayAbility는 적중된 대상에 스턴을 적용합니다. 대상의 활성화된 게임플레이 어빌리티를 취소하려면, 스턴 GameplayTag가 추가될 때 AbilitySystemComponent->CancelAbilities()를 호출합니다.


스턴한 상태에서 새로운 GameplayAbility가 활성화되는 것을 방지하기 위해, GameplayAbility는 활성화 차단 태그인 GameplayTagContainer에 스턴 GameplayTag를 지정합니다. 기절 상태에서 움직이지 않도록 하기 위해 소유자가 기절 GameplayTag를 가지고 있을 때 0을 반환하도록 CharacterMovementComponent의 GetMaxSpeed()를 오버라이드합니다.

 

5.2  스프린트(질주)

샘플 프로젝트는 왼쪽 Shift 키를 누른 상태에서 더 빠르게 달리는 스프린트 방법의 예시를 제공합니다. 더 빠른 움직임은 네트워크를 통해 서버에 플래그를 전송하여 CharacterMovementComponent가 예측적으로 처리합니다. 자세한 내용은 GDCharacterMovementComponent.h/cpp를 참조하세요.
GA는 좌클릭 입력에 대한 응답을 처리하고, CMC에 스프린트를 시작 및 중지하고, 좌클릭을 누르고 있는 동안 스태미나를 예측하여 충전하도록 지시합니다. 자세한 내용은 GA_Sprint_BP를 참조하세요.

 

5.3 조준경 조준

샘플 프로젝트는 스프린트와 똑같은 방식으로 처리하지만 이동 속도를 증가시키는 대신 감소시킵니다.
이동 속도 예측 감소에 대한 자세한 내용은 GDCharacterMovementComponent.h/cpp를 참조하세요.
입력 처리에 대한 자세한 내용은 GA_AimDownSight_BP를 참조하세요. 조준경 조준에는 스태미나 비용이 들지 않습니다.

 

5.4 생명력 흡수

생명력 흡수는 데미지 ExecutionCalculation 안에서 처리합니다. GameplayEffect에는 Effect.CanLifesteal 같은 GameplayTag가 붙습니다. 실행 계산은 GameplayEffectSpec에 해당 Effect.CanLifesteal GameplayTag가 있는지 확인합니다. GameplayTag가 존재하면, 실행 계산은 모디파이어로 부여할 생명력을 가진 동적 인스턴트 게임플레이 이펙트를 생성하여 소스의 ASC에 다시 적용합니다.

if (SpecAssetTags.HasTag(FGameplayTag::RequestGameplayTag(FName("Effect.Damage.CanLifesteal"))))
{
	float Lifesteal = Damage * LifestealPercent;

	UGameplayEffect* GELifesteal = NewObject<UGameplayEffect>(GetTransientPackage(), FName(TEXT("Lifesteal")));
	GELifesteal->DurationPolicy = EGameplayEffectDurationType::Instant;

	int32 Idx = GELifesteal->Modifiers.Num();
	GELifesteal->Modifiers.SetNum(Idx + 1);
	FGameplayModifierInfo& Info = GELifesteal->Modifiers[Idx];
	Info.ModifierMagnitude = FScalableFloat(Lifesteal);
	Info.ModifierOp = EGameplayModOp::Additive;
	Info.Attribute = UPAAttributeSetBase::GetHealthAttribute();

	SourceAbilitySystemComponent->ApplyGameplayEffectToSelf(GELifesteal, 1.0f, SourceAbilitySystemComponent->MakeEffectContext());
}

 

5.5 클라이언트 및 서버에서 난수 생성하기

총알 반동이나 확산과 같은 이유로 GameplayAbility 내부에서 "무작위" 숫자를 생성해야 하는 경우가 있습니다. 이때 클라이언트와 서버는 모두 동일한 난수를 생성하기를 원할 것입니다. 이를 위해서는 GameplayAbility를 활성화할 때 랜덤 시드를 동일하게 설정해야 합니다. 클라이언트가 활성화를 잘못 예측하여 난수 시퀀스가 서버와 동기화되지 않을 경우를 대비하여 GameplayAbility를 활성화할 때마다 난수 시드를 설정해야 합니다.

시드 설정 방법 설명
활성화 예측 키 GameplayAbility 활성화 예측 키는 클라이언트와 서버 모두에서 Activation()에서 동기화되어 사용할 수 있도록 보장되는 int16입니다. 이를 클라이언트와 서버 모두에서 랜덤 시드로 설정할 수 있습니다. 이 방법의 단점은 게임이 시작될 때마다 예측 키가 항상 0에서 시작하고 키를 생성할 때마다 사용할 값이 지속적으로 증가한다는 것입니다. 즉, 매 경기마다 정확히 동일한 난수 시퀀스를 갖게 됩니다. 이는 사용자의 필요에 따라 충분히 무작위적일 수도 있고 그렇지 않을 수도 있습니다.
게임플레이 어빌리티를 활성화할 때 이벤트 페이로드를 통해 시드를 전송 이벤트별 GameplayAbility를 활성화하고 복제된 이벤트 페이로드를 통해 클라이언트에서 서버로 무작위로 생성된 시드를 전송합니다. 이렇게 하면 무작위성을 높일 수 있지만 클라이언트가 게임을 쉽게 해킹하여 매번 동일한 시드 값만 전송할 수 있습니다. 또한 GameplayAbility를 이벤트별로 활성화하면 입력 바인딩에서 활성화되지 않습니다.

 

무작위 편차가 작다면 대부분의 플레이어는 매 게임마다 순서가 동일하다는 것을 눈치채지 못할 것이며, 활성화 예측 키를 무작위 시드로 사용하는 것이 효과적일 것입니다. 해커로부터 보호해야 하는 더 복잡한 작업을 수행하는 경우 서버가 예측 키를 생성하거나 이벤트 페이로드를 통해 전송할 무작위 시드를 생성할 수 있는 서버 시작 게임플레이 어빌리티를 사용하는 것이 더 효과적일 수 있습니다.

 

5.6 크리티컬 히트

치명타 처리는 데미지 ExecutionCalculation 안에서 처리합니다. GameplayEffect에는 Effect.CanCrit와 같은 GameplayTag 가 붙습니다. ExecutionCalculation 은 GameplayEffectSpec 에 해당 Effect.CanCrit GameplayTag가 있는지 확인합니다. GameplayTag 가 존재하면, ExecutionCalculation 은 치명타 확률(소스에서 캡처한 속성)에 해당하는 난수를 생성하고, 성공하면 치명타 대미지(소스에서 캡처한 속성)를 더합니다. 대미지를 예측하지 않기 때문에 ExecutionCalculation은 서버에서만 실행되므로 클라이언트와 서버의 난수 생성기를 동기화하는 것에 대해 걱정할 필요가 없습니다. MMC를 사용하여 대미지 계산을 예측적으로 수행하려면 GameplayEffectSpec->GameplayEffectContext->GameplayAbilityInstance에서 랜덤 시드에 대한 참조를 가져와야 합니다.

GASShooter가 헤드샷을 어떻게 처리하는지 살펴보세요. 무작위 숫자에 의존하지 않고 대신 FHitResult 본 이름을 확인한다는 점을 제외하면 같은 개념입니다.

 

5.7 중첩되지 않는 게임플레이 효과이지만 실제로는 가장 큰 크기만 대상에 미치는 이펙트

파라곤의 둔화 효과는 중첩되지 않았습니다. 각 느린 인스턴스는 정상적으로 적용되고 수명을 추적했지만, 가장 큰 규모의 느린 효과만 실제로 캐릭터에 영향을 미쳤습니다. GAS는 이 시나리오를 AggregatorEvaluateMetaData를 통해 즉시 제공합니다. 자세한 내용과 구현은 AggregatorEvaluateMetaData()를 참조하세요.

 

5.8 게임이 일시 중지된 상태에서 타겟 데이터 생성

플레이어의 WaitTargetData AbilityTask에서 TargetData 생성을 기다리는 동안 게임을 일시정지해야 하는 경우, 일시정지 대신 slomo 0을 사용하는 것이 좋습니다.

 

5.9 원버튼 인터랙션 시스템

GASShooter는 플레이어가 'E'를 길게 눌러 플레이어 부활, 무기 상자 열기, 미닫이문 여닫기 등 상호작용이 가능한 오브젝트와 상호작용할 수 있는 원버튼 상호작용 시스템을 구현했습니다.

6. GAS 디버깅

GAS 관련 문제를 디버깅할 때 종종 다음과 같은 사항을 알고 싶어합니다:

  • "내 어트리뷰트의 값이 어떻게 되는 거지?"
  • "어떤 GameplayTag가 있는 거야?"
  • "현재 어떤 GameplayEffect를 사용할 수 있지?"
  • "내게 부여된 Ability는 무엇이고, 어떤 Ability가 실행 중이며, 어떤 Ability가 활성화되지 않도록 차단되어 있지?"

GAS에는 런타임에 이러한 질문에 답하기 위한 두 가지 기법, 즉 showdebug abilitysystem과 GameplayDebugger의 후크가 있습니다.

 

Tip: 언리얼 엔진은 C++ 코드 최적화를 선호하기 때문에 일부 함수를 디버깅하기 어렵습니다. 드물지만, 코드 깊숙한 곳을 추적할 때 이런 문제가 발생할 수 있습니다. Visual Studio 솔루션 구성을 DebugGame Editor로 설정해도 코드 추적이나 변수 검사가 불가능한 경우, 최적화된 함수를 UE_DISABLE_OPTIMIZATION 및 UE_ENABLE_OPTIMIZATION 매크로 또는 CoreMiscDefines.h에 정의된 ship 변수로 래핑하여 모든 최적화를 비활성화하면 됩니다. 플러그인을 소스에서 리빌드하지 않는 이상 플러그인 코드에는 사용할 수 없습니다. 이 기능은 인라인 함수의 기능과 위치에 따라 작동할 수도 있고 작동하지 않을 수도 있습니다. 디버깅이 끝나면 매크로를 반드시 제거하세요!

UE_DISABLE_OPTIMIZATION
void MyClass::MyFunction(int32 MyIntParameter)
{
    //내 코드
}
UE_ENABLE_OPTIMIZATION

 

6.1 showdebug abilitysystem

게임 내 콘솔에서 showdebug abilitysystem을 입력합니다. 이 기능은 세 개의 "페이지"로 나뉩니다. 세 페이지 모두 현재 가지고 있는 게임플레이 태그를 표시합니다. 콘솔에 AbilitySystem.Debug.NextCategory를 입력하면 페이지 사이를 순환할 수 있습니다.

첫 페이지에는 모든 어트리뷰트의 현재 값이 표시됩니다:

두 번째 페이지에는 모든 지속 효과와 무한 게임플레이 효과, 스택 수, 게임플레이 효과에 부여되는 게임플레이 태그, 게임플레이 효과에 부여되는 수정자가 표시됩니다.

세 번째 페이지에는 내게 부여된 모든 게임플레이 어빌리티, 현재 실행 중인지 여부, 활성화가 차단되었는지 여부, 현재 실행 중인 어빌리티 태스크의 상태가 표시됩니다.

타겟(액터 주위에 초록색 직사각형 프리즘으로 표시됨) 사이를 오가려면 PageUp 키 또는 NextDebugTarget 콘솔 명령을 사용하여 다음 타겟으로 이동하고, PageDown 키 또는 PreviousDebugTarget 콘솔 명령을 사용하여 이전 타겟으로 이동합니다.

 

💡 Note: 현재 선택된 디버그 액터에 따라 어빌리티 시스템 정보가 업데이트되도록 하려면, 디폴트게임.ini 의 AbilitySystemGlobals 에서 bUseDebugTargetFromHud=true를 다음과 같이 설정해야 합니다:
[/Script/GameplayAbilities.AbilitySystemGlobals]
bUseDebugTargetFromHud=true
💡 Note: showdebug 능력 시스템이 작동하려면 게임 모드에서 실제 HUD 클래스가 선택되어 있어야 합니다. 그렇지 않으면 명령을 찾을 수 없고 "알 수 없는 명령"이 반환됩니다.

 

6.2 Gameplay Debugger

GAS는 게임플레이 디버거에 기능을 추가합니다. 아포스트로피(') 키로 게임플레이 디버거에 액세스합니다. 숫자패드의 3을 눌러 어빌리티 카테고리를 활성화합니다. 사용 중인 플러그인에 따라 카테고리가 다를 수 있습니다. 키보드에 노트북과 같은 숫자패드가 없는 경우 프로젝트 설정에서 키 바인딩을 변경할 수 있습니다.

다른 캐릭터의 게임플레이 태그, 게임플레이 이펙트, 게임플레이 어빌리티를 확인하고 싶을 때는 게임플레이 디버거를 사용하세요. 안타깝게도 타깃의 어트리뷰트 CurrentValue는 표시되지 않습니다. 화면 중앙에 있는 모든 캐릭터를 대상으로 합니다. 에디터의 월드 아웃라이너에서 타을 선택하거나 다른 캐릭터를 보고 아포스트로피 키(')를 다시 눌러 타깃을 변경할 수 있습니다. 현재 검사 중인 캐릭터는 그 위에 가장 큰 빨간색 원이 표시됩니다.

 

 

6.3 GAS Logging

GAS 소스 코드에는 다양한 상세도 수준에서 생성되는 많은 로깅 문이 포함되어 있습니다. 이러한 문은 대부분 ABILITY_LOG() 문으로 표시됩니다. 기본 상세 수준은 표시입니다. 그 이상이면 기본적으로 콘솔에 표시되지 않습니다.

로그 카테고리의 상세도 수준을 변경하려면 콘솔에 입력합니다:

log [category] [verbosity]

 

예를 들어 ABILITY_LOG() 문을 사용 설정하려면 콘솔에 입력합니다:

log LogAbilitySystem VeryVerbose

기본값으로 재설정하려면 다음과 같이 입력합니다:

log LogAbilitySystem Display

모든 로그 카테고리를 표시하려면 다음과 같이 입력합니다:

log list
 

주목할 만한 GAS 관련 로깅 카테고리입니다:

로깅 카테고리 기본 상세도 수준
LogAbilitySystem Display
LogAbilitySystemComponent Log
LogGameplayCueDetails Log
LogGameplayCueTranslator Display
LogGameplayEffectDetails Log
LogGameplayEffects Display
LogGameplayTags Log
LogGameplayTasks Log
VLogAbilitySystem Display

자세한 내용은 로깅 관련 위키를 참조하세요.

 

7. 최적화

7.1 Ability 일괄 처리

한 프레임에 활성화하고, 선택적으로 타겟 데이터를 서버로 전송하고, 모두 종료하는 게임플레이 어빌리티를 일괄 처리하여 2~3개의 RPC를 하나의 RPC로 압축할 수 있습니다. 이러한 유형의 어빌리티는 일반적으로 히트스캔 건에 사용됩니다.

7.2 Gameplay Cue 일괄 처리

많은 게임플레이 큐를 동시에 전송하는 경우 하나의 RPC로 일괄 처리하는 것이 좋습니다. 목표는 RPC의 수를 줄이고(게임플레이 큐는 신뢰할 수 없는 넷멀티캐스트입니다) 가능한 한 적은 데이터를 전송하는 것입니다.

7.3 AbilitySystemComponent Replication Mode

기본적으로 ASC는 전체 리플리케이션 모드입니다. 그러면 모든 GameplayEffect가 모든 클라이언트에 리플리케이트됩니다(싱글 플레이어 게임에서는 괜찮습니다). 멀티플레이어 게임에서는 플레이어 소유 ASC는 혼합 리플리케이션 모드로, AI 제어 캐릭터는 최소 리플리케이션 모드로 설정합니다. 이렇게 하면 플레이어 캐릭터에 적용된 GE는 해당 캐릭터의 소유자에게만 리플리케이트되며, AI 제어 캐릭터에 적용된 GE는 클라이언트에 GE를 리플리케이트하지 않습니다. 게임플레이 태그는 여전히 리플리케이트되며, GameplayCue는 리플리케이션 모드에 관계없이 모든 클라이언트에 여전히 불안정한 넷멀티캐스트가 됩니다. 이렇게 하면 모든 클라이언트가 볼 필요가 없는 GE의 네트워크 데이터가 리플리케이트되는 것을 줄일 수 있습니다.

 

7.4 Attribute Proxy Replication

포트나이트 배틀로얄(FNBR)처럼 플레이어가 많은 대규모 게임의 경우, 항상 관련성이 있는 플레이어 스테이트에 다수의 어트리뷰트를 리플리케이트하는 ASC가 많이 존재할 것입니다. 이 병목 현상을 최적화하기 위해 포트나이트는 PlayerState::ReplicateSubobjects()에서 시뮬레이션된 플레이어 제어 프록시에서 ASC와 그 어트리뷰트 세트의 리플리케이션을 모두 비활성화합니다. 자율 프록시와 AI 제어 폰은 여전히 리플리케이션 모드에 따라 완전히 리플리케이트됩니다. 항상 관련성이 있는 플레이어 스테이트의 ASC에 어트리뷰트를 리플리케이트하는 대신, FNBR은 플레이어의 폰에 리플리케이트된 프록시 구조를 사용합니다. 서버의 ASC에서 어트리뷰트가 변경되면 프록시 구조체에서도 변경됩니다. 클라이언트는 프록시 구조체에서 리플리케이트된 어트리뷰트를 받아 로컬 ASC에 변경 사항을 다시 푸시합니다. 이를 통해 어트리뷰트 리플리케이션이 폰의 관련성과 NetUpdateFrequency를 사용할 수 있습니다. 또한 이 프록시 구조체는 비트마스크에 화이트리스트에 있는 작은 게임플레이 태그 세트를 리플리케이트합니다. 이 최적화를 통해 네트워크 상의 데이터 양을 줄이고 폰 관련성을 활용할 수 있습니다. AI 제어 폰은 이미 관련성을 사용하는 폰에 ASC가 있으므로 이 최적화가 필요하지 않습니다.

그 이후로 적용된 다른 서버 측 최적화(리플리케이션 그래프 등)에서도 여전히 필요한지 잘 모르겠고, 유지 관리가 가장 쉬운 패턴도 아닙니다.

커뮤니티 질문 #3에 대한 에픽의 Dave Ratti 답변

 

7.5 ASC 지연 Loading

포트나이트 배틀로얄(FNBR)에는 나무, 건물 등 피해를 입힐 수 있는 액터가 많이 있으며, 각 액터마다 ASC가 있습니다. 이로 인해 메모리 비용이 늘어날 수 있습니다. FNBR은 필요할 때(플레이어가 처음 피해를 입었을 때) ASC를 느리게 로드하여 이를 최적화합니다. 이렇게 하면 일부 액터는 경기 중에 피해를 입지 않을 수 있으므로 전체 메모리 사용량을 줄일 수 있습니다.

 

8. Quality of Life Suggestions

8.1 Gameplay Effect Containers

GameplayEffectContainer는 게임플레이 이펙트 스펙, 타깃 데이터, 단순 타깃팅, 관련 기능을 사용하기 쉬운 구조체로 결합합니다. 게임플레이 이펙트 스펙을 어빌리티에서 스폰된 프로젝타일에 전송한 다음 나중에 충돌할 때 적용하는 데 유용합니다.

 

8.2 ASC 델리게이트에 바인딩할 블루프린트 비동기 태스크

디자이너 친화적인 반복처리 시간을 늘리려면, 특히 UI용 UMG 위젯을 디자인할 때, (C++로) 블루프린트 비동기 태스크를 생성하여 UMG 블루프린트 그래프에서 직접 ASC의 공통 변경 델리게이트에 바인딩하면 됩니다. 한 가지 주의할 점은 (위젯이 소멸될 때처럼) 수동으로 소멸시켜야 한다는 것입니다. 그렇지 않으면 메모리에 영원히 남게 됩니다. 샘플 프로젝트에는 세 개의 블루프린트 비동기 태스크가 포함되어 있습니다.

어트리뷰트 변경을 수신합니다:

쿨타임임이 변경되는지 확인합니다:

GE 스택 변경 사항에 리슨:

 

9. 문제 해결

9.1 LogAbilitySystem: Warning: Can't activate LocalOnly or LocalPredicted ability %s when not local!

클라이언트에서 ASC를 초기화해야 합니다.

9.2 스크립트 구조체 캐시 오류들

UAbilitySystemGlobals::InitGlobalData()를 호출해야 합니다.

 

9.3 애니메이션 몽타주가 클라이언트에 리플리케이트되지 않을 경우.

게임플레이 어빌리티에서 PlayMontage 대신 PlayMontageAndWait 블루프린트 노드를 사용하고 있는지 확인하세요. 이 어빌리티 태스크는 ASC를 통해 몽타주를 자동으로 리플리케이트하는 반면, PlayMontage 노드는 그렇지 않습니다.

 

9.4 블루프린트 액터 복제는 AttributeSet을 nullptr로 설정.

언리얼 엔진에 기존 블루프린트 액터 클래스에서 복제된 블루프린트 액터 클래스에 대해 클래스의 AttributeSet 포인터를 nullptr 로 설정하는 버그가 있습니다. 이에 대한 몇 가지 해결 방법이 있습니다. 제 클래스에서 맞춤형 AttributeSet 포인터를 만들지 않고 (.h 에 포인터를 만들지 않고, 생성자에서 CreateDefaultSubobject 를 호출하지 않고) 대신 (샘플 프로젝트에는 표시되지 않은) PostInitializeComponents()에서 ASC 에 AttributeSet 을 직접 추가하는 데 성공한 적이 있습니다. 리플리케이트된 AttributeSet은 여전히 ASC의 SpawnedAttributes 배열에 존재합니다. 다음과 같이 보일 것입니다:

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

	if (AbilitySystemComponent)
	{
		AbilitySystemComponent->AddSet<UGDAttributeSetBase>();
		// ... any other AttributeSets that you may have
	}
}

 

이 시나리오에서는 매크로에서 만든 AttributeSet의 함수를 호출하는 대신 ASC의 함수를 사용하여 AttributeSet의 값을 읽고 설정합니다.

/** Returns current (final) value of an attribute */
float GetNumericAttribute(const FGameplayAttribute &Attribute) const;

/** Sets the base value of an attribute. Existing active modifiers are NOT cleared and will act upon the new base value. */
void SetNumericAttributeBase(const FGameplayAttribute &Attribute, float NewBaseValue);
 

따라서 GetHealth()는 다음과 같이 보일 것입니다:

float AGDPlayerState::GetHealth() const
{
	if (AbilitySystemComponent)
	{
		return AbilitySystemComponent->GetNumericAttribute(UGDAttributeSetBase::GetHealthAttribute());
	}

	return 0.0f;
}
 

health Attribute을 설정(초기화)하면 다음과 같은 모양이 됩니다:

const float NewHealth = 100.0f;
if (AbilitySystemComponent)
{
	AbilitySystemComponent->SetNumericAttributeBase(UGDAttributeSetBase::GetHealthAttribute(), NewHealth);
}
 

다시 한 번 말씀드리자면, ASC는 AttributeSet 클래스당 최대 하나의 AttributeSet 객체만 기대합니다.

 

9.5 해결되지 않은 외부 심볼 UEPushModelPrivate::MarkPropertyDirty(int, int)

다음과 같은 컴파일러 오류가 발생하면:

error LNK2019: unresolved external symbol "__declspec(dllimport) void __cdecl UEPushModelPrivate::MarkPropertyDirty(int,int)" (__imp_?MarkPropertyDirty@UEPushModelPrivate@@YAXHH@Z) referenced in function "public: void __cdecl FFastArraySerializer::IncrementArrayReplicationKey(void)" (?IncrementArrayReplicationKey@FFastArraySerializer@@QEAAXXZ)
 

FFastArraySerializer에서 MarkItemDirty()를 호출하려고 할 때 발생하는 문제입니다. 
쿨타임 지속 시간을 업데이트할 때와 같이 ActiveGameplayEffect를 업데이트할 때 이 문제가 발생했습니다.

ActiveGameplayEffects.MarkItemDirty(*AGE);
 

무슨 일이 일어나고 있냐면 WITH_PUSH_MODEL이 두 곳 이상에서 정의되고 있다는 것입니다. PushModelMacros.h는 0으로 정의하고 있지만 여러 곳에서 1로 정의하고 있습니다. PushModel.h는 이를 1로 보고 있지만 PushModel.cpp는 이를 0으로 보고 있습니다.

해결책은 Build.cs의 프로젝트의 PublicDependencyModuleNames에 NetCore를 추가하는 것입니다.

 

9.6 열거형 이름이 경로 이름으로 표시됨

다음과 같은 컴파일러 경고가 표시되는 경우:

warning C4996: 'FGameplayAbilityInputBinds::FGameplayAbilityInputBinds': Enum names are now represented by path names. Please use a version of FGameplayAbilityInputBinds constructor that accepts FTopLevelAssetPath. Please update your code to the new API before upgrading to the next release, otherwise your project will no longer compile.
 

UE 5.1에서 BindAbilityActivationToInputComponent() 생성자에 FString 을 사용하는 것이 더이상 사용되지 않습니다. 대신 FTopLevelAssetPath를 전달해야 합니다.

이전, 폐기된 방식입니다:

AbilitySystemComponent->BindAbilityActivationToInputComponent(InputComponent, FGameplayAbilityInputBinds(FString("ConfirmTarget"),
	FString("CancelTarget"), FString("EGDAbilityInputID"), static_cast<int32>(EGDAbilityInputID::Confirm), static_cast<int32>(EGDAbilityInputID::Cancel)));

 

새로운 방식:

FTopLevelAssetPath AbilityEnumAssetPath = FTopLevelAssetPath(FName("/Script/GASDocumentation"), FName("EGDAbilityInputID"));
AbilitySystemComponent->BindAbilityActivationToInputComponent(InputComponent, FGameplayAbilityInputBinds(FString("ConfirmTarget"),
	FString("CancelTarget"), AbilityEnumAssetPath, static_cast<int32>(EGDAbilityInputID::Confirm), static_cast<int32>(EGDAbilityInputID::Cancel)));

자세한 정보는 Engine\Source\Runtime\CoreUObject\Public\UObject\TopLevelAssetPath.h 를 참고하세요.

 

10. 일반적인 GAS 약어

이름 약어
AbilitySystemComponent ASC
AbilityTask AT
Action RPG Sample Project by Epic ARPG, ARPG Sample
CharacterMovementComponent CMC
GameplayAbility GA
GameplayAbilitySystem GAS
GameplayCue GC
GameplayEffect GE
GameplayEffectExecutionCalculation ExecCalc, Execution
GameplayTag Tag, GT
ModifierMagnitudeCalculation ModMagCalc, MMC

 

11. 기타 리소스

 

11.1 에픽게임즈의 데이브 라티 Q&A

11.1.1 커뮤니티 질문 1

Da언리얼 슬래커스 디스코드 서버 커뮤니티 질문에 답하는 데이브 라티의 GAS 관련 답변:

 

1. 게임플레이 어빌리티와 무관하게 필요에 따라 범위가 지정된 예측 창을 만들려면 어떻게 해야 하나요? 예를 들어, 화염 및 망각 발사체가 적에게 적중했을 때 게임플레이 이펙트의 피해를 로컬에서 예측하려면 어떻게 해야 할까요?

PredictionKey 시스템은 실제로 이 작업을 수행하기 위한 것이 아닙니다. 기본적으로 이 시스템은 클라이언트가 예측 동작을 시작하고 키를 사용하여 서버에 이를 전달한 다음, 클라이언트와 서버 모두 동일한 작업을 실행하고 예측 부작용을 주어진 예측 키와 연관시키는 방식으로 작동합니다. 예를 들어, "어빌리티를 예측적으로 활성화하고 있습니다." 또는 "목표 데이터를 생성했으며 WaitTargetData 작업 후에 어빌리티 그래프의 일부를 예측적으로 실행할 것입니다."와 같은 식입니다.

이 패턴을 사용하면 PredictionKey가 서버에서 "바운스"되어 UAbilitySystemComponent::ReplicatedPredictionKeyMap(리플리케이트된 프로퍼티)을 통해 클라이언트로 돌아옵니다. 키가 서버에서 다시 리플리케이트되면 클라이언트는 모든 로컬 예측 부작용(GameplayCues, GameplayEffects)을 실행 취소할 수 있습니다. 예측 부작용을 취소할 시점을 정확히 아는 것이 중요합니다. 너무 이르면 갭이 생기고, 너무 늦으면 "두 배"가 생깁니다. (이는 지속시간 기반 게임플레이 이펙트의 루핑 게임플레이 큐와 같은 상태 저장 예측을 의미합니다. "버스트" 게임플레이 큐와 인스턴트 게임플레이 이펙트는 절대 "실행 취소"되거나 롤백되지 않습니다. 예측 키가 연결된 경우 클라이언트에서 건너뛰기만 합니다.)

다시 한 번 강조하자면, 예측 동작은 서버가 자체적으로 수행하는 것이 아니라 클라이언트가 지시할 때만 수행해야 한다는 점이 중요합니다. 

 

2. 로컬에서 예측된 게임플레이 어빌리티에 범위가 지정된 예측 창을 생성하기 위해 OnlyServerWait와 함께 WaitNetSync AbilityTask를 사용할 때, 서버가 예측 키로 RPC를 기다리는 동안 플레이어가 게임플레이 어빌리티 타이밍을 제어하기 위해 서버로 패킷을 지연시켜 치팅할 수 있을까요? 이 문제가 파라곤이나 포트나이트에서 발생한 적이 있나요? 그리고 발생했다면 에픽은 이를 해결하기 위해 어떤 조치를 취했나요?

예, 이는 타당한 우려입니다. 클라이언트 "신호"를 기다리는 서버에서 실행 중인 어빌리티 블루프린트는 지연 스위치 유형 익스플로잇에 잠재적으로 취약할 수 있습니다.

파라곤에는 UAbilityTask_WaitTargetData와 유사한 커스텀 타깃팅 태스크가 있었습니다. 이 태스크에는 즉각적인 타겟팅 모드를 위해 클라이언트에서 대기하는 시간 초과, 즉 '최대 지연'이 있었습니다. 타겟팅 모드가 사용자 확인(버튼 누름)을 기다리는 경우, 사용자가 시간을 가질 수 있으므로 무시됩니다. 하지만 즉시 타겟팅을 확인하는 어빌리티의 경우, 일정 시간 동안만 기다렸다가 A) 서버 측에서 타겟 데이터를 생성하거나 B) 어빌리티를 취소했습니다.

웨이트넷싱크에는 이런 메커니즘이 없었기 때문에 아주 드물게 사용했습니다.

하지만 포트나이트에서는 이런 메커니즘을 사용하지 않는다고 생각합니다. 포트나이트의 무기 어빌리티는 어빌리티를 활성화하고, 목표 데이터를 제공하고, 어빌리티를 종료하기 위해 하나의 포트나이트 전용 RPC에 특수 케이스로 일괄 처리됩니다. 따라서 배틀로얄에서 무기 능력은 본질적으로 이에 취약하지 않습니다.

제 생각에는 이 문제는 시스템 전반적으로 해결할 수 있는 문제이지만, 조만간 직접 변경할 수 있을 것 같지는 않습니다. 말씀하신 경우의 최대 지연 시간을 포함하도록 WaitNetSync를 수정하는 것은 합리적인 작업일 수 있지만, 다시 말씀드리지만 가까운 시일 내에 저희 쪽에서 이 작업을 수행할 가능성은 낮습니다.

 

3. 파라곤과 포트나이트는 어떤 EGameplayEffectReplicationMode를 사용했으며, 각 모드의 사용 시점에 대한 에픽의 권장사항은 무엇인가요?

두 게임 모두 기본적으로 플레이어가 제어하는 캐릭터는 혼합 모드를, AI가 제어하는 캐릭터(AI 미니언, 정글 크립, AI 허스크 등)는 미니멀 모드를 사용합니다. 멀티플레이어 게임에서 이 시스템을 사용하는 대부분의 사람들에게 이 모드를 권장합니다. 프로젝트 초기에 설정할수록 좋습니다.

포트나이트는 최적화에서 몇 단계 더 나아갔습니다. 실제로 시뮬레이션된 프록시에 대해 UAbilitySystemComponent를 전혀 리플리케이트하지 않습니다. 컴포넌트와 어트리뷰트 서브오브젝트는 소유하는 포트나이트 플레이어 스테이트 클래스의 ::ReplicateSubobjects() 내부에서 건너뜁니다. 어빌리티 시스템 컴포넌트에서 폰 자체의 구조체(기본적으로 비트마스크에 리플리케이트하는 속성 값의 하위 집합과 태그의 화이트리스트 하위 집합)로 최소한의 리플리케이트된 데이터를 푸시합니다. 이를 "프록시"라고 부릅니다. 수신 측에서는 프록시 데이터를 가져와서 폰에 복제하고 플레이어 상태의 어빌리티 시스템 컴포넌트에 다시 푸시합니다. 따라서 FNBR에는 각 플레이어에 대한 ASC가 있지만 직접 복제하지 않고 폰에 있는 최소한의 프록시 구조체를 통해 데이터를 복제하고 수신 측의 ASC로 다시 라우팅합니다. 이는 A) 더 최소한의 데이터 세트 B) 폰 관련성을 활용하기 때문에 이점이 있습니다.

그 이후로 수행된 다른 서버 측 최적화(복제 그래프 등)에서도 여전히 필요한지 확실하지 않으며 가장 유지 관리하기 쉬운 패턴이 아닙니다.

 

4. 게임플레이예측.h에 따라 게임플레이이펙트 제거를 예측할 수 없는데, 게임플레이이펙트 제거에 따른 지연 시간을 완화할 수 있는 전략이 있나요? 예를 들어 이동 속도를 느리게 제거할 때 현재 서버가 GameplayEffect 제거를 리플리케이트할 때까지 기다려야 하므로 플레이어의 캐릭터 위치가 스냅됩니다.

이 문제는 어려운 문제이고 저도 정답이 없습니다. 저희는 일반적으로 허용 오차와 스무딩을 통해 이러한 문제를 피했습니다. 능력 시스템과 캐릭터 이동 시스템과의 정확한 동기화가 제대로 이루어지지 않고 있다는 점에는 전적으로 동의하며, 이를 개선하고 싶습니다.

GE를 예측적으로 제거할 수 있는 선반이 있었지만 모든 에지 케이스를 해결하지 못하고 넘어가야 했습니다. 캐릭터 이동에는 여전히 내부에 저장된 이동 버퍼가 있는데, 이 버퍼는 능력 시스템과 가능한 이동 속도 수정자 등에 대해 아무것도 알지 못하기 때문에 모든 것을 해결하지는 못했습니다. GE 제거를 예측할 수 없는 상황에서도 여전히 수정 피드백 루프에 빠질 수 있습니다.

정말 절박한 상황이라고 생각되면 이동 속도 GE를 억제할 수 있는 GE를 예측해서 추가할 수 있습니다. 저는 직접 해본 적은 없지만 이론적으로 생각해본 적이 있습니다. 특정 종류의 문제에 도움이 될 수 있습니다.

Translated with DeepL.com (free version)

 

5. 어빌리티 시스템 컴포넌트가 파라곤과 포트나이트의 플레이어 스테이트와 액션 RPG 샘플의 캐릭터에 있다는 것을 알고 있습니다. 어빌리티 시스템 컴포넌트가 어디에 있어야 하며, 소유자는 누구여야 하는지에 대한 에픽의 내부 규칙, 가이드라인 또는 권장 사항은 무엇인가요?

일반적으로 리스폰할 필요가 없는 것은 소유자와 아바타 액터가 같아야 한다고 생각합니다. AI 적, 건물, 월드 소품 등이 이에 해당합니다.

리스폰이 필요한 모든 것은 오너와 아바타가 달라야 리스폰 후 어빌리티 시스템 컴포넌트를 저장/재생성/복원할 필요가 없습니다. 플레이어 스테이트는 모든 클라이언트에 리플리케이트되는 논리적인 선택입니다 (플레이어 컨트롤러는 그렇지 않습니다). 단점은 플레이어 스테이트는 항상 관련성이 있기 때문에 100인 게임에서 문제가 발생할 수 있다는 것입니다(질문 #3에서 FN이 한 일에 대한 메모 참조).

 

6. 소유자는 같지만 아바타가 다른 어빌리티 시스템 컴포넌트를 여러 개 가질 수 있나요 ?

(예: 소유자가 PlayerState로 설정된 폰과 무기/아이템/프로젝트에)

가장 먼저 보이는 문제는 소유 액터에 IGameplayTagAssetInterface 및 IAbilitySystemInterface를 구현하는 것입니다. 전자는 가능할 수도 있습니다. 모든 ASC에서 태그를 집계하기만 하면 됩니다(하지만 주의하세요. HasAllMatchingGameplayTags는 교차 ASC 집계를 통해서만 충족될 수 있습니다). 각 ASC에 해당 호출을 전달하고 결과를 합산하는 것만으로는 충분하지 않을 수 있습니다.) 하지만 후자는 더 까다로운 문제입니다. 어떤 ASC가 권한이 있는 ASC일까요? 누군가 GE를 신청하고자 한다면 어느 기관에서 받아야 할까요? 이 문제를 해결할 수 있을지도 모르지만 이 부분이 가장 어려울 것입니다. 소유자는 그 아래에 여러 개의 ASC를 둘 것입니다.

하지만 전당포와 무기에 별도의 ASC를 적용하는 것은 그 자체로 의미가 있을 수 있습니다. 예를 들어, 무기를 설명하는 태그와 소유하고 있는 폰을 설명하는 태그를 구분하는 것입니다. 무기에 부여된 태그는 소유자에게만 '적용'되고 다른 것은 적용되지 않는 것이 합리적일 수도 있습니다(예: 속성과 GE는 독립적이지만 위에서 설명한 것처럼 소유자가 소유한 태그를 집계합니다). 물론 이 방법은 잘 작동할 수 있습니다. 하지만 소유자가 같은 ASC가 여러 개 있으면 문제가 생길 수 있습니다.

 

7. 서버가 소유 클라이언트에서 로컬로 예측된 능력의 재사용 대기시간을 덮어쓰는 것을 막을 수 있는 방법이 있나요? 지연 시간이 긴 시나리오에서는 로컬 재사용 대기시간이 만료되었지만 서버에서 여전히 재사용 대기시간 중일 때 소유 클라이언트가 어빌리티를 다시 활성화하려고 "시도"할 수 있습니다. 소유 클라이언트의 활성화 요청이 네트워크를 통해 서버에 도달할 때쯤이면 서버의 재사용 대기시간이 끝나거나 서버가 남은 밀리초 동안 활성화 요청을 대기열에 넣을 수 있습니다. 그렇지 않은 경우 대기 시간이 긴 클라이언트는 대기 시간이 짧은 클라이언트에 비해 기능을 다시 활성화할 수 있는 시간이 더 길어집니다. 이러한 현상은 재사용 대기시간이 1초 미만인 기본 공격과 같이 재사용 대기시간이 매우 짧은 능력에서 가장 두드러집니다. 서버가 로컬에서 예측된 어빌리티의 재사용 대기시간을 덮어쓰는 것을 막을 방법이 없다면, 에픽은 어빌리티 재활성화에 대한 높은 지연 시간의 영향을 완화하기 위한 전략이 무엇인가요? 예를 들어 다시 말씀드리자면, 에픽은 지연 시간이 긴 플레이어가 로컬 예측을 통해 지연 시간이 짧은 플레이어와 동일한 속도로 공격하거나 능력을 활성화할 수 있도록 파라곤의 기본 공격 및 기타 능력을 어떻게 설계했나요?

짧은 대답은 이를 방지할 수 있는 방법은 없으며, 파라곤은 분명히 문제가 있었습니다. 지연 시간이 긴 연결은 기본 공격에서 ROF가 낮아집니다.

저는 이 문제를 해결하기 위해 GE 지속 시간을 계산할 때 지연 시간을 고려하는 'GE 조정' 기능을 추가했습니다. 기본적으로 서버가 총 GE 시간의 일부를 소비하도록 허용하여 GE 클라이언트 측의 유효 시간이 지연 시간에 관계없이 100% 일치하도록 했습니다(변동으로 인해 여전히 문제가 발생할 수 있음). 하지만 이 기능을 출시할 수 있는 상태로 만들지 못했고, 프로젝트가 빠르게 진행되어 이 문제를 완전히 해결하지 못했습니다.

포트나이트는 무기 발사 속도에 대해 자체적으로 장부를 작성하며, 무기 재사용 대기시간에 GE를 사용하지 않습니다. 이것이 게임에 중요한 문제라면 이 방법을 추천하고 싶습니다.

 

8. 게임플레이어빌리티시스템 플러그인에 대한 에픽의 로드맵은 어떻게 되나요? 에픽은 2019년과 그 이후에 어떤 기능을 추가할 계획인가요?

현재로서는 시스템이 전반적으로 안정적이며, 새로운 주요 기능에 대한 작업은 진행하지 않고 있습니다. 포트나이트에 대한 버그 수정과 작은 개선이 가끔씩 이루어지거나 UDN/풀 요청을 통해 이루어지지만, 지금은 그게 전부입니다.

장기적으로는 언젠가 "V2" 또는 큰 변화가 있을 거라고 생각합니다. 이 시스템을 만들면서 많은 것을 배웠고, 잘한 것도 있고 잘못한 것도 많다고 생각합니다. 이러한 실수를 바로잡고 위에서 지적한 몇 가지 치명적인 결함을 개선할 수 있는 기회가 있었으면 좋겠습니다.

V2가 출시된다면 업그레이드 경로를 제공하는 것이 가장 중요할 것입니다. V2를 만들면서 포트나이트를 영원히 V1에 남겨두지는 않을 것입니다. 가능한 한 자동으로 마이그레이션할 수 있는 경로나 절차를 마련하겠지만, 수동으로 다시 만들어야 하는 부분도 있을 것입니다.

우선순위가 높은 수정 사항은 다음과 같습니다:

  • 캐릭터 이동 시스템과의 상호 운용성 향상. 클라이언트 예측 통합.
  • GE 제거 예측(질문 #4)
  • GE 지연 시간 조정(질문 #7)
  • RPC 및 프록시 구조 일괄 처리와 같은 일반화된 네트워크 최적화.
    대부분 포트나이트에 적용했던 작업이지만, 적어도 게임에서 게임별 최적화를 더 쉽게 작성할 수 있도록 더 일반화된 형태로 세분화하는 방법을 찾고 있습니다.

좀 더 일반적인 리팩터링 유형의 변경을 고려하고 있습니다:

  • 저는 근본적으로 GE가 스프레드시트 값을 직접 참조하는 방식에서 벗어나 매개변수를 방출할 수 있고, 그 매개변수는 스프레드시트 값에 바인딩된 상위 수준의 객체로 채워질 수 있도록 하는 방안을 검토하고 싶습니다. 현재 모델의 문제점은 GE가 커브 테이블 행과 긴밀하게 연결되어 있기 때문에 공유가 불가능하다는 것입니다. 매개변수화를 위한 일반화된 시스템을 작성하여 V2 시스템의 기반이 될 수 있다고 생각합니다.
  • UGameplayAbility의 "정책" 수를 줄입니다. 저는 ReplicationPolicy와 InstancingPolicy를 제거하겠습니다. 리플리케이션은 실제로 거의 필요치 않으며 혼란을 야기합니다. 대신 FGameplayAbilitySpec 을 서브클래싱할 수 있는 UObject 로 만드는 것으로 InstancingPolicy 를 대체해야 합니다. 이것은 이벤트가 있고 블루프린트 가능한 "인스턴스화되지 않은 어빌리티 오브젝트"여야 했습니다. UGameplayAbility 는 "실행당 인스턴스화된" 오브젝트여야 합니다. 실제로 인스턴스화해야 하는 경우 선택 사항일 수 있습니다. 대신 "인스턴스화되지 않은" 어빌리티는 새로운 UGameplayAbilitySpec 오브젝트를 통해 구현할 수 있습니다.
  • 시스템은 "필터링된 GE 애플리케이션 컨테이너"(상위 레벨 게임플레이 로직으로 어떤 GE를 어떤 액터에 적용할지 데이터 구동), "겹치는 볼륨 지원"(콜리전 프리미티브 오버랩 이벤트를 기반으로 "필터링된 GE 애플리케이션 컨테이너" 적용) 등과 같은 "중간 레벨" 구조를 더 많이 제공해야 합니다. 이러한 요소는 모든 프로젝트가 각자의 방식으로 구현하는 기본 요소입니다. 이를 제대로 구현하는 것은 결코 쉬운 일이 아니므로 몇 가지 기본 구현을 더 잘 제공해야 한다고 생각합니다.
  • 일반적으로 프로젝트를 시작하고 실행하는 데 필요한 상용구를 줄이는 것이 좋습니다. 패시브 어빌리티나 기본 적중률 무기와 같은 기능을 바로 사용할 수 있는 별도의 모듈인 'Ex 라이브러리'를 만들 수도 있습니다. 이 모듈은 선택 사항이지만 빠르게 실행할 수 있습니다.
  • 게임플레이 큐를 능력 시스템과 결합되지 않은 별도의 모듈로 옮기고 싶습니다. 여기서 개선할 수 있는 부분이 많다고 생각합니다.

이는 제 개인적인 의견일 뿐 어느 누구의 약속도 아닙니다. 가장 현실적인 방법은 새로운 엔진 기술 이니셔티브가 도입되고, 능력 시스템을 업데이트해야 하며, 그때가 바로 이런 종류의 작업을 할 수 있는 시기라고 생각합니다. 이러한 이니셔티브는 스크립팅, 네트워킹 또는 물리/캐릭터 움직임과 관련이 있을 수 있습니다. 하지만 이 모든 것이 매우 먼 미래를 내다보고 있기 때문에 일정에 대한 약속이나 예상을 말씀드릴 수는 없습니다.

 

11.1.2 커뮤니티 질문 2

커뮤니티 회원인 데이브 라티의 Q&A입니다:

1. 디커플링된 고정 틱에 대한 지원이 계획되어 있나요? 게임 스레드를 고정(예: 30/60fps)하고 렌더링 스레드를 자유롭게 실행하고 싶습니다. 게임플레이의 작동 방식에 대한 몇 가지 가정을 해보기 위해 이 기능이 향후에 제공될지 여부를 묻습니다. 제가 이런 질문을 하는 이유는 현재 피직스에 고정된 비동기 틱이 있고, 이로 인해 시스템의 나머지 부분이 향후 어떻게 작동할지 의문이 들기 때문입니다. 엔진의 다른 부분의 틱 속도도 고정하지 않고 고정 틱 게임 스레드를 가질 수 있다면 정말 멋질 것 같다는 생각은 숨기지 않습니다.

렌더링 프레임 속도와 게임 스레드 틱 프레임 속도를 분리할 계획은 없습니다. 이러한 시스템의 복잡성과 이전 엔진 버전과의 하위 호환성을 유지해야 하는 요구 사항으로 인해 이 작업은 이미 시작되었다고 생각합니다.

그 대신 게임 스레드와 독립적으로 고정 틱 속도로 실행되는 비동기식 '피직스 스레드'를 사용하는 방향으로 전환했습니다. 고정된 속도로 실행해야 하는 작업은 여기서 실행하고 게임 스레드/렌더링은 기존 방식대로 작동할 수 있습니다.

네트워크 예측은 독립 틱 및 고정 틱 모드를 지원한다는 점을 명확히 할 필요가 있습니다. 제 장기적인 계획은 네트워크 예측에서 인디펜던트 틱을 현재와 같이 가변 프레임 속도로 게임 스레드에서 실행하고 "그룹/월드" 예측이 없는 고전적인 "클라이언트가 자신의 폰과 소유 액터를 예측하는" 모델을 유지하는 것입니다. 그리고 고정 틱은 비동기 피직스를 사용하여 피직스 오브젝트나 다른 클라이언트/폰/차량 등과 같이 클라이언트가 제어하거나 소유하지 않는 액터를 예측할 수 있게 해주는 것입니다.

 

2. 네트워크 예측이 어빌리티 시스템과 어떻게 통합될지에 대한 계획이 있나요? 예를 들어 고정 프레임 어빌리티 활성화(서버가 예측 키 대신 어빌리티가 활성화되고 작업이 실행된 프레임을 가져오는 방식)와 같은 방식인가요?

예, 어빌리티 시스템의 예측 키를 다시 작성/제거하고 이를 네트워크 예측 구조체로 대체할 계획입니다. NetworkPredictionExtras의 MockAbility 예제는 이것이 어떻게 작동하는지 보여 주지만, GAS에 필요한 것보다 더 "하드 코딩"되어 있습니다.

주요 아이디어는 ASC의 RPC에서 명시적인 클라이언트->서버 예측 키 교환을 제거한다는 것입니다. 예측 창이나 범위가 지정된 예측 키는 더 이상 존재하지 않습니다. 대신 모든 것이 네트워크 예측 프레임에 고정됩니다. 중요한 것은 클라이언트와 서버가 언제 어떤 일이 일어날지 합의하는 것입니다. 예를 들면 다음과 같습니다:

어빌리티가 활성화/종료/취소된 시점
게임플레이 이펙트가 적용/제거된 경우
속성 값(X 프레임에서 속성 값이 무엇이었음)
이는 어빌리티 시스템 수준에서 일반적으로 수행할 수 있다고 생각합니다. 하지만 실제로 U게임플레이 어빌리티 내부의 사용자 정의 로직을 완전히 롤백할 수 있게 만들려면 여전히 더 많은 작업이 필요합니다. 결국 완전히 롤백할 수 있고 좀 더 제한된 기능 세트에 액세스하거나 롤백 친화적인 것으로 표시된 어빌리티 태스크만 액세스할 수 있는 UGameplayAbility의 서브클래스를 만들게 될 것입니다. 뭐 그런 식입니다. 애니메이션 이벤트와 루트 모션, 그리고 그 처리 방식에도 많은 의미가 있습니다.

좀 더 명확한 답변이 있었으면 좋겠지만, GAS를 다시 다루기 전에 기초를 바로 잡는 것이 정말 중요합니다. 무브먼트와 피직스가 탄탄해야 상위 시스템을 변경할 수 있습니다.

 

3. 네트워크 예측 개발을 메인 브랜치로 옮길 계획이 있나요? 솔직히 말해서 최신 코드를 확인하고 싶습니다. 상태와 상관없이요.

이를 위해 노력 중입니다. 시스템 작업은 여전히 네트워크 예측에서 이루어지고 있으며(네트워크피직스.h 참조), 기본 비동기 피직스(리와인드데이터.h 등)도 모두 사용할 수 있어야 합니다. 하지만 포트나이트에는 아직 공개할 수 없는 사용 사례도 있습니다. 버그, 성능 최적화 등의 작업을 진행하고 있습니다.

좀 더 자세히 설명하자면, 이 시스템의 초기 버전을 작업할 때는 상태와 시뮬레이션이 어떻게 정의되고 작성되는지 등 '프런트 엔드'에 집중했습니다. 거기서 많은 것을 배웠습니다. 하지만 비동기 물리 기능이 온라인화되면서 초기의 일부 추상화를 버리는 대신 이 시스템에서 실제 작동하는 것을 구현하는 데 훨씬 더 집중하게 되었습니다. 여기서 목표는 실제가 작동하고 사물을 통합할 때 다시 돌아가는 것입니다. 예를 들어, '프런트 엔드'로 돌아가서 현재 작업 중인 핵심 기술 위에 최종 버전을 만드는 것입니다.

 

4. 한동안 메인 브랜치에서 게임플레이 메시지 전송을 위한 플러그인(이벤트/메시지 버스처럼 보임)이 있었으나 제거되었습니다. 복원할 계획이 있나요? 게임 기능/모듈형 게임플레이 플러그인을 사용하면 일반 이벤트 버스 디스패처가 있으면 매우 유용할 것입니다.

GameplayMessages 플러그인을 말씀하시는 것 같습니다. 이 API는 아직 완성되지 않았고 작성자가 아직 공개하지 않으려는 의도가 있었기 때문에 언젠가는 다시 등장할 것입니다. 모듈식 게임플레이 디자인에 유용할 것이라는 점에는 동의합니다. 하지만 제 영역이 아니기 때문에 더 자세한 정보는 없습니다.

 

5. 최근에 비동기 고정 피직스로 게임을 해봤는데 결과는 유망하지만, 앞으로 NP 업데이트가 있을 경우 작동하려면 전체 엔진을 고정 틱으로 설정해야 하고 다른 한편으로는 피직스를 33ms로 유지하려고 하기 때문에 그냥 플레이하면서 기다려야 할 것 같습니다. 모든 것이 30fps라면 좋은 경험을 할 수 없습니다 (:). 비동기 캐릭터 무브먼트 컴포넌트에 대한 작업이 있다는 것을 알았지만 이것이 네트워크 예측을 사용하는 것인지 아니면 별도의 작업인지는 확실하지 않습니다. 이 문제를 발견한 후, 저도 고정 틱 속도로 커스텀 비동기 이동을 구현해 보았는데, 정상적으로 작동했지만 보간을 위한 별도의 업데이트도 추가해야 했습니다. 설정은 고정된 33ms 업데이트에서 별도의 워커 스레드에서 시뮬레이션 틱을 실행하고, 계산을 수행하고, 결과를 저장하고, 게임 스레드에서 현재 프레임 속도와 일치하도록 보간하는 것이었습니다. 완벽하지는 않지만 작업을 완료했습니다. 제 질문은, 작성해야 할 상용구 코드가 상당히 많고 (보간 부분) 움직이는 각 오브젝트를 개별적으로 보간하는 것이 특히 효율적이지 않기 때문에 이것이 향후에 설정하기가 더 쉬울 수 있는지 여부입니다. 비동기 기능은 정말 흥미로운데, 고정 업데이트 속도로 게임 시뮬레이션을 실행할 수 있고(고정 스레드가 필요 없게 됨) 더 예측 가능한 결과를 얻을 수 있기 때문입니다. 이 기능은 앞으로 계속 사용할 예정인가요, 아니면 일부 시스템을 선택하기 위한 혜택인가요?

비동기 캐릭터 무브먼트 컴포넌트

이것은 기본적으로 CMC를 물리 스레드에 그대로 포팅하는 초기 프로토타입/실험입니다. 아직은 CMC의 미래라고 생각하지 않지만, 그렇게 발전할 수도 있습니다. 현재로서는 네트워킹 지원이 없기 때문에 제가 실제로 따르고 싶은 것은 아닙니다. 이 작업을 수행하는 사람들은 주로 이 시스템으로 인해 추가되는 입력 지연 시간을 측정하고 이를 완화할 수 있는 방법을 찾는 데 관심이 있습니다.

저는 여전히 전체 엔진을 고정 틱으로 가져와야 하고, 다른 한편으로는 물리 효과를 33ms로 유지하려고 노력합니다. 모든 것이 30fps(:)라면 좋은 경험을 할 수 없으니까요.

비동기 기능은 정말 흥미로운데, 고정 업데이트 속도로 게임 시뮬레이션을 실행할 수 있기 때문에 (고정 스레드가 불필요해지기 때문입니다).

예. 비동기 피직스를 활성화하면 가변 틱 속도로 엔진을 실행하고 물리 및 "핵심" 게임플레이 시뮬레이션(예: 캐릭터 이동, 차량, GAS 등)은 고정 속도로 실행할 수 있습니다.

이 기능을 활성화하기 위해 설정해야 하는 값은 다음과 같습니다.
p.DefaultAsyncDt=0.03333
p.RewindCaptureNumFrames=64

카오스는 피직스 상태에 대한 보간을 제공합니다(예: UPrimitiveComponent 로 푸시되어 게임 코드에 표시되는 트랜스폼). 이제 이를 제어하는 변수인 p.AsyncInterpolationMultiplier가 있으며, 이를 살펴보고 싶으면 이 변수를 사용하면 됩니다. 추가 코드를 작성하지 않고도 물리 바디의 부드러운 연속 동작을 볼 수 있습니다.

비물리 상태를 보간하고 싶다면 지금 바로 보간하는 것은 사용자의 몫입니다. 예를 들어 비동기 피직스 스레드에서 업데이트(틱)하되 게임 스레드에서 부드러운 연속 보간을 표시하여 렌더링 프레임마다 쿨다운 시각화가 업데이트되도록 하려는 쿨다운을 예로 들 수 있습니다. 언젠가는 이 기능을 추가할 예정이지만 아직 예제는 없습니다.

작성해야 할 상용구 코드가 상당히 많습니다,

네, 지금까지 시스템의 일반적인 큰 문제였습니다. 유니티는 숙련된 프로그래머가 성능과 안전성을 극대화하는 데 사용할 수 있는 인터페이스를 제공하고자 합니다(수많은 위험 요소와 해서는 안 될 일 없이 예측 가능하게 '작동하는' 게임플레이 코드를 작성할 수 있는 기능). 예를 들어 템플릿 코드를 작성하고 일괄 업데이트를 수행하거나, 광범위하게 진행하거나, 업데이트 루프를 여러 단계로 나누는 등 성능을 극대화하기 위해 여러 가지 커스텀 작업을 수행할 수 있습니다. 이러한 사용 사례를 위해 비동기 스레드 및 롤백 시스템에 좋은 '로우 레벨' 인터페이스를 제공하고자 합니다. 그리고 이 경우에도 캐릭터의 움직임은 여전히 합리적입니다. 

하지만 자체 '시스템'이 필요하지 않은 단순한 게임플레이 오브젝트에는 이 방식이 적합하지 않다는 것을 알고 있습니다. 언리얼에 더 부합하는 무언가가 필요합니다. 예를 들어 리플렉션 시스템 사용, 일반 블루프린트 지원 등이 있습니다. 다른 스레드에서 블루프린트를 사용하는 예가 있습니다(블루프린트 스레드 안전 키워드와 애니메이션 시스템의 작업 내용 참조). 그래서 언젠가는 어떤 형태로든 구현될 것이라고 생각합니다. 하지만 다시 말씀드리지만 아직은 아닙니다.

보간에 대해 질문하신 것 같지만, 지금은 NetSerialize, ShouldReconcile, Interpolate 등과 같이 모든 것을 수동으로 수행해야 하지만 언젠가는 "반사 시스템만 사용하려면 이런 것을 수동으로 작성할 필요가 없다"는 식의 방법을 제공할 것입니다. 다만 모든 사람에게 리플렉션 시스템을 사용하도록 강요하고 싶지 않은데, 이는 시스템의 가장 낮은 수준에서 다른 제한을 부과하는 것이기 때문입니다.

그리고 앞서 말씀드린 것과 다시 연결해서 말씀드리자면, 지금은 매우 구체적인 몇 가지 예제를 작동하고 성능을 발휘하도록 하는 데 집중하고 있으며, 그다음에는 다시 프론트엔드로 관심을 돌려 모든 사람이 사용하기 쉽고 반복적으로 사용할 수 있도록 상용구 등을 줄이는 데 집중할 것입니다.

 

12. GAS Changelog

이 목록은 공식 언리얼 엔진 업그레이드 변경 로그와 제가 경험한 문서화되지 않은 변경 사항에서 수집한 GAS의 주목할 만한 변경 사항(수정, 변경 및 신규 기능)의 목록입니다. 이 목록에 없는 변경 사항을 발견하신 경우 이슈나 풀 리퀘스트를 제출해 주세요.

 

5.2

  • Bug Fix: Fixed a crash in the UAbilitySystemBlueprintLibrary::MakeSpecHandle function.
  • Bug Fix: Fixed logic in the Gameplay Ability System where a non-Controlled Pawn would be considered remote, even if it was spawned locally on the server (e.g. Vehicles).
  • Bug Fix: Correctly set activation info on predicted instanced abilities that were rejected by the server.
  • Bug Fix: Fixed a bug that would cause GameplayCues to get stuck on remote instances.
  • Bug Fix: Fixed a memory stomp when chaining calls to WaitGameplayEvent.
  • Bug Fix: Calling the AbilitySystemComponent GetOwnedGameplayTags() function in Blueprint no longer retains the previous call's return values when the same node is executed multiple times.
  • Bug Fix: Fixed an issue with GameplayEffectContext replicating a reference to a dynamic object that would never be replicated.
    • This prevented GameplayEffect from calling Owner->HandleDeferredGameplayCues(this) as bHasMoreUnmappedReferences would always be true.
  • New: The Gameplay Targeting System is a way to create data-driven targeting requests.
  • New: Added custom serialization support for GameplayTag Queries.
  • New: Added support for replicating derived FGameplayEffectContext types.
  • New: Gameplay Attributes in assets are now registered as searchable names on save, allowing for references to attributes to be seen in the reference viewer.
  • New: Added some basic unit tests for the AbilitySystemComponent.
  • New: Gameplay Ability System Attributes now respect Core Redirects. This means you can now rename Attribute Sets and their Attributes in code and have them load properly in assets saved with the old names by adding redirect entries to DefaultEngine.ini.
  • Change: Allow changing the evaluation channel of a Gameplay Effect Modifier from code.
  • Change: Removed previously unused variable FGameplayModifierInfo::Magnitude from the Gameplay Abilities Plugin.
  • Change: Removed the synchronization logic between the ability system component and Smart Object instance tags.

https://docs.unrealengine.com/5.2/en-US/unreal-engine-5.2-release-notes/

 

5.1

  • Bug Fix: Fixed issue where replicated loose gameplay tags were not replicating to the owner.
  • Bug Fix: Fixed AbilityTask bug where abilities could be blocked from timely garbage-collection.
  • Bug Fix: Fixed an issue when a gameplay ability listening to activate based on a tag would fail to be activated. This would happen if there were more than one Gameplay Ability listening to this tag, and the first one in the list was invalid or didn't have authority to activate.
  • Bug Fix: Fixed GameplayEffects that use Data Registries correctly from warning on load and improved the warning text.
  • Bug Fix: Removed code from UGameplayAbility that was incorrectly only registering the last instanced ability with the Blueprint debugger for breakpoints.
  • Bug Fix: Fixed Gameplay Ability System Ability getting stuck if EndAbility was called during the lock inside ApplyGameplayEffectSpecToTarget.
  • New: Added support for Gameplay Effects to add blocked ability tags.
  • New: Added WaitGameplayTagQuery nodes. One is based off of the UAbilityTask and the other is of UAbilityAsync. This node specifies a TagQuery, and will trigger its output pin when the query becomes true or false, based on configuration.
  • New: Modified AbilityTask debugging in Console Variables to enable debug recording and printing to log by default in non-shipping builds (with ability to hotfix on/off as needed).
  • New: You can now set AbilitySystem.AbilityTask.Debug.RecordingEnabled to 0 to disable, 1 to enable in non-shipping builds, and 2 to enable all builds (including shipping).
  • New: You can use AbilitySystem.AbilityTask.Debug.AbilityTaskDebugPrintTopNResults to only print the top N results in log (to avoid log spam).
  • New: STAT_AbilityTaskDebugRecording can be used to test perf impact from these on-by-default debugging changes.
  • New: Added a debug command to filter GameplayCue events.
  • New: Added new debug commandsAbilitySystem.DebugAbilityTags, AbilitySystem.DebugBlockedTags, andAbilitySystem.DebugAttribute to the Gameplay Ability System.
  • New: Added a Blueprint function to get a debug string representation of a Gameplay Attribute.
  • New: Added a new Gameplay Task resource overlap policy to cancel existing tasks.
  • Change: Now Ability Tasks should make sure to call Super::OnDestroy only after they do anything needed to the Ability pointer, as it will be nulled out after calling it.
  • Change: Converted FGameplayAbilitySpec/Def::SourceObject to be a weak reference.
  • Change: Made a Ability System Component reference in the Ability Task a weak pointer so Garbage Collection can delete it.
  • Change: Removed redundant enum EWaitGameplayTagQueryAsyncTriggerCondition.
  • Change: GameplayTasksComponent and AbilitySystemComponent now support the registered subobject API.
  • Change: Added better logging to indicate why Gameplay Abilities failed to be activated.
  • Change: Removed AbilitySystem.Debug.NextTarget and PrevTarget commands in favor of global HUD NextDebugTarget and PrevDebugTarget commands.

https://docs.unrealengine.com/5.1/en-US/unreal-engine-5.1-release-notes/

 

5.0

https://docs.unrealengine.com/5.0/en-US/unreal-engine-5.0-release-notes/

 

4.27

  • Crash Fix: Fixed a root motion source issue where a networked client could crash when an Actor finishes executing an ability that uses a constant force root motion task with a strength-over-time modifier.
  • Bug Fix: Fixed a regression in Editor loading time when using GameplayCues.
  • Bug Fix: GameplayEffectsContainer's SetActiveGameplayEffectLevel method will no longer dirty FastArray if setting the same EffectLevel.
  • Bug Fix: Fixed an edge case in GameplayEffect mixed replication mode where Actors not explicitly owned by the net connection but who utilize that connection from GetNetConnection will not received mixed replication updates.
  • Bug Fix: Fixed an endless recursion occuring in GameplayAbility's class method EndAbility which was called by calling EndAbility again from K2_OnEndAbility.
  • Bug Fix: GameplayTags Blueprint pins will no longer be silently cleared if they are loaded before tags are registered. They now work the same as GameplayTag variables, and the behavior for both can be changed with the ClearInvalidTags option in the Project Settings.
  • Bug Fix: Improved thread safety of GameplayTag operations.
  • New: Exposed SourceObject to GameplayAbility's K2_CanActivateAbility method.
  • New: Native GameplayTags. Introducing a new FNativeGameplayTag, these make it possible to do one off native tags that are correctly registered and unregistered when the module is loaded and unloaded.
  • New: Updated GiveAbilityAndActivateOnce to pass in FGameplayEventData parameter.
  • New: Improved ScalableFloats in the GameplayAbilities plugin to support dynamic lookup of curve tables from the new Data Registry System. Added a ScalableFloat header for easier reuse of the generic struct outside the abilities plugin.
  • New: Added code support for using the GameplayTag UI in other Editor customizations via GameplayTagsEditorModule.
  • New: Modified UGameplayAbility's PreActivate method to optionally take in trigger event data.
  • New: Added more support to filter GameplayTags in the Editor using a project-specific filter. OnFilterGameplayTag supplies the referencing property and the tag source, so you can filter tags based on what asset is requesting the tag.
  • New: Added option to preserve the original captured SourceTags when GameplayEffectSpec's class method SetContext is called after initialization.
  • New: Improved UI for registering GameplayTags from specific plugins. The new tag UI now lets you select a plugin location on disk for newly added GameplayTag sources.
  • New: A new track has been added to Sequencer to allow for triggering notify states on Actors built using the GameplayAbiltiySystem. Like notifies, the GameplayCueTrack can utilize range-based events or trigger-based events.
  • Change: Changed the GameplayCueInterface to pass GameplayCueParameters struct by reference.
  • Optimization: Made several performance improvements to loading and regenerating the GameplayTag table were implemented so that this option would be optimized.

https://docs.unrealengine.com/en-US/WhatsNew/Builds/ReleaseNotes/4_27/

 

4.26

  • GAS plugin is no longer flagged as beta.
  • Crash Fix: Fixed a crash when adding a gameplay tag without a valid tag source selection.
  • Crash Fix: Added the path string arg to a message to fix a crash in UGameplayCueManager::VerifyNotifyAssetIsInValidPath.
  • Crash Fix: Fixed an access violation crash in AbilitySystemComponent_Abilities when using a ptr without checking it.
  • Bug Fix: Fixed a bug where stacking GEs that did not reset the duration on additional instances of the effect being applied.
  • Bug Fix: Fixed an issue that caused CancelAllAbilities to only cancel non-instanced abilities.
  • New: Added optional tag parameters to gameplay ability commit functions.
  • New: Added StartTimeSeconds to PlayMontageAndWait ability task and improved comments.
  • New: Added tag container "DynamicAbilityTags" to FGameplayAbilitySpec. These are optional ability tags that are replicated with the spec. They are also captured as source tags by applied gameplay effects.
  • New: GameplayAbility IsLocallyControlled and HasAuthority functions are now callable from Blueprint.
  • New: Visual logger will now only collect and store info about instant GEs if we're currently recording visual logging data.
  • New: Added support for redirectors on gameplay attribute pins in blueprint nodes.
  • New: Added new functionality for when root motion movement related ability tasks end they will return the movement component's movement mode to the movement mode it was in before the task started.

https://docs.unrealengine.com/en-US/WhatsNew/Builds/ReleaseNotes/4_26/

 

4.25.1

  • Fixed! UE-92787 Crash saving blueprint with a Get Float Attribute node and the attribute pin is set inline
  • Fixed! UE-92810 Crash spawning actor with instance editable gameplay tag property that was changed inline

 

4.25

  • Fixed prediction of RootMotionSource AbilityTasks
  • GAMEPLAYATTRIBUTE_REPNOTIFY() now additionally takes in the old Attribute value. We must supply that as the optional parameter to our OnRep functions. Previously, it was reading the attribute value to try to get the old value. However, if called from a replication function, the old value had already been discarded before reaching SetBaseAttributeValueFromReplication so we'd get the new value instead.
  • Added NetSecurityPolicy to UGameplayAbility.
  • Crash Fix: Fixed a crash when adding a gameplay tag without a valid tag source selection.
  • Crash Fix: Removed a few ways for attackers to crash a server through the ability system.
  • Crash Fix: We now make sure we have a GameplayEffect definition before checking tag requirements.
  • Bug Fix: Fixed an issue with gameplay tag categories not applying to function parameters in Blueprints if they were part of a function terminator node.
  • Bug Fix: Fixed an issue with gameplay effects' tags not being replicated with multiple viewports.
  • Bug Fix: Fixed a bug where a gameplay ability spec could be invalidated by the InternalTryActivateAbility function while looping through triggered abilities.
  • Bug Fix: Changed how we handle updating gameplay tags inside of tag count containers. When deferring the update of parent tags while removing gameplay tags, we will now call the change-related delegates after the parent tags have updated. This ensures that the tag table is in a consistent state when the delegates broadcast.
  • Bug Fix: We now make a copy of the spawned target actor array before iterating over it inside when confirming targets because some callbacks may modify the array.
  • Bug Fix: Fixed a bug where stacking GameplayEffects that did not reset the duration on additional instances of the effect being applied and with set by caller durations would only have the duration correctly set for the first instance on the stack. All other GE specs in the stack would have a duration of 1 second. Added automation tests to detect this case.
  • Bug Fix: Fixed a bug that could occur if handling gameplay event delegates modified the list of gameplay event delegates.
  • Bug Fix: Fixed a bug causing GiveAbilityAndActivateOnce to behave inconsistently.
  • Bug Fix: Reordered some operations inside FGameplayEffectSpec::Initialize to deal with a potential ordering dependency.
  • New: UGameplayAbility now has an OnRemoveAbility function. It follows the same pattern as OnGiveAbility and is only called on the primary instance of the ability or the class default object.
  • New: When displaying blocked ability tags, the debug text now includes the total number of blocked tags.
  • New: Renamed UAbilitySystemComponent::InternalServerTryActiveAbility to UAbilitySystemComponent::InternalServerTryActivateAbility.Code that was calling InternalServerTryActiveAbility should now call InternalServerTryActivateAbility.
  • New: Continue to use the filter text for displaying gameplay tags when a tag is added or deleted. The previous behavior cleared the filter.
  • New: Don't reset the tag source when we add a new tag in the editor.
  • New: Added the ability to query an ability system component for all active gameplay effects that have a specified set of tags. The new function is called GetActiveEffectsWithAllTags and can be accessed through code or blueprints.
  • New: When root motion movement related ability tasks end they now return the movement component's movement mode to the movement mode it was in before the task started.
  • New: Made SpawnedAttributes transient so it won't save data that can become stale and incorrect. Added null checks to prevent any currently saved stale data from propagating. This prevents problems related to bad data getting stored in SpawnedAttributes.
  • API Change: AddDefaultSubobjectSet has been deprecated. AddAttributeSetSubobject should be used instead.
  • New: Gameplay Abilities can now specify the Anim Instance on which to play a montage.

https://docs.unrealengine.com/en-US/WhatsNew/Builds/ReleaseNotes/4_25/

 

4.24

  • Fixed blueprint node Attribute variables resetting to None on compile.
  • Need to call UAbilitySystemGlobals::InitGlobalData() to use TargetData otherwise you will get ScriptStructCache errors and clients will be disconnected from the server. My advice is to always call this in every project now whereas before 4.24 it was optional.
  • Fixed crash when copying a GameplayTag setter to a blueprint that didn't have the variable previously defined.
  • UGameplayAbility::MontageStop() function now properly uses the OverrideBlendOutTime parameter.
  • Fixed GameplayTag query variables on components not being modified when edited.
  • Added the ability for GameplayEffectExecutionCalculations to support scoped modifiers against "temporary variables" that aren't required to be backed by an attribute capture.
    • Implementation basically enables GameplayTag-identified aggregators to be created as a means for an execution to expose a temporary value to be manipulated with scoped modifiers; you can now build formulas that want manipulatable values that don't need to be captured from a source or target.
    • To use, an execution has to add a tag to the new member variable ValidTransientAggregatorIdentifiers; those tags will show up in the calculation modifier array of scoped mods at the bottom, marked as temporary variables—with updated details customizations accordingly to support feature
  • Added restricted tag quality-of-life improvements. Removed the default option for restricted GameplayTag source. We no longer reset the source when adding restricted tags to make it easier to add several in a row.
  • APawn::PossessedBy() now sets the owner of the Pawn to the new Controller. Useful because Mixed Replication Mode expects the owner of the Pawn to be the Controller if the ASC lives on the Pawn.
  • Fixed bug with POD (Plain Old Data) in FAttributeSetInitterDiscreteLevels.

https://docs.unrealengine.com/en-US/WhatsNew/Builds/ReleaseNotes/4_24/