멀티플레이어 세션에서 게임 스테이트 정보는 인터넷 접속을 통해 여러 컴퓨터 간에 전달됩니다. 싱글 플레이어 게임의 경우에는 모든 정보가 하나의 컴퓨터에 저장됩니다. 따라서 멀티플레이어 프로그래밍은 본질적으로 싱글 플레이어 게임 프로그래밍보다 더 복잡합니다. 플레이어 간 정보 공유 프로세스는 세심한 주의를 요하며 여러 추가 단계를 거쳐야 합니다. 언리얼 엔진 에는 몇몇 세계 최고 인기 온라인 게임에서 사용하는 강력한 네트워킹 프레임워크가 있어 이러한 프로세스의 복잡도를 줄일 수 있습니다. 이 페이지에서는 멀티플레이어 프로그래밍의 기반이 되는 개념과 프로그래머가 원하는 대로 네트워크 게임플레이를 빌드할 수 있는 툴에 대해 간략히 살펴봅니다.
멀티플레이어를 위한 조기 계획
언제든 프로젝트에 멀티플레이어 기능이 필요하게 될 수 있다면, 프로젝트 시작부터 멀티플레이어를 염두에 두고 모든 게임플레이를 빌드해야 합니다. 팀에서 멀티플레이어 게임을 만들기 위한 추가 단계를 일관되게 구현한다면, 싱글 플레이어 게임에 비해 게임플레이 구축에 그렇게 많은 시간이 걸리지는 않을 것입니다. 장기적으로 팀이 프로젝트를 디버그하고 서비스하는 것이 전체적으로 더 쉬워질 것입니다. 한편, 언리얼 엔진에서 멀티플레이어용으로 프로그래밍한 모든 게임플레이는 싱글 플레이어에서도 작동합니다.
하지만 네트워킹 없이 이미 구축한 코드베이스를 리팩터링하려면, 전체 프로젝트를 샅샅이 뒤져가며 거의 모든 게임플레이 함수를 다시 프로그래밍해야 합니다. 팀원들은 그 시점까지 당연하게 받아들이던 프로그래밍 모범 사례를 다시 배워야 합니다. 또한, 네트워크 속도와 안정성으로 인해 발생하는 기술적 병목 현상에도 대비할 수 없습니다.
프로젝트 후반에 네트워킹을 도입하는 것은 처음부터 계획하는 것보다 리소스가 많이 들고 번거로운 일입니다. 따라서, 멀티플레이어 기능이 절대 필요 없는 프로젝트라는 확신이 없는 한, 항상 멀티플레이어용으로 프로그램하는 것이 좋습니다.
클라이언트-서버 모델
싱글 플레이어 게임이나 로컬 멀티플레이어 게임에서는, 게임이 독립형 게임으로 로컬에서 실행됩니다. 플레이어가 단일 컴퓨터에 입력을 연결하고 해당 로컬 컴퓨터에서 액터와 월드, 각 플레이어의 유저 인터페이스 등, 게임의 모든 것을 직접 제어합니다.

싱글 플레이어와 로컬 멀티플레이어는 하나의 컴퓨터에서만 실행됩니다.
네트워크 멀티플레이어 게임의 경우 언리얼 엔진은 클라이언트-서버 모델을 사용합니다. 네트워크에 있는 하나의 컴퓨터가 서버 가 되어 멀티플레이어 게임 세션을 호스팅하고, 서버에 접속한 다른 모든 플레이어의 컴퓨터는 클라이언트 가 됩니다. 서버는 접속된 각 클라이언트와 게임 스테이트 정보를 공유하고 서로 통신할 수 있는 수단을 제공합니다.

