Make Unreal REAL.
article thumbnail
Advanced Unreal Engine 5 Multiplayer Gameplay Programming


이번에 할 것은 점프 버튼에 장애물을 뛰어넘는 기능을 추가하는 것이다.

  • 이것을 응용하면 향후에 벽타기 등도 추가할 수 있다.


Motion Warping 플러그인을 활성화한다.

  • Motion Warping은 애니메이션이 재생되는 동안, 설정된 Warp Target의 위치와 회전으로 액터의 위치와 회전을 보간시켜주는 기능이다.
  • Character Movement 컴포넌트의 이동 모드가 Flying일 때만 작동한다.

 


빌드 설정에 MotionWarping 모듈을 추가한다.

 


VaultOver 애니메이션을 통해 VaultOver_Montage 애니메이션 몽타주를 생성한다.

 


마지막 착지 부분은 필요 없기 때문에, VaultOver 그룹을 선택한 후 Anim End Time을 1.5로 줄여준다.

 


Notify - Add Notify State - Motion Warping을 클릭해 Anim Notify State를 추가한다.

 


양쪽 끝 부분을 당기고 늘려 구간을 정한다.

  • 언제 변경이 일어날지 정하는 작업이다.
  • 모든 Motion Warping Notify State 동안 보간되며 Motion Warping 컴포넌트에 전달된다.


제자리에서 변경이 일어나는 지점, 발이 땅에 닿지 않는 지점에 Notify State를 추가하는 게 대부분의 경우에서 좋다.

 

 

Root Motion Modifie

  • Scale
    재생 Scale을 유지하는 옵션인 것 같다.
  • Skew Warp
    재생에 변형을 주어 강제로 해당 지점에 위치하도록 조정하는 옵션인 것 같다.


두 Motion Warping Notify State에 다음과 같이 설정한다.

  • Root Motion Modifier: Skew Warp
  • Warp Target Name: JumpToLocation (뛰어넘기를 시작할 지점), JumpOverLocation (뛰어넘을 목적지 지점)
    이 값들은 이 몽타주를 사용하는 액터의 Motion Warping 컴포넌트에서 이름을 통해 가져올 수 있다.

 


AG_CharacterMovementComponent 클래스에 점프 버튼을 눌렀을 때, 첫 번째부터 순서대로 실행할 수 있는 능력을 체크하여 실행 가능한 첫 능력을 실행하는 함수를 선언한다.

  • TraversalAbilitiesOrdered는 첫 번째부터 순서대로 실행 가능한지 확인할 능력 목록이다.

 


헤더를 추가하고 TryTraversal() 함수를 구현한다.

  • TraversalAbilitiesOrdered 목록에 있는 첫 번째 능력부터 순서대로 실행하여, 실행에 성공하면 이후 능력은 실행하지 않는다.

 

능력이 활성화 상태인지 확인하는 것은 매우 중요하다.

  • 능력은 활성화(ActivateAbility)되면, 현재 이 능력을 실행할 수 있는 환경인지 검사(Commit Check)를 수행한다.
  • 검사가 성공하면 실행되고, 실패하는 부분에는 보통 능력을 종료(EndAbility)시키는 로직을 구현한다.
  • 객체화되어(Instanced) 실행된 활성화 상태의 능력은 종료되어야 다시 실행할 수 있다.

 


이후 디버깅에 사용하기 위해, ShowDebugTraversal라는 콘솔 변수를 선언한다.

 

 

캐릭터 클래스에 Character Movement 컴포넌트를 캐싱하도록 한다.

  • 이미 생성자의 ObjectInitializer를 통해 기본 Character Movement 컴포넌트 클래스가 AG_CharacterMovementComponent로 설정되어 있다.


