개인 강의/Unreal Engine 멀티플레이어 게임 개발

Unreal Engine 멀티플레이어 게임 개발 1-1

jh009 2026. 7. 28. 19:17

1-1. 채팅 프로젝트 생성

1. 프로젝트 생성

UE5.5.4 기준 프로젝트 세팅

 

코드 선택 → Ctrl + . 누르기 → Create Declaration / Definition 선택하면 cpp 코드를 자동을 짜줌


2. 새 레벨 만들기

Content Browser > Content 폴더 우클릭 > 새 폴더 “ChatX”

ChatX 폴더 우클릭 > 새 폴더 “Maps”

File > New Level > Empty Level 선택 후 Create

Ctrl + S > Maps 폴더 "Chatting" 이름 설정


3. 레벨 관련 프로젝트 설정

 

Project Settings > Maps & Modes > Chatting 지정

  • Editor Startup Map
  • Game Default Map

4. 게임 프레임워크 블루프린트 에셋 생성

 

New C++ 클래스 > GameModeBase 부모 클래스 > “CXGameModeBase”` 

Path: Game (경로 뒤에 Game 추가)

 

New C++ 클래스 > PlayerController 부모 클래스 > “CXPlayerController”` 

Path: Player (경로 뒤에 Player 추가)

// CXGameModeBase.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "CXGameModeBase.generated.h"

UCLASS()
class CHATX_API ACXGameModeBase : public AGameModeBase
{
	GENERATED_BODY()
	
};


// CXGameModeBase.cpp


#include "Game/CXGameModeBase.h"
// CXPlayerController.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "CXPlayerController.generated.h"

UCLASS()
class CHATX_API ACXPlayerController : public APlayerController
{
	GENERATED_BODY()
	
};


// CXPlayerController.cpp


#include "Player/CXPlayerController.h"
// ChatX.Build.cs

using UnrealBuildTool;

