https://docs.unrealengine.com/5.0/ko/animation-in-lyra-sample-game-in-unreal-engine/

 

라이라의 애니메이션

라이라의 애니메이션 시스템에 관한 개요입니다

docs.unrealengine.com

대부분의 프로젝트에서 그렇듯이 라이라의 캐릭터 애니메이션은 애니메이션 블루프린트 시스템을 사용하여 만들어진다.

 

ABP_Mannequin_Base 블루프린트에 적혀있는 코멘트에 따르면 해당 Anim Graph는 Montage와 애니메이션 레이어의 시작점을 제공하여 혼합하여 재생할 수 있게 해준다. 이러한 접근으로 무기가 로드될 때 애니메이션을 로드하도록 할 수 있다. 예를 들어 Weapon은 필요한 Montage와 애니메이션 레이어에 대한 참조를 갖고 있기 때문에 Weapon이 로드될 때 애니메이션 데이터들이 같이 로드되도록 한다. 

 

블루프린트 스레드 세이프 업데이트 애니메이션 

 

최적의 퍼포먼스를 위해 라이라에서는 이벤트 그래프 대신 멀티 스레드 애니메이션을 사용하여 애니메이션 값을 계산한다.

C++를 통해 애니메이션 업데이트 방법이나 발생 시기를 제어할 수 있도록 해주면, 퍼포먼스를 향상시킬 수 있다.

많은 애니메이션 작업을 워커 스레드에서 돌릴 수 있도록 해주는 프로젝트 세팅

프로젝트 세팅 > Engine > General Settings > Anim Blueprints > Allow Multi Threaded Animation Update 체크

 

ABP_Mannequin_Base의 함수 목록, 모두 블루프린트에 정의되어 있다.

스레드 세이프 함수를 사용할 때는 이벤트 그래프에서와 달리 직접 데이터에 액세스할 수 없다. 대신에 Property Access를 사용하여 데이터에 접근하는 편이 낫다.

 

서브 애니메이션 인스턴스 / 애니메이션 레이어 / 애니메이션 블루프린트 링크

캐릭터의 애니메이션 블루프린트가 복잡해지기 시작하면서, 다른 애니메이션 블루프린트 안의 애니메이션 블루프린트 일부를 재사용하고플 수가 있습니다. 그럴 때 노드 망을 다시 만들기 보다는, 망을 별도의 애니메이션 블루프린트로 나눈 뒤 Sub Anim Instance (서브 애님 인스턴스)를 사용하여 필요할 때 그 애니메이션 블루프린트에 접근하도록 할 수 있습니다.

 

애니메이션 블루프린트 링크(Animation Blueprint Linking) 시스템은 서브 애님 인스턴스의 익스텐션입니다. 이 익스텐션은 애니메이션 그래프의 서브섹션을 동적으로 전환하도록 지원하여 멀티 유저 협업 및 메모리 절감을 실현합니다. 애니메이션 블루프린트가 사용하지 않는 애니메이션 에셋을 더 이상 로드하지 않게 되기 때문입니다.

 

애니메이션 블루프린트 링크(Animation Blueprint Linking) 시스템은 애니메이션 그래프의 여러 하위 섹션이 동적으로 전환되게 합니다. 링크된 레이어 애니메이션 블루프린트(Linked Layer Animation Blueprints)를 통해 메인 애니메이션 블루프린트의 여러 곳을 오버라이드할 수 있습니다.

라이라에서 이는 플레이어가 들고 있는 무기에 따라 로코모션 행동, 애니메이션 에셋, 포즈 수정이 달라질 수 있음을 뜻합니다. 

 

서브 애님 인스턴스 사용

다른 애니메이션 블루프린트 안에서 서브 애니메이션 블루프린트 인스턴스를 사용하는 방법을 안내합니다.

docs.unrealengine.com

 

1. 애니메이션 레이어 인터페이스 애셋 : 레이어 수, 이름, 속하는 그룹, 레이어 Input 등을 정의할 수 있다.

2. 링크할 애니메이션 블루프린트에서 애니메이션 레이어 인터페이스를 추가한다.

3. 마스터 애니메이션 블루프린트에도 애니메이션 레이어 인터페이스를 추가한다.

4. 마스터 애니메이션 블루프린트에서 Input Pose 노드를 Output Pose 노드에 연결한다. (링크된 애니메이션 블루프린트 포즈를 그대로 받아서 사용)

 

디폴트(Default) 스테이트 머신이 포즈를 링크된 애니메이션 레이어를 통해 Output Pose 노드로 전달할 것입니다. 애님 클래스 레이어 링크(Link Anim Class Layers) 를 호출하면 기저 애니메이션 블루프린트가 실행됩니다.

Linked Anim Layer 노드는 인스턴스 클래스(Instance Class) 프로퍼티를 가지며 이를 사용하여 디폴트 레이어 오버라이드를 지정할 수 있습니다. 이는 로직을 다수의 애니메이션 블루프린트로 나누고 싶을 때 특히 유용합니다. 예를 들어 자주 변경되는 IK 로직이 있는 경우 이를 별도의 애니메이션 블루프린트로 옮기고 메인 애니메이션 블루프린트에서 기본으로 실행되도록 설정할 수 있습니다.

 

캐릭터의 메시 컴포넌트에서 Anim Class Layer를 Link, Unllink할 수 있다. 

  

애니메이션 레이어 및 링크된 애니메이션 블루프린트는 복잡한 캐릭터 내에서 확장성과 조직을 유지하는 데 유용할 수 있습니다. 레이어와 링크된 애니메이션 블루프린트를 사용하면 로직을 애니메이션 블루프린트 내에서 레이어로 분리하거나 아예 다른 애니메이션 블루프린트로 나누고 마스터 애니메이션 블루프린트 내에서 링크할 수 있습니다.

 

ALI_ItemAnimLayers 는 애니메이션 블루프린트에서 애니메이션을 오버라이드할 수 있는 곳을 지정하는 애님 레이어 인터페이스(Anim Layer Interface)입니다. 라이라에서 이 작업은 에임과 스켈레톤 제어를 위한 레이어뿐 아니라 로코모션 스테이트에 대해서도 수행됩니다.

 

ABP_ItemAnimLayersBase 는 모든 무기가 사용하는 링크된 레이어 애니메이션 블루프린트의 베이스입니다. 

ㄴ ALI_ItemAnimLayers 인터페이스를 상속받는다.

 

Anim Node 함수

라이라에서는 애님 노드 함수를 사용하여 스테이트별 로직을 생성한다.

애니메이션 포즈 출력 노드에서는 3가지 종류의 함수를 설정할 수 있다.

 

1. OnInitialUpdate : 노드가 처음 업데이트되기 전에 호출되는 함수

2. OnBecomeRelevant : 노드에 연관성이 생길 때 호출되는 함수 

3. OnUpdate : 노드가 업데이트될 때 호출하는 함수

 

예시로 Idle state의 최종 출력 포즈를 계산하는데 Update Idle State 함수를 OnUpdate에 등록한다.

 

State Aliases

캐릭터가 다수의 애니메이션 스테이트로 트랜지션되어야 할 수 있는데, 트랜지션 로직을 단순화(트랜지션 수 Line 수를 줄여준다.) 하는 동시에 스테이트 사이의 개별 트랜지션을 각각 제어할 수 있게 해준다.

 

디테일 패널에서 Jump State로 직접 트랜지션될 수 있는 로코모션 스테이트를 볼 수 있다.

 

상단 / 하단 바디 레이어링

블렌드 노드(Blend Nodes)는 애니메이션을 블렌딩하는 데 사용됩니다. 라이라에 사용되는 로코모션 애니메이션은 대부분 풀 바디입니다. 즉, 애니메이션이 전체 스켈레톤에서 재생되고(jog_fwd 애니메이션 등), 플레이어가 언제든지 사용할 수 있는 다양한 상단 바디 액션과 결합됩니다(무기 발사 또는 재장전 애니메이션 등).

이 작업은 AnimBP_Mannequin_Base > AnimGraph 를 연 다음 상단 바디/하단 바디 분할(Upper/Lower Body Split) 코멘트로 가서 볼 수 있는 본 별로 레이어드 블렌딩(Layered Blend Per Bone) 노드를 사용하여 수행됩니다.

 

Layered blend per bone 노드의 디테일 패널에서 블렌드와 관련된 개별 본의 가중치를 명시적으로 제어할 수 있는 블렌드 마스크를 볼 수 있다.

 

메인 애니메이션 블루프린트에서 데이터 액세스하기

ABP_ItemAnimLayersBase 애니메이션 블루프린트에는 메인 애님 블루프린트 스레드 세이프 얻기(Get Main Anim BPThreadSafe) 라는 커스텀 함수가 있습니다. 메인 애니메이션 블루프린트(AnimBP_Mannequin_Base )에 대한 레퍼런스를 얻는 데 사용되는 함수입니다.

 

이렇게 하면 프로퍼티 액세스를 사용하여 모든 데이터에 액세스할 수 있다.

 

애니메이션 선택을 위해 애님 노드 함수 사용하기

 

