일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 보안
- rpc
- stride
- attribute
- ability task
- 유니티
- Multiplay
- gameplay tag
- os
- CTF
- unity
- Replication
- widget
- Aegis
- animation
- gameplay ability system
- local prediction
- listen server
- nanite
- 언리얼 엔진
- C++
- UI
- gas
- photon fusion2
- 게임 개발
- gameplay effect
- MAC
- 게임개발
- 언리얼엔진
- Unreal Engine
Archives
- Today
- Total
Replicated
** [Drag Down] 채팅 기능 구현 (Chatting) ** 본문
필터링은 없다.
리슨 서버 구조라 언리얼 클라이언트에서 필터링하기엔 너무 취약한 구조라 백엔드에 필터링 요청을 할 예정
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "DDChatEntry.generated.h"
/**
*
*/
UCLASS()
class DRAGDOWN_API UDDChatEntry : public UUserWidget
{
GENERATED_BODY()
public:
void SetChat(const FText& UserName, const FText& Chat);
protected:
UPROPERTY(meta = (BindWidget))
TObjectPtr<class UTextBlock> TxtUserName;
UPROPERTY(meta = (BindWidget))
TObjectPtr<class UTextBlock> TxtChat;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "UI/DDChatEntry.h"
#include "Components/TextBlock.h"
void UDDChatEntry::SetChat(const FText& UserName, const FText& Chat)
{
if ( TxtUserName )
{
TxtUserName->SetText(UserName);
}
if ( TxtChat )
{
TxtChat->SetText(Chat);
}
}
일단 채팅 하나에 대한 엔트리
중요한 점은, 채팅이 길어지는 경우의 줄바꿈을 위해 Size Box로 래핑할 필요가 있음
사이즈 박스에서 Width만 오버라이드 하면 수평은 자동으로 지정되고, Width만 길이를 강제할 수 있음
Chat의 경우 오토 랩 -> 상위 컴포넌트에 맞춰 자동 줄바꿈
래핑 폴리시는 캐릭터 당 래핑을 허용해야 띄어쓰기 없이 길게하는 경우도 줄바꿈이 됨
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "DDChatBox.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FChatDelegate, const FText&, Content);
/**
*
*/
UCLASS()
class DRAGDOWN_API UDDChatBox : public UUserWidget
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
void MakeChatEntry(const FText& UserName, const FText& Content);
FChatDelegate OnChat;
void SetFocusToEditTxt();
protected:
UPROPERTY(meta = (BindWidget))
TObjectPtr<class UScrollBox> ScrollBox;
UPROPERTY(meta = (BindWidget))
TObjectPtr<class UVerticalBox> VerticalBox;
UPROPERTY(EditAnywhere, BlueprintReadOnly)
TSubclassOf<class UDDChatEntry> ChatEntryClass;
UPROPERTY(meta = (BindWidget))
TObjectPtr<class UEditableText> EditTxtChatInput;
UFUNCTION()
void OnTextCommitted(const FText& Text, ETextCommit::Type CommitMethod);
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "UI/DDChatBox.h"
#include "Components/EditableText.h"
#include "Components/VerticalBox.h"
#include "Components/ScrollBox.h"
#include "UI/DDChatEntry.h"
#include "DragDown.h"
void UDDChatBox::NativeConstruct()
{
Super::NativeConstruct();
if ( EditTxtChatInput )
{
EditTxtChatInput->OnTextCommitted.AddDynamic(this, &UDDChatBox::OnTextCommitted);
}
}
void UDDChatBox::MakeChatEntry(const FText& UserName, const FText& Content)
{
if (ChatEntryClass == nullptr) return;
UDDChatEntry* ChatEntry = NewObject<UDDChatEntry>(this, ChatEntryClass);
if (ChatEntry == nullptr) return;
UE_LOG(LogDD, Log, TEXT("MakeChatEntry"));
VerticalBox->AddChild(ChatEntry);
ChatEntry->SetChat(UserName, Content);
ScrollBox->ScrollToEnd();
}
void UDDChatBox::SetFocusToEditTxt()
{
if ( EditTxtChatInput )
{
EditTxtChatInput->SetFocus();
EditTxtChatInput->SetKeyboardFocus();
}
}
void UDDChatBox::OnTextCommitted(const FText& Text, ETextCommit::Type CommitMethod)
{
if ( ETextCommit::OnEnter == CommitMethod )
{
EditTxtChatInput->SetText(FText::FromString(TEXT("")));
OnChat.Broadcast(Text);
}
}
SetFocusToEditTxt 발동 시 에디터블 텍스트에 포커싱
엔터를 누르면 OnChat을 브로드캐스트 -> 채팅
// Chat
public:
void UpdateChat(const FText& UserName, const FText& Content);
protected:
void InitChatWidget();
void ActivateChatWidget();
UFUNCTION()
virtual void OnChatCallback(const FText& Content);
UFUNCTION(Server, Reliable)
void ServerOnChatCallback(const FText& UserName, const FText& Content);
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI")
TSubclassOf<class UUserWidget> ChatBoxWidgetClass;
UPROPERTY()
TObjectPtr<class UDDChatBox> ChatBoxWidget;
void ADDPlayerController::UpdateChat(const FText& UserName, const FText& Content)
{
ChatBoxWidget->MakeChatEntry(UserName, Content);
}
void ADDPlayerController::InitChatWidget()
{
if (ChatBoxWidgetClass == nullptr || ChatBoxWidget != nullptr) return;
ChatBoxWidget = CreateWidget<UDDChatBox>(this, ChatBoxWidgetClass);
if ( ChatBoxWidget == nullptr )
{
UE_LOG(LogDD, Log, TEXT("InitGASWidget: StaminaBarWidget Creation Is Failed"));
return;
}
ChatBoxWidget->AddToViewport();
ChatBoxWidget->OnChat.AddDynamic(this, &ADDPlayerController::OnChatCallback);
}
void ADDPlayerController::ActivateChatWidget()
{
if ( ChatBoxWidget )
{
FInputModeUIOnly InputMode;
SetInputMode(InputMode);
ChatBoxWidget->SetFocusToEditTxt();
}
}
void ADDPlayerController::OnChatCallback(const FText& Content)
{
UE_LOG(LogDD, Log, TEXT("OnChatCallback - %s"), *Content.ToString());
if (!Content.IsEmpty())
{
FText UserName = FText::FromString(GetGameInstance()->GetSubsystem<UDDUserAuthSubsystem>()->GetUserName());
ServerOnChatCallback(UserName, Content);
}
}
void ADDPlayerController::ServerOnChatCallback_Implementation(const FText& UserName, const FText& Content)
{
if ( HasAuthority() )
{
ADDGameState* GameState = Cast<ADDGameState>(GetWorld()->GetGameState());
if (GameState)
{
GameState->NetMulticastChatBroadCast(UserName, Content);
}
}
}
void ADDPlayerController::SetupInputComponent()
{
Super::SetupInputComponent();
InputComponent->BindAction("OpenMenu", IE_Pressed, this, &ADDPlayerController::ToggleMenu);
InputComponent->BindAction("Chat", IE_Pressed, this, &ADDPlayerController::ActivateChatWidget);
}
플레이어 컨트롤러에서 챗 박스를 소유하고 관리
엔터 입력 시 채팅 모드로 변환
OnChatCallback 발생 시 (OnChat 브로드캐스트) ServerOnChatCallback으로 게임 스테이트에서 멀티캐스트
* PlayerController는 모든 클라이언트에 복제되는 액터가 아니라 멀티캐스트 못씀
UFUNCTION(NetMulticast, Reliable)
void NetMulticastChatBroadCast(const FText& UserName, const FText& Content);
void ADDGameState::NetMulticastChatBroadCast_Implementation(const FText& UserName, const FText& Content)
{
ADDPlayerController* PC = Cast<ADDPlayerController>(GetWorld()->GetFirstPlayerController());
if ( PC )
{
PC->UpdateChat(UserName, Content);
}
}
게임 스테이트에선 그냥 멀티캐스트로 각각의 로컬 PC에서 채팅을 업데이트
정상 작동 확인 가능
'언리얼 엔진 > Drag Down' 카테고리의 다른 글
[Drag Down] Patrol 몬스터 추가 (0) | 2025.05.26 |
---|---|
[Drag Down] 맵 이동 포탈 (0) | 2025.05.21 |
[Drag Down] PlayerState에서 이름과 Ready 관리, GameState 전체 상태 관리 (0) | 2025.05.21 |
[Drag Down] 게임 모드 및 컨트롤러 분리 (0) | 2025.05.19 |
[Drag Down] GameState에서 Player Idx 및 Ready 상태 관리하기 (PlayerState로 분리 예정) (0) | 2025.05.18 |