게임 개발 공부/네트워크
Broadcast
Vetenir
2025. 3. 26. 22:07
개요
언리얼 리슨서버로 야구게임을 구현을 위해
함수가 호출될 때 GEngine->AddOnScreenDebugMessage() 바로 호출하는 방식으로 구현을 했고
작동도 잘 되는 것을 확인했으나 해당 방식이 실제 멀티플레이에는
올바른 방식이 아님을 알고 수정한 결과를 담고 있습니다.
해당 글에서 말하는 브로드캐스트는 Multicast Delegate의 Broadcast()가 아님.
BaseballGameMode.cpp
void ABaseballGameMode::HandleOutCondition(APlayerController* OutPlayer)
{
if (IsHostPlayer(OutPlayer))
{
//FString DebugMessage = TEXT("Guest Won!! 다시 게임이 시작됐다.");
//GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, DebugMessage);
BroadcastMessage(TEXT("Guest Won!! 다시 게임이 시작됐다."));
}
else
{
//FString DebugMessage = TEXT("Host Won!! 다시 게임이 시작됐다.");
//GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, DebugMessage);
BroadcastMessage(TEXT("Host Won!! 다시 게임이 시작됐다."));
}
InitializeGame();
}
- 주석 처리된 부분이 기존의 구현하던 방식이고 아래의 BroadcastMessage()로 변경
void ABaseballGameMode::BroadcastMessage(const FString& Message)
{
const ENetMode NetMode = GetWorld()->GetNetMode();
// 모든 플레이어 컨트롤러 처리
for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
{
ABaseballPlayerController* PC = Cast<ABaseballPlayerController>(It->Get());
if (!PC) continue;
// 데디케이트 서버: 모든 클라이언트에 RPC 전송
if (NetMode == NM_DedicatedServer)
{
PC->ClientReceiveMessage(Message);
}
// 리스닝 서버: 로컬 플레이어 제외하고 RPC 전송
else if (NetMode == NM_ListenServer)
{
if (!PC->IsLocalController())
{
PC->ClientReceiveMessage(Message);
}
}
}
}
🔥 1. "직접 출력" vs "브로드캐스트" 방식 비교
특징 | 직접 출력 방식 | 브로드캐스트 방식 |
실행 주체 | 함수 호출 즉시 로컬에서 출력 | 중앙 집중식 관리 (서버 → 클라이언트 전파) |
네트워크 영향 | ❌ 단일 플레이어/로컬 전용 | ⭕ 멀티플레이어 지원 |
코드 분리 | 로직과 출력이 결합 (간단하지만 유지보수 어려움) | 출력 로직이 분리 (유지보수 용이) |
예시 | GEngine->AddOnScreenDebugMessage() 바로 호출 | BroadcastMessage()로 래핑 후 전송 |
🎯 2. 각 방식이 "맞는" 경우
(1) 직접 출력 방식이 좋은 경우
- 단일 플레이어 게임
- 네트워크 복제가 필요 없을 때 (예: 디버그 메시지).
- 간단한 프로토타입
- 빠르게 구현할 때 유용.
- 예시 코드:
void AMyActor::ShowDamage(float Damage) { // 즉시 출력 (네트워크 불필요) GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Red, FString::Printf(TEXT("Damage: %f"), Damage)); }
(2) 브로드캐스트 방식이 필수인 경우
- 멀티플레이어 게임
- 서버가 모든 클라이언트에 동기화해야 할 때 (예: 채팅, 게임 이벤트).
- UI 일관성 요구
- 모든 플레이어가 같은 메시지를 동시에 받아야 할 때.
- 예시 코드:
void ABaseballGameMode::BroadcastMessage(const FString& Message) { // 서버 → 모든 클라이언트 전송 for (auto& PC : GetAllPlayers()) { PC->ClientReceiveMessage(Message); } }
🌟 3. 두 방식의 주요 차이점
(1) 네트워크 복제 유무
- 직접 출력: 로컬에서만 즉시 실행 (클라이언트 간 불일치 가능성 있음).
- 브로드캐스트: 서버가 신뢰할 수 있는 단일 소스로 관리 (동기화 보장).
(2) 유지보수성
- 직접 출력: 출력 로직이 코드 전체에 분산되어 수정이 어려움.
- 브로드캐스트: 출력 로직을 한 곳에서 관리해 일관성 유지.
(3) 확장성
- 직접 출력: 새로운 기능 추가 시 모든 호출부를 수정해야 함.
- 브로드캐스트: BroadcastMessage()만 수정하면 전체 시스템에 적용.
🛠 4. 실제 적용 예시
직접 출력 (단순한 경우)
void APlayerCharacter::TakeDamage(float Damage)
{
Health -= Damage;
// 즉시 출력 (로컬만 확인 가능)
GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Red, FString::Printf(TEXT("체력: %f"), Health));
}
브로드캐스트 (멀티플레이어 필수)
void AGameMode::PlayerWin(APlayerController* Winner)
{
FString Message = FString::Printf(TEXT("%s 승리!"), *Winner->GetName());
BroadcastMessage(Message); // 모든 플레이어에게 전송
}
// 서버 → 클라이언트 RPC
void APlayerController::ClientReceiveMessage_Implementation(const FString& Message)
{
DisplayOnUI(Message); // UI 업데이트
}
✅ 5. 결론: 어떤 방식을 선택해야 할까?
- 멀티플레이어 + 동기화 필요 → 브로드캐스트 필수.
- 단일 플레이어/디버그 → 직접 출력으로 간단히 처리.
- 유지보수성 중요 → 중앙 집중식 브로드캐스트를 권장.