Linked Layer Child Animation Blueprint

라이라의 모든 무기에는 ABP_ItemAnimLayersBase 로부터 상속받는 자손 애니메이션 블루프린트가 있습니다. 애니메이터는 아래 있는 ABP_PistolAnimLayers 애니메이션 블루프린트 이미지에 보이는 것처럼 애니메이션을 삽입하고 무기별로 모든 변수를 편집할 수 있습니다.

 

디스턴스 매칭, 스트라이드 워핑, 오리엔테이션 워핑

디스턴스 매칭(Distance Matching) 은 애니메이션 에셋과 게임플레이 간에 모션을 일치시키기 어려운 경우(시작, 중지, 착지 애니메이션과 같은 로코모션 애니메이션 에셋 등)에 애니메이션의 재생 속도를 조절합니다

싱크를 맞춰 플레이어가 미끄러지는 것처럼 보이지 않게 한다.

 

Stride Warping(스트라이드 워핑)은 캐릭터가 달리기(Jog) 스테이트에 들어갈 때처럼 재생 속도로 캐릭터의 보폭을 조절할 수 없는 경우에 보폭을 동적으로 조절하기 위해 사용됩니다.

 

이 두 가지 기법을 결합하면, 한 기법을 다른 기법보다 우선하도록 동적으로 선택할 수 있습니다. start 스테이트에 먼저 디스턴스 매칭을 사용하여 포즈를 유지한 후, jog 스테이트에 가까워지면 스트라이드 워핑을 사용하여 블렌딩합니다.

 

스트라이드 워핑

오리엔테이션 워핑(Orientation Warping)은 캐릭터 움직임의 루트 모션 각도와 함께 사용하며, 캐릭터의 바디 하단을 구부려 루트 모션 각도에 일치시킵니다. 라이라에서는 네 가지 기본 방향에 대하여 횡이동 애니메이션이 생성됩니다. 플레이어가 360도로 자유롭게 움직일 수 있으므로 오리엔테이션 워핑을 사용해 캐릭터의 포즈를 프로시저럴 방식으로 조절했습니다. 애니메이션의 범위가 제한되기 때문에 이 기법은 start 스테이트에서 사용됩니다.

 

게임플레이 태그 바인딩

라이라는 대부분의 플레이어 행동에 대해 게임플레이 어빌리티 시스템(Gameplay Ability System)을 사용합니다. 애니메이션 블루프린트의 이러한 이벤트에는 게임플레이 태그(Gameplay Tag) 바인딩을 사용해 응답할 수 있습니다. AnimBP_Mannequin_Base 블루프린트 안에서 게임플레이 태그로 이동하려면 클래스 디폴트(Class Defaults) > 디테일(Details) > 게임플레이 태그(Gameplay Tags) > 게임플레이 태그 프로퍼티 맵(Gameplay Tag Property Map) 으로 갑니다.

https://docs.unrealengine.com/5.0/ko/abilities-in-lyra-in-unreal-engine/

 

라이라의 어빌리티

라이라에서 게임플레이를 위해 게임플레이 어빌리티 시스템을 사용한 방식을 간략하게 살펴봅니다.

docs.unrealengine.com

 

라이라의 게임플레이 어빌리티 시스템

라이라에서는 GAS를 사용하여 대부분의 게임플레이를 조직한다. 어빌리티는 점프처럼 영웅 데이터에 이미 선천적으로 내재되어 있을 수도 있고, 게임 피처 같은 액션으로부터 부여받을 수도 있고, 경험이나 장비를 통해 획득할 수도 있다.

액션을 어빌리티로 구현한다. (점프, 대쉬 같은 이동 관련 액션, 사망 후 리스폰 트리거 같은 수동적인 리스닝 액션도 포함)

 

어빌리티 시스템의 코어 클래스

UAbilitySystemComponent 어빌리티 시스템 컴포넌트(Ability System Component, ASC) 는 모든 액터에 추가하여 GAS 함수 기능을 제공할 수 있습니다. 특정 액터의 스테이트를 추적하고 리플리케이션을 처리합니다.
UAttributeSet 어트리뷰트 컬렉션으로, 게임 메커니즘 내에서 특정 의미를 지닌 숫자 값입니다. 어트리뷰트는 ‘체력'과 같은 게임 리소스나, ‘기본 공격력(Base Attack Power)' 같이 다른 게임 규칙에 영향을 줄 수 있는 레퍼런스 값을 나타낼 수 있으며, 심지어 ‘적용된 대미지(Applied Damage)' 같이 비상태성 양도 나타낼 수 있습니다. 어트리뷰트 세트는 하나 이상의 어트리뷰트 프로퍼티를 정의하고 관리하고 리플리케이트합니다.
FGameplayTag 게임 오브젝트에 적용할 수 있는 임의의 계층형 식별자입니다. 게임 엔티티를 식별하고 분류하고 필터링하는 데 사용할 수 있습니다. 게임플레이 이펙트 및 어빌리티에 의해 부여되거나 철회될 수 있으며, 그 행동에 영향을 줄 수 있습니다. 오너의 아바타나 폰에 대한 대미지를 방지하는 'Gameplay.DamageImmunity' 태그를 예로 들 수 있습니다.
UGameplayAbility 요구 사항과 비용, 기타 행동을 결정하기 위한 정보와 함께 GAS가 활성화된 액터에 부여되고 해당 액터가 수행할 수 있는 게임 액션입니다. 기본 근접 공격부터 독립형 게임 메뉴 흐름, 다른 게임 액션으로 인해 트리거되는 행동 등 다양한 예가 있습니다.
UGameplayEffect 게임 액션의 결과입니다. 어트리뷰트를 일시적으로 또는 영구적으로 수정하고, 태그를 부여하거나 철회하고, 다른 어빌리티에 대한 액세스를 활성화하는 등, 다양한 이펙트를 적용할 수 있습니다. 게임플레이 이펙트는 GAS가 활성화된 액터끼리 상호작용하는 가장 일반적인 방법입니다.

 

LyraAbilitySystemComponent

AbilitySystemComponent와 관련된 클래스들

 

LyraGameState

어빌리티 시스템 컴포넌트를 가지며, 게임 페이즈를 어빌리티로 구현한다.

GameState는 클라이언트 측과 서버 측 모두에 존재하지만, 게임 모드는 서버 측에만 존재한다.

 

ULyraGamePhaseAbility 클래스를 정의한다. 어빌리티의 활성화는 해당 페이즈의 시작을, 비활성화는 페이즈 종료를 나타내게 된다.

 

이전 페이즈 어빌리티를 종료하고(실행 중인 경우) 새 페이즈 어빌리티를 활성화하는 LyraGamePhaseSubsystem을 사용하여 블루프린트에서 페이즈를 전환할 수 있다.

 

LyraAbilitySet (데이터 애셋)

라이라에서는 GameplayAbility 플러그인에서 제공하는 GameplayAbilitySet 클래스 대신에 LyraAbilitySet을 새로 정의하여 사용한다. (Primary Data Asset을 상속한다) 

 

GameplayAbilitySet 은 단순히 고정된 (키입력 - Ability) 바인딩 배열과 어빌리티 부여 함수만 존재하는데

LyraAbilitySet은 어빌리티를 Input Tag와 바인딩한다. AbilitySystemComponent에 Ability 외에도 Attribute, Effect를 부여할 수 있고 부여 후 핸들을 반환하여 나중에 따로 따로 제거할 수 있도록 설계되어 있다.

 

여기에 정의된 GiveToAbilitySystem함수는 AbilitySystemComponent에 어빌리티를 부여하는 함수이다.

Lyra AbilitySet 클래스

다양한 방법으로 Ability Set을 부여할 수 있다.

1. GameFeatureAction_AddAbilities 게임 피처 액션 활성화 시 액터에 어빌리티 세트를 부여할 수 있다.

2. LyraEquipmentDefinitions가 추가되는 액터에 어빌리티 세트를 부여할 수 있다. Experience가 로드될 때 LyraPlayerState에 의해 부여된다.

3. LyraExperienceDefinition(데이터 애셋)에서 PawnData를 참조하는데 AbilitySet은 PawnData애셋에 정의된다.

2번 경우와 마찬가지로 Experience가 로드될 때 LyraPlayerState에 의해 부여된다.

 

LyraPawnData (데이터 애셋)

Pawn (캐릭터) 하나와 관련된 여러가지 데이터 애셋을 묶어서 들고 있는 데이터 애셋이다. 

Experience가 로드될 때 ULyraPlayerState에 의해 적용된다.

 

PawnClass : 폰 클래스

AbilitySets : 부여할 어빌리티 집합 

TagRelationshipMapping : 이 폰에 의해 발생하는 액션에 대한 태그 관계 정보 배열

InputConfig : Input Action과 GameplayTag의 매핑 정보

DefaultCameraMode : 카메라 모드 정보

 

HeroData_ShooterGame 애셋 (PawnData)

 

LyraAbilityTagRelationshipMapping (데이터 애셋)

.LyraAbilityTagRelationshipMapping.h

다음은 하나의 태그에 대한 관계 정보이다. FLyraAbilityTagRelationship이라는 구조체로 정의되어 있다.

