언리얼 엔진에서 서버-클라이언트 통신하는 방법에 대해 강의를 들으며 추가로 참고한 위 링크에서 중요하다고 생각되는 내용을 정리할 것이다.
서버-클라이언트 관계 개요
멀티플레이어 게임에서는 실제로 게임이 플레이되는 유일한 authoritative 게임 스테이트가 서버에 존재한다.
클라이언트의 로컬 폰에서 서버에 존재하는 원격 폰으로 Remote Procedure Call (RPC) 을 보내 인게임 행동을 한다.
서버는 게임스테이트 정보를 각 클라이언트로 복제한다. 이 정보는 액터의 위치, 행동 규칙, 변수 값 등을 담고 있다.
각 클라이언트는 이렇게 서버에서 받은 정보를 바탕으로 서버의 상태를 approximate (근사치 바탕으로 모방?)한다.
+ 기본적으로 서버는 비주얼을 직접 클라이언트에 보내지 않고 상태 정보만 게임 인스턴스에 보낸다.
클라이언트의 게임 인스턴스 안에서 이 정보가 재-시각화된다.
+ 앞으로 계속 사용할 authoritative 는 서버에 존재하는, 실제 게임플레이가 이루어지는 버전?을 지칭한다.
네트워크 멀티플레이어 게임플레이 예시
1. Player 1이 로컬에서 공격 버튼 입력
1) Player 1 로컬 폰 --> 서버 폰 : 공격 명령 릴레이
2) 서버 무기 : 프로젝타일 스폰
3) 서버 --> 각 클라이언트 : 클라이언트 내 프로젝타일의 카피를 생성하도록 알림
4) 서버 무기 --> 각 클라이언트 : 공격 사운드와 vfx 재생하도록 알림
2. Player 1 Projectile (서버) 이 전방으로 이동
1) 서버 --> 각 클라이언트 : 클라이언트 내 프로젝타일의 이동을 복제하도록 알림
3. Player 1 Projectile 이 Player 2 와 충돌 (서버)
1) 서버 내 : 충돌 시 프로젝타일 destroy 함수 트리거
2) 서버 --> 각 클라이언트 : 클라이언트 내 프로젝타일 카피 destroy하도록 알림 (자동)
3) 서버 --> 각 클라이언트 : 충돌 시 클라이언트에 사운드/비주얼 재생하라고 알리는 함수 트리거
4) Player 2 서버 폰 : 대미지 입음
5) Player 2 서버 폰 --> Player 2 클라이언트 : 피격 화면 효과 재생 알림
위와 같은 상호작용은 네트워크를 통해 아래와 같은 여러 다른 월드에서 일어난다.
- 서버의 Authoritative 월드
- Player 1 클라이언트 월드
- Player 2 클라이언트 월드
- 그 외 서버의 게임 인스턴스에 연결된 다수의 클라이언트 월드
각 월드는 각자만의 플레이어 컨트롤러, 폰, 무기, 프로젝타일 등을 갖고 있으므로, 서버에서 실제로 플레이되는 게임 이벤트들을 각 클라이언트가 시청각적으로 정확히 나타기 위해서 서버의 이벤트를 정확히 복제해야 한다.
이러한 프로세스를 고려하면 게임플레이 상호작용 (충돌, 이동, 공/피격) 과 장식적 효과 (vfx, 사운드, HUD) 가 나뉘게 된다. 개발자는 어떤 정보가 어떤 클라이언트로 복제되는지 지정하여 모든 플레이어가 일관된 경험을 하는 동시에 네트워크의 대역폭이 과부하되지 않도록 만들어야 한다.
네트워크 모드
: 네트워크 게임 세션에 있어 기계 (PC 등) 가 맺는 관계성
모드 | ENetMode:: | 설명 |
Standalone | NM_Standalone | - 로컬 전용. - 클라이언트/서버의 기능 모두 포함하나 원격 클라이언트의 연결 불허. - 싱글플레이어 또는 로컬 멀티플레이어 에서 사용. - 서버와 클라이언트의 기능을 모두 하므로, 멀티플레이어 로직은 추가 작업 없이 single-player standalone network mode 에서 작동 가능 |
Dedicated Server | NM_DedicatedServer | - 서버 전용. - 원격 클라이언트의 연결만 허용되며 그래픽, 사운드, 입력 등 플레이어-중심 기능은 모두 제외함. - 안정성이 필요하거나 대규모 멀티플레이어 게임의 경우 활용하는 모드. - 다른 플레이어들로부터 분리된 기계와 연결이 필요하여 리슨 서버보다 비용이 크고 세팅이 복잡함. - 모든 플레이어가 원격으로 연결되므로 보다 일관된 / 공정한 경험을 할 수 있음. - 그래픽을 렌더하거나 로컬 플레이어에만 적용되는 로직은 제외하므로 서버 로직 수행에 있어 효율적임. |
Listen Server | NM_ListenServer | - 게임을 호스팅하는 로컬 플레이어를 가진 서버. - 네트워크 상 다른 플레이어들에 노출. - 캐주얼 협동/경쟁 게임에 사용. - 동일한 기계에서 리슨 서버를 시작하고 게임을 플레이할 수 있어 사용자 입장에서 간편 - 리슨 서버를 호스팅하는 플레이어의 경우 직접 그 서버에서 플레이하게 되므로 어드밴티지가 있다고 할 수 있음. - 한편, 서버를 호스팅하는 추가적인 작업을 동시에 수행하므로 디스어드밴티지가 있다고 할 수도 있음. --> 완전한 공정성이 매우 중요하거나 네트워크 로드가 많은 게임에는 부적합. |
Client | NM_Client | - 원격 서버에 연결되는 클라이언트 전용. 서버 로직 제외. |
Replication
: authoritative 서버가 연결된 클라이언트로 상태 데이터를 보내 서버의 게임 상태를 복제하도록 하는 것.
: Replication 설정이 알맞게 되어 각기 다른 기계의 게임 인스턴스들이 동기화될 수 있게 하는 것이 목표
- AActor 베이스 클래스들은 상태가 잘 복제될 수 있도록 디자인 되어 있음.
- UObject 베이스 클래스들은 복제가 가능하나 액터에 subobject 로 부착되어야 적절하게 복제될 수 있음.
언리얼의 Replication 은 크게 두 가지로 볼 수 있다.
1. Actor Replication
- 객체의 replicate + replicate 되어야 하는 프로퍼티들의 지정 + 네트워크 통해 호출될 함수의 정의
2. Replication Systems
- 객체를 정확한 목적지로 replicate 하는 작업을 담당하는 내부 시스템
Actor Replication
Actor 는 여러 메커니즘을 통해 네트워크를 건너 상호작용하는데, 다음과 같은 주요 방식들이 있다.
- Replicated Properties
: 네트워크 건너로 replicate 되는 액터의 프로퍼티들
- Replicated Using Properties
: 네트워크 건너로 replicate 되는 액터의 프로퍼티들 + replicate 직후 지정된 함수를 호출
- Remote Procedure Calls (RPC)
: 액터가 한 기계에서 특정 함수를 호출하고 다른 기계에서 함수가 실행되게 하는 기능
: 예를 들어 액터가 클라이언트 내의 서버 RPC 를 호출하면 해당 네트워크 패킷을 받은 서버에서 함수가 실행
actor replication 은 다음과 같은 주요 단계를 포함한다.
- 클라이언트에서 어떤 액터가 어느 연결에 replicate 되어야 하는지 지정 (참고)
- 서버에서 프로퍼티 업데이트 및 RPC 실행 순서 결정 (참고)
- 서버에서 연결된 클라이언트로 관련 정보 전달
대부분의 액터는 기본적으로 replicate 되지 않고 로컬에서만 작동하게 되어 있다.
따라서 C++ 에서 bReplicates 변수를 set 하거나 블루프린트에서 Replicates 값을 변경해주어야 한다.
액터와 프로퍼티를 replicate 하는 방법에 대해서는 여기를 참고... (이 내용은 내일 정리해보아야겠다)
기능별 Replicate 방식 정리
기능 / 요소 | 설명 | 자동 여부 |
Creation / Destruction | - 서버에 authoritative 액터가 스폰되면, 연결된 클라이언트에 원격 proxy 를 만듦 - replicate 할 정보는 이 proxy 에 전달 - authoritative 액터가 destroy 되면 원격 proxy 를 자동으로 destroy |
자동 |
Movement | authoritative 액터에 bReplicateMovement = true 로 설정하면 위치, 회전, 속도가 자동으로 replicate | 자동 |
Properties | UPROPERTY Replicated 또는 ReplicatedUsing 지정된 프로퍼티는 값이 변경될 때 자동 replicate | 수동 |
Components | - 액터 컴포넌트는 액터가 replicate 되는 경우 액터의 일부로 replicate - 컴포넌트 내 Replicated 지정된 변수값 또한 replicate - 컴포넌트 내 RPC 호출은 액터의 RPC 호출과 일관되게 작동 (무슨 말이지..?) |
수동 |
Subobjects | UObject 기반 객체는 액터에 부착되면 subobject로서 replicate | 수동 |
RPCs | - 특정 기기로 전달되는 함수로, RPC를 호출한 기기에 관계 없이 목적지 기기에서만 실행 - 목적지 기기는 Server, Client, NetMulticast 로 지정 가능 - Server : 서버에서만 실행 - Client : 액터의 owning 클라이언트에서만 실행 - NetMulticast : 세션에 연결된 모든 기기에서 실행 (서버 포함) |
수동 |
위에서 볼 수 있듯이 액터 생성/삭제, 이동은 자동으로 Replicate 되지만 프로퍼티, RPC 함수, 컴포넌트, subobject 등은 수동으로 지정되어야 한다.
특히 다음과 같이 보통 폰이나 캐릭터의 일부인 feature 들도 replicate 되도록 지정이 필요하다:
- 메시 컴포넌트
- 머티리얼
- 애니메이션 블루프린트
- 파티클 컴포넌트
- 사운드 이미터
- 피직스 오브젝트
Replication Systems
: 앞서 설명했듯이 replicate 를 실행하는 내부 시스템. UE에서는 아래 3가지 시스템을 제공한다. 구체적인 게임 제작에 앞서 게임에 맞는 시스템을 선택하는 것이 좋다.
1. Generic Replication System
: 액터 레플리케이션, RPC 등을 지원하는 디폴트 시스템으로 아래와 같은 주요 특징을 가진다.
- Dormancy : Connection의 replicated 액터 리스트에 액터를 추가/제외
- Priority : 대역폭이 제한된 경우 각 프레임에 replicate 할 액터의 중요도 지정
- Relevancy : 특정 connection 에 해당 액터가 관련 있는지 없는지 지정
2. Replication Graph
: 많은 수의 replicated 액터를 수용하기 위해 디자인된 시스템 플러그인. 여기 참고
3. Iris Replication System
: 가장 최근에 추가된 시스템으로 Generic Replication System 과 나란히 작동하여 기존 액터 리플리케이션과 RPC 를 지원하는 동시에 다른 기능들을 개선하고자 디자인 됨. 도큐멘테이션 / Generic 에서 Iris 로 옮기기
주요 특징
- Filtering : 액터가 replicate 되는 타겟 connection 지정, replication 그룹 지정, 다이나믹 replication 필터링 제어
- Prioritization : 각 프레임에 replicate 할 액터의 중요도 지정 - 커스텀, 다이나믹 값으로 지정 가능
네트워크 활용 시 참고/주의
1. RPC 와 블루프린트 함수의 replicate 는 최소화할 것. ReplicatedUsing (= RepNotify) 프로퍼티로 대체할 것.
2. Multicast 함수는 선택적으로만 사용할 것.
3. 서버 내부에서만 작동할 것이 확실한 서버 전용 로직은 RPC에 포함하지 않기.
4. "Reliable" RPC 를 사용자 입력에 바인딩하는 경우, 사용자가 빠르게 반복적으로 입력하는 경우 Reliable RPC 의 대기열을 오버플로우시킬 수 있으므로, 사용자 입력 기반 활성화되는 RPC 수의 제한을 준다던가 하는 메커니즘 필요.
5. RPC 가 Tick 내에 위치하는 등 특별히 자주 호출되는 경우 Unreliable 지정
6. 함수 재활용 최대화. 예) 게임플레이 로직에 반응하여 호출된다던가, RepNotify 로 호출된다던가.
7. 액터의 네트워크 역할 확인할 것. (무슨 말이지?) 서버와 클라이언트 모두에서 활성화되는 함수의 필터링에 유용함.
8. 폰의 IsLocallyControlled 확인할 것. 폰이 owning 클라이언트와 엮여 있는지를 기반으로 필터링 시 유용.
9. 네트워크 Dormancy 활용할 것. 이것이 네트워크 게임플레이에서 가장 중요한 최적화 기능이다.
---------
공부를 계속하여 액터 레플리케이션 플로우, 프로퍼티 레플리케이션 방식, 객체 레플리케이션 순서, 최적화 방식 등에 대해 더 자세히 알아야 할 필요가 있겠다는 생각이 든다.
'언리얼_엔진_게임개발_공부 > 언리얼 네트워크' 카테고리의 다른 글
C++ 로 채팅 예제 구현 - ChatWidget ~ GameMode & 유저네임 중복체크 / 색상 랜덤 부여 (0) | 2025.03.20 |
---|---|
C++로 채팅 예제 구현하기 - PlayerController ~ LoginWidget (0) | 2025.03.19 |
데디케이티드 서버 활용 미로 게임 예제 분석 (0) | 2025.03.18 |
Run on server / Run on owning client 잘 구분하여 쓰기 (0) | 2025.03.13 |
서버 부하 분산 - 로드 밸런싱 - L4 / L7 스위치 (0) | 2025.03.11 |