本质
不管是客户端还是服务器在输入文字后,按下回车发送,将触发RPC调用。然后通过RPC将发送者,输入文本等信息,传入到服务器,然后通过多播RPC传播到所有客户端的聊天框。
UI
UI利用三个组件
ScrollBox
用于在服务器以及每个客户端上显示消息的载体
TextBlock
本地将信息通过一个一个的TextBlock组装,然后将组装的结果添加到ScrollBox中实现消息的显示
UEditableText
每个机器上编辑发送消息的主体,该组件具有OnTextCommitted的委托,可以处理提交事件,可以在自定义一个OnSendText委托,用于当Committed时可以外部调用。
switch (CommitMethod){case ETextCommit::Default:break;case ETextCommit::OnEnter:UE_LOG(LogTemp, Warning, TEXT("TextCommitted_OnEnter"));OnSendText.Broadcast(Text);SendText->SetText(FText::FromString(""));UGameplayStatics::GetPlayerController(GetWorld(), 0)->SetInputMode(FInputModeGameOnly());GetNowUserComponent(GetOwningPlayer())->ToggleChatWindow();break;case ETextCommit::OnUserMovedFocus:break;case ETextCommit::OnCleared:break;default:break;}
实现
将直接在GameMode中的PlayerController中增加用于Chat的Component。
ServerSentText
该函数是一个ServerRPC,当接受到OnSendText委托后,调用。
在函数内部,通过遍历当前World中具有ChatComponent的Controller,获得所有客户端的Actor。然后调用多播RPC实现消息的滚动
TArray<AActor*> Actors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AXBlasterPlayerController::StaticClass(), Actors);
for (auto& it : Actors)
{UActorComponent* cont = it->GetComponentByClass(UXChatComponent::StaticClass());if (cont && cont->Implements<UXChatInterface>()){IXChatInterface::Execute_ChatMessage(cont,Message,MessageType,PlayerName,SetMessageTypeColor(MessageType),ChatType);}
}
多播RPC MultiSendMessage
该函数在服务器上调用后,会在每个客户端进行执行,该函数主要处理发送信息的整合。
通过GetOwner()获取当前的Controller,然后判断Controller是不是本地的,如果不是那么就会跳过,只有是本地的才会进行消息的拼装,否则会出现多条相同的消息
AXBlasterPlayerController* PIC = Cast<AXBlasterPlayerController>(GetOwner());if (!PIC->IsLocalController()) return;AXBlasterHUD* HUD = Cast<AXBlasterHUD>(PIC->GetHUD());if (HUD){if (HUD->ChatWdg){UXTextWidget* TextWidget = CreateWidget<UXTextWidget>(UGameplayStatics::GetPlayerController(GetWorld(), 0),HUD->ChatTextClass);if (TextWidget){TextWidget->PlayerName = FText::FromString(SendName);TextWidget->InText = Message;TextWidget->MessType = MessageType;TextWidget->ChatType = EChatTypes::ECT_All;HUD->ChatWdg->ChatScrollBox->AddChild(TextWidget);HUD->ChatWdg->ChatScrollBox->ScrollToEnd();}}}
通过接口实现多播RPC的调用
由于在Server里是遍历了World中所有的Controller,使用接口可以表面获取实例来进行调用而且可以放置选择到那些没有Chat功能的Controller,因为在C++中会首先检验当前的实例是否实现了接口
if (cont && cont->Implements<UXChatInterface>()){IXChatInterface::Execute_ChatMessage(cont,Message,MessageType,PlayerName,SetMessageTypeColor(MessageType),ChatType);}