AbilityTag는 기준 태그를 뜻하고 아래 Block, Cancel, Required, Blocked는 각각 태그 집합을 뜻한다. 

 

TagRelationships_ShooterHero 애셋

에디터에서 확인한 TagRelationships_ShooterHero 애셋을 보면 여러가지 태그에 대한 관계 정보를 확인할 수 있다.

Ability.Type.Action.WeaponFire 태그를 예시로 보면 이 태그가 활성화되어 있으면 Emote와 Reload가 Block되며, 

Emote와 Reload가 활성화 된 상태에서 이 태그가 활성화되면 Emote와 Reload가 Cancel 될 수 있다는 것을 알 수 있다.

 

LyraGlobalAbilitySystem

게임에 존재하는 모든 AbilitySystemComponent를 추적하고 상호작용할 수 있도록 해주는 서브시스템이다.

어빌리티 시스템 컴포넌트는 초기화 중에 자동으로 서브시스템에 등록된다.

이 서브시스템은 모든 등록된 어빌리티 시스템 컴포넌트에서 어빌리티와 게임플레이 이펙트를 제거하거나 부여하는 블루프린트 호출 가능 함수를 제공한다.

 

LyraGameplayAbility

라이라에서는 AbilitySet의 경우와 비슷하게 Ability 클래스도 새로 만든다. 하지만 이전과 달리 기존의 Gameplay Ability를 상속받아 구현한다.

Ability 클래스의 계층 구조

 

기본 Gameplay Ability 클래스의 기능
GameplayAbility의 중요한 함수들

 

커스텀 Ability 클래스에서 편리하게 느껴지는 함수들은 여러 종류의 Getter 함수들이다.

부모 클래스(Gameplay Ability)의 CurrentActorInfo에 접근하여 OwnerPlayerController, AbilitySystemComponent, AvatarActor, SkeletalComponent 등 오브젝트를 필요한 타입으로 캐스팅하여 반환한다.

예를 들어 아래와 같은 구문은 Ability로부터 사용자 캐릭터의 포인터를 반환한다.

return (CurrentActorInfo ? Cast<ALyraCharacter>(CurrentActorInfo->AvatarActor.Get()) : nullptr);

 

LyraGameplayAbility 클래스에 선언된 다양한 Getter 함수들

 

어빌리티의 활성화 그룹

LyraGameplayAbility의 활성화 그룹

활성화 그룹(Activation Group)은 어빌리티가 자유롭게 활성화될 수 있는지 아니면 해당 어빌리티가 다른 전용 어빌리티를 차단하거나 중단할지를 결정한다. TagRelationship 애셋을 적용하여 복잡한 로직을 쉽게 적용할 수 있다.

 

1. Independent

ㄴ 어빌리티가 다른 어빌리티를 차단하거나 대체하지 않는다. (예시 : 리듬 게임에서 여러개의 키를 동시에 눌러도 모든 키가 정상적으로 입력됨)

 

2. Exclusive_Replaceable

ㄴ 어빌리티가 다른 Exclusive 어빌리티를 block하지는 않지만, 다른 Exclusive 어빌리티의 활성화에 의해 Cancel 될 수 있다. 

 

3. Exclusive_Blocking

ㄴ 어빌리티가 실행되는 동안 다른 Exclusive 어빌리티는 활성화될 수 없다. (Blocking) (리더보드나 인게임 메뉴 같은 경우 한 번에 하나만 보여야 하므로 Exclusive_Blocking으로 세팅될 수 있다)

 

어빌리티의 활성화 정책

라이라 프레임워크에 의해 어빌리티 활성화를 자동으로 처리하도록 하는 정책이다.

 

1. None : 수동 활성화

2. OnSpawn : 플레이어 스테이트에 유효한 아바타가 활당되는 즉시 활성화, 폰이 빙의 해제하기 전까지 활성화 유지

3. OnInputTriggered : 관련 입력 태그가 트리거되는 즉시 어빌리티가 한 번 활성화된다. 

4. WhileInputActive : 입력 태그가 트리거되는 한 지속적으로 어빌리티를 활성화

ㄴ Retrigger Instanced Ability가 False로 설정되어 있는 경우에는 중복 활성화 메세지가 무시되어 입력이 활성화중인 상태라면 활성화-종료-활성화-종료를 반복한다. 반대로 말하면 True인 경우에는 중복으로 활성화가 일어나 원치 않는 결과가 발생할 수 있다는 뜻이다.

 

K2 접두어가 붙은 함수

K2 접두어가 붙은 함수는 블루프린트와 C++ 모두에서 사용할 수 있다.

Ability를 활성화할 수 있는지 검사하는 함수를 C++이 아닌 블루프린트에서 구현할 수도 있다.

대부분의 라이라 Ability 블루프린트들은 ActivateAbility를 오버라이드 하여 ActivateAbility 내부에서 조건을 확인하고 조건에 따라 CommitAbility-EndAbility 혹은 EndAbillity 노드를 실행하는 방식인 것 같다.

 

Ability 사용 시 필요한 비용

일반 GameplayAbility는 비용으로써 단일 비용 및 쿨다운 게임플레이 이펙트만 허용한다.

LyraGameplayAbility는 다중 비용을 제공하기 위해 LyraAbilityCost 배열을 포함한다.

AdditionalCosts 프로퍼티는 게임 플레이 이펙트로 설정하지 않고 지정할 수 있는 추가 비용을 제공하여 기본으로 제공되는 조건에 더해 복잡한 활성화 조건을 만들 수 있게 한다.

LyraAbilityCost 클래스를 C++에서 확장하고 CheckCost 및 ApplyCost 가상함수를 오버라이드하여 커스텀 비용을 생성할 수 있다.

 

LyraPlayerState에는 GameplayTagStackContainer 프로퍼티가 정의되어 있는데 이것은 각 태그마다 int 값 할당 정보를 저장하는 컨테이너 역할을 한다.

라이라는 이 프로퍼티를 사용하여 지정된 아이템(= 특정 아이템 태그를 가진)을 주어진 양(스택) 만큼 소모하는 커스텀 Cost 클래스를 구현했다. GA_Weapon_Fire_Shotgun 어빌리티 같은 경우에는 Lyra.ShooterGame.Weapon.MagazineAmmo로 설정된 아이템 태그 스택을 추가 비용으로 사용한다.

추가 비용 배열에 ULyraAbilityCost_ItemTagStack을 하나 추가한 모습

 

어빌리티 추가/제거 시 이벤트

라이라 어빌리티는 블루프린트에서만 구현할 수 있는 이벤트를 제공한다. 활성화에 의존되지 않기 때문에 초기화나 마무리 작업에 유용하다.

1. OnAbilityAdded : 어빌리티가 부여되는 즉시 이벤트가 호출, 아바타나 Input 컴포넌트가 아직 유효하지 않을 수 있다.

2. OnPawnAvatarSet : 폰이 완전히 초기화되고 아바타와 Input 컴포넌트가 유효한 경우 호출

3. OnAbilityRemoved : 폰이 빙의 해제되거나 파괴되는 등의 이유로 ASC에서 어빌리티가 제거되려고 할 때 호출

 

어빌리티 Type Tag

라이라의 어빌리티를 계층적으로 분류하기 위해 AbilityTags 프로퍼티에 GameplayTag들을 포함한다.

이 태그들은 다른 어빌리티의 Block, Cancel, Require 세팅 관리와 TagRelationship 시스템을 통해서도 사용된다.

 

액션 어빌리티

Ability.Type.Action.Dash

Ability.Type.Action.Jump

 

패시브 어빌리티

Ability.Type.Passive.AutoRespawn

 

예를 들어 캐릭터가 죽으면 모든 액션 어빌리티를 Cancel시키는데, 이때 패시브 어빌리티는 영향을 받지 않게 된다.

 

Native 어빌리티 서브클래스 (C++에서 구현하는 어빌리티)

일부 라이라 어빌리티는 특정 활성화 조건을 적용하거나 복잡한 수학 로직을 수행하기 위해서 어빌리티를 C++에서 구현한다.

ULyraGameplayAbility_Death 사망 게임플레이 이벤트(Death Gameplay Event)를 트리거하기 위해 자동으로 구성됩니다. 모든 다른 어빌리티를 취소하고 폰의 체력 컴포넌트(Health Component)에 신호를 보내 나머지 게임 알림 및 스테이트 변경을 차례로 트리거하는 사망 프로세스를 시작합니다. 비주얼 이펙트는 이 클래스(GA_Hero_Death)의 BP 어빌리티 익스텐션에 의해 수행됩니다.
ULyraGameplayAbility_Jump 어빌리티 오너가 유효하고 로컬에서 제어되는 폰인지 확인하면서 폰의 캐릭터 무브먼트 컴포넌트(Character Movement Component)에 Jump 및 StopJumping 입력을 트리거하는 함수 기능을 제공합니다.
ULyraGameplayAbility_Reset 활성화되면, 이 어빌리티는 즉시 소유 플레이어를 초기 스폰 스테이트의 새 폰으로 리셋하고 모든 다른 어빌리티를 취소합니다.
ULyraGameplayAbility_FromEquipment 라이라의 장비 시스템과 상호작용하고 어빌리티와 관련된 아이템을 검색하는 함수 기능을 제공합니다.
ULyraGameplayAbility_RangedWeapon 무기 발사의 네이티브 구현입니다. 관련 무기와 상호작용하여 탄약 수와 적중률 등을 결정합니다. 발사 원뿔 내에서 탄도를 계산하고 적중 타깃을 찾고 유효성을 검사하는 레이 캐스팅 함수 기능을 제공합니다.

