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

 

객체 지향적 설계 (책임 주도 설계)

협력에 필요한 책임을 결정하고 객체에게 책임을 할당하자 
( <-> 협력 대상 )

 

스킬 컴포넌트

ㄴ 캐릭터가 가진 스킬 목록 보유 ( 새로운 스킬을 넣었다 뺐다 할 것이 아니기 때문에 컨테이너가 아닌 프로퍼티로 )

ㄴ 키보드 입력 관련 처리 ( <-> 캐릭터의 InputComponent를 참조해서 동작과 Bind 해줘야 한다.  )

ㄴ 키보드 입력, 활성화 상태에 따라 스킬 시전 ( <-> Skill )

 

스킬

스킬이 가져야 하는 공통 속성

ㄴ 활성화 상태 (스킬이 비활성화 상태인 경우에는 입력을 받아도 사용이 되지 않아야 함)

ㄴ 쿨타임 

ㄴ 지속시간

 

스킬이 가져야 하는 공통 행동

ㄴ 스킬 시전하기

ㄴ 스킬 멈추기

 

만들고자 하는 스킬의 능력 (스킬 상속 받기)

1. 슬로우

ㄴ 카메라 효과 조정 (<-> Camera 혹은 CineCamera 흑백 효과)

ㄴ 게임 시간 느리게? 만들기 (<-> Timer Manager 모든 플레이어에게 적용)

 

2. 이동기

ㄴ 캐릭터가 움직이는 방향으로 순간적으로 도약 ( ? )

ㄴ 캐릭터 이펙트 적용 (잔상 효과를 주고 싶다) ( 메시 컴포넌트 -> 머터리얼 ? )

 

캐릭터 컴포넌트
ㄴ 캡슐 컴포넌트 (루트 컴포넌트)
  ㄴ 무기 컴포넌트
ㄴ 스킬 컴포넌트

 

스킬 컴포넌트 클래스는 보유한 스킬들을 단순히 관리하는 컴포넌트이다. 기능만 필요할 뿐 계층 구조에 따른 위치 정보가 필요하지 않기 때문에 Actor Component 클래스를 상속하도록 한다.

 

계획 수정..

내가 만들고자 하는 기능 제작을 도와주는 템플릿을 Gameplay Ability System 플러그인에서 제공하기 때문에 새로 Skill Component를 만들지 않고 플러그인을 활용하도록 한다. 플러그인 사용 방법을 익히면서 내가 생각한 설계 방법과 다른 점이 있는지 살펴 보는 것이 더 낫다고 판단했다.

 

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

https://docs.unrealengine.com/5.0/en-US/gameplay-ability-system-for-unreal-engine/

 

Gameplay Ability System

High-level view of the Gameplay Ability System

docs.unrealengine.com

https://www.youtube.com/watch?v=YvXvWa6vbAA 

언리얼 공식 유튜브 채널에서 소개하는 어빌리티 시스템

https://www.youtube.com/watch?app=desktop&v=dLbWVQajnfk 

언리얼 공식 영상은 아니지만 설명이 잘 되어 있음

 

https://github.com/tranek/GASDocumentation#concepts-a

 

GitHub - tranek/GASDocumentation: My understanding of Unreal Engine 5's GameplayAbilitySystem plugin with a simple multiplayer s

My understanding of Unreal Engine 5's GameplayAbilitySystem plugin with a simple multiplayer sample project. - GitHub - tranek/GASDocumentation: My understanding of Unreal Engine 5's Gamepl...

github.com

 

GameplayAbility System과 interact하고 싶은 액터는 스스로 Ability System 컴포넌트를 가지거나 그 컴포넌트를 가진 다른 액터에 접근하면 된다.

 

이 액터는 캐릭터, 플레이어 스테이트, 플레이어 컨트롤러가 될 수 있는데, 내가 만드는 게임에서는 캐릭터가 죽고 리스폰하는 상황이 발생하기 때문에 캐릭터에 이 컴포넌트를 추가하게 된다면 죽을 때마다 능력에 대한 정보가 사라지게 된다. 캐릭터가 아닌 액터를 사용해야 한다.

 

기존에 BullettimePlayerState에서 각 플레이어의 킬/데스 정보를 기록했으므로 이 액터에 Ability System을 상속받도록 하고, 킬/데스 정보 관리를 통합시킬 생각이다. 또한 Gameplay Ability는 비동기적으로 캐릭터 애니메이션이나 파티클/사운드 이펙트 처리, 사용자 입력, Replication 기능도 제공한다고 하니, 여기저기에 만들어 놓은 기능들을 천천히 옮겨야겠다.

 

1. IAbilitySystemInterface 인터페이스를 상속하여 GetAbilitySystemComponent를 오버라이드 해야 한다.

ㄴ Getter 함수를 만들어 줘야 한다는 뜻이다.

 

2. 액터가 Ability를 사용하기 위해서는 Ability System 컴포넌트가 해당 Ability를 승인해야 한다.

ㄴ 어빌리티 시스템은 액터에게 승인해준 어빌리티의 리스트를 들고 있다.

ㄴ 어빌리티 시스템은 Attribute도 들고 있다.

 

3. 어빌리티는 FGameplayAbilitySpec 구조체로 정의되는데, 어빌리티 시스템 컴포넌트의 GiveAbility함수 호출을 통해 승인된다. 이 함수는 FGameplayAbilitySpecHandle을 반환하는데 이 구조체를 통해 어빌리티 기능을 취소시킬 수 있다.

어빌리티의 실행 주기

ㄴ CanActivateAbility : 실행 전에 실행 가능한 상태인지 확인한다.

