Player Controller

 

APlayerController = Client가 실제로 소유하는 첫 번째 Class.

 

Player의 Input이라고도 볼 수 있으며 Server와 Player를 이어준다.

 

모든 Client는 각자 한 개의 PlayerController를 지니고 있으며 Server와 Client에만 존재하고, 각자 자신의 Controller 정보만을 알고으며 Server는 모든 Client의 PlayerController를 참조한다.

또 모든 Input은 PlayerController에 배치되어야 한다.

 

우선 Input은 PlayerController를 첫 번째로 통과한다.

만약 PlayerController가 처리하지 못한다면 같은 Input을 사용하는 다른 Class에 의해 처리된다.

 

Get()

 

GetPlayerController(0) 혹은 UGameplayStatics::GetPlayerController(GetWorld(), 0)를 통해 PlayerController를 Get 할 수 있다.

하지만 이는 Server와 Client에서의 동작이 각각 다르다.

 

  • Listen-Server = Listen-Server의 PlayerController 반환
  • Client = Client의 PlayerController 반환
  • Dedicated Server = 첫 번쨰 Client의 PlayerController 반환

 

사용과 예시

 

RPC를 활용하여 WidgetButton이 눌렸을 경우 GameState 변수를 증가시키려고 할 때 PlayerController를 활용해야 한다.

 

여기서 Widget은 오직 Clinet/Listen-Server에만 존재하며 Clinet가 소유하더라도 Server RPC에서는 Server 내에 실행 할 instance가 존재하지 않는다.

 

이는 단순하게 변수가 Replicate 되지 않았기 때문이다.

 

따라서 Button이 눌렸다는 정보를 Server에게 전달해야 하는데, 이 때 Server는 RPC를 통해 Client에게 그 정보를 전달받아야 하기 때문에 Client는 Server RPC를 지니게 된다.

 

이를 위해서 PlayerController 내부에 ServerRPC를 두어 Client->Server의 정보이동을 가능하게 하는 것이다.

 


 

HUD

 

AHUD Class = 각 Client에만 존재하며 PlayerController를 통해 접근 가능한 Class. 자동적으로 Spawn된다.

 

UMG (Unreal Motion Graphics) 가 나타나기 전엔 HUD Class를 통해 Text나 Texture와 같은 Client Viewport에 존재하는 것들을 그리기 위해 사용되었다.

 

현재는 Widget이 HUD Class를 대부분 대체하였지만, Debug나 Testing을 위한 독립적인 공간을 위해서 HUD Class를 사용할 수 있다.

 


 

Widget

 

Epuc Games의 새로운 UI System으로 UMG (Unreal Motion Grahpics)라고 불린다.

 

Slate를 상속하였으며, C++을 통해 UI를 생성하거나 UE4 Editor를 위해서도 사용된다.

 

Widget은 오직 Clinet (Listen-Server)에 Local하게 존재하며 Replicate 되지 않는다.

만약 Button Press와 같은 Replicate Action을 처리하려면 별도의 Replicated Class를 마련해야 한다.

 


 

Dedicated vs Listen Server

 

  • Dedicated Server
    • Client가 필요하지 않는 StandAlone Server.
    • 즉 Server가 동작하기만 한다면 언제나 Player가 Join/Leave 할 수 있다.
    • Windows / Linux 용으로 Compile 될 수 있으며 Player가 고정 IP 주소를 통해 접속할 수 있는 Virtual Server로도 실행될 수 있다. 
    • DS는 Visual Part가 없다. 즉 UI나 PlayerController가 필요하지 않으며 Character 등 어떠한 비주얼 요소가 필요하지 않다.
       
  • Listen-Server
    • Server이자 Client인 Server. 즉 항상 적어도 1개의 Clinet가 접속해 있어야 한다. (서버장이 Client를 종료하면 서버가 닫힌다..)
    • Client이기도 하기 때문에 UI와 PlayerController 등 Client가 지니는 요소들을 갖는다.
    • Listen-Server는 Clinet이기도 하기 떄문에 Player들이 접속하기 위해 입력하는 IP는 Client 중 하나의 IP이다.
    • 따라서 DS와는 달리 고정 IP를 갖지 않는 인터넷 유저의 경우 종종 문제가 생기곤 한다. (OnlineSubsystem를 사용해 IP를 변경하면 문제 해결이 가능하긴 하다.)

 


 

Replication

 

Information / Data를 Client에게 전달하는 Server의 행위를 말한다.

 

Property를 Replicate 할 수 있는 첫 번째 Class는 바로 Actor이다.

앞서 언급된 모든 Class는 Actor에서 상속되어 필요할 때 Property의 Replicate를 가능하게 하는 기능을 부여받는다.

 

하지만 모두가 이런 것은 아니고, 예를 들어 GameMode는 복제되지 않고 오직 Server에만 존재한다.

 

