초보 코린이의 성장 일지

UE4 Weapon Plugin (2) 본문

언리얼

UE4 Weapon Plugin (2)

코오린이 2023. 5. 30. 17:18

에디터 메뉴바에 Weapon Plugin 아이콘을 입혀 버튼을 등록하고, 클릭했을시 창이 열리도록 만들어 볼 것이다.

 

기존 플러그인 작업을 할때는 Toolbar 모양과 Command를 Module에서 작업했었다. 이번에는 WeaponCommand 클래스를 생성하여 두가지 기능을 하나로 통합해서 사용할 것이다.

 

1. 기능을 통합해서 사용할 클래스를 생성해준다.

더보기
#pragma once

#include "CoreMinimal.h"
#include "Framework/Commands/Commands.h"

class WEAPON_API FWeaponCommand
	: public TCommands<FWeaponCommand>
{
public:
	FWeaponCommand();
	~FWeaponCommand();

	void Startup();

public:
	void RegisterCommands() override;

private:
	TSharedPtr<FExtender> Extender;

	TSharedPtr<FUICommandList> Command; // 받은 커멘드를 리스트에 등록하기
	TSharedPtr<FUICommandInfo> Id; // 커멘드

private:
	void AddToolBar(FToolBarBuilder& InBuilder); // 모양 결정
	void OnClicked(); // 버튼 눌렸을때,


};
#include "WeaponCommand.h"
#include "WeaponStyle.h"
#include "WeaponAssetEditor.h"
#include "LevelEditor.h"

FWeaponCommand::FWeaponCommand()
	// 커멘드에 대한 ID, 출력 이름, 기본 스타일
	: TCommands("Toolbar_Buttons", FText::FromString(""), NAME_None, FEditorStyle::GetStyleSetName())
{
	Command = MakeShareable(new FUICommandList());
}

FWeaponCommand::~FWeaponCommand()
{
	if (Command.IsValid())
		Command.Reset();

	if (Extender.IsValid())
		Extender.Reset();

}

void FWeaponCommand::Startup()
{
	FWeaponCommand::RegisterCommands();

	Extender = MakeShareable(new FExtender());

	// 툴바 등록
	FToolBarExtensionDelegate toolbar = FToolBarExtensionDelegate::CreateRaw(this, &FWeaponCommand::AddToolBar);
	// 연결되있는 toolbar 콜되는 부분, 세팅 뒤쪽에 추가
	Extender->AddToolBarExtension("Settings", EExtensionHook::After, Command, toolbar);

	// 최종적으로 LevelEditor에 추가해준다.
	FLevelEditorModule& levelEditor = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
	levelEditor.GetToolBarExtensibilityManager()->AddExtender(Extender);

}

void FWeaponCommand::RegisterCommands()
{
	// 커멘드 등록
#define LOCTEXT_NAMESPACE ""
	UI_COMMAND(Id, "Weapon", "", EUserInterfaceActionType::Button, FInputChord());
#undef LOCTEXT_NAMESPACE

	FExecuteAction action;
	action.BindRaw(this, &FWeaponCommand::OnClicked); // 클릭시 연결되어 있는 커멘드 콜

	Command->MapAction(Id, action, FCanExecuteAction());
}

void FWeaponCommand::AddToolBar(FToolBarBuilder& InBuilder)
{
	// 툴바에 버튼 추가
	FString name = TEXT("웨폰");

	InBuilder.AddSeparator();
	InBuilder.AddToolBarButton(Id, NAME_None, FText::FromString(name), FText::FromString("Weapon Asset Editor"), FWeaponStyle::Get()->ToolBar_Icon, NAME_None);
}

void FWeaponCommand::OnClicked()
{

	GLog->Log("Test");
}

1. 툴바 등록, 버튼 등록, 버튼 모양, 에디터상 메뉴 위치에 추가.

2, Test 출력으로 제대로 버튼이 동작하는지 확인.

 

1. 한글로 그냥 넣어주면 깨져서 제대로된 글자 출력이 안되므로, 설정을 변경해 줄 것이다.