ㄴ CallActivateAbility : 실행 가능한 상태인지 확인하지 않고 어빌리티 코드를 실행한다.ActivateAbility를 오버라이드 하여 커스텀 기능을 추가해야 한다.

Actor나 컴포넌트에서 사용하는 tick 함수 같은 처리 방식이 아닌 task를 사용하여 작업을 처리한다. Task들의 결과를 핸들링하기 위해 C++에서는 델리게이트를 사용하고, 블루프린트에서는 노드를 output execution pin에 연결하여 처리한다.

CommitAbility 함수는 Ability를 실행하는데 필요한 비용(마나, 쿨타임 적용 등)을 적용하는 함수이다.CancelAbility 함수는 Ability를 취소하는 함수이다. CommitAbility와 다르게 외부에서 호출가능하고 OnGamePlayAbilityCancelled를 오버라이딩하여 캔슬되었을 경우에 처리할 로직을 추가할 수 있다.

ㄴ TryActivateAbilityCanActivateAbility + CallActivateAbility

어빌리티 실행이 끝난 후에는 EndAbility를 호출해주어야 한다. (그렇지 않으면 어빌리티가 계속 동작하는 것으로 인식해서 미래에 이 어빌리티를 사용하지 못하거나 이 어빌리티가 막고있는 다른 어빌리티를 사용하지 못하게 된다.) 

 

4. 태그

ㄴ 어빌리티 사이에 어떻게 상호작용할지 결정하는데 사용된다.

ㄴ 각각의 어빌리티는 태그의 집합을 가진다.

ㄴ 태그는 계층 구조로 이뤄져 있다.

Cancel : 이 어빌리티가 실행되는 동안 이미 실행되고 있던 다른 어빌리티의 실행을 취소한다.

Block : 이 어빌리티가 실행되는 동안 태그가 매칭되는 다른 어빌리티들의 실행을 막는다.

 

5. 어빌리티 시스템 컴포넌트는 다양한 방법으로 어빌리티를 활성화할 수 있도록 다양한 함수들을 갖고 있다.

Input code

tag

AbilitySpecHandles

 

6. AttributeSet, Attribute

ㄴ UAttributeSet을 상속받는 커스텀 AttributeSet클래스를 정의한다. 그리고 어빌리티 시스템 컴포넌트를 가진 액터의 프로퍼티로써 위치하도록 한다.

ㄴ 액터의 BeginPlay에서 어빌리티 시스템 컴포넌트->GetSet<커스텀AttributeSet> 함수를 호출하여 AttributeSet을 초기화한다.

ㄴ 각각의 Attribute는 FGameplayAttributeData 구조체로 정의된다. (UPROPERTY로 정의해야 리플렉션 시스템에 보인다)

ㄴ 각각의 Attribute들에 대해 Getter와 Setter를 빠르게 만들 수 있는 매크로가 있다. +레퍼런스를 반환하는 Property Getter

ㄴ AttributeSet은 Attribute이 바뀌었을때 실행되는 PostGameplayEffectExecute 콜백 함수를 오버라이딩하면 편하다. 내부에서 어떤 속성이 바뀌었는지 확인하여 상황에 맞는 로직을 실행하도록 만들 수 있다.

ㄴ 체력이 최소 체력과 최대 체력을 벗어나지 않도록 하는 로직을 사용한다면 여기에 구현할 수 있다.

ㄴ 인게임 리액션을 발동시키는 로직을 사용한다면 여기에 구현할 수 있다.(캐릭터의 체력이 0이 되었을 때 캐릭터를 죽게하도록)

ㄴ 속성의 Default 값들을 설정하는데 데이터 테이블 에셋을 사용할 수 있다. 액터 블루프린트의 어빌리티 시스템 컴포넌트에서 설정 가능하다. 데이터 테이블은 AttributeMetaData 구조체로 만들어야 한다.

ㄴ 데이터 테이블에서 Row Name을 [AttributeSet 클래스 이름].속성 으로 지정한다.

ㄴ Attribute는 기준 값과 현재 값을 주시한다.

 

+ 주의할 점

AttributeSet과 Attribute는 블루프린트에서 생성될 수 없다 C++에서만 가능하다.

다른 Attribute에서 SetAttribute를 호출하지 말고 Gameplay effect System을 사용하자. (캡슐화)

어빌리티 시스템 컴포넌트는 최대한 높은 레벨의 액터에 붙여주자 (내 프로젝트의 경우 캐릭터 < 플레이어 스테이트)

AttributeSet을 아무데나 붙이지 말고 AbilitySystemComponent를 가진 액터에만 붙이자

 

7. GameplayEffects

ㄴ Attribute를 수정하는 역할을 한다.

ㄴ 지속시간 (즉발, 무한, 유한)을 갖는다.

ㄴ 블루프린트 에디터에서 지속시간, 적용 값 등을 조정할 수 있다.

ㄴ UGameplayEffectExecutionCalculation 클래스를 상속받는 커스텀 클래스 (예를 들면 DamageExecution 클래스)를 정의하고 Execute_Implementation 함수를 구현해주어야 한다.

ㄴ 이 함수의 내부에서는 인자를 통해 이펙트의 source와 target에 대한 액터, 태그 정보를 얻을 수 있고 그 정보들로 값을 계산한다.

ㄴ 계산한 값을 AddOutputModifer를 호출함으로써 타깃의 속성 값에 연산한다.

 

블루프린트에서 이펙트를 적용하는 방법

ApplyGameEffectToTarget에 소스, 타겟 어빌리티 시스템 컴포넌트를 넘겨주어 양쪽에 효과를 적용시킨다

ApplyGameEffectToSelf는 소스 어빌리티 시스템 컴포넌트를 넘겨주어 한 쪽에만 효과를 적용시킨다.

 

Effect의 주요 프로퍼티