Native 어빌리티 서브클래스를 사용한다고해서 블루프린트가 아예 포함되어있지 않다는 뜻은 아니다.

핵심 로직은 C++에 구현하되 블루프린트를 어빌리티를 확장하는 용도로 사용한다.

 

LyraGameplayAbility_Death와 GA_Hero_Death

Death 어빌리티의 생성자
ActivateAbility 내부 구현, StartDeath()

LyraGameplayAbility_Death 클래스의 ActivateAbility함수는 ASC에 접근하여 AbilityTypesToIgnore 태그를 가진 어빌리티를 제외한 모든 어빌리티를 취소시킨다. 

StartDeath 함수

이후 HealthComponent에 신호를 보내 나머지 게임 알림 및 스테이트 변경을 차례로  트리거하는 사망 프로세스를 시작한다.

 

GA_Hero_Death

GA_Hero_Death 블루프린트 어빌리티에서는 비주얼 이펙트, 카메라 모드 관련 문제를 처리한다.

 

LyraGameplayAbility_Jump.cpp와 GA_Hero_Jump

GA_Hero_Jump에서는 Start AbilityState Task를 추가적으로 사용한다.

 

블루프린트 어빌리티 서브클래스 (블루프린트에서 구현하는 어빌리티)

 

GA_AbilityWithWidget

ㄴ 추가 UI 기능을 제공하는 모든 어빌리티의 베이스 클래스이다. 위젯 스테이트를 관리하므로 상태와 쿨다운, 기타 어빌리티 정보를 표시할 수 있다.

ㄴ OnAbilityAdded 이벤트는 UIExtension 서브시스템에 위젯을 등록하고 UIExtension 핸들을 보관한다.

 

GA_Melee

ㄴ Ability.Type.Action.Melee 태그가 활성화 되어있는 동안 Event.Movement.Melee 태그를 승인한다. 이 어빌리티는 InputTag.Weapon.Ads에 의해 트리거된다.

  1. 현재 장착된 무기를 찾고 관련 애니메이션 몽타주를 재생합니다.
  2. 권한을 확인(서버 전용)한 다음, 플레이어 앞에서 캡슐 트레이스를 수행합니다.
  3. 그 트레이스가 폰에 적중하면, 아군 사격을 피하기 위한 팀 비교와 레벨 지오메트리에 의해 타깃이 가려지지 않는지 확인하는 2차 확인 등의 추가 확인이 실행됩니다.
  4. 유효 적중이 등록되면, 캐릭터가 루트 모션(RootMotion) 포스를 통해 타깃을 향해 이동합니다.
  5. 다음으로 근접 대미지 게임플레이 이펙트가 타깃에 적용되고, 근접 적중 게임플레이 큐가 그 오너에게 트리거됩니다.
  6. 마지막으로, 멀티캐스트 RPC를 통해 근접 임팩트 사운드가 모든 클라이언트에서 재생됩니다.

 

GA_Weapon_Fire

ㄴ 발사 및 재장전 어빌리티는 LyraEquipmentDefinition 클래스에 의해 부여된다.

ㄴ ULyraGameplayAbility_RangedWeapon을 상속받는다.

ㄴ InputTag.Weapon.FireAuto (input binding)과 Input.Weapon.Fire 태그 (gameplay event)에 의해 활성화되며 Ability.Weapon.NoFiring 태그에 의해 금지된다.

ㄴ Ability.Type.Action.WeaponFire 태그가 활성화 되어 있는 동안 Event.Movement.WeaponFire 태그를 승인한다.

ㄴ OnAbilityAdded 이벤트는 게임플레이 태그(Ability.PlayMontageOnActivateFail.Message) 에 대한 리스너를 설정한다. 

탄약이 없는 상태에서 무기를 발사하려고 하면 해당 메시지를 수신하여 Dry Fire 몽타주를 재생한다.

 

AbilityActivation이 호출되고 캐릭터를 로컬에서 제어하는 경우, 네이티브 타기팅 트레이스 (PerformLocalTargeting 함수)를 수행하고 다음과 같은 순서로 타기팅 데이터를 빌드합니다.

  1. 어빌리티 비용(탄약 소모)은 기본적으로 커밋됩니다.
  2. 타기팅 데이터는 네트워크에서 예측되어 서버로 전송되며, 서버에서 ULyraWeaponStateComponent 에 의해 검증되고 확인됩니다.
  3. 타기팅 데이터가 서버에 의해 확인되면, BP 이벤트 OnRangedWeaponTargetDataReady 가 호출되고, 찾아낸 모든 타깃을 전달합니다. 이를 통해 어빌리티가 대미지를 적용하고 적중 이펙트를 재생할 수 있습니다.
  4. 발사 애니메이션 몽타주를 재생합니다.
  5. 타이머를 발사 간의 유효한 지연인 발사 딜레이(Fire Delay)로 설정합니다.
  6. 발사 딜레이나 타이머가 종료되면, 어빌리티가 종료됩니다. 추가 발사 시도는 활성화 로직에 의해 처리됩니다.

무기에 적중된 모든 타깃의 유효성이 검증되면 C++에서 On Ranged Weapon Target Data Ready 이벤트가 호출됩니다. 이 이벤트는 무기 오너의 발사 게임플레이 큐를 트리거하고, 그런 다음 첫 번째 hit을 파라미터로 전달합니다. 이 큐는 타깃에 적중할 때마다 반복되어 각 타깃 위치에 임팩트 게임플레이 큐를 재생합니다. 이 어빌리티에 권한이 있다면(서버), 각 타깃 적중 시 대미지 게임플레이 이펙트를 적용합니다.

 

GA_Weapon_ReloadMagazine

재장전 로직은 관련 무기의 세 가지 게임플레이 태그 스택을 중심으로 돌아간다.

Lyra.ShooterGame.Weapon.MagazineSize : 현재 무기의 탄창 하나에 허용된 최대 탄약 수

Lyra.ShooterGame.Weapon.MagazineAmmo : 현재 탄창에 남은 탄약 수

Lyra.ShooterGame.Weapon.SpareAmmo : 현재 탄창에 없는 남은 탄약 수

 

GameplayAbility.h에 선언된 K2_CanActivateAbility 함수를 블루프린트에서 오버라이딩 한다.

Lyra에서 인벤토리 아이템은 LyraInventoryItemInstance라는 오브젝트로 관리되는데 FGameplayTagStackContainer 프로퍼티를 갖고 있기 때문에 여러가지 태그들을 stack이라 불리는 int형 자료와 매핑하여 보관한다.

 

K2_CanActivateAbility의 구현 부분, return value (bool 값)와 블루출력 매개변수 Relevant Tags를 출력 매개변수로 사용하여 결과를 전달한다.

 

InventoryItemInstance에서 각각의 태그의 스택(int) 값을 확인하여 조건에 따라 3가지 상태로 나눠진다.

1. 재장전 가능 상태, true 값 반환

2. 탄창이 가득 차서 재장전이 불가능한 상태 (Ability.ActivateFall.MagazineFull)

3. 예비 탄약이 없어서 재장전이 불가능한 상태 (Ability.ActivateFall.NoSpareAmmo)

 

어빌리티 활성화 시:

  • 관련 아이템의 MagazineAmmo 스택 수를 확인합니다. 현재 탄창에 남은 탄약이 없다면, 태그를 적용하여 무기 발사 어빌리티 활성화를 억제합니다.
  • 무기 재장전 애니메이션 몽타주를 재생한 다음, GameplayEvent.ReloadDone 이벤트를 리스닝합니다. 이 이벤트는 마네킹 몽타주의 애니메이션 노티파이(Animation Notify)를 통해 전송됩니다.
  • 이벤트가 수신되면, 권한(서버)을 확인한 다음, 단순하게 캐릭터 인벤토리에서 관련 무기의 Lyra.ShooterGame.Weapon.MagazineAmmo 및 Lyra.ShooterGame.Weapon.SpareAmmo 값을 변경하는 재장전 로직을 수행합니다. 그런 다음, 어빌리티를 종료합니다.
  • 어떤 이유로든 이벤트가 처리되지 않았다면, 몽타주가 중지되거나 중단될 때 어빌리티가 로컬에서 종료됩니다. On End Ability 이벤트가 호출되어 활성화 시 설정된(앞서 설정된 경우) 사격 억제 태그를 제거합니다.

 

GA_Grenade

OnPawnAvatarSet 이벤트에서 로컬 클라이언트의 UI Extension 서브시스템에 위젯을 등록

