Plugins
  Example
    ButtonCommand.h .cpp
Example.Build.cs

ExampleConsoleCommand.h .cpp 
ExampleDebuggerCategory.h .cpp
ExampleModule.h .cpp
ExampleStyle.h.cpp 생성

StaticMesh_Detail.h .cpp
Source
    Utilities
    CHelper.h
CLog.h .cpp
    Global.h
CStaticMesh.h .cpp
.Build.cs
    .uproject
 

 

 

 

 

Tool Bar 아이콘 만들기

 

 


 

Plugin 아이콘 저장위치

 


 

 

Example.Build.cs

 

Example.Build.cs

더보기
using UnrealBuildTool;
public class Example : ModuleRules
{
public Example(ReadOnlyTargetRules Target) : base(Target) //public은 외부에 공개
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.Add(ModuleDirectory);
PublicDependencyModuleNames.Add("Core");
PrivateDependencyModuleNames.Add("U2212_05"); //본인 모델은 private
PrivateDependencyModuleNames.Add("CoreUObject");
PrivateDependencyModuleNames.Add("Engine");
PrivateDependencyModuleNames.Add("Slate");
PrivateDependencyModuleNames.Add("SlateCore");
PrivateDependencyModuleNames.Add("GameplayDebugger");
PrivateDependencyModuleNames.Add("DesktopPlatform");
PrivateDependencyModuleNames.Add("MainFrame");
PrivateDependencyModuleNames.Add("EditorStyle");//ExampleModule.cpp의 ToolBar(=ButtonCommand)를 위해 추가
PrivateDependencyModuleNames.Add("UnrealEd");
}
}

추가

  • PrivateDependencyModuleNames.Add("UnrealEd");

 


 

 

 

Static 변수를 외부에서 초기화해야 하는 이유

 

static은 클래스 내부가 아니라 클래스 공용에서 객체화된다.   

 

 


 

 

ExampleStyle 생성

 

싱글톤을 사용하였다. 싱글톤은 멀티 스레드 상황에서 문제가 될 수 있다는 단점이 있으므로 다른 대안의 디자인 패턴이 있으면 사용을 지양하자.

 

싱글톤을 사용할 때는 생성과 소멸 시점을 명확히 지정해서 꼭 규칙을 지켜서 사용하자.

  • 싱글톤을 사용할 때는 Get()으로 초기화 하는것보다 외부에서 Create()으로 생성시켜준 후 Get()을 사용하는게 좋다. 이렇게 외부에서 생성시켜주면 생성 시점이 특정이되어 좋다. 
  • 소멸도 같은 의미로 Delete()로 빼주는게 좋다.

 

 

ExampleStyle.h

더보기
#pragma once
#include "CoreMinimal.h"
class EXAMPLE_API FExampleStyle
{
public:
static TSharedPtr<FExampleStyle> Get();
static void Shutdown();
private:
static TSharedPtr<FExampleStyle> Instance;
public:
FExampleStyle();
~FExampleStyle();
private:
void RegisterIcon(const FString& InName, const FString& InPath, const FVector2D& InIconSize, FSlateIcon& OutSlateIcon);
private:
static const FName StyleSetName;//이름을 찾기 쉽게 static으로 만든 후 .cpp에서 이름을 지어준다.
TSharedPtr<class FSlateStyleSet> StyleSet;
public:
FSlateIcon ToolBar_LoadMesh_Icon;
};

 

 

 

ExampleStyle.cpp

더보기
#include "ExampleStyle.h"
#include "Styling/SlateStyle.h"
#include "Styling/SlateStyleRegistry.h" //등록객체 관리
//static 함수는 클래스 내부가 아닌 공용에서 객체화되기 때문에 여기서 초기값을 넣어준다.이름을 찾기 쉽게 헤더가 아닌 .cpp에 이름을 지어준다.
const FName FExampleStyle::StyleSetName = "ExampleStyle";
TSharedPtr<FExampleStyle> FExampleStyle::Instance = nullptr;
TSharedPtr<FExampleStyle> FExampleStyle::Get()
{
if (Instance == nullptr)
Instance = MakeShareable(new FExampleStyle());
return Instance;
}
void FExampleStyle::Shutdown()
{
if (Instance.IsValid())//Instance가 있다면
Instance.Reset();//Instance를 리셋해준다.
}
FExampleStyle::FExampleStyle()
{
StyleSet = MakeShareable(new FSlateStyleSet(StyleSetName));
FString path = FPaths::ProjectPluginsDir() / "Example" / "Resources";
StyleSet->SetContentRoot(path);
RegisterIcon("ToolBar_LoadMesh", path / "Icon128.png", FVector2D(52, 52), ToolBar_LoadMesh_Icon);
FSlateStyleRegistry::RegisterSlateStyle(*StyleSet.Get());//StyleSet을 등록시켜준다.
}
FExampleStyle::~FExampleStyle()
{
if (StyleSet.IsValid() == false)
return;
FSlateStyleRegistry::UnRegisterSlateStyle(StyleSetName);//StyleSet 등록을 해제해준다.
StyleSet.Reset(); //StyleSet을 지워준다.
}
void FExampleStyle::RegisterIcon(const FString& InName, const FString& InPath, const FVector2D& InIconSize, FSlateIcon& OutSlateIcon)
{
FSlateImageBrush* brush = new FSlateImageBrush(InPath, InIconSize);
FString name = StyleSetName.ToString() + "." + InName;
StyleSet->Set(FName(name), brush);
OutSlateIcon = FSlateIcon(FName(StyleSetName), FName(name));//StyleSetName에 name 이름으로 아이콘을 등록한다.
}

 

 


 

 

 

