Number Baseball
Unreal Engine 5.5와 C++로 제작한 데디케이티드 서버 기반 멀티플레이 숫자 야구 프로젝트입니다.
서버가 정답, 턴, 제한 시간, 시도 횟수와 라운드 결과를 관리하고 각 클라이언트는 채팅 형식으로 숫자를 제출합니다.
게임 규칙
- 서버는 라운드 시작 시
1~9중 서로 다른 숫자 세 개로 정답을 생성합니다. - 현재 턴인 플레이어가 정확히 세 자리의 유효한 숫자를 입력하면 숫자 야구 입력으로 처리합니다.
- 숫자와 위치가 모두 같으면
Strike, 숫자만 같으면Ball, 일치하는 숫자가 없으면OUT입니다. - 판정 결과는
1S1B,3S0B,OUT형식으로 전체 플레이어에게 전달됩니다. - 각 플레이어에게 한 라운드당 최대 5회의 기회가 주어집니다.
- 먼저
3S를 달성한 플레이어가 승리합니다. - 모든 플레이어가 기회를 소진할 때까지 승자가 없으면 무승부입니다.
채팅과 숫자 입력 구분
다음 조건을 모두 만족하는 입력만 숫자 야구 시도로 인정합니다.
- 길이가 정확히 세 글자여야 합니다.
- 모든 글자가
1~9사이의 숫자여야 합니다. - 같은 숫자가 중복되면 안 됩니다.
조건을 만족하지 않는 입력은 오류로 거부하지 않고 일반 채팅으로 전체 플레이어에게 전송합니다. 따라서 12, 1234, 12A, 102, 113과 같은 입력은 기회를 소진하지 않습니다.
주요 기능
- 서버 권한 기반 정답 생성 및 판정
- 플레이어별 시도 횟수 복제
- 플레이어 접속 순서 기반 순차 턴
- 서버에서 관리하고
GameState로 복제하는 턴 타이머 - 제한 시간 내 미입력 시 기회 1회 소진
- 현재 턴이 아니거나 제한 시간이 끝난 숫자 입력 차단
- Enhanced Input을 사용한
Enter채팅 활성화 - 플레이어별 채팅 색상
- 승리·무승부 공지와 5초 재시작 카운트다운
- 라운드 종료 중 채팅 및 숫자 입력 차단
- 새 라운드 시작 시 채팅, 알림, 시도 횟수 및 턴 상태 초기화
- WBP에서 자유롭게 디자인할 수 있는 중앙 HUD 구조
네트워크 구조
이 프로젝트는 데디케이티드 서버를 기준으로 서버 권한을 분리합니다.
| 클래스 | 역할 |
|---|---|
ANBGameModeBase |
서버 전용 게임 규칙, 정답, 숫자 판정, 채팅 분기, 턴, 타이머, 승패와 라운드 리셋 관리 |
ANBGameStateBase |
현재 플레이어, 남은 시간과 입력 허용 상태를 모든 클라이언트에 복제 |
ANBPlayerState |
플레이어 이름, 채팅 색상, 현재/최대 시도 횟수와 턴 입력 여부를 복제 |
ANBPlayerController |
로컬 입력과 HUD 생성, 서버 RPC 요청, 클라이언트 RPC 수신 |
UNBGameHUDWidget |
채팅 입력, 채팅 로그, 공지와 타이머 WBP를 한 화면에 모으는 중앙 HUD |
숫자 입력은 다음 순서로 처리됩니다.
클라이언트 Enter 입력
-> 채팅 입력창 활성화
-> 원본 문자열을 Server RPC로 전송
-> GameMode가 숫자 야구 입력인지 검사
-> 유효하지 않음: 일반 채팅으로 전송, 기회 유지
-> 유효함: 턴/시간/남은 기회 검사
-> 정답 판정 및 기회 소진
-> 승리/무승부 검사
-> 게임이 계속되면 다음 플레이어 턴 시작
클라이언트가 보내는 플레이어 이름이나 판정 결과를 신뢰하지 않습니다. 서버가 소유한 PlayerState와 정답을 사용해 표시 문자열과 결과를 생성합니다.
구현 과정
1. 정답 생성과 입력 판별
ANBGameModeBase::GenerateSecretNumber에서 1~9를 TArray<int32>에 저장하고, 선택한 숫자를 배열에서 제거하면서 세 자리를 생성합니다. 같은 숫자를 다시 선택할 수 없으므로 항상 중복 없는 정답이 만들어집니다.
ANBGameModeBase::IsGuessNumberString은 문자열 길이, 문자 범위와 TSet<TCHAR>의 원소 수를 검사합니다. 검증을 통과하지 못한 문자열은 일반 채팅으로 분기합니다.
2. Strike/Ball/OUT 판정
ANBGameModeBase::JudgeResult는 정답과 입력을 자리별로 비교합니다.
- 같은 위치의 같은 숫자: Strike 증가
- 정답에 존재하지만 위치가 다른 숫자: Ball 증가
- Strike와 Ball이 모두 0:
OUT
3. 시도 횟수와 플레이어 상태
ANBPlayerState가 CurrentGuessCount와 MaxGuessCount를 관리합니다. 기본 최대 횟수는 5회이며 두 값은 복제되어 클라이언트에서도 [현재 / 최대] 형식으로 표시할 수 있습니다.
일반 채팅은 시도 횟수에 영향을 주지 않습니다. 유효한 세 자리 숫자를 정상적으로 제출하거나 자신의 턴 제한 시간이 끝났을 때만 횟수가 증가합니다.
4. 턴과 시간 동기화
ANBGameModeBase의 서버 타이머가 매초 남은 시간을 감소시킵니다. 현재 턴 플레이어와 남은 시간은 FNBTurnState에 저장되어 ANBGameStateBase를 통해 복제됩니다.
클라이언트 타이머 위젯은 복제 알림 델리게이트를 구독해 같은 서버 값을 표시합니다. 남은 시간이 0이면 서버의 CanAcceptGuess 검사도 실패하므로 UI 상태와 관계없이 숫자 입력이 처리되지 않습니다.
5. 승리, 무승부와 라운드 리셋
3S가 나오면 즉시 승자를 확정합니다. 승자가 없고 모든 플레이어가 최대 시도 횟수에 도달하면 무승부를 확정합니다.
라운드가 끝나면 다음 과정이 실행됩니다.
- 턴 타이머를 중지하고 숫자 및 채팅 입력을 차단합니다.
- 승자 또는 무승부 메시지와 5초 카운트다운을 표시합니다.
- 카운트다운이 끝나면 새 정답을 생성합니다.
- 모든 플레이어의 시도 횟수와 턴 입력 상태를 초기화합니다.
- 채팅 로그, 입력창과 공지 UI를 초기화합니다.
- 첫 번째로 플레이 가능한 플레이어부터 새 라운드를 시작합니다.
6. C++ 공용 라이브러리
NumberBaseballFunctionLibrary는 화면/로그 출력과 NetMode 문자열 변환처럼 객체 상태가 필요 없는 공용 C++ 기능을 정적 함수로 제공합니다. 이 클래스는 C++ 코드 재사용을 위한 일반 헬퍼이며, 블루프린트 노드 노출이 목적이 아니므로 UBlueprintFunctionLibrary를 상속하지 않습니다.
UI 구성
모든 UI는 WBP_GameHUD에서 한 번에 배치합니다. C++ 부모 클래스는 동작과 데이터 연결을 담당하고 색상, 폰트, 크기와 위치는 WBP Designer에서 수정합니다.
중앙 HUD 필수 자식 이름
WBP_GameHUD는 UNBGameHUDWidget을 부모로 사용하며 다음 자식 WBP를 정확한 이름으로 배치합니다.
| 자식 WBP | WBP_GameHUD 내부 인스턴스 이름 |
|---|---|
WBP_ChatInput |
Widget_ChatInput |
WBP_ChatLog |
Widget_ChatLog |
WBP_NotificationText |
Widget_Notification |
WBP_TurnTimer |
Widget_TurnTimer |
각 자식 WBP의 C++ 연결용 위젯 이름은 다음과 같습니다.
| WBP | 부모 클래스 | 연결 위젯 이름 |
|---|---|---|
WBP_ChatInput |
UNBChatInput |
EditableTextBox_ChatInput |
WBP_ChatLog |
UNBChatLogWidget |
ScrollBox_ChatLog, VerticalBox_ChatLog |
WBP_TurnTimer |
UNBTurnTimerWidget |
TextBlock_TurnTimer |
사용한 언리얼 자료구조와 기능
TArray: 접속 플레이어, 숫자 후보, 채팅 메시지와 색상 목록TSet: 입력 숫자 중복 검사TObjectPtr: UObject 및 Actor 참조TWeakObjectPtr: 위젯에서 GameState를 안전하게 참조TSubclassOf: WBP 클래스 지정FTimerHandle: 턴 타이머와 라운드 리셋 카운트다운- Property Replication과
ReplicatedUsing - Server, Client, NetMulticast RPC
- Enhanced Input
- UMG
BindWidget
프로젝트 실행
- Unreal Engine 5.5에서
NumberBaseball.uproject를 엽니다. - 에디터 시작 및 기본 맵은
/Game/NumberBaseball/Maps/L_Chatting입니다. - 멀티플레이 테스트 시 플레이어 수를 2명 이상으로 설정합니다.
- 데디케이티드 서버 기준 테스트는 Play 설정에서 별도 서버 실행을 활성화하고 클라이언트 모드로 실행합니다.
- 게임 화면에서
Enter를 눌러 채팅창을 활성화하고 메시지 또는 세 자리 숫자를 입력합니다.
테스트
NumberBaseball.GameMode.Validation 자동화 테스트가 다음 항목을 확인합니다.
- 반복 생성한 정답이 항상 1~9의 중복 없는 세 자리인지 확인
- 유효/무효 숫자 입력 구분
- Strike/Ball/OUT 결과
- 라운드 종료 카운트다운 문자열
- 기본 시도 횟수 5회와 표시 형식
- 초기 턴 입력 차단 상태
- 타이머 및 현재 턴 표시 형식
Unreal Editor의 Tools > Test Automation에서 NumberBaseball.GameMode.Validation을 검색해 실행할 수 있습니다.
주요 소스 위치
Source/NumberBaseball/
├─ Game/
│ ├─ NBGameModeBase.* # 서버 게임 규칙과 라운드 진행
│ └─ NBGameStateBase.* # 복제되는 턴 상태
├─ Player/
│ ├─ NBPlayerController.* # 입력, RPC와 HUD 생성
│ └─ NBPlayerState.* # 플레이어별 복제 상태
├─ UI/
│ ├─ NBGameHUDWidget.* # 중앙 HUD 컨테이너
│ ├─ NBChatInput.* # 채팅 입력 동작
│ ├─ NBChatLogWidget.* # 채팅 기록과 색상
│ └─ NBTurnTimerWidget.* # 복제 타이머 표시
└─ Tests/
└─ NBGameModeBaseTest.cpp # 자동화 테스트'UE > 멀티플레이' 카테고리의 다른 글
| UE C++ 언리얼 패키징 관련 (+AWS) (1) | 2026.08.12 |
|---|---|
| UE C++ 언리얼 멀티플레이 서버 및 생명주기 (0) | 2026.08.11 |
| UE C++ 멀티플레이 동기화 (0) | 2026.08.07 |
| UE C++ RPC오너십, 프로퍼티 리플리케이션 (1) | 2026.08.06 |
| UE C++ 액터 리플리케이션 (0) | 2026.08.05 |