ㄴ Duration

지속기간

ㄴ Modifiers and Executions

attribute를 어떻게 수정할 것인지, 어떤 계산식을 사용할 것인지

ㄴ Application Requirements

이펙트를 적용하는 조건 (어떤 태그가 있거나, 없거나, 무작위)을 정한다.

ㄴ Granted Abilities

액터에게 어빌리티를 부여

ㄴ Stacking

이미 버프나 디버프를 가진 타깃에 다시 적용할때와 같은 상황에서 어떤 정책을 사용하여 처리할 것인지

ㄴ Gameplay Cue Display

비주얼 이펙트 네트워크 친화적으로 다룰 수 있게 해줌

 

8. Gameplaycue

ㄴ 각각의 비주얼 이펙트가 Cue (GameplayCueNotifyStatic 혹은 GameplayCueNotifyActor 상속) 에 대응됨, 각각 태그를 설정함 (Static은 한번 실행되고 사라지는 이펙트에, Actor는 실제로 월드에 생성되어 액터처럼 다뤄짐)

ㄴ 액터큐의 경우 캐릭터에 attatch 하거나 인스턴스 복제관련 속성이 추가적으로 존재한다.

ㄴ 이펙트가 진행되는 동안 visual effect를 트리거하기 위해 사용

ㄴ Effect의 Display 속성에서 GameplayCue 태그를 설정해 두면 이펙트가 적용될 때 해당 태그를 가진 큐를 적용시킨다.

 

9. Ability 블루프린트와 GameplayAbilityTask

ㄴ 여러가지 종류의 Task가 존재한다. (Move to Location, Wait Gameplay Event, Wait for Confirm Input, Wait Delay 등)

ㄴ GameplayAbility Task는 Ability를 실행시키는 가장 대중적인 방법이다.

ㄴ Abiltiy task는 ability 블루프린트에서 노드로 표현된다. 

ㄴ Task 노드는 여러 개의 실행 경로를 가진다.

ㄴ 어빌리티가 활성화 되어 있는 동안 특정 유형의 태그가 있는 다른 능력이 발생하지 않도록 차단할 수 있다.

ㄴ 커스텀 task도 만들 수 있다.

ㄴ CommitAbility 노드는 AbilitySystemComponent에게 ability의 비용을 적용하라고 알려주는 역할이다.

ㄴ Wait Gameplay Event 노드를 생성하면 태그가 일치하는 이벤트를 기다린다 (listen). Event가 Received 되었을 때 이펙트 적용되도록 연결하여 이펙트를 처리할 수 있다. 

ㄴ 이벤트는 캐릭터나 플레이어 스테이트의 Send Gameplay Event to Actor 노드를 실행할 경우 전달된다.

ㄴ Apply Root Motion Jump Force 노드는 캐릭터 무브먼트 컴포넌트를 멈추고 이 노드를 대신 실행하게 한다. ability 실행 도중 캐릭터의 움직임을 제어할 수 있다.

ㄴ ApplyGameEffectToTarget 노드는 타겟을 Target Data라는 액터 배열을 인자로 받아 여러 타겟에 대해 이펙트를 일으킬 수 있다.

 

10. 레플리케이션

ㄴ 어빌리티의 내부 상태와 Gameplay Event의 레플리케이션을 지원한다.

ㄴ 어빌리티에는 멀티 플레이어 게임에서 어빌리티를 어떻게 레플리케이트 할 것인지에 대한 정책이 있다.

Local Predicted

ㄴ 서버가 클라이언트를 믿어줄 것이라 생각하고 클라이언트가 즉시 어빌리티를 실행, 빠른 반응성과 정확도

Local Only

ㄴ 클라이언트에서만 어빌리티가 실행됨

Server Initiated

ㄴ 서버에서 어빌리티가 실행되고 클라이언트로 전파되는 방식, 빠른 액션의 어빌리티는 Local Predicted 만큼은 부드럽지 않을 수 있다.

Server Only

ㄴ 서버에서만 실행되고 레플리케이트되지 않는 어빌리티, 어빌리티가 server-authoritative data에만 영향을 주도록 하고, 이 data들은 client로 replicate 되도록 한다.

 

11. Instancing Policy

ㄴ 어빌리티가 실행될 때마다 새로운 객체가 생성되기 떄문에, 어빌리티의 실행이 잦은 경우에 성능적인 문제가 생길 수 있다. 사용자가 기능성과 성능 사이를 타협할 수 있도록 생성 정책이 있다.

Instanced per Execution

ㄴ 실행할 때마다 객체 스폰, 블루프린트 그래프 사용 가능, 모든 멤버 변수를 기본값으로 초기화 & 접근 가능, 오버헤드가 크기 때문에 자주 사용하지 않는 어빌리티에 적합함

ㄴ 롤로 치면 평타는 부적합, 궁극기는 적합

Instanced per Actor

ㄴ 어빌리티가 처음 실행될 때 액터가 어빌리티의 인스턴스를 하나 생성하고 미래의 실행에서는 이것을 재활용

ㄴ 실행 사이에 멤버 변수의 값 정리가 요구된다.

ㄴ 레플리케이션에 이상적인 방법

Non-Instanced

ㄴ 가장 효율적으로 동작하는 정책

ㄴ 인스턴스를 새로 생성하지 않고 CDO를 사용한다.

ㄴ 레플리케이션, RPC 안됨

ㄴ 어빌리티가 C++로 작성되어야 한다.

ㄴ 어빌리티 실행 도중 멤버 변수 값을 변경할 수 없다.

ㄴ 자주 사용되고 많은 캐릭터에 의해 사용되는 어빌리티에 적합하다.

 