네트워크 멀티플레이어에서는 게임이 서버(1)와 서버에 접속한 여러 클라이언트(2) 사이에서 실행됩니다. 서버는 게임플레이를 처리하고 클라이언트는 사용자에게 게임을 보여줍니다.
게임을 호스팅하는 서버는 하나의 진정한 오소리티 있는 게임 스테이트를 보유합니다. 즉, 서버는 멀티플레이어 게임이 실제로 벌어지는 곳입니다. 클라이언트는 각각 서버에 소유한 폰(Pawns) 을 원격 제어하는데, 게임 내 액션을 수행하도록 폰에 프로시저 호출을 전송합니다. 하지만, 서버는 클라이언트 모니터에 직접 영상을 스트리밍하지 않습니다. 대신, 서버는 게임 스테이트에 대한 정보를 각 클라이언트에 리플리케이트 하여 존재해야 하는 액터와 그러한 액터의 행동 방식, 보유해야 하는 다양한 변수 값을 알려줍니다. 그러면 각 클라이언트는 이 정보를 사용하여 서버에서 플레이되는 게임을 매우 비슷하게 시뮬레이션합니다.
클라이언트-서버 게임플레이 예시
이 경우 실제적인 게임플레이 프로그래밍이 어떻게 달라지는지를 보여주기 위해 멀티플레이어 게임의 두 플레이어를 예시로 들어 보겠습니다. 시스템에서 이 두 플레이어를 플레이어 1(Player 1) 과 플레이어 2(Player 2) 로 칭하고, 서로에게 발사체를 발사하는 프로세스를 자세히 살펴보겠습니다.
로컬 게임플레이 | 네트워크 게임플레이 |
---|---|
![]() |
![]() |
플레이어 1이 입력을 눌러 무기를 발사합니다.
|
플레이어 1이 로컬 머신에서 입력을 눌러 무기를 발사합니다.
|
플레이어 1의 발사체가 무기에서 앞으로 이동합니다. |
서버에 있는 플레이어 1의 발사체가 무기에서 앞으로 이동합니다.
|
플레이어 1의 발사체가 플레이어 2의 폰과 충돌합니다.
|
서버에 있는 플레이어 1의 발사체가 플레이어 2의 폰과 충돌합니다.
|
독립형 게임에서는 이러한 상호작용이 모두 같은 컴퓨터의 같은 월드 에서 벌어지므로 문자 그대로 이해하기도 쉽고 프로그래밍하기도 쉽습니다. 예를 들어, 오브젝트 스폰 시, 모든 플레이어가 그 장면을 볼 수 있다는 것을 당연하게 받아들일 수 있습니다.
네트워크 게임에서는 이러한 상호작용이 서버에 존재하는 월드, 플레이어 1의 클라이언트에 존재하는 월드, 플레이어 2의 클라이언트에 존재하는 월드, 그리고 세션에 참여하는 각 클라이언트의 추가 월드 등, 여러 개의 서로 다른 월드에서 벌어집니다. 서로 다른 컴퓨터의 각 월드에 각각의 폰과 폰의 무기, 폰이 발사하는 발사체의 사본이 있습니다. 게임이 실제 플레이되는 곳은 서버이지만, 클라이언트의 월드에서 똑같은 이벤트가 발생하는 것처럼 보이게 해야 합니다. 따라서, 각 클라이언트에 선별적으로 정보를 전송하여 서버에 있는 월드의 시각적 표현을 생성해야 합니다.
이 프로세스에서는 필수 게임플레이 상호작용(콜리전, 무브먼트, 대미지), 장식성 이펙트(비주얼 이펙트 및 사운드) 및 개인 플레이어 정보(HUD 업데이트)를 구분합니다. 이러한 각 요소는 특정 컴퓨터나 네트워크에 있는 컴퓨터 집합과 관련됩니다. 하지만, 이 정보를 리플리케이트하는 프로세스는 완전히 자동이 아니므로, 게임플레이를 프로그래밍하면서 어떤 정보를 어떤 컴퓨터에 리플리케이트할지 지정해야 합니다. 주요 과제는 어떤 정보를 리플리케이트해야 할지, 그리고 모든 플레이어에게 일관된 경험을 제공하는 동시에 리플리케이트하는 정보량을 최소화하여 가능한 한 네트워크 대역폭을 적게 사용하도록 하기 위해 어떻게 할지를 선택하는 것입니다.
기본 네트워킹 개념
아래 섹션에서는 언리얼 엔진 내에서 네트워크 게임플레이를 구동하는 개념을 자세하게 설명합니다. 멀티플레이어 게임을 직접 구축하는 데 도움이 되는 몇 가지 툴을 개괄적으로 쉽게 살펴볼 수 있습니다.
네트워크 모드 및 서버 타입
네트워크 모드(Network mode) 는 네트워크 멀티플레이어 세션과 컴퓨터의 관계를 설명합니다. 게임 인스턴스는 다음 네트워크 모드 중 하나를 취할 수 있습니다:
네트워크 모드 | 설명 |
---|---|
독립형(Standalone) | 게임이 원격 클라이언트 접속을 허용하지 않는 서버로 실행됩니다. 게임에 참여하는 모든 플레이어는 로컬 플레이어뿐입니다. 이 모드는 싱글 플레이어 및 로컬 멀티플레이 게임에 사용됩니다. 서버 측 로직과 클라이언트 측 로직 모두를 로컬 플레이어에 맞게 실행합니다. |
클라이언트(Client) | 게임이 네트워크 멀티플레이어 세션으로 서버에 접속된 클라이언트로 실행됩니다. 어떠한 서버 측 로직도 실행하지 않습니다. |
리슨 서버(Listen Server) | 게임이 네트워크 멀티플레이어 세션을 호스팅하는 서버로 실행됩니다. 원격 클라이언트의 접속을 허용하며, 서버에 바로 로컬 플레이어가 있습니다. 이 모드는 가벼운 협동 및 경쟁 멀티플레이어에 자주 사용됩니다. |
데디케이티드 서버(Dedicated Server) | 게임이 네트워크 멀티플레이어 세션을 호스팅하는 서버로 실행됩니다. 원격 클라이언트의 접속을 허용하지만, 로컬 플레이어는 없습니다. 따라서, 더 효율적인 운영을 위해 그래픽, 사운드, 입력 및 기타 플레이어 중심 기능을 버립니다. 이 모드는 높은 지속성이나 보안, 대규모 멀티플레이어가 필요한 게임에 자주 사용됩니다. |
리슨 서버는 게임 사본이 있는 어떤 사용자라도 리슨 서버를 시작하고 같은 컴퓨터에서 플레이할 수 있기에 사용자가 자발적으로 쉽게 구성할 수 있습니다. 리슨 서버를 지원하는 게임은 서버를 시작하거나 참여할 서버를 검색하기 위한 게임 내 UI를 제공하는 경우가 많습니다. 하지만, 리슨 서버를 호스팅하는 플레이어가 서버에서 직접 플레이하기 때문에 네트워크 접속을 통해 플레이해야 하는 다른 플레이어보다 유리하므로 공정성과 부정행위 문제가 대두됩니다. 또한, 서버로 실행하면서 그래픽과 사운드 같은 플레이어 관련 시스템을 지원해야 하므로 추가 부하가 발생합니다. 이러한 요인 때문에 경쟁이 치열한 환경 게임이나 게임에 관련된 네트워크 부하가 매우 높은 게임에는 적합하지 않지만, 소규모 플레이어 그룹이 즐기는 가벼운 협동 및 경쟁 멀티플레이어용으로는 매우 편리합니다.
데디케이티드 서버는 더 비싸고 환경설정이 어려우며, 게임에 참여하는 모든 플레이어가 별도의 컴퓨터에서 자체적으로 네트워크에 접속해야 합니다. 하지만, 데디케이티드 서버에 참여하는 모든 플레이어가 같은 타입의 접속을 통해 게임을 경험하므로 공정성이 보장됩니다. 데디케이티드 서버는 그래픽을 렌더링하지도 않고 기타 로컬 플레이어에만 관련된 다른 로직을 수행하지도 않으므로, 게임플레이와 네트워킹을 더 효율적으로 처리할 수 있습니다. 따라서 보안이나 공정성, 안정성 때문에 신뢰할 수 있는 서버가 필요하거나 고성능이 필요한 게임, 많은 수의 플레이어가 필요한 게임용으로 데디케이티드 서버가 선호됩니다. 이러한 게임에는 MMO, 경쟁적인 MOBA, 빠르게 진행되는 온라인 슈팅 게임 등이 있습니다.
액터 리플리케이션
리플리케이션은 네트워크 세션에 있는 다양한 컴퓨터 간에 게임 스테이트 정보를 재현하는 프로세스입니다. 리플리케이션이 올바로 설정되면, 다른 컴퓨터의 게임 인스턴스가 동기화됩니다. 기본적으로 대부분의 액터는 리플리케이션이 활성화되어 있지 않으며, 로컬에서 모든 함수를 수행합니다. C++ 액터 클래스에서 bReplicates
변수를 설정하거나 액터 블루프린트의 리플리케이트(Replicates) 세팅을 true 로 설정하여 특정 클래스의 액터에 대한 리플리케이션을 활성화할 수 있습니다.
다음은 네트워크 게임플레이 제작에 가장 많이 사용하는 리플리케이션 기능입니다:
리플리케이션 기능 | 설명 |
---|---|
생성 및 소멸(Creation and Destruction) | 서버에서 오소리티 있는 버전의 액터가 스폰되면 접속된 모든 클라이언트에 자동으로 원격 프록시가 생성됩니다. 그런 다음, 정보가 그러한 원격 프록시에 리플리케이트됩니다. 오소리티 있는 액터를 소멸시키면 접속된 모든 클라이언트에 있는 원격 프록시가 자동으로 소멸됩니다. |
무브먼트 리플리케이션(Movement Replication) | 오소리티 있는 액터에 대해 무브먼트 리플리케이트(Replicate Movement) 가 활성화되어 있거나 C++에서 bReplicateMovement 가 true 로 설정되어 있으면, 자동으로 위치(Location)와 회전(Rotation), 속도(Velocity)가 리플리케이트됩니다. |
변수 리플리케이션(Variable Replication) | 리플리케이티드 변수로 지정된 변수는 값이 변경될 때마다 오소리티 있는 액터에서 원격 프록시로 자동으로 리플리케이트됩니다. |
컴포넌트 리플리케이션(Component Replication) | 액터 컴포넌트가 액터 컴포넌트를 소유한 액터의 일부로 리플리케이트됩니다. 리플리케이트되는 것으로 지정된 컴포넌트 내 모든 변수가 리플리케이트되며, 컴포넌트 내에서 호출된 모든 RPC가 액터 클래스에서 호출된 RPC와 일관되게 행동합니다. |
원격 프로시저 호출(RPC) | RPC는 네트워크 게임에서 특정 컴퓨터로 전송되는 특별 함수입니다. 처음 RPC를 호출한 컴퓨터가 무엇이든 지정된 컴퓨터에서만 RPC 구현이 실행됩니다. 서버(서버에서만 실행)나 클라이언트(액터 소유 클라이언트에서만 실행) 또는 NetMulticast(서버를 포함, 세션에 접속된 모든 컴퓨터에서 실행)로 지정할 수 있습니다. |
생성 및 소멸, 무브먼트 같은 일반적인 사용 사례는 자동으로 처리되지만, 리플리케이션을 활성화해도 다른 모든 게임플레이 피처는 기본적으로 자동으로 리플리케이트되지 않습니다. 게임에 적합하도록 리플리케이트하려는 변수와 함수를 정확하게 지정해야 합니다. 위의 모든 리플리케이션 기능에 대한 자세한 내용은 프로퍼티 리플리케이션 가이드에서 확인할 수 있습니다.
액터와 폰, 캐릭터의 몇 가지 일반적인 피처는 리플리케이트되지 않습니다.
- 스켈레탈 메시(Skeletal Mesh) 및 스태틱 메시(Static Mesh) 컴포넌트
- 머티리얼(Materials)
- 애니메이션 블루프린트(Animation Blueprints)
- 파티클 시스템(Particle Systems)
- 사운드 이미터(Sound Emitters)
- 피직스 오브젝트(Physics Objects)
이러한 각 요소는 모든 클라이언트에서 개별적으로 실행됩니다. 하지만, 이러한 시각적 요소를 구동하는 변수가 리플리케이트되면, 모든 클라이언트가 같은 정보를 보유하게 되어 거의 같은 방식으로 해당 요소가 시뮬레이션됩니다.
네트워크 역할 및 오소리티
액터의 네트워크 역할 은 네트워크 게임 중 액터를 제어할 컴퓨터를 결정합니다. 오소리티 있는 액터는 액터의 스테이트를 제어하는 것으로 간주되며, 정보를 네트워크 멀티플레이어 세션 내에 있는 다른 컴퓨터로 리플리케이트합니다. 원격 프록시 는 원격 컴퓨터에 있는 해당 액터의 사본으로, 오소리티 있는 액터로부터 리플리케이트된 정보를 수신합니다. 이는 로컬 역할(Local Role) 과 원격 역할(Remote Role) 변수로 추적되며, 이러한 변수는 다음 값을 취할 수 있습니다:
네트워크 역할 | 설명 |
---|---|
None(없음) | 액터가 네트워크 게임에서 역할이 없으며 리플리케이트하지 않습니다. |
Authority(오소리티) | 액터가 오소리티 있는 액터이며 다른 컴퓨터에 있는 액터의 원격 프록시로 정보를 리플리케이트합니다. |
Simulated Proxy(시뮬레이션된 프록시) | 액터가 다른 컴퓨터의 오소리티 있는 액터에 의해 완전히 제어되는 원격 프록시입니다. 픽업이나 발사체, 인터랙티브 오브젝트 같은 대부분의 네트워크 게임 액터는 원격 클라이언트에서 시뮬레이션된 프록시로 나타납니다. |
Autonomous Proxy(자율 프록시) | 액터가 일부 함수를 로컬에서 수행할 수 있지만, 오소리티 있는 액터로부터 보정을 수신하는 원격 프록시입니다. 자율 프록시는 보통 폰처럼 플레이어가 직접 제어하는 액터로 예약되어 있습니다. |
언리얼 엔진에서 사용하는 기본 모델은 서버-오소리티(server-authoritative) 모델로, 이는 해당 서버가 항상 게임 스테이트에 대한 오소리티를 보유하며, 정보가 항상 서버에서 클라이언트로 리플리케이트된다는 뜻입니다. 서버의 액터가 오소리티의 로컬 역할을 보유할 것으로 예상하며, 원격 클라이언트의 대응 액터가 시뮬레이션된 프록시 또는 자율 프록시의 로컬 역할을 보유할 것으로 예상합니다.
액터의 네트워크 롤에 대한 자세한 내용은 액터 롤 및 리모트 롤 가이드를 참조하세요.
클라이언트 오너십
네트워크 게임에 있는 폰은 특정 클라이언트 컴퓨터의 플레이어 컨트롤러 가 소유합니다. 폰이 클라이언트 전용 함수를 호출할 때마다, 소유 플레이어의 컴퓨터로만 전달됩니다. 함수를 어떤 컴퓨터에서 호출하는지는 상관없습니다. 오너 변수가 특정 폰으로 설정된 액터는 연관을 통해 해당 폰의 소유 클라이언트에 속합니다. 또한 이러한 액터는 클라이언트 전용 함수도 오너의 컴퓨터로 전달합니다. C++에서 IsLocallyControlled
함수나 블루프린트에서 Is Locally Controlled 노드를 사용하여 폰이 해당 소유 클라이언트에 속하는지 여부를 결정할 수 있습니다.
생성 중에는 아직 폰에 할당된 컨트롤러가 없을 수 있으므로 커스텀 폰 클래스의 생성자에서는 IsLocallyControlled
를 사용하지 마세요.
오너십에 대한 자세한 내용은 액터와 액터 및 액터 소유 접속 가이드를 참조하세요.
관련성 및 우선순위
관련성(Relevance) 은 멀티플레이어 게임 중 액터를 리플리케이트할 가치가 있는지 여부를 결정하는 데 사용됩니다. 관련 없다고 간주되는 액터는 리플리케이션 중 컬링됩니다. 이렇게 대역폭이 절감되어 관련 있는 액터가 더 효율적으로 리플리케이트될 수 있습니다. 플레이어가 소유하지 않고 물리적으로 플레이어 근처에 없는 액터는 관련 없는 액터로 간주되어 리플리케이트되지 않습니다. 관련 없는 액터는 서버에 존재하고 오소리티 있는 게임 스테이트에 영향을 줄 수 있지만, 플레이어가 근처에 있게 될 때까지 클라이언트에 정보를 전송하지 않습니다. IsNetRelevantFor
함수를 오버라이드하여 수동으로 관련성을 제어할 수 있으며, NetCullDistanceSquared
프로퍼티를 사용하여 액터가 관련성을 갖는 거리를 결정할 수 있습니다.
게임플레이 단일 프레임 동안 모든 관련 있는 액터를 리플리케이트할 만큼 대역폭이 충분하지 않을 때도 있습니다. 따라서, 액터에는 먼저 리플리케이트될 액터를 결정하는 우선순위(Priority) 값이 있습니다. 기본적으로 폰과 플레이어 컨트롤러의 NetPriority
는 3.0 으로, 게임에서 우선순위가 가장 높은 액터이며, 베이스 액터의 NetPriority
는 1.0 입니다. 액터가 리플리케이트되지 않은 시간이 오래될수록 리플리케이트될 때까지 각 연속 패스에서 우선순위가 높아집니다.
액터의 관련성 및 우선순위에 대한 자세한 내용은 액터 관련성 및 우선순위 가이드를 참조하세요.
변수 리플리케이션
C++의 UPROPERTY
매크로에서 Replicated
또는 ReplicateUsing
지정자를 사용하거나, 블루프린트의 디테일(Details) 패널에서 리플리케이트됨(Replicated)으로 지정하여 변수 및 오브젝트 레퍼런스에 리플리케이션을 추가할 수 있습니다. 오소리티 있는 액터에서 리플리케이티드 변수의 값이 변경될 때마다, 해당 정보가 자동으로 오소리티 있는 액터에서 세션에 접속된 원격 프록시로 전송됩니다.
RepNotifies
특정 변수의 리플리케이트된 정보를 성공적으로 수신한 액터에 대한 응답으로 호출되는 RepNotify 함수를 지정할 수 있습니다. RepNotify는 변수가 업데이트될 때 로컬에서만 트리거되므로, 오소리티 있는 액터의 변수 변경에 대한 응답으로 오버헤드가 적게 게임플레이 로직을 트리거하는 방법입니다. 이 함수 기능을 이용하려면 C++에서 변수의 UPROPERTY
매크로에 있는 ReplicatedUsing
지정자를 사용하거나, 블루프린트에서 RepNotify를 사용하도록 변수의 리플리케이션 세팅을 변경하면 됩니다.
RepNotify는 다른 게임플레이 함수 기능과 관계없이 리플리케이트가 필요한 변수에 추가할 수 있어 RPC 또는 리플리케이티드 함수를 사용하는 것보다 선호되며, 추가 네트워크 호출을 생성하는 것에 비해 대역폭을 상당히 절감할 수 있습니다.
원격 프로시저 호출(RPC)
원격 프로시저 호출(RPC)는 리플리케이티드 함수라고도 합니다. 모든 컴퓨터에서 호출할 수 있지만, 네트워크 세션에 접속된 특정 컴퓨터에서 구현되도록 지시됩니다. 세 가지 타입의 RPC를 사용할 수 있습니다:
RPC 타입 | 설명 |
---|---|
서버(Server) | 게임을 호스팅하는 서버에서만 호출됩니다. |
클라이언트(Client) | 함수가 속한 액터를 소유한 클라이언트에서만 호출됩니다. 액터에 소유 접속이 없으면 이 로직은 실행되지 않습니다. |
NetMulticast | 서버에 접속된 모든 클라이언트와 서버 자체에서 호출됩니다. |
블루프린트의 이벤트와 함수는 디테일(Details) 패널의 리플리케이트(Replicates) 드롭다운에서 이 세 가지 타입 중 하나로 설정하여 동일하게 지정할 수 있습니다.

