카테고리 없음

7/31 TIL

blog13869 2026. 7. 31. 21:03

[ CH4_Unreal Engine 멀티플레이어 게임 개발 ]

 

[강의 1-3. 서버의 종류와 데디케이티드 서버]

 

< 서버의 종류와 구조 >

더보기

P2P Server(Peer to Peer Server)
 ㄴ 각각의 컴퓨터가 클라이언트이자 서버인 구조

 

Listen Server
  ㄴ 클라이언트이자 서버인 방장(Host)이 있고, 나머지 참가자(Guest)는 모두 클라이언트 역할만 맡는 형태
       P2P의 일종

Dedicated Server
  ㄴ 서버를 담당하는 컴퓨터가 별도로 존재

      서버 - 클라이언트 구조

< Dedicated Server의 실행 흐름도 >

더보기

1. PIE를 했거나, Server.exe를 실행하는 식으로 서버 프로세스 실행.

  • 실행할 때 Open {Level이름}?Listen 명령어가 인자로 전달
  • 해당 Level을 열고 Socket이 생성되며 다른 PC가 접속 가능하게끔 설정
  • Listen 명령어가 없다면 싱글플레이

 

 

2. Level의 정보를 토대로 GameMode와 GameState 액터를 생성

  • Level의 WorldSettings 속성이 존재하고 WorldSettings에는 GameMode와 GameState 정보가 할당
  • 이를 통해 GameMode와 GameState 액터를 생성
  • 중요한 것은 GameMode 액터는 전체 컴퓨터에서 딱 한 곳(Server)에만 존재
  • 그래서 GameMode와 Server를 동일시 설정해도 무방

 

 

3. 클라이언트에서 접속 요청

  • 클라이언트는 서버의 IP주소와 포트번호로 접속 시도
  • 서버는 접속 시도하는 클라이언트에게 Level 정보를 전달
  • 클라이언트도 해당 Level을 열고, Level을 여는데 성공했다고 데디 서버에게 알림

 

 

4. 접속 성공 시 게임의 정보를 클라이언트에 복제/대입

  • Level을 여는데 성공한 클라이언트 전용 PlayerState, PlayerController, PlayerCharacter가 서버에 생성
  • PlayerState, PlayerController, PlayerCharacter 등 이 다시 Client1에 복제
  • GameState 도 복제

 

 

5. 또 다른 클라이언트도 접속 ( 3~4 단계 반복 )

  • 마찬가지로 서버는 접속 시도하는 또 다른 클라이언트에게 Level 정보를 전달
  • 클라이언트도 해당 Level을 열고, Level을 여는데 성공했다고 데디 서버에게 알림
  • Client2 전용 PlayerState, PlayerController, PlayerCharacter가 서버에 생성
  • 이것이 다시 Client2에 복제
  • GameState도 복제
  • 이때, 클라이언트 간의 PlayerState와 PlayerCharacter도 복제되면서 서로 존재 확인 가능

 ※ 다른 클라이언트의 PlayerState와 PlayerCharacter의 정보는 저장하지만

     PlayerController는 본인의 클라이언트의 정보만 저장 ※

 

< 이미지 설명 >

                                   ↓

                                   ↓

                                           ↓

                                           ↓

                                                        ↓

< 서버 - 클라이언트 구조의 중요한 특징 >

더보기
  • 클라이언트와 클라이언트 간의 통신이 불가능
  • 오직 서버와 클라이언트 사이의 통신만 가능
  • RPC나 프로퍼티 레플리케이션에도 영향

 

[ 강의 1-4. 실습 환경 설정 ]

더보기
  • 위젯 객체는 Owning Client에서만 생성
  • Owning Client를 판별하기 위해서 IsLocalController() 함수를 호출

< PlayerController 의  BeginPlay 함수 안에 작성 >

// Owning Client 인지 확인
if (IsLocalController() == false)
{
    return;
}

< 데디케이티드 서버 환경 설정 >

더보기

< Allow Late Joining 체크 >

 

Add another cliner 버튼으로 클라이언트 추가 가능

< Always on top > (필수 X)

  • 새 클라이언트 화면이 항상 위에 생성

< Multiplayer Options - Launch Seperate Server >

  • 체크 시 서버 따로 생성
  • 체크 해제 시 에디터 화면이 클라이언트이자 서버

< Multiplayer Options - Run Under One Process >

  • 체크하면 같은 프로세스 내라서 공유하는 에셋이 생성
  • 따라서 확실한 멀티플레이 환경이라고 보기엔 무리
  • 성능상의 이점 : 체크하지 않으면 서버와 각 클라이언트들이 다른 프로세스로 분기
  • 추후에 멀티플레이 디버깅 시 체크하고 로깅으로 확인 가능

< “Net Mode”는 “Play as Client”로 설정 >

  • Net Mode - Play as Client : Dedicated 환경

[ 강의 1-5. NetMode, NetConnection, NetDriver ]

 

<NetMode >

더보기