------------------------------------2----------------------------------------
시네 카메라
슈팅게임에서 데미지를 입었을 때 화면 가장자리를 붉게 만들기

포스트 프로세스 볼륨
오픈 월드 게임에서 월드 전체에 포스트 프로세스 효과를 추가하고 싶을 때 

영화적 기법
그레인 : 옛날 영화 필름효과 주기
비네팅 : 주변 어둡게
색수차 : 3색 분리 - 안티 앨리어싱의 효과로 사용 가능

bloom : 가우시안 블러 이용
렌즈 플레어 : 텍스처를 사용하는 이미지 기반 기법 화면 눈부시게 하는 효과

포스트 프로세스 머터리얼을 이용한 공포 영화 박동 효과 구현하기

포스트 프로세스 : 안티앨리어싱 함수 프로젝트 세팅에서 선택
-------------------------3--------------------------------------------
마스크 텍스처를 사용하여 하나의 머터리얼에 여러 재질을 표현하기

컬러 마스크

머터리얼 인스턴스 애셋 생성하여 적용해보기
ㄴ 기본 머터리얼 그래프가 같을 때 색상 러프니스 텍스처와 같은 것들을 변경하고 싶을 때
ㄴ 다이내믹 머터리얼 인스턴스를 사용하여 실행 중에 머터리얼 매개변수를 변경하기

작은 물체에 텍스처링하기
ㄴ Cheap Contrast, Custom Rotator 노드

모바일 플랫폼의 텍스처
ㄴ 최대크기 2K, 이미지의 변의 길이가 2의 승수이거나 크기가 정방형이어야 한다.
ㄴ 텍스처 Compression 속성을 Default나 Normal Map 옵션으로 설정 (메모리 사용량 줄이기)

플라스틱 질감의 천 표현하기
ㄴ Fresnel 노드
ㄴ Detail Texturing 노드 (엔진 컨텐츠 함수) (머터리얼의 질감을 향상시키기 위해 두 가지 다른 텍스처를 사용하는 노드)
ㄴ Texture Object 노드

반자동 절차적 머터리얼 생성하기
ㄴ 랜덤 패턴으로 둘 이상의 텍스처를 혼합하여 타일링이 덜 도드라져 보이게 한다.
ㄴ 최대한 다른 마스크를 사용
ㄴ Noise 노드 (앱의 성능 저하, 완전 자동 절차적 머터리얼, 무작위 패턴 생성)

머터리얼을 텍스처로 굽기
ㄴ 복잡한 원본 머터리얼 그래프의 결과를 단순한 텍스처에 구워서 원본
머터리얼 그래프의 복잡도를 제거하기
ㄴ Actor 블루프린트 (BP_MaterialBaker)-> DrawMaterialToRenderTarget 함수
ㄴ 베이킹할 머터리얼의 셰이딩 모델을 Unlit으로 변경하고 렌더 타겟에 그리기
ㄴ 렌더 타겟으로 텍스처 생성하기

+ 머터리얼의 성능문제 해결방법
ㄴ 1. 렌더 타겟의 해상도 조절로 해결
ㄴ 2. Merge Actors 툴을 활용하여 머터리얼을 단순한 버전으로 만들기

거리 기반 텍스처 혼합
ㄴ 머터리얼 그래프에서 카메라 위치와 정점 위치 거리를 이용하여
두 텍스처의 블렌드 알파 값을 계산
----------------------------------------4-----------------------------------------------
UV 채널 여러개?
SSR ( Screen Space Reflection ) 실시간 반사 효과
ㄴ 화면 공간 밖의 구역은 기하연산하지 못하다는 단점, 스크린 스페이스 바깥의 
물체는 반사하지 못하기 때문에 그런 부분들에 fade out 기법을 사용할 수 있다.
ㄴ Ray Marching을 사용한다. 레이 트레이싱에 비해 가볍지만, 현실적인 그래픽
ㄴ 여러가지 버퍼를 사용해 GPU에서 연산, 화면 내부에 있는 정보로만 표현

SSAO ( Screen Space Ambient Occlusion)

Subsurface scattering 머터리얼(SSS) (빛이 반투명한 물체 표면에 투과되어 내부로 퍼져나간 후 다른 위치로 빠져나가는 현상)
왁스 재질 (사람 피부, 촛불)
머터리얼의 셰이딩 모델을 Subsurface Profile로 설정
Opacity -> 투명도가 아닌 서브서피스 효과의 강도를 나타냄
Metallic -> 사용하면 안됨
Subsurface Profile 애셋 필요
귓볼같은거

+ 서브서피스 효과를 가능하게 하는 셰이딩 모델
Standard Subsurface 모델
Preintegrated Skin 모델
Subsurface Profile 모델
ㄴ 스크린 공간 기법 (더 좋은 품질 제공 (비용 증가))
Two Sided Foliage 모델

완전한 투명 유리 재질 만들기
셰이딩 모델 Translucent + Default Lit
프레넬 값을 사용하여
Refraction (굴절) - 1. 굴절 지수, 2. 굴절모드 Pixel Normal Offset로 설정하기
Opacity (투명도)
Reflection (반사) 값들 보간

홀로그램 - (투명, 텍스처 애니메이션, 이미션)
셰이딩 모델 Translucent + UnLit (빛과 머터리얼이 상호작용하지 않음)

이미시브 입력을 상요하는 머터리얼을 설정하는 것이 조명을 대신할 수 있다.
ㄴ 디테일 -> Use Emissive for Static Lighting

실사같은 반사 표현하기
1. SSR
2. Reflection Capture

3. planar Reflections
ㄴ 프로젝트 세팅 > Support global clip for Planar Reflections
ㄴ Planar Reflection 액터를 사용 > 장면을 두번 렌더링 하기 때문에 기본적으로
 비용이 많이 든다 대신 씬 캡처 기법