UFUNCTION
매크로에서 서버
, 클라이언트
또는 NetMulticast
지정자를 제공하여 C++의 함수를 RPC로 지정할 수 있습니다. 코드 구현에서 _Implementation
접미사를 사용하여 이를 구현합니다.
//서버 RPC MyFunction의 선언입니다.
UFUNCTION(Server, Reliable, WithValidation)
void MyFunction(int myInt);
ExampleClass.cpp
//서버 RPC MyFunction의 구현입니다.
void AExampleClass::MyFunction_Implementation(int myInt)
{
//여기에 게임플레이 코드가 들어갑니다.
}
함수를 RPC로 지정한 후에는 게임플레이 로직을 부여하고 다른 함수와 같은 방식으로 호출할 수 있습니다. RPC와 관련된 자세한 내용은 원격 프로시저 호출(RPC) 가이드를 참조하세요.
신뢰성
RPC를 신뢰성(Reliable) 또는 비신뢰성(Unreliable) 으로 지정해야 합니다. 블루프린트에서 함수와 이벤트는 기본적으로 비신뢰성으로 가정됩니다. 디테일 패널에서 신뢰성(Reliable) 설정을 true 로 설정하여 함수를 신뢰성으로 지정할 수 있습니다. C++에서 서버
, 클라이언트
또는 NetMulticast
함수로 상태를 지정하는 것과 함께 모든 RPC의 UFUNCTION
매크로에 신뢰성
또는 비신뢰성
지정자를 추가해야 합니다.
비신뢰성 RPC는 의도한 목적지 도착이 보장되지 않지만, 신뢰성 RPC보다 더 자주, 더 빨리 전송할 수 있습니다. 비신뢰성 RPC는 게임플레이에 중요하지 않거나 매우 자주 호출되는 함수에 가장 적합합니다. 예를 들어, 액터 무브먼트는 프레임마다 변경될 수 있기 때문에 비신뢰성 RPC를 사용하여 리플리케이트됩니다.
신뢰성 RPC는 의도한 목적지 도착을 보장하며, 성공적으로 수신될 때까지 큐에 남아 있습니다. 신뢰성 RPC는 게임플레이에 중요하지만, 그렇게 자주 호출되지 않는 함수에 가장 적합합니다. 신뢰성 RPC의 예로는 콜리전 이벤트, 무기 발사 시작 또는 종료, 액터 스폰 등이 있습니다.
신뢰성 함수를 과도하게 사용하면 해당 함수의 큐가 오버플로우될 수 있습니다. 그러면 강제로 접속이 끊어집니다. 프레임 단위로 리플리케이티드 함수를 호출할 경우에는 함수를 비신뢰성으로 만들어야 합니다. 플레이어 입력에 바인딩된 신뢰성 함수가 있다면, 플레이어가 해당 함수를 호출할 수 있는 빈도를 제한해야 합니다.
유효성 검사
WithValidation
지정자는 함수 구현 외에도 들어오는 함수 호출의 데이터 유효성을 검사하는 함수도 있다는 사실을 나타냅니다. 이러한 유효성 검사 함수는 담당하는 함수와 같은 시그니처를 보유하지만, 원래 반환 값 대신 boolean을 반환합니다. true
를 반환하면, RPC의 Implementation
실행을 허용합니다. false
를 반환하면 실행을 막습니다.
//서버 RPC MyFunction의 유효성 검사입니다
bool AExampleClass::MyFunction_Validation(int myInt)
{
/*
myInt 값이 음수이면, MyFunction_Implementation 실행을 허용하지 않고자 합니다.
따라서, myInt 값이 0보다 클 때만 true를 반환합니다.
*/
return myInt >= 0;
}
팁 및 추가 자료
다음은 효과적이고 일관된 멀티플레이어 게임 시스템 구현을 위한 기본 지침입니다.
기본 리플리케이티드 액터 체크리스트
리플리케이티드 액터를 생성하려면, 다음 단계를 따르세요:
- 액터의 리플리케이트됨(Replicated) 세팅을 True로 설정합니다.
- 리플리케이티드 액터가 이동해야 하면, 무브먼트 리플리케이트(Replicates Movement)를 True로 설정합니다.
- 리플리케이티드 액터를 스폰하거나 소멸할 때, 서버에서 해야 합니다.
- 리플리케이트할 컴퓨터 간에 공유되어야 하는 모든 변수를 설정합니다. 이러한 변수는 보통 게임플레이 필수 변수입니다.
- 언리얼 엔진의 사전 제작 무브먼트 컴포넌트(Movement Components)는 이미 리플리케이션용으로 구축되어 있으므로, 가급적 사용합니다.
- 서버-오소리티 모델을 사용한다면, 플레이어가 수행할 수 있는 모든 새로운 액션은 서버 함수가 트리거하도록 해야 합니다.
네트워킹 팁
- RPC나 리플리케이티드 블루프린트 함수는 최대한 적게 사용합니다. 대신 RepNotify를 사용할 수 있다면, 그렇게 해야 합니다.
- 멀티캐스트 함수는 세션에 접속된 각 클라이언트에 대해 추가 네트워크 트래픽을 생성하므로 특히 사용을 자제합니다.
- 리플리케이트되지 않은 함수가 서버에서만 실행된다는 것을 보장할 수 있다면, 서버 전용 로직을 서버 RPC에 꼭 포함하지 않아도 됩니다.
- 신뢰성 RPC를 플레이어 입력에 바인딩할 때는 신중하게 결정합니다. 플레이어는 매우 빠르게 반복해서 버튼을 누르므로 신뢰성 RPC의 큐가 오버플로우됩니다. 플레이어가 이를 활성화할 수 있는 빈도를 어떻게든 제한해야 합니다.
- 게임이 틱에서처럼 RPC나 리플리케이티드 함수를 매우 자주 호출한다면, 이를 비신뢰성으로 만들어야 합니다.
- 일부 함수는 게임플레이 로직에 대한 응답으로 호출한 다음 RepNotify에 대한 응답으로 호출하여 클라이언트와 서버가 병렬 실행하게 함으로써 재활용할 수 있습니다.
- 액터의 네트워크 역할을 확인하여
ROLE_Authority
인지 아닌지 알 수 있습니다. 이는 서버와 클라이언트 양쪽에서 활성화되는 함수 실행을 필터링하는 데 유용한 방법입니다. - 폰이 C++의
IsLocallyControlled
함수나 블루프린트의 Is Locally Controlled 함수를 사용하여 로컬로 제어되는지 확인할 수 있습니다. 이는 소유 클라이언트와의 관련 여부에 따라 실행을 필터링하는 데 유용합니다. - 생성 중에는 폰에 할당된 컨트롤러가 없을 수 있으므로 생성자 스크립트에서는
IsLocallyControlled
를 사용하지 않도록 합니다.
튜토리얼
위의 원칙을 사용하여 간단한 멀티플레이어 게임을 구현하는 방법에 대한 자세한 안내는 멀티플레이어 프로그래밍 퀵스타트를 참조하세요.