UPROPERTY는 Shared Pointer처럼 동작하는데, 현재 AG_CharacterMovementComponent는 소유자인 Character 클래스에 의해 관리되고 이미 갖고 있기 때문에 그냥 캐싱해서 사용해도 된다.

  • CreateDefaultSubobject() 함수로 할당해 줄 필요가 없다.
  • 유효성 검사를 할 필요가 없다.

 

 

점프 버튼에 바인딩되어 있는 기존 OnJumpActionStarted() 함수는 주석 처리하고 새로 구현한다.

  • AG_CharacterMovementComponent에 구현되어 있는 Traversal() 함수를 통해, 실행 가능한 첫 번째 능력을 실행할 것이기 때문이다.

 


 Motion Warping 컴포넌트 C++ 클래스를 생성한다.

 

 

캐릭터 클래스에 이번에는 Motion Warping 컴포넌트와 Getter를 추가해준다.

 


생성자에서 Motion Warping 컴포넌트를 할당하고 Getter를 구현한다.

 

 

AG_GameplayAbility를 상속 받은 GA_Vault C++ 클래스를 생성한다.

 


GA_Vault 클래스에 생성자를 구현한다.

  • EndAbility() 함수에서 충돌 처리, Motion Warping 컴포넌트를 통한 처리 등 해줄 것들이 있으므로, InstancingPolicy를 InstancedPerActor로 지정한다.

 


GameplayAbility.h 헤더에서 CommitCheck(), ActivateAbility(), EndAbility() 함수의 시그니처를 복사해와 오버라이드한다.

 

 

ActivateAbility() 함수가 호출되는 시점은 이미 CanActivateAbility() 함수에서는 true가 반환된 상황이다.

 

내부적으로 CommitAbility() 함수를 호출해 능력 수행에 필요한 비용을 지불한다.

 


CommitAbility() 함수는 CommitCheck() 함수를 통해 능력의 비용을 지불할 수 있는지 확인하고, CommitExecute() 함수를 통해 실제로 비용을 지불한다.

  • CommitCheck() 함수가 Commit이 실제로 수행될 수 있는지 확인하는 것이다.

 

CanActivateAbility() 함수는 능력을 실행할 수 있는지 확인하고, CommitAbility() 함수를 통해 비용을 지불하는데, CommitCheck() 함수는 비용이 충분한지 확인하고, CommitExecute() 함수는 실제로 비용을 지불한다.

 

예를 들어, CanActivateAbility() 함수에서 스킬을 사용하기 위한 쿨타임이 돌았는지 확인하고, CommitCheck() 함수에서 MP가 충분한지 확인하고, CommitExecute() 함수에서 실제로 마나를 감소시킨다.

 

 

헤더에 필요한 변수들을 선언한다.

 

 

1단계 수 Sphere Trace는 장애물의 앞 부분을 감지하기 위함이고, 2단계 수직 Sphere Trace는 장애물의 끝 부분을 감지하기 위함이다.

 


오버라이드한 함수를 구현하기에 앞서 헤더를 추가해준다.

 

 

여기서는 간소화시켜 CommitCheck() 함수에서 Sphere Trace를 통해 뛰어넘기 능력을 실행하기 적합한 환경인지 확인할 것이고, 적합하면 Motion Warping 컴포넌트에 전달할 시작/목표 지점을 저장할 것이다.

 

이론적으로, 이 과정은 CanActivateAbility() 함수에 구현해도 되긴 하다.

  • 하지만 CanActivateAbility()는 const 함수이기 때문에 멤버 변수에 값을 설정할 수 없다.
  • CanActivateAbility() 함수에 구현하면, CommitCheck() 함수에서 수행했던 확인을 시작/목표 지점을 구하기 위해 1번 더 해야 하기 때문에 총 2번을 해야 한다.


장애물에 의한 공간도 확인하기 위해 Line Trace가 아닌, Sphere Trace로 진행한다.

  • 구가 지나친 공간에는 아무 장애물도 없다는 걸 확신할 수 있기 때문이다.