< 정의 >

  • 해당 게임 프로세스가 네트워크 상에서 어떤 역할을 하고 있는지를 의미
  • 싱글(NM_StandAlone) 서버(NM_Listen, NM_DedicatedServer), 클라이언트(NM_Client)

< NetMode의 필요성 >

  • 로직이 서버에서 돌고 있는지 클라이언트에서 돌고 있는지를 확인 할 때 사용
  • 멀티플레이 개발 시에 같은 코드가 여러 PC에서 동작 -> 서버 PC에서, 내 PC에서, 다른 사람의 PC에서 동작

위 사진처럼 구조가 짜여져 있다는 가정 하에

PlayerCharacter :: BeginPlay 함수에 UE_LOG를 작성했다면

 ㄴ Server ,C1,C2에 해당돼있는 모든 PlayerCharacter의 UE_LOG가 호출된다

빨간 PlayerController :: BeginPlay 함수에 UE_LOG를 작성했다면

 ㄴ Server,C1 의 PlayerController에 UE_LOG가 호출된다

 

 

< NetMode 와 NetRole >

  • NetMode : 월드 단위의 속성값
  • NetRole : 액터 단위의 속성값

< UWorld::InternalGetNetMode() 함수 살펴보기 >

ENetMode UWorld::InternalGetNetMode() const
{
    if ( NetDriver != NULL )                                      // 넷드라이버가 설정되어 있으면
    {
       const bool bIsClientOnly = IsRunningClientOnly();
       return bIsClientOnly ? NM_Client : NetDriver->GetNetMode(); // 클라 아니면 서버 둘 중 하나다.
    }

    ...
}

 

// UNetDriver.cpp

ENetMode UNetDriver::GetNetMode() const
{
#if WITH_EDITOR
    if (World && World->WorldType == EWorldType::PIE && IsServer())
    {
       FWorldContext* WorldContext = GEngine->GetWorldContextFromWorld(World);
       if (WorldContext && WorldContext->RunAsDedicated) // 데디로 실행했다면
       {
          return NM_DedicatedServer;                      // 데디 서버 반환.
       }
    }
#endif

    return (IsServer() ? (GIsClient ? NM_ListenServer : NM_DedicatedServer) : NM_Client);
    // 서버인데, 클라이언트로 참여하고 있다? 리슨서버. 서버인데 참여하고 있지 않다? 데디서버.
}

 

bool UNetDriver::IsServer() const
{
    return ServerConnection == NULL;
    // 클라이언트는 무조건 서버 커넥션을 가지고 있다.
}

< NetConnection >

더보기

< 정의 >

  • 다른 PC와의 연결이 발생하면 그에 대응하는 UNetConnection 객체도 생성
  • 서버에 클라이언트가 접속하면 서버에는 ClientConnection 객체가 추가
  • 반대로 클라이언트에는 ServerConnection 객체가 생성
  • 두 PC는 UNetConnection 객체를 통해 통신
  • UNetDriver는 생성된 UNetConnection 객체를 소유하고 관리
  • 서버 PC에 생성된 UNetDriver는 접속한 클라이언트의 수 만큼 UNetConnection을 관리
  • 클라이언트 PC에 생성된 UNetDriver는 ServerConnection 단 하나만을 관리
    서버-클라이언트 구조의 템플릿
  • NetDriver : 멀티플레이 실행 장치
// UNetDriver.h

...

UCLASS(...)
class UNetDriver : public UObject, public FExec
{
    ...
    
    // 클라이언트 전용 (단일 취급)
    UPROPERTY()
    TObjectPtr<class UNetConnection> ServerConnection;

    // 서버 전용 (복수 취급 (배열))
    UPROPERTY()
    TArray<TObjectPtr<UNetConnection>> ClientConnections;
    
    ...

}

 

UNetConnection* AActor::GetNetConnection() const
{
    return Owner ? Owner->GetNetConnection() : nullptr;
    // 여기서 오너는 액터/폰/컨트롤러 등등이 해당
    // Owner가 지정되어 있지 않으면 통신 불가
}

 

class UNetConnection* APawn::GetNetConnection() const
{
    if ( Controller ) // 컨트롤러가 있다면
    {
       return Controller->GetNetConnection(); // 컨트롤러의 GetNetConnection()을 호출.
    }
    return Super::GetNetConnection(); // 없다면 AActor::GetNetConnection() 함수 호출.
}

 

UNetConnection* APlayerController::GetNetConnection() const
{
    return (Player != NULL) ? NetConnection : NULL;
    // 플레이어가 존재한다면 NetConnection 객체를 반환.
}

< 언리얼에서의 Ownership >

더보기
  • 하나의 ClientConnection은 하나의 PlayerController를 소유 -> PlayerController의 Owning Connection은 ClientConnection 와 동일
  • PlayerController가 빙의하는 폰의 Owner 속성은 해당 PlayerController로 설정
  • 폰에 무기 액터가 생성되고, 무기 액터의 Owner 속성에 해당 폰을 설정 가능
  • 패밀리 : ClientConnection에서부터 무기 액터에 이르는 소유 관계
  • 소유 관계 속에 있는 액터가 본인의 Owning Connection을 얻으려면
    AActor::GetNetConnection() 함수를 호출
  • 이 소유 관계가 후에 배울 RPC와 Property Replication과 관련

 