1. 다른 이름으로 저장하기 클릭 -> 변화할 cpp 선택 -> 저장 버튼 옆에 클릭하여 인코딩하여 저장 -> 인코딩 옵션에서 유니코드 UTF-8로 설정 후 저장

2. 변화가 완료되었다.


 

더보기
#pragma once

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

class FWeaponModule : public IModuleInterface
{
public:

	virtual void StartupModule() override;
	virtual void ShutdownModule() override;

private:
	TSharedPtr<class FWeaponContextMenu> ContextMenu;
	TSharedPtr<class FWeaponCommand> Command;


};
#include "WeaponModule.h"
#include "WeaponContextMenu.h"
#include "WeaponStyle.h"
#include "WeaponCommand.h"
#include "IAssetTools.h"
#include "AssetToolsModule.h"

#define LOCTEXT_NAMESPACE "FWeaponModule"
IMPLEMENT_MODULE(FWeaponModule, Weapon) // 여러 플러그인을 작업할때 모듈들 햇갈릴수 있기 때문에 상단에 올려놓으면 좋다.

void FWeaponModule::StartupModule()
{
	// 생성
	FWeaponStyle::Get();

	IAssetTools& assetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
	// 에셋 카테고리 생성, 출력될 이름
	EAssetTypeCategories::Type categories = assetTools.RegisterAdvancedAssetCategory("WeaponAsset", FText::FromString("Weapon"));

	ContextMenu = MakeShareable(new FWeaponContextMenu(categories));
	assetTools.RegisterAssetTypeActions(ContextMenu.ToSharedRef()); // 메뉴 등록

	Command = MakeShareable(new FWeaponCommand());
	Command->Startup();
}

void FWeaponModule::ShutdownModule()
{
	// 필요 없어지면 리셋.
	if (ContextMenu.IsValid())
		ContextMenu.Reset();

	if (Command.IsValid())
		Command.Reset();

	// Off
	FWeaponStyle::Shutdown();
}

#undef LOCTEXT_NAMESPACE

1. 기존에 있던 Module 코드에 Get()으로 생성해주고, Shutdown으로 Off 시켜주는 코드를 추가해준다.

2. Command 할당해준다.


더보기
using UnrealBuildTool;

public class Weapon : ModuleRules
{
	public Weapon(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;

		PrivateIncludePaths.Add(ModuleDirectory);

        PublicDependencyModuleNames.Add("Core");

        PublicDependencyModuleNames.Add("U2212_06");


        PrivateDependencyModuleNames.Add("CoreUObject");
        PrivateDependencyModuleNames.Add("Engine");
        PrivateDependencyModuleNames.Add("Slate");
        PrivateDependencyModuleNames.Add("SlateCore");

        PrivateDependencyModuleNames.Add("UnrealEd");
        PrivateDependencyModuleNames.Add("EditorStyle");


    }
}

1. 컴파일 해보면 링크 오류가 발생할 것이다. 모듈에서 추가해준다.

2. EditorStyle 추가.


 

 

1. 실행해보면, 카테고리에 웨폰 아이콘과 버튼이 추가되있는걸 확인할 수 있다.

2. 버튼을 클릭해보면 Log가 잘 출력되는걸 알 수 있다.


언리얼은 기본적으로 형식이 다 동일하다. 위에 메뉴가 존재, 툴바 존재, 디테일창까지 형식이 똑같다.

그래서 템플릿 클래스로 이 기능들을 제공해주는데 이걸 사용해서 창을 만들어 볼 것이다.

1. 클래스를 생성해준다.

 

더보기
#pragma once

#include "CoreMinimal.h"
#include "Toolkits/AssetEditorToolkit.h"

class WEAPON_API FWeaponAssetEditor
	: public FAssetEditorToolkit
{
public:
	static void OpenWindow(FString InAssetName = "");
	static void Shutdown();

private:
	static TSharedPtr<FWeaponAssetEditor> Instance;

private:
	void Open(FString InAssetName);

public:
	void RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager) override;

private:
	TSharedRef<SDockTab> Spawn_ListViewTab(const FSpawnTabArgs& InArgs);

