본문 바로가기
언리얼5 & C++

[언리얼5] Data Table 로더 만들기 *4부* / 커스텀 에디터 버튼 생성하고 기능연결

by 17번 일개미 2024. 6. 13.
728x90

 

전체 소스 코드는 GitHub 에 있습니다.

 

CSV 로 시작하는 것이 Excel 을 번거롭게 다른이름으로 저장할 필요가 있어서,

신버전을 만들었습니다.

 

1. CSV -> DataTableAsset (구버전)

https://github.com/Kympy/Unreal_CSV_To_DataTableAsset

 

GitHub - Kympy/Unreal_CSV_To_DataTableAsset: Python tool for unreal 5 / CSV to DataTable Asset and auto generate C++ Struct

Python tool for unreal 5 / CSV to DataTable Asset and auto generate C++ Struct - Kympy/Unreal_CSV_To_DataTableAsset

github.com

 

2. Excel -> DataTableAsset (신버전)

https://github.com/Kympy/Unreal_Excel2Data

 

GitHub - Kympy/Unreal_Excel2Data: UE5 Data Table Asset auto creator from excel

UE5 Data Table Asset auto creator from excel. Contribute to Kympy/Unreal_Excel2Data development by creating an account on GitHub.

github.com

 


 

 

제일 먼저 할 일은 모듈 생성이다.

 

모듈을 만드는 법에 대해서는 따로 기술하겠다.

 

#pragma once

#include "CoreMinimal.h"
#include "Modules/ModuleInterface.h"

class FDGExcel2Data : public IModuleInterface
{

public:
	// 모듈 시작 시 생성한 Extender -> 해제 시 필요함.
	TSharedPtr<FExtender> Extender;
	
	virtual void StartupModule() override;
	virtual void ShutdownModule() override;
	
	void AddMenuBar(FMenuBarBuilder& MenuBarBuilder);

	void FillMenu(FMenuBuilder& MenuBuilder);

	void MakeDataTableStruct();
	void MakeDataTableAsset();
	void CreateFolders();
};

 

 

헤더 파일을 대충 이렇게 정의하고 설명하자면,

 

AddMenuBar 는 에디터 상단에 커스텀 버튼을 추가 하기 위해 필요하고,

FillMenu 는 추가된 버튼에 하위 메뉴들을 만들기 위해 필요하다.

 

void FDGExcel2Data::AddMenuBar(FMenuBarBuilder& MenuBarBuilder)
{
	MenuBarBuilder.AddPullDownMenu(
		FText::FromString("DGExcel2Data"),
		FText::FromString("Convert Excel file to csv and generate c++ struct file and data table assets."),
		FNewMenuDelegate::CreateRaw(this, &FDGExcel2Data::FillMenu)
	);
}

 

AddMenuBar 는 위와 같다. 메뉴버튼의 하위 동작을 &로 링크해준다.

 

void FDGExcel2Data::FillMenu(FMenuBuilder& MenuBuilder)
{
	MenuBuilder.BeginSection("Data Table");
	{
		MenuBuilder.AddMenuEntry(
			FDataTableMakerCommand::Get().Cmd_MakeStruct,
			NAME_None,
			FText::FromString("1. Make Data Table C++ Struct"),
			FText::FromString("Make data table c++ struct. *FIRST*"),
			FSlateIcon()
		);
		MenuBuilder.AddMenuEntry(
			FDataTableMakerCommand::Get().Cmd_MakeAsset,
			NAME_None,
			FText::FromString("2. Make Data Table Asset"),
			FText::FromString("Make data table asset from struct."),
			FSlateIcon()
		);
		MenuBuilder.AddMenuEntry(
			FDataTableMakerCommand::Get().Cmd_CreateFolder,
			NAME_None,
			FText::FromString("Create Folders"),
			FText::FromString("Create necessary folders."),
			FSlateIcon()
		);
	}
	MenuBuilder.EndSection();
}

 

FillMenu 는 위와 같다.

여기서 3개의 하위메뉴를 만들었는데, 그대로 복붙하면 FDataTableMakerCommand 여기에서 에러가 날 것이다.
직접 만든 클래스이기 때문이다.

 

각 하위메뉴를 눌렀을 때 실행될 커맨드를 연결해주는 것인데 그러려면 이 클래스 부터 만들어야한다.

 

#pragma once

class FDataTableMakerCommand : public TCommands<FDataTableMakerCommand>
{
	
public:
	FDataTableMakerCommand();

	virtual void RegisterCommands() override;
	
	TSharedPtr<FUICommandInfo> Cmd_MakeStruct;
	TSharedPtr<FUICommandInfo> Cmd_MakeAsset;
	TSharedPtr<FUICommandInfo> Cmd_CreateFolder;
};

<FDataTableMakerCommand.h>

클래스 이름은 편한대로 짓는다.

 

TSharedPtr<FUICommandInfo> 로 사용할 3개의 커맨드 변수를 선언했다.

RegisterCommands() 는 상속받는 인터페이스에 정의된 함수이다.

 

#include "DataTableMakerCommand.h"

// NSLOCTEXT( 로컬라이징된 네임스페이스 (모듈이름 등등), 텍스트 식별할 키, 실제 텍스트)
FDataTableMakerCommand::FDataTableMakerCommand() : TCommands("DGExcel2Data", NSLOCTEXT("DGExcel2Data", "GenerateDataTable", "Generate Data Table"), NAME_None, FAppStyle::GetAppStyleSetName())
{
}

<FDataTableMakerCommand.cpp>

cpp 파일에는 생성자를 써줘야 하는데 조금 까다롭다.

정확히 이해하지는 못했지만 위 같은 형식으로 사용한다.

 

