어제 만든 기본세팅을 사용하여 Plugin 버튼을 만들어보자. 이번에 만들 Plugin 버튼은 Unreal Editor 상단에 Ribbon Button 형태로 만들어질 것이다. 오늘의 목표는 버튼을 만들어 창을 띄우기다.

 

 

 


 

 
Plugins
 
  Weapon
 
    Resource
 
      Icon128.png
weapon_thumbnail_icon.png
 
    Source
 
      Weapon  
      Weapon.Build.cs
WeaponAssetEditor.h .cpp 생성
WeaponAssetFactory.h .cpp
WeaponCommand.h .cpp 생성
WeaponContextMenu.h .cpp
WeaponModule.h .cpp
WeaponStyle.h .cpp 
 
   

 

 

 
Source
    Characters
    CAnimInstance.h .cpp
CEnemy.h .cpp 
CPlayer.h .cpp
ICharacter.h .cpp
    Components
    CMontagesComponent.h .cpp 
CMovementComponent.h .cpp 
CStateComponent.h .cpp 
CWeaponComponent.h .cpp 
    Notifies
    CAnimNotifyState_BeginAction.h .cpp 
CAnimNotify_CameraShake.h .cpp
CAnimNotifyState_EndAction.h .cpp
CAnimNotify_EndState.h .cpp
CAnimNotifyState_Collision.h .cpp 
CAnimNotifyState_Combo.h .cpp
CAnimNotifyState_Equip.h .cpp
    Utilities
    CHelper.h
CLog.h .cpp
    Weapons
    CDoAction_Combo.h .cpp
CAttachment.h .cpp
CDoAction.h .cpp
CEquipment.h .cpp
CWeaponAsset.h .cpp
CWeaponStructures.h .cpp
    Global.h
CGameMode.h .cpp
.Build.cs
    .uproject
 

 

 

 

Weapon Plugin 만들기

 

 


 

Slate UI 의 디자인 패턴 - 재귀적 호출

 

new A()  →  Add("  ")  →   Add("  ")

  • Slate UI의 디자인 패턴을 만들 때 자기 자료형으로 리턴하는 재귀적 호출을 사용한다.

 


 

Slate의 동적할당?

 

SNew(SButton)
.Content()
[
SNew(STextBlock)
.Text( ) //여기위치
]

 

SLATE_ARGUMENT

SLATE_EVENT

SLATE_ATTRIBUTE

  • TAttibute<FText>
  • 외부에서 값이 바뀌면 적용된다.

 

 


 

 

Weapon.Build.cs

 

Weapon.Build.cs

더보기
using UnrealBuildTool;
public class Weapon : ModuleRules
{
public Weapon(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PrivateIncludePaths.Add(ModuleDirectory);
PublicDependencyModuleNames.Add("Core");
PrivateDependencyModuleNames.Add("U2212_06");//프로젝트명
PrivateDependencyModuleNames.Add("CoreUObject");
PrivateDependencyModuleNames.Add("Engine");
PrivateDependencyModuleNames.Add("Slate");
PrivateDependencyModuleNames.Add("SlateCore");
PrivateDependencyModuleNames.Add("UnrealEd");//UFactory
PrivateDependencyModuleNames.Add("EditorStyle");//
}
}

 

 PrivateDependencyModuleNames.Add("EditorStyle"); 추가

 


 

 

 

WeaponCommand 생성

 

새 C++ 클래스 - 없음 -  WeaponCommand 생성

 

WeaponCommand .h

더보기
#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();//버튼이 눌렸을때
};

 

 

WeaponCommand .cpp

더보기
#include "WeaponCommand.h"
#include "WeaponStyle.h"
#include "WeaponAssetEditor.h"
#include "LevelEditor.h"
FWeaponCommand::FWeaponCommand()
: 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);
Extender->AddToolBarExtension("Settings", EExtensionHook::After, Command, toolbar);
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()
{
FWeaponAssetEditor::OpenWindow();
}

 

 

※ 툴바에 한글이 포함되는 경우

한글이 포함되는 파일을 다른 이름으로 저장 - 고급 저장 옵션: 유니코드(서명 있는 UTF-8) - 코드 페이지65001 적용

 


 

 

WeaponAssetEditor 생성

 