public:
	// 추상클래스 안에 있는 함수로서, 반드시 재정의 필요
	FName GetToolkitFName() const override;
	FText GetBaseToolkitName() const override;
	FString GetWorldCentricTabPrefix() const override;
	FLinearColor GetWorldCentricTabColorScale() const override;

private:
	static const FName EditorName;
	static const FName ListViewTabId;
	static const FName DetailTabId;

private:
	FReply OnClicked();

};
#include "WeaponAssetEditor.h"
#include "Weapons/CWeaponAsset.h"

const FName FWeaponAssetEditor::EditorName = "WeaponAssetEditor";
const FName FWeaponAssetEditor::ListViewTabId = "ListView";
const FName FWeaponAssetEditor::DetailTabId = "Details";

TSharedPtr<FWeaponAssetEditor> FWeaponAssetEditor::Instance = nullptr;

// 이 함수에 못 들어온다면 툴바 버튼을 클릭했다는 의미, 들어왔다면 DataAsset를 클릭했다는 의미
void FWeaponAssetEditor::OpenWindow(FString InAssetName)
{
	// 창이 만들어져 있다면
	if (Instance.IsValid())
	{
		// 창이 한번이라도 열려 있었다면, off 해준다.
		Instance->CloseWindow();

		Instance.Reset();
		Instance = nullptr;
	}

	Instance = MakeShareable(new FWeaponAssetEditor());
	Instance->Open(InAssetName);
}

void FWeaponAssetEditor::Shutdown()
{
	// Shutdown을 OpenWindow가 끝났을때 콜을하면 터진다. 그 이유는 다른곳에서도 사용하기 때문에
	if (Instance.IsValid())
	{
		Instance->CloseWindow();

		Instance.Reset();
		Instance = nullptr;
	}
}

void FWeaponAssetEditor::Open(FString InAssetName)
{
	// 모든 창은 TabManager를 가지고있다. TabManager를 이용하여 레이아웃을 잡아주면 된다.
	TSharedRef<FTabManager::FLayout> layout = FTabManager::NewLayout("WeaponAssetEditor_Layout")
		->AddArea // 구역 추가, 전체화면에 메인 영역이 될 것, 이 영역을 갈라서 사용할 것이다.
		(
			FTabManager::NewPrimaryArea()->SetOrientation(Orient_Vertical) // 화면에 보여질 주 영역, 어떠한 방향으로 배치할 것인지
			->Split // 공간을 분활
			(
				FTabManager::NewStack()
				->SetSizeCoefficient(0.1f) // 전체에 10프로만 사용.
				->AddTab(GetToolbarTabId(), ETabState::OpenedTab) // Tab 열린상태로 추가
			)
			->Split
			(
				// 한번더 분활하기 위해 Splitter 사용, Splitter는 BP내에서 창을 마우스로 움직일수 있는 창
				FTabManager::NewSplitter()->SetOrientation(Orient_Horizontal)
				->Split // 다시 분활
				(
					FTabManager::NewStack()
					->SetSizeCoefficient(0.175f)
					->AddTab(ListViewTabId, ETabState::OpenedTab)
					->SetHideTabWell(true)// 공간을 true 해줘야 펼칠수 있다
				)
				->Split
				(
					FTabManager::NewStack()
					->SetSizeCoefficient(0.725f) // 나머지 공간
					->AddTab(DetailTabId, ETabState::OpenedTab)
					->SetHideTabWell(true)
				)
			)
		);

	UCWeaponAsset* asset = NewObject<UCWeaponAsset>();
	// 독림형 창으로 뛰우기, 부모창 호스트는 없으므로 비워주고, 창에 이름을 설정, 창 추가, 메뉴를 보여줄 것인지, 툴바도 보여줄 것인지, 어떤 오브젝트를 편집할 것인지
	FAssetEditorToolkit::InitAssetEditor(EToolkitMode::Standalone, TSharedPtr<IToolkitHost>(), EditorName, layout, true, true, asset);
}

