4. 원격으로 호출하기
언리얼 엔진의 공식 문서에 따르면, RPC(Remote Procedure Call)는 호출하는 곳과 다른 곳에서 원격 실행되는 함수를 말한다. 온라인 채팅은 이 RPC를 이용하여 구현한다.
RPC 함수를 만드는 것은 간단하다. 원격으로 실행될 함수에 UFUNCTION을 붙이고 RPC관련 키워드를 삽입하면 된다.
//BaseballPlayerController.h
private:
//클라이언트에서 실행하는 채팅 출력 함수
UFUNCTION(Client, Reliable)
void ClientRPCPrintChatMessageString(const FString& ChatMessage);
//서버에서 실행하는 채팅 출력 함수
UFUNCTION(Server, Reliable)
void ServerRPCPrintChatMessageString(const FString& ChatMessage);
//BaseballPlayerController.cpp
void ABaseballPlayerController::ClientRPCPrintChatMessageString_Implementation(const FString& ChatMessage)
{
//클라이언트는 그저 자기 자신의 채팅 출력을 실행할 뿐
PrintChatMessageString(ChatMessage);
}
void ABaseballPlayerController::ServerRPCPrintChatMessageString_Implementation(const FString& ChatMessage)
{
//서버는 접속한 모든 플레이어 컨트롤러의 채팅 출력 함수를 실행한다.
for (TActorIterator<ABaseballPlayerController> Iter(GetWorld()); Iter; ++Iter)
{
if (ABaseballPlayerController* PlayerController = *Iter; IsValid(PlayerController))
{
PlayerController->ClientRPCPrintChatMessageString(ChatMessage);
}
}
}
플레이어 컨트롤러에 2개의 함수를 만들었는데 이 두 함수는 UFUNCTION이며 각각 실행 위치와 실행 보장에 대한 키워드가 있다.
실행 위치에 대한 키워드는 3개가 있다. 이 키워드가 있으면 RPC함수가 되며 구현되는 함수의 이름은 선언시의 이름 뒤에 _Implementation라는 것이 붙는다.
- Client : 클라이언트에서 호출하면 그 클라이언트에서 실행하며 서버에서 실행하면 주인인 클라이언트에서 실행한다.
- Server : 서버 전용. 서버나 주인인 클라이언트에서 호출시 서버에서 실행하며 주인이 아닌 클라이언트의 호출은 무시된다.
- NetMulticast : 클라이언트에서 호출하면 그 클라이언트에서 실행하며 서버에서 실행하면 서버 포함 모든 곳에서 실행한다.
실행 보장에 대한 키워드는 2개가 있다. 반드시 위의 실행 위치 관련 키워드가 있어야 한다.
- Reliable : 원격 호출시 반드시 실행함을 보장한다. 이 키워드가 없으면 Unreliable이 있는 것으로 간주한다.
- Unreliable : 원격 호출이 있어도 실행하지 않을 수도 있다.
즉, 위에 적은 코드 중 위쪽에 있는 함수는 클라이언트용 RPC함수이며 자신의 화면에 채팅을 출력하는 함수를 실행한다. 아래쪽에 있는 함수는 서버용 RPC함수이며 서버에 있는 모든 플레이어 컨트롤러가 가진 클라이언트용 RPC함수를 호출한다.
여기서 문뜩 떠올릴 수 있는게, 그냥 NetMulticast로 선언하면 굳이 서버용, 클라이언트용 함수로 나누고 서버용 함수에서 for문을 써가면서 각 플레이어 컨트롤러마다 일일이 호출할 필요가 없을 것이라고 생각할 수도 있다. 그러나 어느 한 플레이어의 컨트롤러는 주인인 클라이언트와 서버에만 존재하며 주인이 아닌 클라이언트에는 존재할 수가 없다. 위 코드는 플레이어 컨트롤러에 있는 함수라서 NetMulticast 키워드를 사용하면 안된다.
5. 플레이 도중 새로운 플레이어의 접속
A. 에디터 설정

