언리얼 엔진

Unreal Engine - 다빈치코드 보드게임 개발 - 네트워크 멀티플레이 정리

로안님 2025. 3. 28. 21:57

이 포스트는 언리얼 엔진을 사용한 다빈치코드 보드게임의 네트워크 및 멀티플레이 관련 로직을 정리한 내용입니다. 멀티플레이 게임을 개발하면서 직접 겪은 시행착오를 토대로, 중요한 코드 설계 포인트와 흐름을 공유합니다.


1. 멀티플레이 주요 구성요소

GameMode

  • 서버 전용 로직 처리
  • 플레이어 접속 시 블록 분배
  • 게임 시작 트리거 및 턴 제어

GameState

  • 모든 클라이언트와 동기화되는 게임 정보 보관소
  • 플레이어들의 블록 정보(PlayerHands), 턴 상태 등 저장
  • Replicated 변수 및 Getter 함수 제공

PlayerController

  • 플레이어별 로직 관리
  • 버튼 클릭 처리
  • 서버 함수 호출 (Server RPC)
  • 위젯 표시 (Client RPC)

2. 변수/구조체 설계

FDaVinciBlock

USTRUCT(BlueprintType)
struct FDaVinciBlock {
    GENERATED_BODY();

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    int32 Number;

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    bool bIsBlack; // true면 흑 블록, false면 백 블록
};

FPlayerHand

USTRUCT(BlueprintType)
struct FPlayerHand {
    GENERATED_BODY();

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    TArray<FDaVinciBlock> Hand;
};

GameState에 다음과 같이 선언:

UPROPERTY(Replicated)
TArray<FPlayerHand> PlayerHands;

3. 서버에서 클라이언트로 블록 분배 흐름

  1. 모든 플레이어 접속 감지 (GameMode 또는 Level Blueprint)
  2. 블록 전체 생성 + 셔플 (GenerateShuffledBlocksWithJokers 함수)
  3. 플레이어 수에 따라 블록 분배 (DistributeBlocksToPlayers 함수)
  4. GameState의 PlayerHands에 저장 (Replicated)
  5. 클라이언트는 GameState에서 자신의 블록 정보를 가져와 UI에 표시

4. 조커 배치 (클라이언트 -> 서버)

1) 버튼 클릭 → 조커 배치 요청

UFUNCTION(Server, Reliable)
void ServerRequestPlaceJoker(bool bIsBlack, int32 TargetIndex);
  • PlayerController에서 호출
  • 조커 색상과 넣을 인덱스를 인자로 전달

2) 서버에서 실제 반영

UFUNCTION()
void ServerPlaceJoker(int32 PlayerIndex, bool bIsBlack, int32 TargetIndex);
  • GameState에서 실행
  • PlayerHands[PlayerIndex] 수정

PlaceJokerInHand 함수는 C++ 라이브러리에 별도 분리하여 순수 로직 유지


5. 위젯 표시 로직 (Client RPC)

UFUNCTION(Client, Reliable)
void Client_ShowStartButton();
  • 서버가 게임 준비 완료되었을 때 호출
  • 클라이언트에서 위젯을 띄워 시작 버튼 표시
void ADaVinciPlayerController::Client_ShowStartButton_Implementation() {
    if (IsLocalController() && StartWidgetClass) {
        UUserWidget* StartWidget = CreateWidget<UUserWidget>(this, StartWidgetClass);
        if (StartWidget) {
            StartWidget->AddToViewport();
        }
    }
}

 


이제 이 구조 위에 턴 처리, 승패 판정, UI 반응 등을 확장해 나갈 수 있습니다.