< NetDriver >

더보기
  • 언리얼 네트워크 통신에서 로우레벨 동작들을 관리하는 클래스
  • 싱글플레이에서는 UNetDriver 객체가 생성 X
  • 멀티플레이에서만 UWorld::Listen() 함수를 통해 UNetDriver 객체가 생성
  • 멀티플레이에 참여하는 각 PC마다 UNetDriver 객체가 생성
  • ex) 컴퓨터 포맷 후 Wifi Driver를 설치하는 것과 유사합니다.

 

// UWorld.cpp

bool UWorld::Listen( FURL& InURL )
{
#if WITH_SERVER_CODE
    ...

    // Create net driver. 넷드라이버 만드는 부분이 있다~ 정도.
    if (GEngine->CreateNamedNetDriver(this, NAME_GameNetDriver, NAME_GameNetDriver))
    {
       NetDriver = GEngine->FindNamedNetDriver(this, NAME_GameNetDriver);

       ...
    }

    ...
}

 

[ NetMode에 대한 추가적인 설명 ]

 


< StandAlone >

더보기
  • 게임이 원격 클라이언트의 연결을 허용하지 않는 서버로 실행
  • 커넥션이 없음. 게임에 참여하는 모든 플레이어는 로컬 플레이어
  • 싱글 플레이 및 로컬 플레이 게임에 사용
  • 로컬 플레이어에 맞게 서버 측 로직과 클라이언트측 로직을 모두 실행

< Client >

더보기
  • 게임이 네트워크 멀티플레이 세션에서 서버에 연결된 클라이언트로 실행
  • 서버 측 로직을 실행하지 않습니다. 서버로부터 복제된 Proxy를 보여주는 역할

< Listen Server >

더보기
  • 게임이 네트워크 멀티플레이어 세션을 호스팅 하는 서버로 실행
  • 원격 클라이언트의 연결을 수락하고 로컬 플레이어를 서버에 직접 배치
    즉, 서버 자기 자신도 게임에 직접 참여
  • 이 모드는 캐주얼 협동 및 경쟁 멀티플레이어에 자주 사용

< Dedicated Server >

더보기
  • 게임이 네트워크 멀티플레이 세션을 호스팅 하는 서버로 실행
  • 원격 클라이언트의 연결을 허용하지만 로컬 플레이어는 존재 X
    따라서 그래픽, 사운드, 입력 및 기타 플레이어 중심 기능이 필요 없으니 삭제
  • 이 모드는 지속적이고 안전한 대규모 멀티플레이어가 필요한 게임에 주로 사용

 

 

[ NetMode에 따른 액터 위치 ]

더보기

서버에만 존재하는 액터

  • GameMode

서버와 모든 클라이언트에 존재하는 액터

  • 배경 액터와 Pawn

서버와 클라이언트에만 존재하는 액터

  • PlayerController , ABP

클라이언트에만 존재하는 언리얼 오브젝트

  • UI

 

 

 

[ 강의 1-6. NetRole ]

 

< Authority와 Proxy >

더보기
  • 서버에 스폰 된 액터가 가진 NetRole 속성 값은 언제나 Authority
     ㄴ 서버에 스폰 된 액터에서 수행될 로직은 “권한을 가지고 있다”를 의미
       ㄴ 게임에 중대한 영향을 끼치는 로직은 NetRole이 Authority일 때만 수행하게 설정
  • NetRole이 Authority인 액터가 클라이언트로 복제되었을 때,
    클라이언트에 복제된 액터의 NetRole 속성 값은 Proxy : “허상”
  • 여기서는 중요한 로직을 수행 X

< 로컬 롤과 리모트 롤 >

더보기

로컬 롤(Local Role) : 현재 동작하는 컴퓨터에서의 롤

리모트 롤(Remote Role) : 커넥션으로 연결된 반대편 컴퓨터에서의 롤

< NetRole의 종류 >

더보기

< None >

  • 레플리케이션 되지 않는 액터
  • 다만, 무조건적이진 않음
  • ex) LocalRole은 Authority이고 RemoteRole이 None이라면
    서버에서 스폰 되고, 클라쪽으로 레플리케이션 되지 않는 액터라고 취급

< Authority >

  • 게임에 중대한 영향을 끼칠 수 있는 권한을 가진 액터
  • 서버에서 스폰된 액터가 LocalRole으로 Authority를 가질 수 있음
  • ex) GameMode

< Autonomous Proxy >

  • Authority 액터의 복제본
    서버로부터 데이터를 수신 받아서 동기화도 되면서, 서버로 송신도 가능
  • ex) PlayerController

< Simulated Proxy >

  • Authority 액터의 복제본
    서버로부터 수신 받아서 동기화만 가능
  • ex) AnotherPlayer 의 PlayerCharacter