IConsoleManager::Get().FindConsoleVariable() 함수로 다른 클래스에 선언된 콘솔 변수를 가져와 사용할 수 있다.

 


ActivateAbility() 함수를 구현한다.

  • CommitAbility() 함수를 호출하지 않으면, CommitCheck() 함수를 통해 뛰어넘기가 가능한지 검사가 되지 않는다.
  • CanActivateAbility() 함수를 통과해 ActivateAbility() 함수가 호출되면 CommitCheck() 함수를 통해 지금 능력을 실행하기에 적합한 환경인지 검사한다.
  • CommitCheck() 함수가 false를 반환하면 곧 바로 능력이 종료되어 EndAbility() 함수가 호출된다.

 

Motion Warping은 Movement 모드가 Flying이 아니면 동작하지 않는다.

  • 따라서 능력을 활성화할 때 Flying으로 설정해주고, 끝날 때 Falling으로 바꿔준다.

 

Motion Warping 컴포넌트에 키와 값을 설정해 놓으면, 애니메이션 몽타주가 재생될 때 Motion Warping Notify State는 컴포넌트에서 변수 이름으로 출발지와 목적지를 가져온다.

 

충돌과 이동 모드 등을 설정하고, EndAbility() 함수를 콜백 함수로 등록시켜 뛰어넘기 애니메이션 몽타주를 비동기로 재생한다.

  • 재생을 위해서는 UAbilityTask_PlayMontageAndWait::ReadyForActivation() 함수를 꼭 실행시켜 주어야 한다.

 

 

EndAbility() 함수를 구현한다.

 

 

현재 작성된 코드로도 정상 작동은 하지만, 성능적인 문제가 있다.

 

GA_Vault의 ActivateAbility() 함수에서 CommitAbility() 함수를 호출해 뛰어넘기를 수행 가능한지 검사하는데, Super::ActivateAbility() 함수 내부에 이미 CommitAbility() 함수를 호출하는 부분이 있기 때문에, 실제로는 CommitAbility() 함수를 통해 CommitCheck() 함수가 2번 호출되는 것이다.

 

 

CommitAbility() 함수와 CommitCheck() 함수는 결과를 반환할 뿐, 변수로 저장하지는 않기 때문에 반환 값을 통해서가 아닌 별도의 로직을 통해 확인 결과를 알 수 있도록 해야 한다.

 

이를 위해 가장 마지막에 뛰어넘기를 성공했던 출발지와 목적지를 능력에 저장해, CommitCheck() 함수를 통해 성공적으로 출발지와 목적지가 갱신되었는지를 통해 성공 여부를 확인하도록 한다.

  • 현재 능력의 Instancing Policy가 InstancedPerActor이기 때문에 가능하다.

 

 

CommitCheck() 함수가 수행되는 중에 JumpToLocation, JumpOverLocation을 직접 변경하지 않고, 모든 검사가 완료되어 최종적으로 뛰어넘기를 할 수 있을 때만 갱신하도록 한다.

 

 

기존에 CommitAbility() 함수를 1번 더 실행해 검사했던 부분을 주석 처리하고, 마지막에 뛰어넘기를 수행했던 위치와 CommitCheck()에서 갱신된 위치를 비교해 성공 여부를 검사한다.

  • CommitCheck() 함수가 true를 반환했다면, JumpToLocation과 JumpOverLocation이 다른 값으로 갱신되기 때문이다.

 

 

비동기 뛰어넘기 애니메이션 몽타주 재생이 종료되어 콜백 함수인 EndAbility() 함수가 호출되면, 마지막에 가장 최근 출발지와 목적지를 저장해 다음에 비교할 수 있도록 한다.

 

이렇게 구현하면 CommitCheck()를 1번만 수행할 수 있게 된다.

 

 