ActivateAbility 이벤트에서는 어빌리티 비용과 쿨다운을 확인하고 Commit한다. 실패하면 어빌리티를 종료한다. 다음과 같은 프로세스를 수행한다.

GE_Grenade_Cooldown를 쿨다운 이펙트로 사용한다.

 

  1. 어빌리티 비용과 쿨다운을 커밋합니다.
  2. 수류탄 스폰 위치와 회전을 계산합니다.
  3. 권한을 확인합니다. 권한이 서버에 있으면, 계산된 값으로 수류탄 액터를 스폰하고 소유 라이라 캐릭터를 인스티게이터로 설정합니다.
  4. 스폰된 B_Grenade 액터는 폭발을 확인하고 관련 게임플레이 이펙트를 적용합니다.
  5. B_Grenade는 적 폰과 충돌 시 자동으로 폭발합니다. 유발 캐릭터(수류탄을 던진 캐릭터)에 아군 사격을 적용하지만, 해당 팀원에게는 적용하지 않습니다.
  6. 수류탄 투척 몽타주를 재생한 다음, 관련 위젯이 쿨다운 표시를 동기화할 수 있도록 게임플레이 메시지 서브시스템을 통해 남은 쿨다운 시간을 브로드캐스팅합니다. (Ability.Grenade.Duration.Message 채널로 LyraInteraction DurationMessage 메세지를 브로드캐스팅)
  7. 몽타주 완료를 기다리지 않고 즉시 어빌리티를 종료합니다.

 

GA_ADS (Aim Down Sights)

활성화된 동안 로컬에서 예측된다. (클라이언트에서 즉시 실행된 다음 서버에서 동기화하여 따라간다.)

  1. 커스텀 카메라 모드를 적용하여 필드 오브 뷰(FOV)를 좁힙니다.
  2. 캐릭터의 걷기 속도를 캐시하고 오버라이드합니다. 어빌리티가 로컬에서 예측되므로, 소유 클라이언트와 서버에서 실행한 다음, 로컬이 아닌 클라이언트에 리플리케이트합니다.
  3. 임시 입력 매핑 컨텍스트를 적용하고 배수가 더 작은 입력에 그 이동 입력을 오버라이드합니다. 입력 규모를 줄임으로써 추가 이동 입력은 더 작은 가속을 생성하고, 가속 값이 더 작은 서버에 복제되어 캐릭터가 조준하는 동안 걷게 합니다.
  4. 로컬 플레이어의 경우, UI를 업데이트하고 ‘조준 시작' 사운드를 재생합니다. (Ability.ADS.Message 채널로 Struct_UIMessaging 구조체를 전달)
  5. 입력 버튼을 놓을 때까지 기다렸다가, 버튼을 놓으면 어빌리티를 종료합니다.

활성화 도중에 우선순위가 더 높은 Input Mapping Context를 Enhanced Input Local Player Subsystem에 포함하여 활성화 중에 플레이어의 Aim Speed를 오버라이드 하며, EndAbility에서 InputMappingContext를 제거한다.

 

GA_Hero_Dash

 

GE_HeroDash_Cooldown 게임플레이 이펙트는 액터에 GameplayCue.Character.Dash.Cooldown 태그를 적용한다.

 

  1. 어빌리티 비용을 확인합니다. 비용을 지불 가능하면 비용을 커밋하고, 아니면 어빌리티를 종료합니다.
  2. 로컬 컨트롤을 확인하고, 서버에서 어빌리티를 종료합니다.
  3. 로컬 클라이언트에서, 입력 및 시선 방향에 따라 질주 방향을 선택합니다. 이동 입력이 없다면, 이 어빌리티는 클라이언트에서 종료됩니다(질주는 질주 방향 입력이 있을 때만 발생합니다).
  4. 이동 방향과 캐릭터의 오리엔테이션에 따라 재생할 애니메이션 몽타주를 선택합니다.
  5. 캐릭터가 웅크리고 있다면 웅크린 자세에서 벗어납니다.
  6. 어빌리티에 권한이 없다면(로컬 클라이언트), 서버 RPC를 통해 질주 방향과 선택한 몽타주를 리플리케이트합니다.
  7. 소유 클라이언트와 서버 양측에서 선택된 몽타주를 재생한 다음, 질주 방향으로 루트 모션 포스를 적용합니다.
  8. 메시징 서브시스템을 통해 메시지를 전송하여 클라이언트 측 UI에서 쿨다운 시간을 동기화할 수 있게 합니다. (Ability.Dash.Duration.Message 채널로 LyraInteractionDurationMessage 메세지를 브로드캐스팅)
  9. 서버에서 질주 이펙트 게임플레이 큐를 트리거하여 모든 클라이언트에 리플리케이트되도록 합니다.
  10. 루트 모션 포스가 완료되면, 추가로 잠시 딜레이한 다음, 어빌리티를 종료합니다. 이렇게 하면 추가 지연 시간 동안 부여된 어빌리티 태그를 유지하고 사격이나 점프 같은 다른 액션이 제한합니다.

 

FLyraGameplayEffectContext

기본 GameplayEffectContext 구조체

 

GAS에서 제공하는 기본 GameplayEffectContext 구조체에서 확장되어 GameplayCueNotifies에 전송할 추가 데이터 멤버와 함수를 정의한다.

FGameplayEffectContext는 구조체 유형이므로, 언리얼과 게임플레이 어빌리티 시스템에서 제대로 인식할 수 있는 대체 유형이 필요합니다. 라이라 게임플레이 이펙트 컨텍스트는 다음과 같은 여러 함수를 오버라이드합니다.

 

Duplicate() HitResults 및 메모리 복사가 불가능한 다른 멤버에 대한 전체 복사를 수행합니다.
GetScriptStruct() FLyraGameplayEffectContext::StaticStruct() 를 반환하여 블루프린트에 올바른 리플렉션 데이터를 제공합니다.
NetSerialize() 정의된 모든 추가 멤버에 대한 리플리케이션을 추가합니다.

 

라이라 게임플레이 이펙트 컨텍스트에 대해 구조체 템플릿(TStructOpsTypeTraits<> )이 정의됩니다. 이 헬퍼 구조체는 복제 및 시리얼라이제이션 함수 기능을 모두 바인딩하여 리플리케이션 시스템에서 사용할 수 있게 만듭니다.

 

라이라 어빌리티 시스템 글로벌(Lyra Ability System Globals) (ULyraAbilitySystemGlobals) 클래스는 어빌리티 시스템 글로벌(Ability System Globals) (UAbilitySystemGlobals) 클래스를 확장하고, AllocGameplayEffectContext() 함수를 오버라이드하여 라이라 게임플레이 이펙트 컨텍스트 구조체를 구성하고 반환합니다.

이를 통해 게임플레이 어빌리티 시스템이 새 게임플레이 이펙트 컨텍스트 오브젝트가 생성될 때마다 확장된 구조체를 할당하도록 합니다.

 

추가 데이터에 액세스하는 방법

라이라 게임플레이 이펙트 컨텍스트 구조체는 구조체 유형이기 때문에 그 데이터에 액세스하기 위해 블루프린트 노출 함수를 직접 포함할 수 없습니다.

이 제한을 해결하는 방법은 접근자를 블루프린트 함수 라이브러리에서 스태틱 함수로 구현하고, 컨텍스트 핸들(Context Handle)을 입력 파라미터로 전달한 다음, 내부적으로 파생된 구조체 유형에 캐스팅하는 것입니다.

헬퍼 함수 FLyraGameplayEffectContext::ExtractEffectContext 는 특수 유형에 대한 컨텍스트 포인터 캐스팅을 처리합니다. 현재 이러한 방식을 FLyraGameplayAbilityTargetData_SingleTargetHit 구조체에서 사용하고 있습니다.

비슷한 방식을 'FGameplayCueParameters'를 통해 게임플레이 큐에 전달된 이펙트 컨텍스트 핸들을 캐스팅하는 데 사용하여 추가 함수 기능에 액세스할 수 있습니다.

 

ULyraAttributeSet, ULyraCombatSet, ULyraHealthSet

기본 AttributeSet을 상속받는 LyraAttributeSet을 사용한다.

ㄴ Get, Set, Initialize 함수 기능에 대한 값과 게임플레이 어트리뷰트 프로퍼티를 자동화하기 위한 편리한 ATTRIBUTE_ACCESSORS 매크로를 제공한다.

 

라이라에서는 다음 두 개의 특수 어트리뷰트 세트 클래스를 제공한다.

 

LyraHealthSet에는 캐릭터의 현재 체력과 최대 체력을 관리하는 어트리뷰트가 포함되어 있다.

Health 현재 체력 값으로, MaxHealth가 최대치입니다.
MaxHealth 최대 허용 체력 값을 결정합니다.
Healing 캐릭터에 적용된 치유량을 누적합니다. 체력 값에 영향을 준 다음, 자동으로 0으로 리셋됩니다.
Damage 캐릭터에 적용된 대미지 양을 누적합니다. 체력 값에 영향을 줍니다.