4. 씬 캡처 기법 (캡처링 리플렉션)
ㄴ 장면을 텍스처에 구워서 반사하는 물체에 제공
ㄴ Scene Capture Cube 액터를 사용 (카메라와 같음), 
ㄴ 렌더 타깃은 씬 캡처 큐브에서 바라보는 장면을 저장하는 텍스처
ㄴ 거울 머터리얼이 렌더 타겟을 텍스처로 사용하면 됨

풀장의 물 표현하기
Translucent 셰이더 모델
ㄴ Depth Fade 노드 ( 깊이가 깊어질 수록 투과율이 떨어짐 ) - 투명도 설정에 사용
스크린 공간 계산에 의존하는 기법이기 때문에 뷰포트를 통해만 효과를 확인할 수 있다.
ㄴ Panner 노드 ( 좌표계, 시간, 속도 입력으로 받음)
+ Pixel Depth 함수, Scene Depth 함수

물 화선 표현
라이트 함수를 사용해 화선 효과를 표현
라이팅 채널 : 특정 표면에만 영향을 주는 특수한 효과를 만들고 싶을 때
물이 담긴 욕조 메시의 라이팅 채널을 수정
스포트 라이트에 머터리얼(라이트 함수)을 할당

애니메이션되는 바다 셰이더 만들기 (큰 규모의 표면)
Motion_4WayChaos_Normal 함수 ( 노말 텍스처를 기반으로 무작위 애니메이션 동작을 생성하는 함수)

----------------------------------------------5----------------------------------------------

레벨에 조명으로 에미시브 머터리얼 사용하기
ㄴ 조명 계산에 포함됨
ㄴ == 스태틱 조명 (동적 그림자 생성 X, 조명 빌드 필요)
ㄴ 월드 세팅 라이트매스 속성 조절
ㄴ 레벨에서 에미시브 머터리얼이 블룸에 영향을 주는 방법
1. 레벨 > Post Process Volume > Bloom 세기 조절
2. 머터리얼의 Emissive Boost 속성 조절

화면에 인터넷에서 받은 비디오 재생하기
미디어 플레이어 애셋
미디어 텍스쳐 애셋
스트림 미디어 소스 에셋

CCTV 카메라 만들기
액터(웹캠) 추가
static mesh 루트 컴포넌트로 만들고 하위에 SceneCaptureComponent2D 지정, 렌더 타깃 생성
이벤트 그래프에서 Timeline 노드 사용해서 static mesh 주기적으로 회전하게 만들기

게임에서 상호작용 가능한 물체 하이라이팅하기
포스트 프로세스 머터리얼
포스트 프로세싱 파이프라인에서 포스트 프로세스 머터리얼이 적용될 단계는 Before Tonemapping
합성곱 연산을 사용해 외곽선 판별을 수행한다.
Scene Texture : CustomDepth = 어떤 메시를 보고 싶은지 결정할 수 있도록 레벨에 있는 각 모델마다 활성화할 수 있는 속성
ㄴ Rendering 카테고리의 Render Custom Depth Pass 속성

Scene Depth = 카메라에 보이는 픽셀과 카메라 사이의 거리
모든 픽셀은 Scene Depth와 Custom Depth를 가진다. Custom Depth는 따로 설정하지 않으면 무한대의 값

게임 속 나침반 만들기
배그 0 ~ 360
머터리얼 블렌드 모드 Masked > Alpha 채널을 사용해 보이는 것과 보이지 않는 것을 조절할 수 있다.
UI 이벤트 그래프 > Tick > 이미지의 Dyanmic Material Get > PlayerRotation 파라미터에 값 설정

미니맵 만들기
오버레이 위젯

--------------------------------------------------6-----------------------------------------
정점 칠하기 기능으로 메시 칠하기
ㄴ 물체에 특정 효과를 텍스처에 직접 칠하지 않고 적용하고 싶을 때
Mesh Paint Mode로 메시 정점에 브러시로 색을 칠할 수 있다.
Vertex Color 노드에서 정점의 색을 딸 수 있다.
거대한 크기의 표면에서 종종 나타나는 반복 패턴을 제거하는데 사용
+ 텍스처 칠하기도 사용한다.

데칼 사용해 레벨에 세밀함 추가하기
1. 표준 디퍼드 데칼
2. 메시 데칼 - 프로젝트 세팅 필요 Rendering > Lighting > DBuffer Decals
데칼 처리과정
1. 머터리얼 구현
2. 머터리얼 투영

1. 디퍼드 데칼
ㄴ Material Domain Deferred Decal로 설정
ㄴ Decal Blend Mode Translucent
2. 메시 데칼
ㄴ CameraDirection Vector 노드를 사용하여  World Position Offset 조절
ㄴ 단순한 평면이 아니라 어떤 메시 위에도 텍스처를 투영할 수 있다.

시차 차폐 매핑을 사용한 벽돌 벽 표현하기
ㄴ 기하 셰이딩 같은 거? 실제로 픽셀을 위, 아래로 움직여서 노멀 맵으로 표현할 수 없는 진짜 3D 효과를 만들 수 있다.
ㄴ Parallax Occlusion Mapping 노드
ㄴ DitherTemporalAA 노드
ㄴ HeightMap?
ㄴ Pixel Depth Offset

디스플레이스먼트를 사용한 벽돌 벽

메시 디스턴스 필드를 사용한 거리 기반 마스킹
ㄴ 레벨에 있는 폴리곤 사이의 거리에 따라 머터리얼의 질감을 바꾸기
ㄴ 프로젝트 세팅 > 엔진 > 렌더링 > Lighting > Generate Mesh Distance Fields
ㄴ 동적이기 때문에 레벨에서 물체가 움직이면 효과가 자동으로 갱신된다.