ButtonCommand

 

ButtonCommand.h

더보기
#pragma once
#include "CoreMinimal.h"
#include "Framework/Commands/Commands.h"
class EXAMPLE_API FButtonCommand
: public TCommands<FButtonCommand>
{
public:
FButtonCommand();
~FButtonCommand();
public:
void RegisterCommands() override;
public:
TSharedPtr<FUICommandList> Command;
public:
TSharedPtr<FUICommandInfo> LoadMesh;
private:
void OnClicked_LoadMesh();
};

변경사항 없음.

 

 

 

ButtonCommand.cpp

더보기
#include "ButtonCommand.h"
#include "Misc/MessageDialog.h"
#include "Misc/FileHelper.h"
#include "Serialization/BufferArchive.h" //행렬화 관련 헤더. Buffer를 활용하여 내보내는데 필요하다.
#include "DesktopPlatformModule.h"
#include "Interfaces/IMainFrameModule.h"
#include "StaticMesh_Detail.h"
#include "Editor.h"
#include "LevelEditorViewport.h"
FButtonCommand::FButtonCommand()
: TCommands("ToolBar_Buttons", FText(), NAME_None,FEditorStyle::GetStyleSetName())//oolBar_Buttons는 총괄 객체의 이름
{
Command = MakeShareable(new FUICommandList());
}
FButtonCommand::~FButtonCommand()
{
if (Command.IsValid())
Command.Reset();
}
void FButtonCommand::RegisterCommands()
{
#define LOCTEXT_NAMESPACE ""
UI_COMMAND(LoadMesh, "LoadMesh", "", EUserInterfaceActionType::Button, FInputChord());//q버튼, 체크박스, 라디오버튼 등 원는 방식으로 버튼을 만들어준다.
#undef LOCTEXT_NAMESPACE
Command->MapAction(LoadMesh, FExecuteAction::CreateRaw(this, &FButtonCommand::OnClicked_LoadMesh));//LoadMesh실행을 위해 OnClicked_LoadMesh를ExecuteAction::CreateRaw으로 연결해준다.
}
void FButtonCommand::OnClicked_LoadMesh()
{
IMainFrameModule& mainFrame = FModuleManager::LoadModuleChecked<IMainFrameModule>("MainFrame");
void* handle = mainFrame.GetParentWindow()->GetNativeWindow()->GetOSWindowHandle();//NativeWindow()가 실제 창 객체. GetOSWindowHandle()는 각 운영체제에 맞는 WindowHandle를 리턴해준다.//WindowHandle식별자는 dword x64면 가변형이다. 4byte로 처리가능하면 4byte, 불가능하면 8byte이다.
FString path;
FPaths::GetPath(path);
TArray<FString> fileNames;
IDesktopPlatform* platform = FDesktopPlatformModule::Get();
platform->OpenFileDialog(handle, "Open Mesh File", path, "", "Mesh Binary File(*.bin)|*.bin", EFileDialogFlags::None, fileNames);//title은 파일 이름, EFileDialogFlags는 공유속성(None으로 설정하였다)
if (fileNames.Num() < 1) return;
FBufferArchive buffer;
FFileHelper::LoadFileToArray(buffer, *fileNames[0]);//파일을 buffer에 읽어놓는다.
FMemoryReader reader = FMemoryReader(buffer, true);
reader.Seek(0);
//reader로 파일을 읽음
FStaticMesh_DetailData data;
reader << data;
reader.FlushCache();
reader.Close();
GLog->Logf(L"Vertex Count : %d", data.Positions.Num());
GLog->Logf(L"Triangle Count : %d", data.Indices.Num() / 3);
GLog->Logf(L"Extent : %s", *data.Extent.ToString());
GLog->Logf(L"Radius : %f", data.Radius);
FString text;
for (int32 i = 0; i < data.Positions.Num(); i++)
{
text.Append(data.Positions[i].ToString() + ", ");
text.Append(data.Normals[i].ToString() + ", ");
text.Append(data.Uvs[i].ToString() + ", ");
text.Append(data.Colors[i].ToString() + "\r\n");
}
FString textFileName = FPaths::GetBaseFilename(fileNames[0], false);
FString textSaveName = textFileName + "_Copied.csv";
FFileHelper::SaveStringToFile(text, *textSaveName);
//FLevelEditorViewportClient* client = dynamic_cast<FLevelEditorViewportClient*>(GEditor->GetActiveViewport()->GetClient()); //현재 활성화된 뷰포트
FLevelEditorViewportClient* client = (FLevelEditorViewportClient*)GEditor->GetActiveViewport()->GetClient(); //현재 활성화된 뷰포트
if (client == nullptr) return; //clinet가 없다면 리턴
FVector start = client->GetViewLocation();
FVector end = start + client->GetViewRotation().RotateVector(FVector(1000, 0, 0));
FHitResult hitResult;
UWorld* world = GEditor->GetEditorWorldContext().World();
world->LineTraceSingleByChannel(hitResult, start, end, ECollisionChannel::ECC_Visibility);//보이는것 다 수정.
if(hitResult.bBlockingHit == false)
{
FMessageDialog dialog;
dialog.Debugf(FText::FromString("Can't be placed in this location."));
return;
}
}