체력이 고갈되면 실행되는 FLyraAttributeEvent 델리게이트를 구현하는데, 게임에 체력 값을 노출하고 사망을 처리하고자 체력 알림을 수신하기 위해 이 델리게이트에 다른 클래스들이 바인딩된다.

 

LyraCombatSet에는 데미지 및 치유에 대한 지원을 제공한다. 

BaseDamage 대미지 실행 시 가해질 베이스 대미지 양입니다. 대미지 실행 계산에 입력으로 제공되어 실제 가해진 대미지를 결정합니다.
BaseHeal 치유가 실행될 때 회복되는 체력 양입니다.

 

치유 및 대미지 작동 방식

기본으로 체력 어트리뷰트는 모디파이어로부터 숨겨지므로, 정규 어트리뷰트 등의 게임플레이 이펙트에 의해 직접 변경될 수 없습니다. 대신, Healing 어트리뷰트와 커스텀 실행 ULyraHealExecution 및 ULyraDamageExecution 을 통해 간접적으로 값이 설정됩니다.

대미지와 치유를 어트리뷰트로 취급하면 수정된 어트리뷰트에서 개별 대미지 인스턴스를 분리할 수 있어 대미지 값으로 작업하는 것이 더 쉬워집니다. 이를 통해 중요 어트리뷰트를 실수로 수정하는 일을 방지할 수 있습니다. 체력 값은 게임플레이 이펙트 모디파이어로부터 숨겨지므로, 베이스 값 위에 기간 이펙트나 무한 이펙트를 적용하여 장기적인 측면에서 문제를 일으킬 위험이 없습니다.

 

치유 실행

ULyraHealExecution은 소스에서 BaseHeal 어트리뷰트의 스냅샷을 찍은 다음, 0으로 고정하여 마이너스 치유를 방지합니다. 마지막으로, 타깃(Target) 의 Health 어트리뷰트를 수정합니다. 이 작업은 소스에서 수행되므로, 체력 값이 문제없이 수정될 수 있습니다.

 

대미지 실행

ULyraDamageExecution은 BaseDamage 및 Health 어트리뷰트 값을 집계합니다. BaseDamage는 소스에서 캡처되고 스냅샷이 찍히지만, Health는 타깃에서 캡처됩니다. 그런 다음, 임팩트 위치를 확인합니다(이를 통해 대미지 수치 이펙트를 스폰할 위치를 파악합니다).

아군 사격을 위해 타깃의 팀을 확인하고, 거리와 피지컬 머티리얼에 따라 어테뉴에이션을 적용합니다. 마지막으로, 타깃의 Health 어트리뷰트를 수정합니다. 이 작업은 소스 코드에서 수행되므로, 체력 값이 문제없이 수정될 수 있습니다.

대미지 수치 게임플레이 큐는 체력 어트리뷰트 변경 사항을 그 크기로 수신하도록 설정되고 정규화되지 않은 원시 값에서 디스플레이를 파생합니다.

 

추가 정보

대미지 수치 작동 방식

대미지 어빌리티가 활성화되면, 테스트를 실행하여 무엇에 적중했는지 확인한 다음, 어빌리티가 타깃 액터에 대미지 게임플레이 이펙트를 적용합니다.

GameplayEffectParent_Damage_Basic (또는 거기서 상속된 모든 자손)은 무기 캐스트 및 수류탄에 의해 적용됩니다. 그러면, BaseDamage를 체력으로 변환하고 아군 사격을 필터링하는 실행을 통해 대미지가 적용된 다음, 게임플레이 큐 GameplayCue.Character.DamageTaken 의 크기가 LyraHealthSet.Health 큐에 적용됩니다.

GCN_Character_DamageTaken 이 클라이언트에서 호출됩니다. 이는 이펙트 인스티게이터가 로컬 플레이어인 경우에만 표시됩니다. 대미지는 게임플레이 큐의 원시 크기에 의해 결정됩니다. 위치는 적중 결과의 위치 값에 의해 결정되며, 위치 값은 로컬 컨트롤러의 ULyraNumberPopComponent에 전달됩니다. 이 컴포넌트는 디스플레이와 풀링을 처리하며, 적중 위치에 나이아가라 이미터를 스폰하고 구성하여 실제 대미지를 표시합니다.

 

블루프린트 애셋 명명 규칙

앞서 다른 튜토리얼 프로젝트에서는 일반적인 블루프린트 접두사를 BP로 사용했으나 여기에서는 B로 사용한다.

 

https://docs.unrealengine.com/5.0/ko/common-user-plugin-in-unreal-engine-for-lyra-sample-game/

 

일반 사용자 플러그인

일반 사용자 플러그인으로 C++, 블루프린트 스크립팅과 온라인 서브시스템(OSS) 또는 다른 온라인 백엔드 간 일반 인터페이스를 제공합니다.

docs.unrealengine.com

 

CommonUser 플러그인

CommonUser 플러그인은 OnlineSubsystem interface를 사용한다.

현재는 OSS 인터페이스v1 (비동기 명령과 오류 처리 부분이 개선되지 않음)가 default이다.
추후에 새로운 인터페이스v2 로 교체하려면 CommonUser.Build.cs를 수정하여 bUseOnlineSubsystemV1 boolean 값을 false로 변경하고 게임의 Config/DefaultEngine.ini 환경설정 파일로 가서 다음을 추가하자.

[/Script/Engine.OnlineEngineInterface] 
bUseOnlineServicesV2=true

현재는 로그인, 세션, 인증과 같은 핵심 작업들만 지원하지만 나중에 업적, 순위표를 위한 기능도 업데이트 한다고 한다.


라이라는 C++ 블루프린트 모두에서 Common User 플러그인과 상호작용한다.
라이라 프로젝트에서 CommonUser 플러그인 사용 예시


1. CommonGameInstance 클래스를 상속받는 LyraGameInstance 서브클래스를 작성하여 사용한다. (CommonUser 및 CommonUI 플러그인의 기능을 사용할 수 있다). 클래스 안의 코드는 프로젝트를 다른 UI 시스템과 통합되게 만들어준다. 

ㄴ User 서브시스템의 각종 델리게이트(OnHandleSystemMessage, OnUserPrivilegeChanged) 에 사용자 정의 함수를 추가하고, UserState들과 Session을 초기화하는 함수를 정의한다.

2. LyraFrontEndStateComponent 클래스는 소스 코드로부터 직접 CommonUserSubsystem에 액세스한다.
CommonUI 플러그인을 사용하여 플랫폼별 메인 메뉴가 표시되기 전에 로딩 화면을 표시하는 로직을 가진다.

ㄴ User 서브시스템과 Session 서브시스템에 접근하여 UserState들과 Session을 초기화한다.

3. W_LyraStartup 을 비롯한 여러 위젯이 CommonUserSubsystem에 직접 액세스하여 사용자 작업을 처리하기 위해 함수 또는 비동기 노드를 사용한다. local player를 초기화하는 과정에서 CommonUserSubsystem을 사용한다.

ㄴ C++ 에서는 User 초기화에 사용하는 비동기 함수들을 UAsyncAction_CommonUserInitialize 클래스로 관리한다.

InitializeForLocalPlay 함수는 플랫폼 전용 로그인과 권한 체크 (유효한 라이센스인지? 차단된 계정인지?) 를 진행하며, 그러한 과정이 끝나면 OnInitializationComplete 델리게이트를 broadcast한다.

LoginForOnlinePlay 함수는 존재하는 유저를 플랫폼 전용 온라인 백엔드 서버에 로그인 시도하며, 과정이 끝나면 OnInitializationComplete 델리게이트를 broadcast한다.

ㄴ 블루프린트에서도 노드로 이러한 함수를 호출할 수 있다.

 

4. LyraUserFacingExperienceDefinition (ULyraUserFacingExperienceDefinition ) 클래스는 메뉴에서 어떤 옵션들을 가지고 세션을 만들지 정의한다 (Request를 정의한다). W_HostSessionScreen 같은 위젯은 이것을 사용하여 게임 세션의 호스팅 또는 참여를 위해 정의한 Request를 CommonSessionSubsystem의 HostSession함수의 인자로 보낸다.


라이라는 Null 서브시스템을 디폴트로 사용하는데, 이는 에디터 또는 로컬 네트워크에서만 멀티플레이어를 지원한다. EOS 지원을 활성화하려면 패키지로 만든 빌드를 생성할 때 LyraGameEOS 타깃을 사용하거나 명령줄에 다음 옵션을 추가해야 한다.

 -customconfig=EOS


그러면 c:\Lyra\Config\Custom\EOS 디렉터리에서 EOS용 환경설정 파일이 로드됩니다. 이 환경설정 파일은 프로젝트 전용 EOS 식별자를 사용하도록 수정되어야 합니다.

 

CommonUser 플러그인의 서브 시스템

Common User Subsystem과 Common Session Subsystem은 CommonUser 플러그인에 포함된 서브 시스템들이다. 

 

CommonUserSubsystem

UGameInstance* GameInstance = UGameplayStatics::GetGameInstance(this);
UCommonUserSubsystem* UserSubsystem = GameInstance->GetSubsystem<UCommonUserSubsystem>();

 

