초보 코린이의 성장 일지

UE4 Weapon Plugin (5) 본문

언리얼

UE4 Weapon Plugin (5)

코오린이 2023. 6. 7. 13:45

왼쪽 카테고리창은 마무리가 되었으며, 오른쪽 창을 이제 커스텀 해보도록 하겠다.


1. 오른쪽 창을 커스텀하려면 Class를 하나 생성해줘야 한다. 

더보기
#pragma once

#include "CoreMinimal.h"
#include "IDetailCustomization.h"

class WEAPON_API SWeaponDetailsView
	: public IDetailCustomization
{
public:
	static TSharedRef<IDetailCustomization> MakeInstance();
	void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;

};
#include "SWeaponDetailsView.h"

TSharedRef<IDetailCustomization> SWeaponDetailsView::MakeInstance()
{
	// 자신의 타입 return
	return MakeShareable(new SWeaponDetailsView()); 
}

void SWeaponDetailsView::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
	GLog->Log("CustomizeDetails");
}

1. 카테고리를 선택해서 정보창이 보여질때 출력 로그가 나오게 될 것이다.


더보기
#include "WeaponAssetEditor.h"
#include "SWeaponLeftArea.h"
#include "SWeaponDetailsView.h"
#include "SWeaponEquipmentData.h"
#include "Weapons/CWeaponAsset.h"

void FWeaponAssetEditor::Open(FString InAssetName)
{
	// DetailsView
	{
		// 디테일 View 바꿔주기.
		FDetailsViewArgs args(false, false, true, FDetailsViewArgs::HideNameArea);
		args.ViewIdentifier = "WeaponAssetEditorDetailsView"; // 창이 많아지면 햇갈리므로, 식별자 이름을 설정
		DetailsView = prop.CreateDetailView(args);

		FOnGetDetailCustomizationInstance detailView;
		detailView.BindStatic(&SWeaponDetailsView::MakeInstance);
		DetailsView->SetGenericLayoutDetailsDelegate(detailView); // 델리게이트 연결
	}

}

1. 디테일창을 뛰울때 사용했던 DatailsView를 자세히 들여다보면 안에 이벤트가 하나 존재한다. Layout를 커스텀 해주는 이벤트 함수를 사용할 것이다.

2. 원래 만든 View 창을  델리게이트 이벤트를 연결해줘서 변경해주기.

1. 카테고리를 클릭해서 창이 변경될때마다 출력로그에 로그가 남는걸 알 수 있다.

더보기
#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)
{
	//GLog->Log("CustomizeDetails");
	UClass* type = UCWeaponAsset::StaticClass(); // 타입 받아오기.

	// Class Settings
	{
		// 언리얼에 보여질 식별자 이름, 실제로 보여질 카테고리 이름
		IDetailCategoryBuilder& category = DetailBuilder.EditCategory("ClassSettings", FText::FromString("Class Settings"));
		category.AddProperty("AttachmentClass", type);
		category.AddProperty("EquipmentClass", type);
		category.AddProperty("DoActionClass", type);

	}
}

1. 이제 Class Type와 다른 상세 카테고리들을 구역을 나눠서 각자 알맞은 카테고리에 몰아 넣어서 관리하며 사용할 것이다.

1. 실행해보면 원래 아래에 있었던 각자의 정보란이 생성해서 구역을 나눈  카테고리 안으로 들어와져 있는걸 확인할 수 있다.

더보기
#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)
{
	//GLog->Log("CustomizeDetails");
	UClass* type = UCWeaponAsset::StaticClass(); // 타입 받아오기.

	// Class Settings
	{
		// 언리얼에 보여질 식별자 이름, 실제로 보여질 카테고리 이름
		IDetailCategoryBuilder& category = DetailBuilder.EditCategory("ClassSettings", FText::FromString("Class Settings"));
		category.AddProperty("AttachmentClass", type);
		category.AddProperty("EquipmentClass", type);
		category.AddProperty("DoActionClass", type);

	}

	// EquipmentData
	{
		IDetailCategoryBuilder& category = DetailBuilder.EditCategory("EquipmentData", FText::FromString("Equipment Data"));
		category.AddProperty("EquipmentData", type);
	}

}

1. Equipment Data도 카테고리를 등록, 영역을 나눠서 분활해 놓을 것이다.

1. 영역안으로 잘 들어와져 있다.

 

