Acero의 UI시스템은 모두 MVC(Model View Controller) 패턴을 따른다.

User는 Controller와 연결되어있지만, Controller안에서는 그어떠한 연산도 하지 않고, HUD를 통해 소통만 하는 구조이다.
팀 정보, 특정 ID나 색상같은게 바뀌더라도, Controller에서 수정하지 않고 데이터 구조(Model)만 수정한다. 이를 통해 디커플링을 극대화할 수 있다.
유저가 눈으로 보고 상호작용하는 인터페이스다.
뷰는 로직을 절대 직접처리하지 않는다.
3가지를 생성하되, 컨트롤러에 알릴 뿐이다. 컨트롤러가 명령을 내려야 직접 갱신한다.
컨트롤러는 Model-View를 연결하며, 접근이 필요한 모든 로직을 담당한다.
ACDPlayerController이 RPC를 전담하며, 유저의 입력을 받아 서버전송(ServerSendMessage)하고 배포한다.ACDHUD이 위젯들을 생성하며 어떤 위젯에 전달할지(AddToViewport, Remove)결정한다.Chat과 같은 메시지를 전달하는데 서버의 검증이 필요한것은 당연하다.
다른 Action과 마찬가지로, 누군가가 악의적인 의도로 도배나 욕설 혹은 피싱메시지를 전달할 수 있기에, 서버의 검수조사 후에 Client로 전달해야 한다.
Chat Message는 다음과 같은 구성으로 이루어져 있다.
USTRUCT(BlueprintType)
struct FChatMessage
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString SenderName;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString MessageContent;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FDateTime TimeStamp;
};
조작을 막기 위해 서버측에서 PS에 접근해 직접 이름을 가져와 패키징한다.
즉 서버가 데이터를 생산하고, 각 클라이언트들에게 순회를 통해 브로드캐스트한다.
void ACDPlayerController::ServerSendMessage_Implementation(const FString& Message)
{
if (Message.IsEmpty()) return;
FChatMessage ChatData;
ChatData.SenderName = GetPlayerState<ACDPlayerState>()->GetPlayerName();
ChatData.MessageContent = Message;
ChatData.TimeStamp = FDateTime::Now();
// 모든 클라이언트에게 브로드캐스트
for (auto It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
{
if (ACDPlayerController* TargetPC = Cast<ACDPlayerController>(It->Get()))
{
TargetPC->ClientReceveMessage(ChatData);
}
}
}
필요하다면 Validation 매크로를 통해 메시지 수를 조절하거나 욕설필터링, 혹은 링크에 대한 차단이 가능하다.
이제 우리는 Client가 보낸 Packet에 대하여 어떻게 구성되어있는지를 만들었다. 다음은 이러한 채팅을 어떤식으로 담을것인가를 결정하면 된다.
텍스트의 나열뿐만이 아니라, 채팅 한 줄(RowClass)을 별도의 위젯으로 분리하면 각 텍스트에 대한 전수조사가 가능해진다.
// ChatWidget.h
UPROPERTY(meta=(BindWidget))
class UScrollBox* ChatScrollBox;
UPROPERTY(meta=(BindWidget))
class UEditableText* ChatInputText;
UPROPERTY(EditAnywhere)
TSubclassOf<class UChatRow> MessageRowClass;
쉽게 말하면 ScrollBox안에 여러개의 ChatData(RowClass)를 담아둬서 언제던지 조사할 수 있게, 그리고 유연성있게 만든것이다.

Rowclass가 우리가 전에 만들었던 Sender와 Content의 레이아웃이다.
void ACDPlayerController::ToggleChat(bool bEnable)
{
if (bEnable)
{
FInputModeGameAndUI InputMode;
InputMode.SetWidgetToFocus(CDHUD->ChatWidget->ChatInputText->TakeWidget());
SetInputMode(InputMode);
bShowMouseCursor = true;
}
else
{
SetInputMode(FInputModeGameOnly());
bShowMouseCursor = false;
}
}
엔터를 치고 채팅을 치는데, w를 눌렀더니 채팅을 치다말고 앞으로 이동하더라. 라는 현상은 절대 일어나서는 안된다. 때문에 게임조작과 채팅입력을 처리하되, Focus가 호출된다면 타이핑에 집중하는것으로 처리하였다.
