싱글플레이어 슈팅 게임을 완성하고, 드디어 멀티플레이어의 세계로 첫발을 내디뎠다. 이번 포스트는 멀티플레이 학습의 시작으로, 가장 기본이 되는 채팅 프로젝트의 구조를 잡는 과정을 다룬다. 기존에 알고 있던 게임 프레임워크(게임모드, 플레이어 컨트롤러)가 서버와 클라이언트 환경에서 어떻게 명확히 분리되고 각자의 역할을 수행하는지 다시 한번 개념을 정립했다. 또한, 앞으로의 개발 효율을 위해 C++로 핵심 구조를 설계하고 블루프린트로 확장하는 워크플로우를 체계적으로 적용하는 연습을 했다.
슈팅 게임을 만들 때는 모든 로직이 내 컴퓨터(하나의 인스턴스)에서 돌아갔기에 서버와 클라이언트 개념이 모호했다. 하지만 멀티플레이는 시작부터 다르다. '누가 서버(Server)이고 누가 클라이언트(Client)인가?', '어떤 코드가 서버에서 돌고, 어떤 코드가 클라이언트에서 도는가?'를 명확히 해야 한다. 오늘 만든 채팅 프로젝트는 바로 이 개념을 잡기 위한 완벽한 첫걸음이었다.
싱글플레이어 프로젝트에서 게임모드는 그저 게임의 규칙을 담는 편리한 공간 정도였다. 하지만 멀티플레이에서는 그 위상이 완전히 달라진다. 게임모드는 오직 서버에만 존재하며, 게임의 규칙과 상태를 총괄하는 '권위 있는(Authoritative)' 존재가 된다. 어떤 플레이어가 입장하고 퇴장하는지 관리하고, 게임의 승리 조건을 판정하는 등 모든 중요한 판단은 서버의 게임모드가 독점한다.
반면, 플레이어 컨트롤러는 각 클라이언트에 접속한 플레이어의 분신이다. 키보드 입력, 마우스 움직임 등 플레이어의 입력을 직접 받는 창구 역할을 한다. 이 입력을 서버로 전달하면, 서버는 그에 맞춰 게임 월드에 변경을 가하고, 다시 그 결과를 모든 클라이언트에게 전파(Replicate)한다. 오늘은 이 플레이어 컨트롤러에 UI 위젯을 생성하고 화면에 띄움으로써, "UI는 각 플레이어의 화면에 개별적으로 표시되어야 한다"는 기본 원리를 실습했다.
프로젝트 규모가 커질수록, 특히 멀티플레이처럼 로직이 복잡해질수록 C++과 블루프린트의 역할을 명확히 나누는 것이 중요해진다. 네트워크 데이터 동기화(Replication)와 같이 성능에 민감하고 핵심적인 로직은 C++에, 눈에 보이는 비주얼 요소나 간단한 데이터 할당은 블루프린트에 위임하는 것이 정석이다.
오늘 실습이 바로 그 좋은 예시다. PlayerController C++ 클래스에 채팅 UI 위젯을 담을 변수를 '틀'로서 선언만 해두고, 실제 어떤 UI 위젯을 사용할지는 C++을 상속받은 블루프린트 에디터에서 지정해주었다.
#pragma once
// ... (includes)
class UCXChatInput; // 전방 선언
UCLASS()
class CHATX_API ACXPlayerController : public APlayerController
{
GENERATED_BODY()
protected:
// 게임이 시작될 때 호출
virtual void BeginPlay() override;
// 'EditDefaultsOnly' 키워드를 통해 블루프린트 기본값 설정에서만 이 프로퍼티를 수정할 수 있게 함
// 실제 어떤 위젯 블루프린트를 쓸지는 에디터에서 결정
UPROPERTY(EditDefaultsOnly, Category = "UI")
TSubclassOf<UCXChatInput> ChatInputWidgetClass;
// 생성된 위젯의 인스턴스를 저장할 포인터
UPROPERTY()
TObjectPtr<UCXChatInput> ChatInputWidgetInstance;
};
이렇게 하면, 나중에 UI 디자인을 A에서 B로 바꾸고 싶을 때 C++ 코드를 다시 컴파일할 필요 없이, 블루프린트에서 할당된 에셋만 교체하면 된다. 이런 설계는 유지보수성과 확장성 측면에서 큰 이점을 가져다준다. 💪
| 개념 | 설명 | 비고 |
|---|---|---|
게임모드 (GameMode) | 게임의 규칙과 흐름을 관리하는 서버-전용(Server-Only) 객체. | 멀티플레이 환경에서 게임의 '심판' 역할을 한다. |
플레이어 컨트롤러 (PlayerController) | 각 플레이어의 입력을 처리하고 게임 세계와 상호작용하는 대리인. | 서버와 해당 클라이언트에 각각 존재하며, UI 관리의 좋은 시작점. |
| C++/블루프린트 워크플로우 | 핵심 로직과 데이터 구조는 C++로, 시각적 요소와 데이터 할당은 블루프린트로 분리하는 설계 방식. | 프로젝트의 안정성과 유연성을 동시에 확보하는 전략. |