새 C++ 클래스 - 없음 - WeaponAssetEditor 생성

 

WeaponAssetEditor.h

더보기
#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();
};

부모로 설정한 : public FAssetEditorToolkit는 추상 클래스다.

  • FAssetEditorToolkit에서 선언한 아래의 함수들을 WeaponAssetEditor에서 재정의한다.
  • FName GetToolkitFName() const override;
  • FText GetBaseToolkitName() const override;
  • FString GetWorldCentricTabPrefix() const override;
  • FLinearColor GetWorldCentricTabColorScale() const override;
  • void RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager) 

 

 

WeaponAssetEditor.cpp

더보기
#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;//헤더에서 선언한 static 외부 초기화.
void FWeaponAssetEditor::OpenWindow(FString InAssetName)
{
//아래의 코드 대신에 Shutdown()을 사용하면 안 된다! Shutdown()은 다른 곳에서도 콜이 되기 때문에 아래의 코드 대신에 사용하면 문제가 된다.
if (Instance.IsValid()) //창이 만들어졌다면
{
Instance->CloseWindow();//창을 닫는다.
Instance.Reset();
Instance = nullptr;
}
Instance = MakeShareable(new FWeaponAssetEditor());
Instance->Open(InAssetName);
}
void FWeaponAssetEditor::Shutdown()
{
if (Instance.IsValid())
{
Instance->CloseWindow();
Instance.Reset();
Instance = nullptr;
}
}
void FWeaponAssetEditor::Open(FString InAssetName)
{
//Layout 설정
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)
)
->Split
(
FTabManager::NewSplitter()->SetOrientation(Orient_Horizontal)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(0.175f)//왼쪽 17.5% 사용
->AddTab(ListViewTabId, ETabState::OpenedTab)//ListViewTabId
->SetHideTabWell(true)
)
->Split
(
FTabManager::NewStack()
->SetSizeCoefficient(0.725f)//오른쪽 72.5% 사용
->AddTab(DetailTabId, ETabState::OpenedTab)//DetailTabId
->SetHideTabWell(true)
)
)
);
UCWeaponAsset* asset = NewObject<UCWeaponAsset>();//현재 없는 상태이므로 일단 빈 상태로 만들어준다.
FAssetEditorToolkit::InitAssetEditor(EToolkitMode::Standalone, TSharedPtr<IToolkitHost>(), EditorName, layout, true, true, asset);
}
void FWeaponAssetEditor::RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager)
{
FAssetEditorToolkit::RegisterTabSpawners(InTabManager);
FOnSpawnTab tab;
tab.BindSP(this, &FWeaponAssetEditor::Spawn_ListViewTab);
TabManager->RegisterTabSpawner(ListViewTabId, tab);
}
TSharedRef<SDockTab> FWeaponAssetEditor::Spawn_ListViewTab(const FSpawnTabArgs& InArgs)
{
TSharedPtr<SDockTab> tab = SNew(SDockTab)
[
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
{
return EditorName.ToString();////외부에서 어떤 식별자를 쓸 지 정해준다.
}
FLinearColor FWeaponAssetEditor::GetWorldCentricTabColorScale() const
{
return FLinearColor(0, 0, 1);//파란색으로 설정.
}
FReply FWeaponAssetEditor::OnClicked()
{
GLog->Log("Test");
return FReply::Handled();//Handled()는 처리하고 끝낸다.
}

 

 

※ 참고

Editor에서는 Super:: 를 사용하면 안 된다.

 

 

※ 참고 - Handled(), Unhandled()

  • Handled()를 사용하면 처리하고 끝낸다.
  • Unhandled()를 사용하면 처리하지 않으면 끝낸다.

https://docs.unrealengine.com/4.26/en-US/BlueprintAPI/Widget/EventReply/Unhandled/

 

Unhandled

Unhandled

docs.unrealengine.com


 

 

WeaponModule

 

WeaponModule.h

더보기
#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;//언리얼 내 우클릭 대분류목록
};

언리얼 내 우클릭 대분류목록

  • TSharedPtr<class FWeaponCommand> Command;

 

 

WeaponModule.cpp

더보기
#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()
{
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();
FWeaponStyle::Shutdown();
}
#undef LOCTEXT_NAMESPACE

 

 


 

 

실행화면