ㄴ CommonUserSubsystem은 사용자 로그인, 인증, 권한 확인 작업을 처리하도록 설계되었다.
ㄴ OSS 인터페이스의 초기화를 처리하고 다른 서브시스템이나 게임플레이 코드에 대한 액세스를 제공한다.

ㄴ 개별 사용자의 상태를 설명하는데 사용되는 열거형과 구조체를 정의한다.


이 시스템에서 사용되는 주요 타입 
1. ECommonUserOnlineContext
ㄴ 로우 레벨 온라인 시스템 식별자

ㄴ 일반적으로는 Game Default Context를 많이 사용하지만 전용 인증 단계가 있는 플랫폼에서는 플랫폼 Context를 사용하여 접근할 수 있고 이후 EOS 같은 서비스에 서비스 Context를 사용하여 액세스하여 플랫폼 레이어에서 작동하는 크로스플레이 기능을 제공받을 수 있다.


2. UCommonUserInfo

UCommonUserInfo* FirstUser = UserSubsystem->GetUserInfoForLocalPlayerIndex(0)
ㄴ 시스템이 개별 사용자의 상태를 저장하는 용도의 오브젝트

ㄴ LocalUserInfo TMap에 의해 관리된다 <int32, UCommonUserInfo>


3. ECommonUserInitializationState

ㄴ 사용자의 전체적인 로그인 / 초기화 상태

User Initialization Flow (로그인 단계)를 거치며 상태가 변경된다.

UserInfo->InitializationState


4. ECommonUserPrivilege

게임 세션의 일부로서 유저가 수행할 수 있는 능력/권한 상태


5. ECommonUserAvailability

ECommonUserPrivilege(권한) 의 이용 가능 상태

 

User Initialziation Flow

  1. 게임을 플레이하려는 사람이 사용자로 식별되어야 합니다. PC나 모바일 등의 플랫폼에서는 게임이 시작되기 전에 이 단계가 발생하며, 사용자의 식별 정보가 런처 프로세스에서 전송됩니다. 다수의 로그인된 로컬 사용자를 지원하는 플랫폼의 경우, 참여 화면(시작 누르기)에서 플레이어가 게임을 플레이할 때 사용하려는 물리적 컨트롤러를 식별해야 합니다.
  2. 사용자는 플랫폼 인터페이스를 사용해 로그인하고 인증받아야 합니다. 처음 식별된 사용자에게 이 단계를 자동으로 수행하는 경우도 있지만, 플랫폼별 화면에서 플랫폼 사용자에게 물리적 컨트롤러를 명시적으로 매핑해야 하는 경우도 있습니다.
  3. 사용자의 권한을 쿼리해 게임을 플레이할 권한이 있는지 판단해야 합니다. 이러한 권한은 유효한 소프트웨어 라이선스 또는 차단된 계정에 대한 플랫폼별 확인을 포함합니다.
  4. 로컬 전용 로그인(LoggedInLocalOnly)된 초기화 상태에 도달했습니다. 사용자는 로컬로 게임을 플레이할 수 있으며, 게임 저장과 같은 프로필 관련 작업에 온전히 액세스할 수 있습니다. 그러나 온라인으로 플레이하거나 다른 네트워크 권한을 사용하지는 못할 수도 있습니다. 온라인 초기화까지 완료하려면 추가적인 단계를 거쳐야 합니다.
  5. 사용자에게 다시 한번 로그인 및 인증해야 할 수 있으며, 이때 다른 온라인 서비스를 사용해야 할 수도 있습니다.
  6. 해당 사용자의 권한이 온라인 플레이를 위해 특별히 쿼리되어야 합니다. 여기에는 구독 활성 상태나 연령 제한에 대한 확인이 포함될 수 있습니다.
  7. 두 번째 권한 확인을 통과한 후에는 사용자가 온라인 플레이를 위해 온전히 초기화되며(온라인으로 로그인), 멀티플레이어 세션을 생성하거나 참여할 수 있습니다.
  8. 프로세스의 어느 단계에서나 사용자는 플랫폼에서 강제로 로그아웃될 수 있습니다. 그러면 게임 저장과 같은 프로필 액션을 더 이상 사용할 수 없으므로 게임을 재시작해야 합니다. 이 경우 대개는 전체 초기화 플로를 재시작해야 합니다.

Requesting User Initialization

Common User Subsystem에는 스테이트 머신이 내장되어 사용자가 초기화 프로세스의 어느 단계에 있는지 트래킹한다.

Initialize for Local Play Login for Online Play These Blueprint nodes can be placed in the Event graph of a Widget or other Blueprint to request a local-only or online login for a specific local player and physical controller. This is the simplest setup and it will activate the complete exec pin when initialization has succeeded or failed.
OnUserInitializeComplete A delegate that can be bound from either Blueprint or C++ and is activated whenever a user completes initialization.
TryToInitializeUser A function used to start a more advanced initialization process that will eventually execute the OnUserInitializeComplete delegate.
Listen for Login Key Input A function used to create an initial engagement screen(Press Start) or allow the capability to add controllers in local multiplayer. This is achieved by registering a temporary input handler that listens for certain controller buttons or keyboard keys, then calls TryToInitializeUser with the correct parameters.
LoginLocalUser A function that can be called from C++ if you want to manage the initialization manually..

위와 같이 여러가지 방법으로 사용자 초기화를 요청할 수 있다.

 

 

CommonSessionSubsystem

ㄴ 온라인 게임플레이 세션의 생성, 검색, 참여 기능에 액세스할 수 있는 단순한 인터페이스를 제공한다.

ㄴ 플랫폼 전용 매치메이킹 인터페이스 및 EOS 등의 크로스 플랫폼 시스템과 상호작용할 수 있는 작업에 플랫폼 독립적인 Wrapper를 제공한다.

ㄴ 멀티플레이어 세션은 Dedicated 서버혹은 게임을 플레이 중인 플레이어가 호스팅할 수도 있다.

ㄴ Listen서버(플레이어 호스팅 서버)는 P2P NAT traversal 형태로만 액세스할 수 있다.

ㄴ 모든 멀티플레이어 게임플레이 세션의 목적은 두 게임 인스턴스를 연결해 언리얼 엔진 네트워크 시스템의 모든 기능을 사용할 수 있게 하는 것이다.

 

Requesting Session Initialization

1. 세션 호스팅 (HostSession) 

ㄴ 로드할 특정 게임플레이 모드와 맵을 설명하는 호스팅 요청으로 호출할 수 있다.

ㄴ 백엔드에 세션을 등록할 필요 없는 싱글 플레이어 세션을 만드는데에도 사용될 수 있다.

ㄴ 세션이 만들어지면 지정된 맵이 로드되고 게임플레이가 시작된다.

ㄴ 요청과 호스팅하는 PlayerController의 LocalPlayer가 nullptr이 아니라면 정상 진행된다.

 

2. 세션 찾기 (FindSessions)

ㄴ 검색 요청과 일치하는 참여 가능한 세션의 목록을 온라인 시스템에 요청하기 위해 호출할 수 있다.

ㄴ Request 오브젝트는 세션 검색에 필요한 정보를 갖고 있으며 검색이 끝난 후에는 Request 오브젝트에 검색 결과 오브젝트가 채워지고, OnSearchFinished 델리게이트가 호출된다. 이 때 검색 결과 오브젝트를 사용하여 세션에 참여할 수 있다.

 

UCommonSession_SearchSessionRequest 오브젝트 내부에 배열로 검색 결과 오브젝트가 관리되고 있다.

세션 서브시스템에서 현재 검색에 대한 SearchSetting을 멤버로 가지기 때문에 아래와 같은 방법으로 검색 결과에 대한 직접 접근이 가능하다.

SearchSettings->SearchRequest->Results

 

3. 세션 참여 (JoinSession)

검색 결과 오브젝트 (UCommonSession_SearchResult) 를 인자로 넘겨주어 세션에 대한 연결, 참여를 시도한다.

성공하면 해당 피어 또는 Dedicated 서버에 연결되며 네트워크 클라이언트로서 게임 참여를 시도한다.

 

 

4. 세션 퀵 플레이 (QuickPlaySession)

ㄴ 위 3가지 세션 초기화 함수가 결합된 메서드이다.

ㄴ 호스트 파라미터와 일치하는 퍼블릭 세션을 찾는 프로세스를 시작(FindSession), 가능하다면 세션에 참여(JoinSession), 참여가 불가능하면 새로운 세션을 시작(HostSession) 

ㄴ Session 서브 시스템에서는 이렇게 기본적인 구현을 제공하지만 필요할 경우 퀵 플레이 함수를 오버라이딩 하면 된다.

 

Common User Subsystem과 라이라는 모두 EOS 로비 인터페이스를 사용하는 P2P 호스팅 세션을 검색 및 생성하도록 설계되었다. 이것은 에디터에서 실행하는 멀티플레이어 (Null OSS 사용)와 같이 동작할 수 있다.

다른 온라인 백엔드를 완전히 지원하기 위해서는 온라인 세션 검색 (FOnlineSessionSearch) 등의 일부 옵션을 변경해야 할 수도 있다.

