목차

     

     


     

     

     
    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();

     


     

     

     

    실행화면