-----------------------------------------7---------------------------------------
머터리얼 레이어링을 사용한 물체 위에 눈이 쌓인 효과
ㄴ 실험적인 기능, 기능 활성화 필요 (프로젝트 세팅 > 렌더링 > Support Material Layers)
ㄴ 추가적인 픽셀 셰이더 명령어가 필요 없어 성능 향상이 있다.
ㄴ Material Layer 에셋 사용, Make Material Attribute 노드
ㄴ 두개의 머터리얼 레이어 사용, 머터리얼 레이어를 사용하는 머터리얼
ㄴ 머터리얼에 Material Attribute 노드 추가 > 레이어, 백그라운드 레이어, 블렌드 에셋 (WorldAlignedBlend 노드는 어디에 입힐지 결정)
ㄴ 캐릭터 피부의 색상 스키마

커브 아틀라스를 통한 빠른 계절 변화
ㄴ Material Parameter Collection 에셋 
ㄴ Curve Linear Color, Curve Atlas 에셋
ㄴ DeSaturation 노드, CurveAtlasRowParameter 노드, Blend_Overlay 노드
ㄴ 레벨 블루프린트에서 CreateDynamicMaterialInstance, SetScalarParameterValue, Delay 노드로
주기적으로 파라미터 (CurveAtlasRowParameter의 입력) 값을 바꿔준다. (0 ~ 1 사이)

랜드스케이프 머터리얼 혼합하기
ㄴ Landscape Layer Blend 노드 (랜드스케이프 모드에서 텍스처나 머터리얼을 레이어로 사용할 수 있다)

UV 커스터마이징
ㄴ 메인 노드의 CustomizedV UV0 속성
ㄴ 머터리얼 타일링과 애니메이션 구현에 좋음

----------------------------------------8-----------------------------------------
모바일 플랫폼 머터리얼 만들기
ㄴ 머터리얼 노드 > Fully Rough 체크하면 사용하는 명령어 개수 줄어듬
ㄴ Float 연산 정확도를 Full로 하면 비용이 많이 들기 때문에 
ㄴ Use Lightmap Directionality 속성 : 노멀 맵 사용시 효과가 명확하게 보이기 위해 비용을 사용한다.
ㄴ Forward Shading 카테고리의 High Quality Reflections 속성 비활성화하기
반사를 표시하는데 사용할 큐브맵을 사용하지 않는다.
ㄴ 되도록 Default와 Unlit Shading 모델을 사용하고 투명이나 마스크 셰이딩 모델은 계산적인 측면에서 비싸므로
사용하는 머터리얼 수를 제한해야 한다.

Forward Shading과 Deferred Shading?
ㄴ 디퍼드 셰이딩은 고퀄리티 리플렉션, 다수의 다이내믹 라이트, 릿 데칼(lit decal), 그리고 모바일용 디폴트 포워드 렌더링 모드에서는
대체로 사용할 수 없는 다른 고급 라이팅 기능을 지원한다.

셰이딩 렌더러를 사용한 VR 개발하기
ㄴ Forward Rendering 설정에서 활성화 (언리얼 엔진은 기본적으로 디퍼드 렌더러를 사용)
ㄴ 안티 앨리어싱 MSAA로 변경
디퍼드 렌더링은 VR 소프트웨어를 실행하는데 적합하지 않다. 성능 때문에

텍스처 아틀라스(= 스프라이트 시트)를 이용해 최적화하기
ㄴ 작은 이미지를 한곳에 모아 놓은 한 장으 ㅣ이미지
ㄴ TexCoord 노드의 UTiling과 VTiling 속성으로 이미지를 줌인 줌아웃하고 Add 노드로 오프셋을 조정

3D 모델이 적용된 머터리얼을 텍스처로 굽기
ㄴ 여러 머터리얼이 적용된 스태틱 메시가 레벨에 많으면 비용이 많이 들기 때문에 
하나의 물체에 하나의 머터리얼만 사용하여 한번의 드로우 콜만 일어나도록 셰이딩 결과를 텍스처로
구워서 복잡도를 낮출 수 있다.
액터 병합 툴 이용하기
ㄴ Tools > Merge Actor > Merge Materials 체크
ㄴ 텍스처 사이즈를 2048x2048로 설정하여 원본과 비슷하게 만들 수 있다.

HLOD 도구로 여러 메시 결합하기 
ㄴ 레벨에 개별적으로 배치된 스태틱 물체를 가져다가 하나의 메시로 결합해 드로우 콜을 줄일 수 있게 해준다.
ㄴ World Settings의 HLOD System 카테고리에서 레벨을 추가할 수 있다.
ㄴ HLOD Outliner 툴을 사용해 레벨에 존재하는 메시들을 클러스터로 그룹화한다. 프록시 메시를 빌드할 수 있다.
ㄴ 멀어질수록 = 레벨이 높을수록 더 많은 액터들이 합쳐진다. 합쳐지는 물체들을 둘러싼 구체의 크기가 크다.

HLOD를 사용혀려면 모든 레벨에서 활성화해야 한다. (월드 세팅)
HLOD는 스태틱 메시에만 사용할 수 있다.

일반적인 머터리얼 최적화 기법
ㄴ 머터리얼 퀄리티 레벨 시스템 사용하기
ㄴ 머터리얼 Quality Switch 노드
ㄴ 장치의 기능 레벨에 따라 특정 동작을 하게 하는 머터리얼을 만들 수 있다. (낮은 사양의 그래픽
카드를 탑재한 장치에서도 동작하게 할 때 셰이더에서 덜 복잡한 수학적 연산을 사용하도록)