함수 정의

  • void FButtonCommand::OnClicked_LoadMesh()

 

 

 

ExampleModule

 

ExampleModule.h

더보기
#pragma once
#include "CoreMinimal.h"
class FExampleModule : public IModuleInterface
{
public:
virtual void StartupModule() override;
virtual void ShutdownModule() override;
private:
TSharedPtr<class FExampleConsoleCommand> ConsoleCommand;
private:
TSharedPtr<FExtender> Extender;//확장을 위한 Extender
private:
void AddToolBar(class FToolBarBuilder& InBuilder);//버튼을 추가하는 역할
};

변경사항 없음.

 

 

 

ExampleModule.cpp

더보기
#include "ExampleModule.h"
#include "ExampleStyle.h"
#include "ExampleDubuggerCategory.h"
#include "ExampleConsoleCommand.h"
#include "StaticMesh_Detail.h"
#include "ButtonCommand.h"
#include "U2212_05/CStaticMesh.h"
#include "GameplayDebugger.h"
#include "LevelEditor.h"
#define LOCTEXT_NAMESPACE "FExampleModule"
IMPLEMENT_MODULE(FExampleModule, Example)//모듈의 시작점을 알려준다.
void FExampleModule::StartupModule()
{
FExampleStyle::Get(); //싱글톤 객체 생성
//Debugger
{
IGameplayDebugger::FOnGetCategory category;
category.BindStatic(&FExampleDubuggerCategory::MakeInstance);
IGameplayDebugger::Get().RegisterCategory("Example", category, EGameplayDebuggerCategoryState::EnabledInGameAndSimulate, 5); //5번 슬롯 할당
IGameplayDebugger::Get().NotifyCategoriesChanged();
}
//Console Command
{
ConsoleCommand = MakeShareable(new FExampleConsoleCommand());
}
//Property Editor - StaticMeshActor
{
FOnGetDetailCustomizationInstance instance; //싱글톤
instance.BindStatic(&FStaticMesh_Detail::MakeInstance);//Bind로 묶어준다.//StaticMeshActor_Detail.h에서 static으로 만든 MakeInstance 사용.
FPropertyEditorModule& prop = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
prop.RegisterCustomClassLayout(ACStaticMesh::StaticClass()->GetFName(), instance);
}
//ToolBar(=ButtonCommand)
{
FButtonCommand::Register();
Extender = MakeShareable(new FExtender());//Extender 생성
FToolBarExtensionDelegate toolBar;
toolBar.BindRaw(this, &FExampleModule::AddToolBar);//toolBar와 AddToolBar를 연결
Extender->AddToolBarExtension("Compile", EExtensionHook::Before, FButtonCommand::Get().Command, toolBar);
FLevelEditorModule& levelEditor = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");//LevelEditorModule을 불러온다.
levelEditor.GetToolBarExtensibilityManager()->AddExtender(Extender);//levelEditor에 Extender를 추가
}
}
void FExampleModule::ShutdownModule()
{
if (IGameplayDebugger::IsAvailable())
IGameplayDebugger::Get().UnregisterCategory("Example");
if (ConsoleCommand.IsValid())
ConsoleCommand.Reset(); //스마트포인터를 제거할 수 있도록 Reset()을 콜 해준다. 자동화가 문제가 되는경우가 있으므로 넣어준다.
FExampleStyle::Shutdown(); //싱글톤 객체 제거
}
void FExampleModule::AddToolBar(FToolBarBuilder& InBuilder)
{
FString name = TEXT("메시");
InBuilder.AddSeparator(); //언리얼 에디터 툴바의 구분선(|)
InBuilder.AddToolBarButton
(
FButtonCommand::Get().LoadMesh,
"LoadMesh", //InExtensionHook
FText::FromString(name), //InLabelOverride
FText::FromString("Load Mesh Data"), //InToolTipOverride
FExampleStyle::Get()->ToolBar_LoadMesh_Icon //아이콘
);
}
#undef LOCTEXT_NAMESPACE

헤더 추가

  • #include "ExampleStyle.h"

 

void FExampleModule::StartupModule()

  • 싱글톤 객체 생성
    • FExampleStyle::Get();

 

void FExampleModule::ShutdownModule()

  • 싱글톤 객체 제거
    • FExampleStyle::Shutdown();

 


 

 

 

실행화면