GAS에 사용할 뛰어넘기 능력 BP를 생성하고 변수에 값들을 설정해준다.Trace Object Type 지정 (장애물로 간주할 오브젝트 타입)
Collision Channels to Ignore 지정 (Vault 중에 무시할 충돌 채널)
Vault Montage 지정
Ability.Movement.VaultOver 태그 지정

 

 

능력의 고유 태그인 Ability Tags를 설정해준다.

 

액터 별로 객체화 되어 실행되는 능력이기 때문에, 이펙트를 통해 상태 태그를 적용하지 않고 Activation Owned Tags에 바로 지정해 사용할 수도 있다.

  • 액터에 뛰어넘기 상태임을 나타내기 위해 State.Movement.Vault 태그를 추가한다.

 


캐릭터의 기본 이펙트와 능력을 적용하는 데 사용되는 BaseCharacter 데이터 에셋에 BP_GA_Vault 뛰어넘기 능력을 추가한다.

 


캐릭터 BP에 점프 버튼을 눌렀을 때 순서대로 실행 가능한지 확인할 능력들을 할당해준다.

 


뛰어넘기 애니메이션 몽타주에서 두 Motion Warping Noify State의 Ignore Z Axis 옵션을 꺼준다.

  • Z축 Root Motion은 Character Movement 컴포넌트의 이동 모드가 Flying일 때만 작동한다.
  • Motion Warping 시에 높이에 대한 조정도 적용해주기 위함이다.

 


잘 되나, 멀티 플레이어에서 글리치(떨림)가 생긴다.

 

클라이언트별로 검사를 수행하는 위치와 회전이 조금씩 다를 수 있어, 시작점과 끝점의 위치가 다를 수 있기 때문이다.


이런 상황에서 Motion Warping 컴포넌트가 네트워크 상의 서로 다른 위치, 회전 간에 보간을 시도하면서 떨리는 현상이 발생하는 것이다.

 

 

따라서 Motion Warping 컴포넌트에 두 지점이 설정될 때, 두 지점을 동기화하기 위한 RPC 호출을 수행하도록 한다.

  • 언리얼 엔진 5.1.1 버전 이후부터는 Motion Warping 컴포넌트가 Warp Target을 알아서 복제하도록 수정되어 아래 과정을 하지 않아도 된다.

 

우선, 네트워크를 통해 한 번에 위치, 회전을 동기화하기 위해 ActionGameTypes.h 헤더에 구조체 자료형을 선언한다.

  • FVector_NetQuantize 자료형을 사용해 대역폭을 절약할 수도 있지만, 정확성 문제로 글리치가 사라지지 않을 수 있으므로 그냥 FVector를 사용해준다.

 

 

RPC 호출을 위해 Motion Warping 컴포넌트를 복제된 컴포넌트로 설정한다.

 


 서버가 호출할 래퍼 함수와 실제 RPC 함수를 선언한다.

  • NetMulticast는 서버가 서버와 클라이언트 모두에게 보내는 RPC 요청이다.

 


래퍼 함수와 RPC 함수를 구현한다.

  • WarpTargetMap은 Motion Warping 컴포넌트에 이미 선언되어 있는 멤버 변수다.
  • Motion Warping 컴포넌트는 Warp Target을 Map 형식으로 관리한다.

 


GA_Vault 뛰어넘기 능력의 ActivateAbility() 함수에 RPC 요청을 통해 Warp Target을 동기화하는 부분을 추가한다.

 

Warp Target을 동기화함으로써 글리치가 사라졌다.

 

GAS와 Motion Warping 기능을 통해 성공적으로 뛰어넘기 능력을 구현했다.

 

'Unreal Engine > Advanced UE5 Multiplayer Programming' 카테고리의 다른 글

능력 간의 관계 설정  (0) 2023.05.15
벽 달리기 능력  (1) 2023.05.14
달리기 능력  (0) 2023.05.12
앉기 능력  (0) 2023.05.11
베이스 능력  (0) 2023.05.10
profile

Make Unreal REAL.

@diesuki4

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그