라이라 샘플 게임은 UE5의 모듈식 구조 설계와 유용한 플러그인을 적용한 학습용 리소스이다.

샘플의 모든 부분을 하나하나 분석하기는 어렵지만 나에게 새로운 용어, 기술, 프레임워크를 이해하고자 글을 남겨 본다.

언리얼 한국 채널 공식 유튜브 영상을 보고 정리하긴 했으나 부족한 부분이 있을 수 있다. 

 

라이라 게임의 구성 요소

Gameplay

Modular gameplay setup

Multiplayer ability system

Inventory and equipment

Gameplay messaging

Ranged Wapons

Aim Assist

Hit impacts and number pops

Accolades

Team Management

Game phases

 

User interface

Build cross-platform UI and preview in editor

Seamlessly switch between Mouse+Keyboard, Gamepad, or Touch

Extension System

 

Audio

Audio mix and device settings

Dynamic music system

Contextual effects

 

Content creation

Level-building tools

Prototype grid materials

Manny and Quinn characters

Rich animation setup

Basic cosmetics system

 

User-facing options

Typical video and audio options

Rebindable controls

Accessibility features

Easy to add new settings

 

Shippability

Scalability across platforms

Localization pipeline

Loading screens

Asset validation and audits

Cheats and developer settings

 

Multiplayer / online

EOS and EAS integration

Online subsystem abstractions

 

 

모듈형 게임 방식

GameMode를 데이터 기반 방식으로 대체한다고 생각

피처를 다음과 같은 액터에 구성

Character / Pawn

Controller

Player State

Game State

 

게임 피처 플러그인 

일반적인 플러그인과 다르게 런타임에 활성화를 동적으로 컨트롤하는 게임 로직이 있다.

활성화되었을 때 수행할 액션을 표시한다.

애셋 매니저를 확장하고 스캔할 프라이머리 애셋 유형을 새로 정의할 수 있다.

 

액션

ㄴ 활성화 또는 비활성화됐을 때 필수 애셋을 미리 로드하고 코드를 실행하는 역할을 한다.

ㄴ 흔히 사용하는 액션은 Add Abilities, (폰에 능력 추가), Add Components, Add Widgets 이다.

 

Pawn Data

ㄴ 월드에 스폰할 플레이어 폰 클래스 종류

ㄴ 폰을 빙의할 때 사용할 디폴트 카메라 모드

ㄴ 폰이 할 수 있는 일 - 어빌리티 세트, 어빌리티 관계 매핑, 입력 설정

 

Ability Set

ㄴ 어빌리티 게임플레이 어빌리티, 이펙트, 어트리뷰트로 구성된 그룹

ㄴ 어빌리티는 캐릭터가 새로운 액티브 또는 패시브 어빌리티를 수행하도록 허용한다.

ㄴ 이펙트는 적용될 때마다 어트리뷰트를 변경한다.

 

User Facing Experience

ㄴ Primary Asset이라 런타임에서 애셋 매니저를 이용하여 검색할 수 있다. (애셋 매니저에 대한 인사이드 언리얼 비디오 참조)

 

Online Feautres

ㄴ 온라인 서브시스템을 사용

ㄴ 사용자 로그인, 인증

ㄴ 매치 호스팅, 매치 검색

ㄴ 빠른 플레이

ㄴ CommonUser CommonSession 플러그인

 

Scalability 

ㄴ 플랫폼 확장성 세팅

ㄴ 프레임 페이싱 모드 (콘솔, 모바일, 데스크탑)

ㄴ 콘솔 : 세밀한 비디오 세팅

ㄴ 모바일 : FPS와 품질간의 자동적인 트레이드 오프

ㄴ 콘솔 : 옵션 프리셋 제공

 

Manny & Quinn

ㄴ 메타휴먼 스켈레톤과 풍부한 애니메이션 구성

 

Animation setup

ㄴ 링크된 애니메이션 레이어를 사용하면 블루프린트를 모듈식으로 빌드할 수 있다.

ㄴ 동시에 모두 로드하지 못할 수 있는 애니메이션 레이어가 많은 게임에서 더욱 효율적으로 메모리를 관리할 수 있다.

ㄴ Distance matching과 stride warping을 사용하여 플레이어 이동속도에 맞춰 애니메이션을 조정

ㄴ Orientation warping을 사용하여 다양한 이동 각도에 맞춰 애니메이션을 조정

ㄴ Turn in place (제자리 회전) 시스템은 플레이어가 정지 상태에서 회전할 때 애니메이션을 더욱 자연스럽게 연출

상체를 회전하기 때문에 회전 반경이 너무 클 경우 리포지셔닝 애니메이션을 재생

ㄴ foot placement 시스템은 애니메이션 컨택트 부분 도중에 발이 표면을 관통하지 않고 표면 위에 있도록 조정한다.

 

Cosmetics and Team Colors

Cosmetic system

보이지 않는 애니메이션 스켈레톤에 임의 액터를 붙일 수 있다.

전체 메시를 교체하거나 모자나 배낭같은 개별 어태치먼트를 추가하는데 사용할 수 있다.

cosmetic의 게임플레이 태그에 따라 다른 애니메이션을 선택하도록 할 수 있다.

Team Color System

머터리얼 또는 나이아가라 파라미터를 현재 팀에 따라 구동할 수 있고 팀 변경을 살펴볼 수 있다.

런타임에서 팀 컬러 디스플레이 에셋을 편집하여 색을 미세조정하면서 어떻게 보이는지 확인할 수 있다.

 

Enhanced Input System

ㄴ 사용자가 키 입력을 지정할 수 있다.

ㄴ 게임 플레이 중에도 입력 시스템을 추가할 수 있다.

ㄴ 액션은 트리거나 모디파이어를 포함할 수 있다.

ㄴ 새로운 액션을 추가하려면 Key Mapping Context에 디폴트 키 바인드를 입력 액션에 적용하고 입력 환경설정에서 트리거될 태그를 정의하고 정의된 태그를 어빌리티와 연결해야 한다.

 

레벨 디자인

ㄴ 지오메트리 스크립트를 이용하여 레벨을 빌드할 수 있는 툴을 생성하고 프로덕션 과정에서 조정

ㄴ 구운 스태틱 메시 우클릭 > Swap to Generated Mesh > 스태틱 메시를 블루프린트와 게임 뷰포트를 통해 편집할 수 있다. 다시 구우면 된다.

 

레벨 만들기

ㄴ Contents / Tools / BakedGenerateMeshSystem의 콜드 스토리지 블루프린트는 구운 스태틱 메시 전체와 해당 스태틱 메시의 라이브 버전을 추적한다. 

ㄴ ShooterCore Content / Blueprint 에 어빌리티 스포너, 무기 스포너, 런치패드 등이 있다. 맵에 자유롭게 배치할 수 있다.

ㄴ 봇을 위한 NavMeshBoundVolume

ㄴ 에디터 설정 > 라이라 메뉴에서 코스메틱을 추가할 수 있다. 개발자 세팅에서는 스폰할 봇의 개수를 오버라이드할 수 있다.

 

어빌리티와 input tag가 동작하는 방식

ㄴ Experience > HeroData > AbilitySet > Ability

ㄴ 어빌리티는 태그를 기반으로 트리거된다. 대쉬의 태그는 InputTag.Ability.Dash (계층 구조)

ㄴ Experience 내에서 히어로 마네킹에 이 태그가 호출되면 해당 어빌리티가 트리거된다.

ㄴ 입력 환경설정에서 어빌리티와 Input Action(Enhanced Input system)과 태그를 링크한다.

ㄴ 입력 환경설정에서 InputAction과 태그를 링크하고, AbilitySet에서 어빌리티와 태그를 링크한다.

ㄴ 키를 누르면 링크된 태그가 폰에 호출되고, 폰은 태그와 연결된 어빌리티를 트리거한다.

ㄴ 폰은 input, input tag, ability만 받을 뿐이다. 나중에 폰 클래스를 바꾸더라도 어빌리티와 입력 시스템은 정상적으로 추가된다.

 

아이템과 장비가 처리되는 방식

아이템 (Lyra Inventory Item Definition)

ㄴ Fragment 여러 개로 구성된다. (장착 가능여부, UI, Stat Setting)

ㄴ 픽업한 아이템은 라이라 인벤토리 시스템에 추가된다.

장비 정의 (Lyra Equipment Definition)

ㄴ 아이템 인스턴스가 필요하다 (아이템의 종류, 장착시 이펙트, 장착한 플레이어에게 부여할 어빌리티 세트

ㄴ 어빌리티를 오버라이드하여 아이템 사용 시 발생하는 이펙트와 코스메틱 관련 사항을 결정

https://www.youtube.com/watch?v=30lakZ4s8FE 

 

https://www.youtube.com/watch?v=0lEr62LQFpk 

 

https://docs.unrealengine.com/5.0/ko/lyra-sample-game-in-unreal-engine/

 

라이라 샘플 게임

라이라 샘플 게임의 기술을 사용하여 언리얼 엔진 5 프로젝트를 개발하는 방법을 알아봅니다.

docs.unrealengine.com

 

+ Recent posts