
지난 시간에는
NetMode를 통해 '컴퓨터'의 역할을 구분했다면, 오늘은 더 깊이 들어가 개별 '액터'의 역할을 구분하는NetRole에 대해 학습했다. 서버에 존재하는 진짜 '실체(Authority)' 와 클라이언트에 복제된 '허상(Proxy)' 이라는 핵심 개념을 이해했다. 특히, 같은 Proxy라도 입력을 보낼 수 있는 '나 자신(Autonomous Proxy)'과 보기만 하는 '다른 플레이어(Simulated Proxy)'가 어떻게 다른지, 그리고 이 모든 것을 '관점'에 따라 나누어 보는 Local Role과 Remote Role의 필요성까지 파고들었다.
NetMode)과 액터의 역할(NetRole)의 차이점 이해하기Authority(실체)와 Proxy(허상)의 개념을 명확히 구분하기Autonomous Proxy와 Simulated Proxy의 역할과 차이점 설명하기Local Role과 Remote Role이라는 '관점'이 왜 필요한지 이해하기NetMode를 통해 우리는 HasAuthority()로 서버 코드를 분리할 수 있게 되었다. 하지만 "이 액터는 플레이어가 직접 조종하는 액터인가?" 혹은 "이 액터는 내 화면 속의 다른 플레이어인가?"와 같은 더 세밀한 질문에 답하려면 액터 자체의 역할을 알아야 한다. 바로 이 역할을 정의하는 것이 NetRole이다.
언리얼 멀티플레이에서 모든 액터는 두 개의 얼굴을 가질 수 있다.
ROLE_Authority (권위, 실체): 서버에 존재하는 원본 액터. 이 액터는 게임 월드에 실질적인 영향을 미치는 모든 권한을 가진다. HP 변경, 아이템 생성 등 중요한 로직은 반드시 Authority를 가진 액터에서만 수행되어야 한다. GameMode는 오직 서버에만 존재하므로 항상 Authority다.Proxy (대리, 허상): Authority 액터가 클라이언트로 복제된 복사본. 말 그대로 '허상'이므로, 게임의 중요 로직을 스스로 실행해서는 안 된다. 오직 서버로부터 받은 데이터로 자신의 모습을 갱신하고, 애니메이션을 재생하는 등 '보여주는' 역할에 충실해야 한다.이 원칙 덕분에 클라이언트에서 아무리 자기 HP를 9999로 바꿔도, Authority를 가진 서버가 "너의 진짜 HP는 10이야"라고 알려주면 소용이 없는 것이다.
여기서부터 조금 복잡해지지만, 이것이 NetRole의 핵심이다. 언리얼은 액터의 역할을 두 가지 관점에서 바라본다.
GetLocalRole(): "현재 이 코드가 실행되는 컴퓨터에서" 이 액터의 역할은 무엇인가?GetRemoteRole(): "이 액터가 연결된 원격 컴퓨터에서" 이 액터의 역할은 무엇인가?예를 들어, 서버 컴퓨터에서 내 캐릭터(PlayerCharacter_A)를 본다고 생각해보자.
LocalRole: 서버에 있으니 당연히 실체, 즉 Authority다.RemoteRole: 이 캐릭터는 클라이언트 A에게 복제되어 조종받고 있다. 즉, 원격지(클라이언트 A)에서는 '조종하는 프록시'인 AutonomousProxy 역할을 한다.이 두 가지 '시선'이 있기 때문에, 서버는 "아, 이 캐릭터는 그냥 스폰된 몬스터가 아니라, 특정 클라이언트가 조종하는 캐릭터구나!"라는 것을 구분하고 그에 맞는 특별한 처리(클라이언트 전용 RPC 등)를 해줄 수 있게 된다.
클라이언트에 존재하는 '허상'인 프록시는 다시 두 종류로 나뉜다.
ROLE_AutonomousProxy (자율적인 프록시): "내 컴퓨터 화면 속의 나 자신". 이 액터는 특별히 서버로 입력을 보낼 수 있는 '자율권'을 가진다. 내가 W키를 누르면, 내 AutonomousProxy 캐릭터는 이 입력 정보를 서버의 Authority에게 전달하여 "나를 앞으로 움직여줘!"라고 요청할 수 있다. PlayerController가 대표적인 예다.ROLE_SimulatedProxy (시뮬레이션되는 프록시): "내 컴퓨터 화면 속의 다른 플레이어들". 이 액터들은 오직 서버로부터 위치, 회전, 애니메이션 등의 정보를 받아서 그대로 '따라하며 흉내 낼(Simulate)' 뿐이다. 서버로 입력을 보낼 수 있는 권한이 전혀 없다.이 구분을 통해 언리얼 엔진은 수많은 플레이어들의 입력을 효율적으로 처리하고, 내 캐릭터의 반응성은 빠르게, 다른 캐릭터의 움직임은 부드럽게 동기화할 수 있다.
// 액터의 LocalRole과 RemoteRole을 출력해보는 유용한 디버깅 함수
#include "Engine/Engine.h"
static FString GetRoleString(const AActor* InActor)
{
FString LocalRoleString = UEnum::GetValueAsString(TEXT("Engine.ENetRole"), InActor->GetLocalRole());
FString RemoteRoleString = UEnum::GetValueAsString(TEXT("Engine.ENetRole"), InActor->GetRemoteRole());
return FString::Printf(TEXT("Local: %s / Remote: %s"), *LocalRoleString, *RemoteRoleString);
}
| 개념 | 설명 | 대표 예시 |
|---|---|---|
ROLE_Authority | 서버에 존재하는 원본 액터. 모든 권한을 가진다. | GameMode, 서버의 모든 액터 |
ROLE_AutonomousProxy | 클라이언트의 '나 자신'. 서버로 입력을 보낼 수 있는 특별한 프록시. | PlayerController, IsLocallyControlled()가 true인 Pawn |
ROLE_SimulatedProxy | 클라이언트의 '다른 플레이어들'. 서버의 데이터를 받아 보여주기만 하는 프록시. | 다른 플레이어들의 PlayerCharacter |
Local Role / Remote Role | 액터의 역할을 '현재 PC'와 '원격 PC' 두 관점에서 보는 것. | 서버가 플레이어 소유의 액터인지 아닌지를 구분하는 데 사용됨. |