void FDataTableMakerCommand::RegisterCommands()
{
// UI_COMMAND 사용하려면 LOCTEXT_NAMESPACE 정의가 필요함 - "" 로 설정 시 특정 네임 스페이스에 속하지 않게 가능 
#define LOCTEXT_NAMESPACE "DGExcel2Data"

	UI_COMMAND(Cmd_MakeStruct, "Make data table struct", "Make C++ sturct", EUserInterfaceActionType::Button, FInputGesture());
	UI_COMMAND(Cmd_MakeAsset, "Make data table asset", "Make data table asset", EUserInterfaceActionType::Button, FInputGesture());
	UI_COMMAND(Cmd_CreateFolder, "Create folder", "Create folder", EUserInterfaceActionType::Button, FInputGesture());

#undef LOCTEXT_NAMESPACE
}

 

오버라이드한 함수는 위와 같이 작성한다.

#define 부분이 중요하다.

LOCTEXT_NAMESPACE 이후 부분이 생성자의 NSLOCTEXT() 첫번째 요소와 동일해야한다는 것을

해외 포럼에서 본것 같은데 테스트는 해보지 않았다.
일단 나는 동일하도록 작성했다.

 

여기까지 만들었으면 FDataTableMakerCommand 에 대한 컴파일 에러는 해결되었을 것이다.

 

 

다시 돌아와 StartUpModule() 함수를 작성한다.

 

#include "DGExcel2Data.h"
#include "LevelEditor.h"
#include "Commands/DataTableMakerCommand.h"

IMPLEMENT_MODULE(FDGExcel2Data, DGExcel2Data)

void FDGExcel2Data::StartupModule()
{
	UE_LOG(LogTemp, Log, TEXT("DGExcel2Data Module Started"));
	// 커맨드 생성
	TSharedPtr<FUICommandList> GenerateDataTableCommand = MakeShareable(new FUICommandList);
	if (!GenerateDataTableCommand)
	{
		UE_LOG(LogTemp, Error, TEXT("New FUICommandList failed."));
		return;
	}

	FDataTableMakerCommand::Register();
	
	// 액션 맵핑
	GenerateDataTableCommand->MapAction(
		FDataTableMakerCommand::Get().Cmd_MakeStruct, 
FExecuteAction::CreateRaw(this, &FDGExcel2Data::MakeDataTableStruct), 
		FCanExecuteAction()
	);
	GenerateDataTableCommand->MapAction(
		FDataTableMakerCommand::Get().Cmd_MakeAsset,
		FExecuteAction::CreateRaw(this, &FDGExcel2Data::MakeDataTableAsset),
		FCanExecuteAction()
	);
	GenerateDataTableCommand->MapAction(
		FDataTableMakerCommand::Get().Cmd_CreateFolder,
		FExecuteAction::CreateRaw(this, &FDGExcel2Data::CreateFolders),
		FCanExecuteAction()
	);
	
	Extender = MakeShareable(new FExtender);
	Extender->AddMenuBarExtension(
		"Help",
		EExtensionHook::After,
		GenerateDataTableCommand,
		FMenuBarExtensionDelegate::CreateRaw(this, &FDGExcel2Data::AddMenuBar)
	);

	FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
	LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(Extender);

 

 

일단 제일 첫 줄이 매우 중요하다. 없으면 안된다.

IMPLEMENT_MODULE(FDGExcel2Data, DGExcel2Data)

 

MakeShareable 로 생성한 포인터에 MapAction 을 호출하여 아까 만든 클래스의 커맨드들을 등록하고,
커맨드와 연결될 나의 함수를 연결한다.

 

Extender 를 생성하고 AddMenuBarExtension 함수를 호출하여 나의 AddMenuBar 함수를 연결한다.

"Help" 는 뭐냐면,

에디터에 상단바를 보면 Help 메뉴가 있을 건데 그 Help 를 말하는 것이다. 한글 버전이라면 도움말? 이 될 것이다.

Hook::After 는 Help 다음에 내 커스텀 메뉴를 위치하겠다는 의미다.

 

생성한 Extender 를 LevelEditorModule 에 추가해주는 과정까지 하게되면,

내 모듈이 실행 될 때 커스텀 버튼을 추가하고 하위메뉴를 만들고 기능까지 연결되는 것이다.

 

void FDGExcel2Data::ShutdownModule()
{
	IModuleInterface::ShutdownModule();

	if (Extender.IsValid())
	{
		FLevelEditorModule* LevelEditorModule = FModuleManager::GetModulePtr<FLevelEditorModule>("LevelEditor");
		if (LevelEditorModule)
		{
			LevelEditorModule->GetMenuExtensibilityManager()->RemoveExtender(Extender);
		}
		Extender.Reset();
	}
	

	FDataTableMakerCommand::Unregister();
}

 

 

모듈 종료 시에는 위와같이 사용한 것들을 해제하는 과정이 필요하다.

 

void FDGExcel2Data::MakeDataTableAsset()
{
	FString PythonScriptPath = FPaths::Combine(FPaths::ProjectContentDir(), "Python", "DGExcel2Data", "asset_generator.py");
	PythonScriptPath = FPaths::ConvertRelativePathToFull(PythonScriptPath);
	PythonScriptPath.InsertAt(0, "py ");
	GEngine->Exec(nullptr, *PythonScriptPath);
}

 

마지막으로 하위메뉴에 각자 연결했던 실행하고자 하는 함수 중 하나만 설명하겠다.

 

함수를 만들고 그 안에 원하는 기능을 작성하면 되는데,

여기서 내가 필요한 건 Python 스크립트 실행이다.

 

py 파일의 경로를 알아 낸 후 맨 앞에 py 를 붙인다.

이 string을 엔진의 커맨드라인에 넣어 실행시킨다.

728x90