목차

     

     


     

     

     

     
    Plugins
     
      Weapon
     
        Resource
     
          Icon128.png
    weapon_thumbnail_icon.png
     
        Source
     
          Weapon  
          SWeaponDetailsView.h .cpp 생성
    SWeaponEquipmentData.h .cpp 생성

    SWeaponLeftArea.h .cpp
    Weapon.Build.cs
    WeaponAssetEditor.h .cpp
    WeaponAssetFactory.h .cpp
    WeaponCommand.h .cpp 
    WeaponContextMenu.h .cpp
    WeaponModule.h .cpp
    WeaponStyle.h .cpp 
     
       

     

     

     

     

    Weapon Plugin 5

     

     


     

     

    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);
    
    protected:
    	bool OnRequestClose() override;//창이 닫힐 때 콜이 되게 설계.
    
    public:
    	void RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager) override;
    
    private:
    	TSharedRef<SDockTab> Spawn_LeftAreaTab(const FSpawnTabArgs& InArgs);
    	TSharedRef<SDockTab> Spawn_DetailsViewTab(const FSpawnTabArgs& InArgs);
    
    private:
    	void OnListViewSelectedItem(FWeaponRowDataPtr InDataPtr);//LeftArea에서 선택한 FWeaponRowDataPtr
    
    public:
    	FName GetToolkitFName() const override;
    	FText GetBaseToolkitName() const override;
    	FString GetWorldCentricTabPrefix() const override;
    	FLinearColor GetWorldCentricTabColorScale() const override;
    
    private:
    	TSharedPtr<class SWeaponLeftArea> LeftArea;
    	TSharedPtr<class IDetailsView> DetailsView;//IDetailsView는 DetailView의 최상위 자료형.
    
    private:
    	static const FName EditorName;
    	static const FName LeftAreaTabId;
    	static const FName DetailTabId;
    
    private:
    	FReply OnClicked();
    };

    변동사항 없음.

     

     

     

    WeaponAssetEditor.cpp

    더보기
    #include "WeaponAssetEditor.h"
    #include "SWeaponLeftArea.h"
    #include "SWeaponDetailsView.h"
    #include "SWeaponEquipmentData.h"
    #include "Weapons/CWeaponAsset.h"
    
    //설정한 이름들
    const FName FWeaponAssetEditor::EditorName = "WeaponAssetEditor";
    const FName FWeaponAssetEditor::LeftAreaTabId = "LeftArea";
    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)
    {
    	LeftArea = SNew(SWeaponLeftArea)//SWeaponLeftArea에서 받은 자료형을 생성하여 넣어준다.
    		.OnSelectedItem(this, &FWeaponAssetEditor::OnListViewSelectedItem);//LeftArea에서 선택한 데이터
    
    	FPropertyEditorModule& prop = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
    
    	//DetailsView
    	{
    		FDetailsViewArgs args(false, false, true, FDetailsViewArgs::HideNameArea);//기본값 설정. ActorsUserNameArea, ObjectsUserNameArea, HideNameArea
    		args.ViewIdentifier = "WeaponAssetEditorDetailsView";//식별자 설정. 게임 Editor쪽에서 DetailView 접근시 이 식별자로 찾을 수 있다.
    		DetailsView = prop.CreateDetailView(args);//Detail 창 띄우기.
    
    		FOnGetDetailCustomizationInstance detailView;
    		detailView.BindStatic(&SWeaponDetailsView::MakeInstance);//Static은 객체가 필요없다. 그래서 함수 주소로 바로 연결한다.
    		DetailsView->SetGenericLayoutDetailsDelegate(detailView);//Delegate를 연결해준다.
    	}
    
    	//EquipmentData
    	{
    		FOnGetPropertyTypeCustomizationInstance instance;
    		instance.BindStatic(&SWeaponEquipmentData::MakeInstance);
    		prop.RegisterCustomPropertyTypeLayout("EquipmentData", instance);//instance를 delegate 등록
    	}
    
    
    	//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(LeftAreaTabId, ETabState::OpenedTab)//ListViewTabId
    					->SetHideTabWell(true)
    				)
    				->Split
    				(
    					FTabManager::NewStack()
    					->SetSizeCoefficient(0.725f)//오른쪽 72.5% 사용
    					->AddTab(DetailTabId, ETabState::OpenedTab)//DetailTabId
    					->SetHideTabWell(true)
    				)
    			)
    		);
    
    	UCWeaponAsset* asset = nullptr; 
    	asset = LeftArea->GetFirstDataPtr()->Asset;//LeftArea의 맨 위 첫번째 데이터 선택
    
    	FAssetEditorToolkit::InitAssetEditor(EToolkitMode::Standalone, TSharedPtr<IToolkitHost>(), EditorName, layout, true, true, asset);
    
    	//DetailsView->SetObject(asset);//어느 DetailView든 asset객체가 세팅된다. 창은 여러개지만 실제 관리는 내부적으로 하나로 관리한다. 그래서 창이 종료될 때 DetailsView가 해제 안 되어 있으면 터진다.
    	LeftArea->SelectDataPtr(asset);
    }
    
    bool FWeaponAssetEditor::OnRequestClose()
    {
    	if (!!DetailsView)
    	{
    		//AssetEditorSubsystem안에(=DetailView 안에)
    		//GetEditingObject()가 등록되어 있었다면 해제하고 Editor에 알린다.
    		if (!!GEditor && !!GEditor->GetEditorSubsystem<UAssetEditorSubsystem>())
    			GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->NotifyAssetClosed(GetEditingObject(), this);
    
    		//해당 모듈이 읽힌적이 있다면 해제.
    		if (FModuleManager::Get().IsModuleLoaded("PropertyEditor"))
    		{
    			FPropertyEditorModule& prop = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");//해당모듈을 가져온다.
    			prop.UnregisterCustomClassLayout("EquipmentData");//등록 해제
    		}
    	}
    
    	if (LeftArea.IsValid())
    		LeftArea.Reset();
    
    	if (DetailsView.IsValid())
    		DetailsView.Reset();
    
    	return true;
    	//false 리턴인 경우 창이 닫힐 수 없다는 것을 의미한다.
    }
    
    void FWeaponAssetEditor::RegisterTabSpawners(const TSharedRef<FTabManager>& InTabManager)
    {
    	FAssetEditorToolkit::RegisterTabSpawners(InTabManager);
    
    	FOnSpawnTab tab;
    	tab.BindSP(this, &FWeaponAssetEditor::Spawn_LeftAreaTab);
    	TabManager->RegisterTabSpawner(LeftAreaTabId, tab);
    
    	FOnSpawnTab tab2;
    	tab2.BindSP(this, &FWeaponAssetEditor::Spawn_DetailsViewTab);
    	TabManager->RegisterTabSpawner(DetailTabId, tab2);
    }
    
    TSharedRef<SDockTab> FWeaponAssetEditor::Spawn_LeftAreaTab(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)
    	[
    		LeftArea.ToSharedRef()
    	];
    }
    
    TSharedRef<SDockTab> FWeaponAssetEditor::Spawn_DetailsViewTab(const FSpawnTabArgs& InArgs)
    {
    	return SNew(SDockTab)
    	[
    		DetailsView.ToSharedRef()
    	];
    }
    
    void FWeaponAssetEditor::OnListViewSelectedItem(FWeaponRowDataPtr InDataPtr)
    {
    	if (InDataPtr == nullptr)//LeftArea에서 선택한게 없다면(또는 빈 공간을 선택했다면)
    		return;
    
    	if (!!GetEditingObject())//편집하는 객체가 있다면
    		RemoveEditingObject(GetEditingObject());//현재 창에서 편집중인 애들 제거한다.
    
    	AddEditingObject(InDataPtr->Asset);//현재 창에 편집해줄 객체를 등록해준다.
    	DetailsView->SetObject(InDataPtr->Asset);//창 안의 DetailsView도 변경해준다.
    }
    
    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()는 처리하고 끝낸다.
    }

    void FWeaponAssetEditor::Open(FString InAssetName) 내의 수정사항

    •  

     

     

     

     

     

    ※ IDetailCustomization을 사용하는 DELEGATE

     

    PropertyEditorDelegates.h 에 정의된 DECLARE_DELEGATE_RetVal

    WeaponAssetEditor.cpp에 사용한 부분

     


     

     

    SWeaponDetailsView 생성

     

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

     

     

    SWeaponDetailsView.h

    더보기
    #pragma once
    
    #include "CoreMinimal.h"
    #include "IDetailCustomization.h"
    
    class WEAPON_API SWeaponDetailsView
    	: public IDetailCustomization
    {
    public:
    	static TSharedRef<IDetailCustomization> MakeInstance();
    	void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
    };

    IDetailCustomization로 부터 상속받는다.

     

     

     

    SWeaponDetailsView.cpp

    더보기
    #include "SWeaponDetailsView.h"
    #include "DetailLayoutBuilder.h"
    #include "DetailCategoryBuilder.h"
    #include "IDetailPropertyRow.h"
    #include "Weapons/CWeaponAsset.h"
    
    TSharedRef<IDetailCustomization> SWeaponDetailsView::MakeInstance()
    {
    	//자신의 클래스 타입을 만들어서 return해준다.
    	return MakeShareable(new SWeaponDetailsView());
    }
    
    void SWeaponDetailsView::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
    {
    	UClass* type = UCWeaponAsset::StaticClass();//UClass 타입 하나를 받아온다.
    
    	DetailBuilder.HideCategory("CWeaponAsset");//CWeaponAsset 카테고리를 숨겨준다. 현재 작업의 편의를 위해 숨겨주고 추후에 보여주게 만들 예정이다. 
    
    	//Class Settings
    	{
    		//.EditCategory 해당 타입에 해당 카테고리가 있으면 그 카테고리를 return, 없으면 새로 만들어서 return
    		//ClassSettings는 없으므로 새로 만들어서 return
    		IDetailCategoryBuilder& category = DetailBuilder.EditCategory("ClassSettings", FText::FromString("Class Settings"));
    		//CWeaponAsset에서 직렬화된 변수들을 카테고리에 추가한다.
    		category.AddProperty("AttachmentClass", type);//CWeaponAsset의 AttachmentClass를 카테고리에 추가.
    		category.AddProperty("EquipmentClass", type);//CWeaponAsset의 EquipmentClass를 카테고리에 추가.
    		category.AddProperty("DoActionClass", type);//CWeaponAsset의 DoActionClass를 카테고리에 추가.
    	}
    
    	//EquipmentData
    	{
    		//.EditCategory 해당 타입에 해당 카테고리가 있으면 그 카테고리를 return, 없으면 새로 만들어서 return
    		IDetailCategoryBuilder& category = DetailBuilder.EditCategory("EquipmentData", FText::FromString("Equipment Data"));
    		IDetailPropertyRow& row = category.AddProperty("EquipmentData", type);
    	}
    }

     

     


     

    SWeaponEquipmentData 생성

     

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

     

     

    SWeaponEquipmentData.h

    더보기
    #pragma once
    
    #include "CoreMinimal.h"
    #include "IPropertyTypeCustomization.h"
    
    class WEAPON_API SWeaponEquipmentData
    	: public IPropertyTypeCustomization
    {
    public:
    	static TSharedRef<IPropertyTypeCustomization> MakeInstance();
    	void CustomizeHeader(TSharedRef<IPropertyHandle> InPropertyHandle, FDetailWidgetRow& InHeaderRow, IPropertyTypeCustomizationUtils& InCustomizationUtils) override;
    
    	void CustomizeChildren(TSharedRef<IPropertyHandle> InPropertyHandle, IDetailChildrenBuilder& InChildBuilder, IPropertyTypeCustomizationUtils& InCustomizationUtils) override;
    };

    IPropertyTypeCustomization로 부터 상속받는다.

     

     

     

    SWeaponEquipmentData.cpp 

    더보기
    #include "SWeaponEquipmentData.h"
    #include "IPropertyUtilities.h"
    #include "IDetailPropertyRow.h"
    #include "IDetailChildrenBuilder.h"
    #include "DetailWidgetRow.h"
    
    TSharedRef<IPropertyTypeCustomization> SWeaponEquipmentData::MakeInstance()
    {
    	//자신의 클래스 타입을 만들어서 return해준다.
    	return MakeShareable(new SWeaponEquipmentData);
    }
    
    void SWeaponEquipmentData::CustomizeHeader(TSharedRef<IPropertyHandle> InPropertyHandle, FDetailWidgetRow& InHeaderRow, IPropertyTypeCustomizationUtils& InCustomizationUtils)
    {
    	InHeaderRow
    	.NameContent()
    	[
    		InPropertyHandle->CreatePropertyNameWidget()
    	]
    	.ValueContent()
    	.MinDesiredWidth(FEditorStyle::GetFloat("StandardDialog.MinDesiredSlotWidth"))
    	.MaxDesiredWidth(FEditorStyle::GetFloat("StandardDialog.MaxDesiredSlotWidth"))
    	[
    		SNew(STextBlock)
    		.Text(FText::FromString("Value"))
    	];
    }
    
    void SWeaponEquipmentData::CustomizeChildren(TSharedRef<IPropertyHandle> InPropertyHandle, IDetailChildrenBuilder& InChildBuilder, IPropertyTypeCustomizationUtils& InCustomizationUtils)
    {
    	uint32 number = 0;
    	InPropertyHandle->GetNumChildren(number);
    
    	for(uint32 i = 0; i < number; i++)
    	{
    		TSharedPtr<IPropertyHandle> handle = InPropertyHandle->GetChildHandle(i);
    		IDetailPropertyRow& row = InChildBuilder.AddProperty(handle.ToSharedRef());//handle를 가지고 기본모양을 추가하여 만들어준다. 
    		
    		row.CustomWidget()
    		.NameContent()
    		[
    			handle->CreatePropertyNameWidget()
    		]
    		//줄이거나 늘렸을 때 Min 이하로는 고정. Max 이상으로는 고정.
    		.ValueContent()
    		.MinDesiredWidth(FEditorStyle::GetFloat("StandardDialog.MinDesiredSlotWidth"))
    		.MaxDesiredWidth(FEditorStyle::GetFloat("StandardDialog.MaxDesiredSlotWidth"))
    		[
    			handle->CreatePropertyValueWidget()
    		];
    	}
    }

     

     

     

    ※ 참고 - Header, Children 위치

     


     

     

    실행화면