public class ChatX : ModuleRules
{
	public ChatX(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
	
		PublicDependencyModuleNames.AddRange(new string[]
		{
			// Initial Dependencies
			"Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput",
			
		});

		PrivateDependencyModuleNames.AddRange(new string[] {  });

		PublicIncludePaths.AddRange(new string[] { "ChatX" }); // 추가
	}
}

 

  • Content Browser > Content 폴더 > ChatX 폴더 우클릭 > 새 폴더 “Blueprint”` 
  • Blueprint > 새 폴더 “Game”, “Player”` 
  • Game > 새 Blueprint 에셋 > CXGameModeBase 부모 클래스 > “BP_GameModeBase”` 
  • Player > 새 Blueprint 에셋 > CXPlayerController 부모 클래스 > “BP_PlayerController”

Chatting 레벨 > World Settings > GameMode Override에 BP_GameMode 지정

 

BP_GameModeBase > Details > Player Controller Class에 BP_PlayerController 지정


5. 채팅 입력창 위젯 구현

 

New C++ 클래스 > UserWidget 부모 클래스 > “CXChatInput”

Path: UI

// ChatX.Build.cs

...

public class ChatX : ModuleRules
{
	public ChatX(ReadOnlyTargetRules Target) : base(Target)
	{
		...
	
		PublicDependencyModuleNames.AddRange(new string[]
		{
			...
			
			// UI
			"UMG",
			
		});

		...
	}
}
// CXChatInput.h

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "CXChatInput.generated.h"

class UEditableTextBox;

/**
 * 
 */
UCLASS()
class CHATX_API UCXChatInput : public UUserWidget
{
	GENERATED_BODY()

public:
	UPROPERTY(meta = (BindWidget))
	TObjectPtr<UEditableTextBox> EditableTextBox_ChatInput;
	
};


// CXChatInput.cpp


#include "UI/CXChatInput.h"

 

 

  • Content Browser > Content 폴더 > Chat 폴더 > Blueprint 우클릭 > 새 폴더 “UI”
  • UI > 새 User Interface 에셋 > Widget Blueprint
  • CXChatInput 부모 클래스 > “WBP_ChatInput”

WBP_ChatInput > Hierarchy에 Cavas Panel 추가

 

WBP_ChatInput > Hierarchy에 EditableTextBox 추가

 

Text Box > Details > Anchors에서 하단을 꽉채우는 걸로 선택

Text Box > Details


6. 채팅 위젯 연결

코드 작성 후, BP_PlayerController > Chat Input Widget Class에 WBP_ChatInput 지정

// CXPlayerController.h

...

class UCXChatInput;

/**
 * 
 */
UCLASS()
class CHATX_API ACXPlayerController : public APlayerController
{
	GENERATED_BODY()

public:
	virtual void BeginPlay() override;

protected:
	UPROPERTY(EditDefaultsOnly)
	TSubclassOf<UCXChatInput> ChatInputWidgetClass;
	
	UPROPERTY()
	TObjectPtr<UCXChatInput> ChatInputWidgetInstance;	
	
};
// CXPlayerController.cpp


#include "Player/CXPlayerController.h"

#include "UI/CXChatInput.h"

void ACXPlayerController::BeginPlay()
{
	Super::BeginPlay();
	
	FInputModeUIOnly InputModeUIOnly;
	SetInputMode(InputModeUIOnly);

	if (IsValid(ChatInputWidgetClass) == true)
	{
		ChatInputWidgetInstance = CreateWidget<UCXChatInput>(this, ChatInputWidgetClass);
		if (IsValid(ChatInputWidgetInstance) == true)
		{
			ChatInputWidgetInstance->AddToViewport();
		}
	}
}

 

BP_PlayerController > Details > Chat Input Widget Class에 WBP_ChatInput


7. 채팅 메세지 관리

// ChatX.Build.cs

...

public class ChatX : ModuleRules
{
	public ChatX(ReadOnlyTargetRules Target) : base(Target)
	{
		...
	
		PublicDependencyModuleNames.AddRange(new string[]
		{
			...
			
			// UI
			"UMG", "Slate", "SlateCore", 
			
		});

		...
	}
}
// CXPlayerController.h

...
class CHATX_API ACXPlayerController : public APlayerController
{
	...

public:
	virtual void BeginPlay() override;

	void SetChatMessageString(const FString& InChatMessageString);
	
protected:
	...

	FString ChatMessageString;
};
// CXPlayerController.cpp


...

void ACXPlayerController::SetChatMessageString(const FString& InChatMessageString)
{
	ChatMessageString = InChatMessageString;
}
// CXChatInput.h

...
#include "Types/SlateEnums.h"
#include "CXChatInput.generated.h"

...
class CHATX_API UCXChatInput : public UUserWidget
{
	GENERATED_BODY()

public:
	virtual void NativeConstruct() override;

	virtual void NativeDestruct() override;

protected:
	UFUNCTION()
	void OnChatInputTextCommitted(const FText& Text, ETextCommit::Type CommitMethod);
	
public:
	...
	
};
// CXChatInput.cpp


#include "UI/CXChatInput.h"

#include "Components/EditableTextBox.h"
#include "Player/CXPlayerController.h"

void UCXChatInput::NativeConstruct()
{
	Super::NativeConstruct();

	if (EditableTextBox_ChatInput->OnTextCommitted.IsAlreadyBound(this, &ThisClass::OnChatInputTextCommitted) == false)
	{
		EditableTextBox_ChatInput->OnTextCommitted.AddDynamic(this, &ThisClass::OnChatInputTextCommitted);		
	}	
}

void UCXChatInput::NativeDestruct()
{
	Super::NativeDestruct();

	if (EditableTextBox_ChatInput->OnTextCommitted.IsAlreadyBound(this, &ThisClass::OnChatInputTextCommitted) == true)
	{
		EditableTextBox_ChatInput->OnTextCommitted.RemoveDynamic(this, &ThisClass::OnChatInputTextCommitted);
	}
}

void UCXChatInput::OnChatInputTextCommitted(const FText& Text, ETextCommit::Type CommitMethod)
{
	if (CommitMethod == ETextCommit::OnEnter)
	{
		APlayerController* OwningPlayerController = GetOwningPlayer();
		if (IsValid(OwningPlayerController) == true)
		{
			ACXPlayerController* OwningCXPlayerController = Cast<ACXPlayerController>(OwningPlayerController);
			if (IsValid(OwningCXPlayerController) == true)
			{
				OwningCXPlayerController->SetChatMessageString(Text.ToString());

				EditableTextBox_ChatInput->SetText(FText());
			}
		}
	}
}

8. 채팅 메세지 출력

// CXPlayerController.h

...
class CHATX_API ACXPlayerController : public APlayerController
{
	GENERATED_BODY()

public:
	...

	void PrintChatMessageString(const FString& InChatMessageString);
	
protected:
	...
	
};
// CXPlayerController.cpp

..
#include "Kismet/KismetSystemLibrary.h"

...

void ACXPlayerController::SetChatMessageString(const FString& InChatMessageString)
{
	...

	PrintChatMessageString(ChatMessageString);
}

void ACXPlayerController::PrintChatMessageString(const FString& InChatMessageString)
{
	UKismetSystemLibrary::PrintString(this, ChatMessageString, true, true, FLinearColor::Red, 5.0f);
}