C++ 의 경우 bReplicates = true; 를 해주어야 Actor의 Replicate가 가능해지는데, bReplicates를 TRUE로 함으로써 Server에서 Spawn 된 Actor를 모든 Client에 Replicate 해준다. (서버에서 Spawn되어야만 Replicate된다.)

 

또 변수의 Replicate는 UPROPERTY(Replicated)를 설정하여 선언해준 뒤, .cpp 에서 GetLifetimeReplicatedProps 내부에서 DOREPLIFETIME을 통해 관리해준다.

(DOREPLIFETIME_CONDITION을 통해 Condition 또한 관리가 가능한다.)

 

Conditions

  • COND_InitialOnly = Initial Bunch에만 전송을 시도한다.
  • COND_OwnerOnly = Actor의 Owner에게만 전송한다.
  • COND_SkipOwner = Owner를 제외한 접속인원 모두에게 전송한다.
  • COND_SimulatedOnly = Simulated Actors에게만 전송한다,
  • COND_AutonomousOnly = Autonomous Actor에게만 전송한다. 
  • COND_SimulatedOrPhysics = Simulated / bRepPhysics Actors에게만 전송한다.
  • COND_InitialOrOwner = Initial pack 혹은 Actor의 Owner에게만 전송한다. 
  • COND_Custom = 특정 Condition은 없지만, SetCustomIsActiveOverride를 통해 on/off 토글이 가능한 능력을 얻는다.

 

또 다른 Replication Version으로는 "RepNotify"가 있다.

 

이것은 업데이트된 값을 수신할 때 모든 인스턴스에서 호출될 함수를 사용한다.
이를 통해 Value가 복제된 후 호출해야 하는 로직을 호출할 수 있게 된다.
 

C++ RepNotify 예시

 

'ReplicatedUsing=FUNCTIONAME' 을 통해 변수가 성공적으로 복제될 때 호출되어야 하는 함수를 지정한다.

위와 같은 Function은 UNFUNCTION()가 필수적으로 필요하다.

 


 

RPC ( Remote Procedure Calls )

 

Replication을 위한 다른 방법.

다른 인스턴스에서 무언가를 호출하는 데에 사용된다.

예를 들면 Clinet에서 호출하여 Server를 실행하거나, Server->Client, Client / Server -> Group 등이 있다.

 

RPC는 특정 룰에서만 온전히 작동한다.

 

  • Run on Server - Actor의 Server 인스턴스에서 실행.
  • Run on owning Client - Actor의 Owner에서 실행. 
  • NetMulticast - Actor의 모든 Instance에서 실행.

 

 

또 RPC가 완전히 작동하려면 몇 가지 필요사항이 존재한다.

 

  1. 반드시 Actor에서 호출되어야 한다.
  2. Actor는 반드시 Replicate 되어야 한다.
  3. RPC가 Server에서 호출되어 Client에서 실행되고자 할 때, Actor를 Own한 Client만이 Function을 실행할 수 있다.
  4. RPC가 Client에서 호출되어 Server에서 실행되고자 할 때, Client는 반드시 RPC가 호출될 Actor를 Own해야 한다.
  5. Multicast RPC는 예외가 존재한다.
    • 만약 Server에서 호출되면 Server가 Local로 실행하고, 현재 접속한 모든 Client에서 실행한다.
    • 만약 Client에서 호출되면 Client가 Local로 실행하고, Server에는 실행되지 않는다.

 

 

 

 

Validation

 

Validation이란 잘못된 RPC가 Parameter를 감지했을 경우

RPC를 호출한 Client/Server 연결을 끊도록 알릴 수 있는 함수이다.

 

Client to Server RPC는 '_Validate' Function을 이용해 Server RPC Function을 보호하고, 입력 제한에 대해 모든 매개변수가 유효한지 확인하기 편리하게 해주기 때문에, 필요하다고 볼 수 있다.

 


 

Ownership

 

Server나 Client는 Actor를 Own할 수 있다.

예를 들면 PlayerController와 같은 경우 Client 혹은 Listen-Server에 의해 Own될 수 있다.

 

하지만 Scene에 Spawn 혹은 Place 된 문과 같은 경우, 대부분 Server가 지니며 여기서 문제가 발생한다.

위의 사진과 같이 Client가 Own하지 않은 Actor에서는 Server RPC가 호출되었을 때 drop이 된다는 점이다.

때문에 이를 해결하기 위해 Client가 실제로 Own하고있는 Class / Actor를 사용하며, 대표적인 예로 PlayerController가 있다.

 

문 내부에서 Input을 가능하게 하여 Server RPC를 호출하기보다, PlayerController에 Server RPC를 생성하여 Server가 문에서 인터페이스 기능을 호출하도록 하는 것이 좋다.

 