더보기
#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)
{
	//GLog->Log("CustomizeDetails");
	UClass* type = UCWeaponAsset::StaticClass(); // 타입 받아오기.

	DetailBuilder.HideCategory("CWeaponAsset"); // 카테고리 숨기기

	// Class Settings
	{
		// 언리얼에 보여질 식별자 이름, 실제로 보여질 카테고리 이름
		IDetailCategoryBuilder& category = DetailBuilder.EditCategory("ClassSettings", FText::FromString("Class Settings"));
		category.AddProperty("AttachmentClass", type);
		category.AddProperty("EquipmentClass", type);
		category.AddProperty("DoActionClass", type);

	}

	// EquipmentData
	{
		IDetailCategoryBuilder& category = DetailBuilder.EditCategory("EquipmentData", FText::FromString("Equipment Data"));
		category.AddProperty("EquipmentData", type);

	}

}

1. CWeapon Asset이 중간에 껴있는걸 작업하는 동안에 방해가 되므로, HideCategory를 사용하여 임시로 숨겨 놓을 것이다.

1. CWeapon Asset이 사라진걸 확인할 수 있다.

 

더보기
#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)
{
	//GLog->Log("CustomizeDetails");
	UClass* type = UCWeaponAsset::StaticClass(); // 타입 받아오기.

	DetailBuilder.HideCategory("CWeaponAsset"); // 카테고리 숨기기

	// Class Settings
	{
		// 언리얼에 보여질 식별자 이름, 실제로 보여질 카테고리 이름
		IDetailCategoryBuilder& category = DetailBuilder.EditCategory("ClassSettings", FText::FromString("Class Settings"));
		category.AddProperty("AttachmentClass", type);
		category.AddProperty("EquipmentClass", type);
		category.AddProperty("DoActionClass", type);

	}

	// EquipmentData
	{
		IDetailCategoryBuilder& category = DetailBuilder.EditCategory("EquipmentData", FText::FromString("Equipment Data"));
		IDetailPropertyRow& row = category.AddProperty("EquipmentData", type);

	}

}

1.category.AddProperty("EquipmentData", type)를 -> IDetailPropertyRow& row = category.AddProperty("EquipmentData", type) 로 변경해준다.

2.  그 이유는 바로 아래 그림과 설명을 보면 알게될 것이다.

3. EquipmentData를 커스텀 마이징하고, Type을 변경할 것이기 때문.


1. 나눠놓은 영역안에 정보를 확인하기 위해 펼쳤을때, 더 간편하게 관리가 용이하도록 체크박스를 만들어서 기본값에서 바뀌지 않는 정보들은 해제해서 보이지 않도록 만들어 줄 것이다.

2.기본값이 바뀌는 정보들은 체크해서 그 부분만 수정할 수 있도록 만들것이다. 또 다음에 열였을때 기본값이 바뀐 정보들은 바뀌었다고 체크가 되면서 보여지게 만들것이다.


1. EquipmentData를 커스텀 하기 위해 클래스 생성해준다.

더보기
#pragma once

#include "CoreMinimal.h"
#include "IPropertyTypeCustomization.h"