void FWeaponAssetEditor::RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager)
{
	// 부모함수를 사용했지만, 에디터 안에서는 Super 사용 불가.
	FAssetEditorToolkit::RegisterTabSpawners(InTabManager);

	FOnSpawnTab tab;
	// 슬레이트 쪽이라 SP를 사용, 
	tab.BindSP(this, &FWeaponAssetEditor::Spawn_ListViewTab);
	TabManager->RegisterTabSpawner(ListViewTabId, tab); // 구역에 대한 ID, 
}

TSharedRef<SDockTab> FWeaponAssetEditor::Spawn_ListViewTab(const FSpawnTabArgs& InArgs)
{
	// 도킹할수 있는 Tab을 DockTab라 부른다. 마우스로 드래그해서 구역을 왔다갔다 할 수 있는 것
	TSharedPtr<SDockTab> tab = SNew(SDockTab) // [] 대괄호 사용시 그 영역까지는 Content 영역이며, []로 생략이 가능하다
		[
			SNew(SButton)
			.OnClicked(this, &FWeaponAssetEditor::OnClicked) // OnClicked 이벤트
			[
				SNew(STextBlock)
				.Text(FText::FromString("Test"))
			]
		];

	return tab.ToSharedRef();

	//return SNew(SDockTab)

}

FName FWeaponAssetEditor::GetToolkitFName() const
{
	// 외부에서 어떠한 이름으로 사용할지
	return EditorName;
}

FText FWeaponAssetEditor::GetBaseToolkitName() const
{
	// 외부에서 어떠한 이름으로 사용할지, 위와 동일한 문맥
	return FText::FromName(EditorName);
}

FString FWeaponAssetEditor::GetWorldCentricTabPrefix() const
{
	// Tab을 표현해주기 위해 식별자가 붙는다 그 이름
	return EditorName.ToString();
}

FLinearColor FWeaponAssetEditor::GetWorldCentricTabColorScale() const
{
	// 색상 파란색으로 설정
	return FLinearColor(0, 0, 1);
}

FReply FWeaponAssetEditor::OnClicked()
{
	// 클릭 이벤트 테스트
	GLog->Log("Test");

	return FReply::Handled(); // Handled 처리하고 끝내고, UnHandled는 패스 시킨다.
}

1. 클래스에서 만들 창을 싱글톤 형식으로 다룰것이다.

2. 창을 여러개 선택해서 열수 있으며, 공통적인 부분만 나타나게 할 수 있다. 하지만 프로그래밍에 있어 편리성을 높이기 위해 한창이 무조건 에셋 하나라고 간주하고 작업을 진행할 것이다.

3. 창을 만들어준다. 

4. 만든 창을 분활시켜주고, 알맞은 Layout를 설정할 수 있도록 구역을 나눠준다.

5. DockTab을 사용하여 분활해놓은 Layout에 미확인 탭이라는 부분을 없애준다.  왜냐면 창은 분활되어있지 공간을 정의해준건 아니기 때문이다.

 


더보기
#include "WeaponCommand.h"
#include "WeaponStyle.h"
#include "WeaponAssetEditor.h"
#include "LevelEditor.h"

void FWeaponCommand::OnClicked()
{

	//GLog->Log("Test");

	// WeaponAssetEditor에서 만든 창을 실행시켜준다.
	FWeaponAssetEditor::OpenWindow();
}

1. 만들어 놓은 창을 Command로 와서 실행시켜준다.

 

1. 나눠놓은 구역중 공간을 정의해준 곳만 "미확인 탭" 문구가 사라져 있다.

2. 버튼 또한 클릭이 가능하게 만들어졌다.

3. OnClick 이벤트로 인해 버튼 클릭시 Test라고 출력로그가 나오는걸 확인할 수 있다.

 


https://www.youtube.com/watch?v=4TZXOeFNCDM 

 

'언리얼' 카테고리의 다른 글

UE4 Weapon Plugin (4)  (0) 2023.06.01
UE4 Weapon Plugin (3)  (0) 2023.05.31
UE4 Weapon Plugin  (0) 2023.05.26
UE4 Fist, Camera Shake  (0) 2023.05.11
UE4 Hit Effect, Status Component, Hammer  (2) 2023.05.10
Comments