------------------------------------------9----------------------------------------
동일한 모델에 무작위성 추가하기
ㄴ 모델의 인스턴스에 약간의 변화를 추가하여 보이는 방식을 변경하고 싶을 때
ㄴ Instanced Static Mesh 컴포넌트에 머터리얼 할당하고 Instance 배열에 요소 추가
ㄴ 머터리얼에 Per Instance Random 노드를 사용하여 인스턴스마다 다른 부동소수점 값을 사용할 수 있다..
ㄴ Camera Depth Fade 노드를 Opacity Mask에 연결 (카메라에 너무 가까운 물체를 숨김)
ㄴ Per Instanced Fade Amount 노드를 카메라에서 가까이 있는 모델 대신에 멀리 있는 모델을 숨기는 데 사용할 수 있다.
(카메라에서 인스턴스의 자체 위치에 따라서 값을 제공)

+ HISM (Hierarchical Instanced Static Mesh) 컴포넌트도 비슷한 상황에서 성능을 향상시키는데 사용되는 컴포넌트이다.
일반 스태틱 메시에 다른 레벨 오브 디테일을 생성해 인스턴싱함

차폐된 영역에 먼지 추가하기
ㄴ 조명 빌드가 선행되어야 한다 (라이트 맵이 필요) (레벨에 배치된 조명이 Stationary나 Static으로 설정되어 있어야 한다.)
ㄴ 월드 세팅 > Lightmass > Generate Ambient Occlusion 속성 켜기
ㄴ PrecomputedAOMask 노드 : 머터리얼에서 조명데이터를 이용하여 질감을 계산하도록 돕는 노드

+ 무버블 조명을 사용할 때는 다른 기법을 사용할 수 있다. 외부 소프트웨어에서 AO 텍스처를 생성하거나 Distance To Nearest Surface 노드와
같은 유사한 노드를 사용할 수 있다.

여러 메시를 가로지르는 텍스처 좌표 일치시키기
ㄴ 하나의 텍스처가 여러개의 메시에 자연스럽게 이어지며 입혀지도록 하는 방법
ㄴ 물체의 UV 좌표 대신 월드 좌표계에서의 각 픽셀 위치를 활용하여 텍스처를 입힌다.
ㄴ Absolute world Position노드와 PixelNormalWS 노드를 활용한다.

퀄리티 스위치를 통한 머터리얼 복잡도 조절하기 (UE4)
ㄴ Quality Swith 노드, Feature Level Switch 노드 (ue5는 메인 머터리얼 노드에 테셀레이션 멀티플라이어 입력이 없다..)
UE4에서 UE5로 넘어가면서 테셀레이션 관련 설정은 나나이트로 대체되었다. 메인 머터리얼 노드의 World Displacement 속성과
Tesselation Multiplier 속성이 없다.
+ 모바일 셰이더 모델 : ES2 ES3_1
PC : SM5 SM6

인테리어 큐브맵 사용해 건물 내부 텍스처링하기 (건물 내부를 실제적으로 보이게 하는 기법)
ㄴ Interior Cubemap 노드, 마스크 텍스처 사용하여 마스크된 영역에 큐브맵을 적용
ㄴ 큐브맵 생성은 큐브맵 도구를 사용해서 만들어야 한다.
https://docs.unrealengine.com/4.26/ko/RenderingAndGraphics/Textures/Cubemaps/CubemapTools/

완전 절차적 Noise 패턴 사용하기
ㄴ 비용이 상당하기 때문에 다른 애셋을 생성하는 수단으로 이 기법을 사용하는 것이 좋다.
ㄴ 다양한, 반복패턴이 없는 텍스처 제작에 사용
ㄴ Noise 노드가 사용됨
ㄴ Scale 속성 : (커질수록 노이즈가 작아짐)
ㄴ Quality 속성 : (높을수록 전이가 일어나는 부분이 부드러워짐)
ㄴ Function 속성 : 패턴을 생성하는 로직을 제어
+ Function은 종류에 따라 명령어의 개수가 다르고, 특정 효과를 표현하는데 특화된 경우가 많아 신경 써서 알아두면 좋다.

세션 생성 옵션

FCreateServerInfo 구조체를 만들고 세션 이름, 최대 인원수, LAN 환경을 멤버로 갖도록 한다.

 

방을 만들고자 하는 사용자는 이러한 내용을 설정하고 CreateServer 버튼을 누른다.

 

 

HostMatch_UI의 CreateServer 버튼을 누르면 OnClick 함수가 호출되는데 OnClick 함수 내부에서는 GameInstance를 찾고 캐스팅하여 CreateServer 함수를 호출하게 된다.

 

인자로 들어오는 FCreateServerInfo 구조체 변수는 HostMatch_UI의 블루프린트에서 만들어서 보내준다.

세션을 만들 때 SessionSettings에 없는 속성 (서버 이름같은 경우) Set 함수를 통해 Key와 Value 방식을 통해 전달하고 받을 수 있다.

 

세션 목록 검색 / 참가

FindSessions 함수를 통해 얻을 수 있는 정보는 다음의 구조체로 정리할 수 있다.

PlayerCountStr은 "CurrentPlayers / MaxPlayers" 문자열이다.

 

ServerArrayIndex는 세션 검색 결과 배열에 해당 세션이 몇 번째로 저장되어 있는지를 뜻한다. 각각의 세션에 대한 위젯(방)이 같은 순서로 보여지기 때문에 의미 있는 정보이다.

사용자가 여러 개의 세션을 찾았을 때 어느 세션에 입장할 것인지를 결정하는 데 필요하다.

 

Join 버튼을 누르면 호스가 만든 세션에 참가한다. 

