본문 바로가기
언리얼_엔진_게임개발_공부/언리얼 네트워크

C++ 로 채팅 예제 구현 - ChatWidget ~ GameMode & 유저네임 중복체크 / 색상 랜덤 부여

by jaboy 2025. 3. 20.

C++ 로 채팅 예제를 구현해보며 아래와 같은 기능을 만들어 보았다.

1. 채팅 기능

2. 서버에 유저네임 저장하여 중복 체크 & 메세지 출력 색상 지정

 

아래와 같은 요소들로 구현하였다.

1. 채팅 기능

- UEditableText 로 메세지 작성 & 커밋

- 텍스트 커밋 시 플레이어 컨트롤러에서 서버 (게임모드) 로 전송

- 게임모드에서 메세지 각 클라이언트 컨트롤러에 프린트하는 함수 호출

2. 유저네임 저장 / 중복 체크 / 색상 지정

- 게임모드에 TMap 으로 유저네임-색상 관리

- 로그인 위젯에서 유저네임 설정 시 TMap 에 이미 등록되어 있는지 체크 후 유저네임-랜덤색상 페어 저장

- 중복 시 안내 메세지 표시

- 클라이언트에 메세지 출력 요청 시 해당 색상 전달하여 지정된 색상으로 출력

 

1. 채팅 기능

1) 위젯 생성 & 함수 바인드

void AChatController::CreateChatWidget()
{
	if (ChatWidgetClass && ChatWidget == nullptr && IsLocalController())
	{
		ChatWidget = CreateWidget<UChatWidget>(this, ChatWidgetClass, TEXT("ChatWidget"));
		if (ChatWidget)
		{
			ChatWidget->AddToViewport();

			// Bind ServerSendMessage to OnTextCommitted delegate of TextBox
			ChatWidget->MessageTextBox->OnTextCommitted.AddDynamic(this, &AChatController::ServerSendMessage);
		}
	}
}

 

2) 텍스트 커밋 시 게임모드의 서버 RPC 를 호출하는 아래 함수를 바인드한다.

void AChatController::ServerSendMessage_Implementation(const FText& Text, ETextCommit::Type CommitType)
{
	if (CommitType == ETextCommit::OnEnter)
	{
		if (AChatModeBase* ChatModeBase = Cast<AChatModeBase>(UGameplayStatics::GetGameMode(this)))
		{
			ChatModeBase->ServerRelayMessage(Username, Text);
		}
	}
}

 

3) 게임모드의 서버 RPC 는 서버 내 플레이어 컨트롤러에 클라이언트 RPC로 출력을 요청한다.

후술할 내용이지만, 유저네임-컬러 맵에 저장된 컬러를 클라이언트 RPC에 전달해 해당 색상으로 출력하도록 한다.

void AChatModeBase::ServerRelayMessage_Implementation(const FString& Username, const FText& Message)
{
	for (FConstPlayerControllerIterator Iterator = GetWorld()->GetPlayerControllerIterator(); Iterator; Iterator++)
	{
		if (AChatController* ChatController = Cast<AChatController>(*Iterator))
		{
			FColor Color = FColor::White;
			if (FColor* ColorPtr = UserColorMap.Find(Username))
			{
				Color = *ColorPtr;
			}
			ChatController->ClientPrintMessage(FText::FromString(Username + TEXT(": ") + Message.ToString()), Color);
		}
	}
}

 

4) 컨트롤러의 클라이언트 RPC 에서 전달받은 색상으로 텍스트 출력

// Client RPC that prints the message relayed from ChatModeBase (server)
void AChatController::ClientPrintMessage_Implementation(const FText& Text, const FColor& Color)
{
	if (HasAuthority())
	{
		GEngine->AddOnScreenDebugMessage(-1, 3.f, Color, Text.ToString());
	}
}

* HasAuthority() 체크로 중복 출력 방지

 

5) ChatWidget 에는 텍스트 커밋 시 텍스트를 지우도록 간단히 구현

void UChatWidget::NativeConstruct()
{
	MessageTextBox->OnTextCommitted.AddDynamic(this, &UChatWidget::EmptyTextBox);
}

void UChatWidget::EmptyTextBox(const FText& Text, ETextCommit::Type CommitType)
{
	MessageTextBox->SetText(FText::GetEmpty());
}

 

2. 유저네임 서버 관리

1) 게임모드에서 TMap<FString, FColor> 로 유저네임-지정색상 관리

UPROPERTY()
TMap<FString, FColor> UserColorMap;

 

2) 로그인 위젯에서 유저네임 입력 후 Confirm 버튼 클릭 시 중복 아닌지 체크

// Call Server RPC to check the username when 'Confirm' button is clicked
void AChatController::UsernameConfirmed()
{
	if (LoginWidget)
	{
		ServerCheckUsername(LoginWidget->Username);
	}
}

 

// Check if the username already exists (in GameMode)
void AChatController::ServerCheckUsername_Implementation(const FString& InUsername)
{
	if (AChatModeBase* ChatModeBase = Cast<AChatModeBase>(UGameplayStatics::GetGameMode(this)))
	{
		// If the username already exists, call Client RPC to display a "fail" message on the widget
		if (ChatModeBase->UserColorMap.Find(InUsername) != nullptr)
		{
			ClientNotifyUsernameFailed();
			return;
		}
	}
	// Set server-side username
	ServerSetUsername(InUsername);
}

- 게임모드의 UserColorMap 에 Username 이 이미 존재하는지 체크

 

3) 이미 있으면 클라이언트에 "중복" 메세지 출력

// Display "Username already taken" message on the widget
void AChatController::ClientNotifyUsernameFailed_Implementation()
{
	if (LoginWidget)
	{
		LoginWidget->DisplayUsernameTaken();
	}
}

 

4) 중복이 아니면

- 게임모드 TMap 에 추가

- Username 변수 업데이트

void AChatController::ServerSetUsername_Implementation(const FString& InUsername)
{
	// Add username to TMap in GameMode
	if (AChatModeBase* ChatModeBase = Cast<AChatModeBase>(UGameplayStatics::GetGameMode(this)))
	{
		ChatModeBase->ServerAddUser(InUsername);
	}

	// Update Username variable (replicated)
	Username = InUsername;

	// For the Listen Server Host Client (whose replication doesn't automatically incur OnRep function)
	if (IsLocalController())
	{
		OnRep_UsernameUpdated();
	}
}
void AChatModeBase::ServerAddUser_Implementation(const FString& Username)
{
	UserColorMap.Add(Username, FColor::MakeRandomColor());
}

 

꿀팁. FColor::MakeRandomColor() 라는 편리한 함수가 있다!

 

계획 없이 진행에서 시행착오가 많았지만... 이렇게 보니 간단한 구현이었네... ㅎㅎ