에디터 개인설정 화면에서 실험단계 기능을 눌러서 PIE - 늦은 조인 허용(Allow Late Joining)을 체크한다.

그 다음에 실행해보면 버튼이 1개 더 생긴다. 이 버튼은 실행한 후에 다른 클라이언트를 추가하는 버튼이 생긴다.
B. 다른 플레이어의 접속 알림
//BaseballGameStateBase.h
UCLASS()
class BASEBALLGAME_API ABaseballGameStateBase : public AGameStateBase
{
GENERATED_BODY()
public:
UFUNCTION(NetMulticast, Reliable)
void MulticastRPCBroadcastLoginMessage(const FString& NameString);
};
//BaseballGameStateBase.cpp
void ABaseballGameStateBase::MulticastRPCBroadcastLoginMessage_Implementation(const FString& NameString)
{
if (!HasAuthority())//서버가 아닐때만 동작
{
//새 플레이어 접속 알림
if (APlayerController* PlayerController = UGameplayStatics::GetPlayerController(GetWorld(), 0); IsValid(PlayerController))
{
if (ABaseballPlayerController* BaseballPlayerController = Cast<ABaseballPlayerController>(PlayerController); IsValid(BaseballPlayerController))
{
const FString NotificationString = NameString + TEXT("님이 입장하셨습니다.");
BaseballPlayerController->PrintChatMessageString(NotificationString);
}
}
}
}
게임 스테이트를 만들고 RPC함수를 만든다. 이 함수는 누군가가 접속하면 모두에게 알리는 기능을 수행한다.
이 함수에서 사용한 HasAuthority함수는 서버에서 호출시 true를, 아니면 false를 반환하는 함수라서 이를 이용하여 서버를 제외한 모든 클라이언트에서의 실행을 구현할 수 있다.
//BaseballGameMode.h
UCLASS()
class BASEBALLGAME_API ABaseballGameMode : public AGameModeBase
{
GENERATED_BODY()
public:
virtual void BeginPlay() override;
virtual void OnPostLogin(AController* NewPlayer) override;
private:
int32 PlayerCount = 1;
};
//BaseballGameMode.h
void ABaseballGameMode::BeginPlay()
{
Super::BeginPlay();
PlayerCount = 1;
}
void ABaseballGameMode::OnPostLogin(AController* NewPlayer)
{
Super::OnPostLogin(NewPlayer);
if (ABaseballGameStateBase* BaseballGameState = GetGameState<ABaseballGameStateBase>(); IsValid(BaseballGameState))
{
//접속한 플레이이어의 이름을 "PlayerN"형식으로 정한다.
const FString NewName = TEXT("Player") + FString::FromInt(PlayerCount++);
BaseballGameState->MulticastRPCBroadcastLoginMessage(NewName);
}
}
계속해서, 지금까지 빈 껍데기였던 게임 모드에 함수 2개를 만든다. 여기서 중요한 것은 OnPostLogin함수다. 네트워크 환경에서는 게임모드가 클리이언트에 존재하지 않고 서버에 존재한다. 그래서 존재하는 함수인데 클라이언트가 접속하면 그 클라이언트의 컨트롤러를 매개변수로 하여 서버에서 실행하는 함수다.
여기서 이 함수는 접속한 순서대로 클라이언트에 "PlayerN"형식의 이름을 부여하며 플레이어의 접속을 알리는 함수를 실행한다. (지금은 접속 외에 이름을 쓰는 일이 없어서 이름 부여 기능은 없다.)

여기까지 만든 것을 실행해보니 잘 작동한다.
'언리얼엔진 학습 및 과제' 카테고리의 다른 글
| 네트워크 채팅을 이용한 숫자야구 게임 (1) (0) | 2025.11.27 |
|---|---|
| 언리얼 네트워크 관련 사전 정리 (0) | 2025.11.26 |
| 온라인 채팅 구현하기 (3) (0) | 2025.11.21 |
| 온라인 채팅 구현하기 (2) (0) | 2025.11.20 |
| 온라인 채팅 구현하기 (1) (0) | 2025.11.19 |