Players가 0/2로 보이는 것은 아마 LAN으로만 테스트해서? 그런 것 같은데 잘 모르겠다. 

 

앞으로의 개발 방향

지금까지 개발한 내용은 여러 개의 튜토리얼을 참고하여 단순하게 구현한 멀티플레이어 FPS 게임의 프로토타입이라고 볼 수 있다. 앞으로 여기에 기능을 추가해 나가면서 실감나고 재미있는 시스템을 구현하고자 한다.

앞으로 어떤 것들을 해볼까 하면 당장 떠오르는 것들은 5가지 정도가 있다.

 

1. 특수 기술을 사용할 때 셰이더를 적용하기 

2. 지형 지물을 생성하는 툴을 제작 (DirectX로) , 생성한 모델을 넣어보기

3. 캐릭터의 도약, 순간이동 능력, 획득할 수 있는 아이템 구현하기

4. 다양한 머터리얼 제작해보고 적용해보기

5. 대중적인 플러그인 다뤄보기

 

처음에는 서버도 직접 구현하는 것을 목표로 했으나 할 수 있다면 가장 마지막에 도전해 볼 생각이다..

스팀 온라인 서브시스템 사용하기

DefeaultEngine.ini -> [OnlineSubSystem]의 DefaultPlatformService=Steam으로 설정

 

 

CreateServer 함수에서 SessionSettings의

bIsLANMatch = false 설정

bUseObbiesIfAvailable = true 설정

 

FindServer함수에서 SessionSearch의 bIsLanQuery = false 설정

 

각자 다른 스팀 ID로 로그인 되어있어야 한다.

프로젝트를 패키징하여 다른 컴퓨터로 옮겨 테스틀 진행하는 것은 시간이 많이 소요 되므로 처음에 한 번 연결이 잘 되는 것을 확인한 후에는 DefeaultEngine.ini -> [OnlineSubSystem]의 DefaultPlatformService=Null로 설정해 빠르게 테스트할 수 있도록 한다.

세션(방) 리스트 검색

MainMenu_UI의 Join 버튼을 누르면 FindSession이 실행되고, SessionResults에 검색 결과들이 배열로 저장된다.

 

FindSession의 결과로 얻을 수 있는 MaxPlayer, CurrentPlayers 정보 + 서버 이름을 갖는 구조체를 정의한다.

이 구조체 하나만을 인수로 갖는 다이나믹 멀티캐스트 델리게이트 FServerDel을 선언해준다.

 

FindSession 이후 호출되는 OnFindSessionComplete에서는 FindSession의 결과로 얻은 FOnlineSessionSearchResult의 배열을 순회하면서  (검색된 세션의 개수만큼의 요소를 가지는 배열) ServerListDel를 브로드캐스트 해준다.

브로드캐스트 해줄 때마다 블루프린트에서 바인딩한 ServerReceived 커스텀 이벤트가 실행되어 위젯을 만들어주게 된다.

 

 

처음에 Join 버튼을 누르면 위젯 스위처에 의해 오른쪽 부분이 방 리스트를 선택할 수 있는 UI로 바뀌고,

새로운 검색을 위해 Scroll Box의 자식들을 Clear 해준다.

 

 

만들어진 위젯은 ScrollBox의 자식으로 들어간다. 

 

 

방 한 개 마다 생성되는 위젯은 반응하지 않는 텍스트 하나로 구성되어 있다. 

 

만들어진 세션들을 검색할 수 있다.

 

세션 만들기 

게임 인스턴스의 BeginPlay에서 온라인 서브 시스템에서 세션 인터페이스를 얻고

 

UI의 Host 버튼의 OnClick에서 CreateServer를 호출하면 세션 세팅 이후에 세션 인터페이스->CreateSession 함수를 호출하도록 한다.

 

세션 생성 작업이 끝난 후 자동으로 실행시키고 싶은 함수(예를 들면 Lobby 레벨로 이동)은 GameInstance Init에서 OnCreateSessionCompleteDelegates델리게이트에 함수를 바인드 해주면 된다.

 

세션 찾기

// SessionSearch는 FOnlineSessionSearch의 TSharedPtr

// 멤버로 추가

TSharedPtr<FOnlineSessionSearch> SessionSearch;

 

SessionSearch = MakeShareable(new FOnlineSessionSearch());

// SessionSearch에 검색 쿼리에 대한 정보를 설정할 수 있음

SessionSearch->QuerySettings.Set (쿼리 세팅)

 

검색에 대한 결과는 SessionSearch->SearchResults에서 받아볼 수 있다. (TArray<FOnlineSessionSearchResult> 자료형)

SessionInterface->FindSessions(0, SessionSearch.ToSharedRef());

 

세선 참가

SearchResults[0]은 세션 찾을 때 얻은 SessionSearch->SearchResults의 첫 번째 요소이다. 세션에 참가할 때 인자로 사용된다.

JoinSession은 로컬 유저 아이디, 세션 이름, 세션 인자를 받아 호출될 수 있다.

SessionInterface->JoinSession(0, "My Session", SearchResults[0]);

 

세션 생성과 마찬가지로 Join 이후에 실행시키고 싶은 작업은 

OnJoinSessionCompleteDelegates 델리게이트에 등록한 OnJoinSessionComplete 함수 내부에서 작성해주면 된다.

 

테스트에 사용한 UProject 파일 실행 인수

 

배치 파일을 만들고 [언리얼 에디터 경로] [Uproject 경로] [옵션 인자]

게임을 실행하며 로그를 확인할 수 있는 명령 인수

 

에디터에서 Play 버튼을 눌러 테스트하지 않고 배치 파일을 여러개 실행하여 호스트와 조인이 잘 작동하는지 테스트할 수 있다.

 

+ Recent posts