class WEAPON_API SWeaponEquipmentData
	: public IPropertyTypeCustomization
{
public:
	static TSharedRef<IPropertyTypeCustomization> MakeInstance();
	void CustomizeHeader(TSharedRef<IPropertyHandle> PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override;
	void CustomizeChildren(TSharedRef<IPropertyHandle> PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override;

};
#include "SweaponEquipmentData.h"
#include "IPropertyUtilities.h"
#include "IDetailPropertyRow.h"
#include "IDetailChildrenBuilder.h"
#include "DetailWidgetRow.h"
#include "IDetailCustomization.h"

TSharedRef<IPropertyTypeCustomization> SWeaponEquipmentData::MakeInstance()
{
    return MakeShareable(new SWeaponEquipmentData());
}

void SWeaponEquipmentData::CustomizeHeader(TSharedRef<IPropertyHandle> InPropertyHandle, FDetailWidgetRow& InHeaderRow, IPropertyTypeCustomizationUtils& InCustomizationUtils)
{
    GLog->Log("CustomizeHeader");
}

void SWeaponEquipmentData::CustomizeChildren(TSharedRef<IPropertyHandle> InPropertyHandle, IDetailChildrenBuilder& InChildBuilder, IPropertyTypeCustomizationUtils& InCustomizationUtils)
{
    GLog->Log("CustomizeChildren");
}

1. 커스텀에 필요한 상속받은 가상함수를 사용.

2. 출력 로그로 확인부터 해 볼 것이다.


더보기
#include "WeaponAssetEditor.h"
#include "SWeaponLeftArea.h"
#include "SWeaponDetailsView.h"
#include "SWeaponEquipmentData.h"
#include "Weapons/CWeaponAsset.h"

void FWeaponAssetEditor::Open(FString InAssetName)
{
	// EquipmentData
	{
		FOnGetPropertyTypeCustomizationInstance instance;
		instance.BindStatic(&SWeaponEquipmentData::MakeInstance); // Type를 만들어서 return 해주기 때문에, BindStatic 사용
		// Editor에 등록 해준다. 변수화된 Type을 다룰것이기 때문에 RegisterCustomPropertyTypeLayout 사용,
		prop.RegisterCustomPropertyTypeLayout("EquipmentData", instance);  // 편집할 변수
        
     }
}

bool FWeaponAssetEditor::OnRequestClose()
{
	if (!!DetailsView)
	{
   	    // 해당 모듈이 읽힌적(불러진적)이 있는지 체크
		if (FModuleManager::Get().IsModuleLoaded("PropertyEditor"))
		{
        // 등록해제를 안해주면, 다른곳에 등록된 곳에서도 유지되어 버린다. 그래서 창이 열려져서 닫힐 동안만큼은 내가 만든걸 화면에 띄어줄 것이다.
			FPropertyEditorModule& prop = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
			prop.UnregisterCustomPropertyTypeLayout("EquipmentData");

		}
	}
	return true;
}

1. FWeaponAssetEditor에 등록해준다.

2. 불러진적이 있다면 사용했으니 해제해준다.

1. 카테고리를 선택하면, SWeaponEquipmentData에 설정해 놓은 Log가 출력이 되는걸 확인할 수 있다.

2. 실제로 보이진 않지만 커스텀 마이징했기 때문에 열릴때마다 EquipmentData 창이 가려져 있는 것이다.

3. 다시 보이도록 만들어 줄 것이다.


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

};
#include "SweaponEquipmentData.h"
#include "IPropertyUtilities.h"
#include "IDetailPropertyRow.h"
#include "IDetailChildrenBuilder.h"
#include "DetailWidgetRow.h"
#include "IDetailCustomization.h"

TSharedRef<IPropertyTypeCustomization> SWeaponEquipmentData::MakeInstance()
{
    return MakeShareable(new SWeaponEquipmentData());
}

// 매개변수 InPropertyHandle가 해당 변수에 식별자를 나타낸다.
void SWeaponEquipmentData::CustomizeHeader(TSharedRef<IPropertyHandle> InPropertyHandle, FDetailWidgetRow& InHeaderRow, IPropertyTypeCustomizationUtils& InCustomizationUtils)
{
   // GLog->Log("CustomizeHeader");

    // 접히는 라인
    InHeaderRow
        .NameContent()
        [
            SNew(STextBlock)
            .Text(FText::FromString("Name"))
        ]
		.ValueContent() // 슬레이트 UI 다루는 문법과 동일
        [
            SNew(STextBlock)
            .Text(FText::FromString("Value"))
        ];
}

void SWeaponEquipmentData::CustomizeChildren(TSharedRef<IPropertyHandle> InPropertyHandle, IDetailChildrenBuilder& InChildBuilder, IPropertyTypeCustomizationUtils& InCustomizationUtils)
{
   // GLog->Log("CustomizeChildren");

    uint32 number = 0;
    InPropertyHandle->GetNumChildren(number); // HeaderHandle를 통해서 자식이 몇개가 있는지 알아내야한다.

    for (uint32 i = 0; i < number; i++)
    {
        // 각 줄에 식별자
        TSharedPtr<IPropertyHandle> handle = InPropertyHandle->GetChildHandle(i);
        // 자식부분을 담당, 이 Handle이 기본모양을 추가해서 만들어준다. 커스텀 마이징도 가능하다
        IDetailPropertyRow& row = InChildBuilder.AddProperty(handle.ToSharedRef());

        FString name = FString("Name ") + FString::FromInt(i + 1);

        row.CustomWidget()
        .NameContent()
        [
            SNew(STextBlock)
            .Text(FText::FromString("Name"))
        ]
		.ValueContent() // 슬레이트 UI 다루는 문법과 동일
        [
            SNew(STextBlock)
            .Text(FText::FromString("Value"))
        ];
    }
}

1. 카테고리안에 있는 정보란을 눌렀을때 접히는 라인을 정해준다.

2. 각 줄에 알맞은 데이터가 들어올 수 있도록 식별자로 판단.

1. for문을 돌린 그대로 4개가 아래에 나오는걸 확인할 수 있다.

더보기
#include "SweaponEquipmentData.h"
#include "IPropertyUtilities.h"
#include "IDetailPropertyRow.h"
#include "IDetailChildrenBuilder.h"
#include "DetailWidgetRow.h"
#include "IDetailCustomization.h"

TSharedRef<IPropertyTypeCustomization> SWeaponEquipmentData::MakeInstance()
{
    return MakeShareable(new SWeaponEquipmentData());
}

// 매개변수 InPropertyHandle가 해당 변수에 식별자를 나타낸다.
void SWeaponEquipmentData::CustomizeHeader(TSharedRef<IPropertyHandle> InPropertyHandle, FDetailWidgetRow& InHeaderRow, IPropertyTypeCustomizationUtils& InCustomizationUtils)
{
   // GLog->Log("CustomizeHeader");

    // 접히는 라인
    InHeaderRow
        .NameContent()
        [
            // Handle에는 기본 모양이 존재하지만, 커스텀 한 모양대로 만들어주기
            InPropertyHandle->CreatePropertyValueWidget()
        ]
		.ValueContent() // 슬레이트 UI 다루는 문법과 동일
		// 사이즈 조절, 마우스로 끌어서 크기를 변경할때 적정선을 못넘어 가도록 고정 (최대, 최소 사이즈)
        .MinDesiredWidth(FEditorStyle::GetFloat("StandardDialog.MinDesiredSlotWidth"))
        .MaxDesiredWidth(FEditorStyle::GetFloat("StandardDialog.MaxDesiredSlotWidth"))
        [
            InPropertyHandle->CreatePropertyValueWidget()
        ];
}

void SWeaponEquipmentData::CustomizeChildren(TSharedRef<IPropertyHandle> InPropertyHandle, IDetailChildrenBuilder& InChildBuilder, IPropertyTypeCustomizationUtils& InCustomizationUtils)
{
   // GLog->Log("CustomizeChildren");

    uint32 number = 0;
    InPropertyHandle->GetNumChildren(number); // HeaderHandle를 통해서 자식이 몇개가 있는지 알아내야한다.

    for (uint32 i = 0; i < number; i++)
    {
        // 각 줄에 식별자
        TSharedPtr<IPropertyHandle> handle = InPropertyHandle->GetChildHandle(i);
        // 자식부분을 담당, 이 Handle이 기본모양을 추가해서 만들어준다. 커스텀 마이징도 가능하다
        IDetailPropertyRow& row = InChildBuilder.AddProperty(handle.ToSharedRef());

        FString name = FString("Name ") + FString::FromInt(i + 1);

        row.CustomWidget()
            .NameContent()
            [
               handle->CreatePropertyValueWidget()
            ]
    	    .ValueContent()
            .MinDesiredWidth(FEditorStyle::GetFloat("StandardDialog.MinDesiredSlotWidth"))
            .MaxDesiredWidth(FEditorStyle::GetFloat("StandardDialog.MaxDesiredSlotWidth"))
            [
                handle->CreatePropertyValueWidget()
            ];
    }
}

1. 이제 본래의 커스텀한 모양으로 만들어 주기 위해 코드 변경.

1. 다시 정보가 나오게되고, 정보창을 접을수도 있다.


 

1. 이제 위에서 언급한 체크박스를 생성해 커스텀 마이징 할 수 있도록 기본 세팅을 시작해 볼 것이다.

2. 우선은 클래스를 생성해 준다.


https://www.youtube.com/watch?v=x592tSSdPwY 

 

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

UE4 Weapon Plugin (7)  (0) 2023.06.12
UE4 Weapon Plugin (6)  (0) 2023.06.09
UE4 Weapon Plugin (4)  (0) 2023.06.01
UE4 Weapon Plugin (3)  (0) 2023.05.31
UE4 Weapon Plugin (2)  (0) 2023.05.30
Comments