모든 Connection은 PlayerController를 Own하며, Player가 Actor를 Own한다면 Connection 또한 해당 Actor를 Own한다.

만약 PlayerController가 Actor를 Un-possessing 중이라면, 해당 Actor를 Own 하고있지 않다는 뜻이 된다.

 

따라서  Run on sever 이벤트는 클라이언트가 소유하는 액터에서만 호출 가능하며, Run on owning Client 이벤트를 전송하는 서버의 경우, 그 이벤트 역시 이 액터 중 하나에서 호출되어야 한다. (그렇지 않으면 서버에서만 실행된다.)

 


 

Actor Relevancy and Priority

 

Relevancy

 

Bandwidth 향상을 위해 Unreal의 Network Code를 사용하면 Server가 해당 Client의 Relevant set에 있는 Actor에 대해서만 Client에게 알릴 수 있다.

 

Unreal은 Relevant set 결정을 위해 다음 규칙을 순서대로 적용시킨다.

 

  1. Actor가 'bAlwaysRelevant'이고, Pawn 또는 PlayerController가 소유하고 있거나, Pawn이거나, Pawn이 noise 또는 damage와 같은 일부 작업의 Instigator인 경우 Relevant 이다.
  2. Actor가 'bNetUserOwnerRelevancy'이고 Owner가 있는 경우 Owner의 Relevancy를 사용한다.
  3. Actor가 'bOnlyRelevantToOwner'이고 첫 번째 검사를 통과하지 못하면 Not Relevant이다.
  4. Actor가 다른 Actor의 Skeleton에 부착된 경우 Relevacy는 Base의 Relevancy에 의해 결정된다.
  5. Actor가 숨겨져있고 ('bHidden == true') Root Component가 not Collide이면 Actor는 Not Relevant이다.
    (루트 구성요소가 없는 경우 'AActor::IsNetRelevantFor()는 경고를 기록하고 Actor를 'bAlwaysRelevant'로 설정해야 하는지 묻는다.)
  6. 'AGameNetworkManager'가 거리 기반 Relevancy를 사용하도록 설정된 경우 Actor가 Net Cull Distance보다 가까우면 Relevant이다.

**Pawn과 PlayerController는 'IsNetRelevantFor()'를 재정의하고 결과적으로 Relevacy Condition이 다르다.

 

 

Prioritization

 

Unreal은 모든 Actor에게 우선순위를 부여하는 Load-Balancing 기술을 사용하여 게임플레이에 얼마나 중요한지에 따라 각 Actor에게 공정한 대역폭을 분배한다.

 

Actor에게는 'NetPriority'라는 Float 변수가 존재하며 값이 높을수록 Actor는 더 많은 대역폭을 할당받는다.

(비율로 할당받기 때문에 모든 Actor의 Priority가 높다고 해서 성능향상이 이루어지는 것은 아니다.)

 

Actor의 현재 우선순위는 가상함수 'AActor::GetNetPriority()'를 사용하여 계산되며, 기아상태를 피하기 위해 Actor가 마지막으로 Replicate 된 이후에 'NetPriority'를 곱해준다.

 


 

Actor Role and RemoteRole

 

Role은 Actor에 대한 Authority Owner Actor의 Replication 여부

Remote Role은 Replication Mode를 나타낸다. 

 

현재 실행중인 엔진 인스턴스가 특정 Actor의 Authority인지를 알아내기 위해 Role이 ROLE_Authority인지 검사한다.

만약 ROLE_Authority라면 현재 실행중인 엔진 인스턴스가 이 액터를 담당한다.

 

이 값을 가진 Instance가 이 Actor를 담당한다.

또 Server와 Client는 Role과 Remote Role의 값이 반대이다.

 

Server는 대역폭과 CPU 리소스 자원 문제로 업데이트 할 때마다 Actor를 업데이트 하지 않고,  'AActor::NetUpdateFrequency' 속성에 지정된 빈도로 Actor를 Replicate 한다.

 

즉 Actor 업데이트와 Client 사이에 약간의 시간이 흐르고 이로 인해 Actor의 움직임이 고르지 않게 보일 수 있다.

이를 보상하기 위해 Client는 Update 사이에 Actor를 시뮬레이션 하게되며, 시뮬레이션 유형엔 두 가지가 존재한다.

 

  • ROLE_SimulatedProxy
    일반적으로 마지막 알려진 속도에 따라 움직임을 외삽하는 것을 기반한다.
    Server가 특정 Actor에 대한 업데이트를 전송할 때, Client는 그 위치를 새로운 위치쪽으로 조정한 다음, 업데이트 사이마다 Client는 Server에서 전송된 최근 속도에 따라 액터를 계속해서 움직인다.

  • ROLE_AutonomousProxy
    자율 프록시는 보통 Player Controller에 빙의된 Actor에만 사용된다.

    이 Actor는 사람의 Controller에서 입력을 받으므로 외삽을 할 때 약간의 정보가 더 필요하며, 실제 사람 입력을 사용하여 빠진 정보를 채울 수 있다.

 

Role이 Authority이고, Remote Role은 ROLE_SimulatedProxy 또는 ROLE_AutonomousProxy인 경우,

이 엔진 인스턴스는 이 액터를 원격 접속으로 다시 Replicate하는 것을 담당한다.

 


 

Traveling in Multiplayer

 

Non-/Seamless travel

 

Seamless tarvel = 비차단 작업

Non-seamless tarvel = 차단 호출

 

Client에 대한 Non-seamless tarvel은 Client가 Server에서 연결을 끊었다가, 동일한 서버에 다시 연결하여 새 맵을 로드할 준비가 된 것을 의미하며.

Seamless travel은 더 부드러운 경험을 할 수 있고, 재연결 과정에서 발생할 수 있는 문제를 피할 수 있기 때문에 더욱 권장된다.

 

하지만 반드시 Non-seamless tarvel이 발생해야 하는 세 가지 유형이 있다.

  1. 처음으로 Map을 로딩할 때.
  2. Client가 처음으로 Server에 접속할 때
  3. Multiplayer 게임을 종료하고, 새로운 것을 실행할 때

 


 

Main Traveling Functions

 

 

UEngine::Browse

  • 새로은 Map을 로딩할 때의 hard reset과 같다.
  • 항상 Non-seamless tarvel이다.
  • 즉 대상 Map으로 이동하기 전 Server가 현재 Client의 연결을 끊는다.
  • Client는 현재 Server에서 연결을 끊는다.
  • Dedicated Server는 다른 Server로의 travel이 불가능하기 때문에 Map은 Local이어야만 한다.

 

UWorld::ServerTravel

  • Server 전용이다.
  • Server는 새로운 World/Level로 이동한다.
  • 연결된 Client들은 따라간다.
  • Multiplayer 게임이 Map->Map으로 이동하는 방식이며 Server는 이 기능을 호출하는 역할을 한다.
  • Server는 연결된 모든 Client Player에 대해 'APlayerController::ClientTravel'를 호출한다.

 

APlayerController::ClientTravel

  • Client에서 호출되면 새 Server로 이동한다.
  • Server에서 호출되는 경우 특정 Client가 새 Map으로 이동하도록 지시한다. (현 Server에 연결된 상태를 유지)

 


 

Enabling Seamless Travel

 

Seamless travel은 Transition Map과 함께해야 한다.

이것은 'UGameMapsSettings::TransitionMap' 속성을 통해 구성된다.

기본적으로 이 속성은 비어있으며, 비운 채로 놔두면 빈 Map이 생성된다.

 

Transition Map이 존재하는 이유는 항상 로드된 World가 있어야 하므로 새 Map을 로드하기 전 이전 맵을 해제할 수 없기 때문이다.

Map은 매우 클 수 있으므로 이전 Map과 새 Map을 동시에 저장하는 것은 좋지 않기 때문에, Transition Map이 필요한 것이다.

 

Transition Map을 설정한 후 'AGameMode::bUseSeamlessTravel' 값을 true로 변경시켜주면 Seamless travel이 동작한다.

 


 

Persisting Actors / Seamless Travel

 

Seamless travel을 할 때 현재 Level에서 새 Level로 Actor를 지속할 수 있다.

이는 인벤토리 Item, Player 등 특정 Actor에게 유용하며

기본적으로 다음 Actor는 자동으로 유지된다.

 

  • GameMode (Server Only)
    • 'AGameMode::GetSeamlessTravelActorList'을 통해 추가로 추가된 모든 Actor 
  • 유효한 PlayerState가 있는 모든 Controller (Server Only)
  • 모든 PlayerController (Server Only)
  • 모든 Local PlayerController (Server & Client)
    • local PlayerController에서 호출되는 'APlayerController::GetSeamlessTravelActorList'를 통해 추가로 추가된 모든 Actor

 

다음은 Seamless travel이 실행될 때의 흐름이다.

 

  • Transition Level까지 지속할 Actor 표시.
  • Transition Level로 이동.
  • Final Level까지 지속할 Actor 표시.
  • Final Level로 이동.

'UnrealEngine' 카테고리의 다른 글

[UE4] BTTask에서 Node Memory 활용하기  (0) 2023.04.09
[UE4] Gameplay Ability System (GAS) 활용  (0) 2023.04.09
[UE4] Gameplay Ability System (GAS)  (0) 2022.06.13
[UE4] Shooter AOS Pt.1  (0) 2022.06.13
[UE4] UE4 Network Compendium Pt.1  (0) 2022.03.15

+ Recent posts