언리얼 개발/이것저것 테스트

[UE_5] 모듈 제작 -5 (엔진 메뉴 Extension)

WhNi 2024. 4. 22. 21:50

엔진을 쓰다보면  Custom하는 경우가 생기기 마련이다. 

 

이걸 플러그인으로 만들 수 있는데, 

 

먼저 플러그인 처음으로 생성하면 IModuleInterface 를 상속받는  헤더가 있다.

 

여기에 추가적인 함수들을 추가해주고, 델리게이트로 연결해줘야 한다.

 

(#include "Modules/ModuleManager.h") 를 추가해줘야 한다.  

 

	private:
    void InitMenuExtention();
	TSharedRef<FExtender>  CustomMenuExtender(const  TArray<FString>& Path);
	void AddMenuEntry(class FMenuBuilder& Menu);
	void OnDeleteUnUsedAssetButtonClicked();

그 다음 cpp 파일에서  ContentBrowserModule.h 도 추가해 줘야한다. 아무래도 ContentBrowser 에서 적용되는 메뉴 Extension이기에, 여기에 하는게 당연하다. 

 

void FSuperManagerModule::InitMenuExtention()
{
	//모듈 매니저에서 모듈이 로드 되어있는지 체크하고
	FContentBrowserModule&  ContentBrowerModule =
	FModuleManager::LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));

	//GetAllPathViewContextMenuExtenders() 찾아준다.
	TArray<FContentBrowserMenuExtender_SelectedPaths>& ContentBrowerModuleMenuExtenders =
	ContentBrowerModule.GetAllPathViewContextMenuExtenders();

	//델리게이트 연결로 커스텀 메뉴를 추가해야 한다.
	FContentBrowserMenuExtender_SelectedPaths CustomMenuDelegate;
	CustomMenuDelegate.BindRaw(this, &FSuperManagerModule::CustomMenuExtender);

	ContentBrowerModuleMenuExtenders.Add(CustomMenuDelegate);
}

// CustomMenuExtender는 리턴 타입이 TSharerdRef라는  스마트 포인터로 되어있다.
TSharedRef<FExtender> FSuperManagerModule::CustomMenuExtender(const TArray<FString>& Path)
{
	// 포인터를 새로 할당하고
	TSharedRef<FExtender> MenuExtender(new FExtender());
	
    // Path의 배열이 0보다 크다면
	if (Path.Num() > 0)
	{
    	// "Delete"라는 메뉴에 위치에 또 델리게이트를 추가하는데
		MenuExtender->AddMenuExtension(FName("Delete"),EExtensionHook::After,
        TSharedPtr<FUICommandList>(),
		FMenuExtensionDelegate::CreateRaw(this,&FSuperManagerModule::AddMenuEntry));
	}

	// 반환을 포인터로 해야 한다.
	return MenuExtender;
}

// FMemuBuilder 라는 class을 매개변수로 갖는 FMenuExtensionDelegate.
void FSuperManagerModule::AddMenuEntry(FMenuBuilder& Menu)
{
	이 메뉴에서 새로이 메뉴 Extenstion을 추가할 수 있다.
	Menu.AddMenuEntry
	(
		FText::FromString(TEXT("Delete Unused Assets")),
		FText::FromString(TEXT("Safely delete all unused assets")),
		FSlateIcon(),
        // 여기서는 그 메뉴에  들어가는 기능을 델리게이트 해야한다.
		FExecuteAction::CreateRaw(this, &FSuperManagerModule::OnDeleteUnUsedAssetButtonClicked)
		);
}

void FSuperManagerModule::OnDeleteUnUsedAssetButtonClicked()
{
	//Print(TEXT("Working"), FColor::Red);
    //파일의 갯수가 2개면 한 폴더에서만 가능해야하니까 return 
if (FolderPath.Num() > 1)
{
	ShowMsgDialog(EAppMsgType::Ok,TEXT("You can only do this to one folder"));
	return;
}

//선택된  폴더 이름 표시
Print(TEXT("Selected folder: ") + FolderPath[0], FColor::Green);

// 선택된 에셋의 갯수
TArray<FString> AssetsPathCount =UEditorAssetLibrary::ListAssets(FolderPath[0]);

// 선택된 폴더 내에 에셋이 없다는 뜻
if (AssetsPathCount.Num() == 0)
{
	ShowMsgDialog(EAppMsgType::Ok,TEXT("No Assets found under selected folder"));
	return;
}

// 있다면 몇개 있는지 보여주는 다이얼로그. Yes No 라는 문구로, No라면 return
EAppReturnType::Type ConfirmResult =
ShowMsgDialog(EAppMsgType::YesNo, TEXT("A total of ")+ FString::FromInt(AssetsPathCount.Num()) + TEXT(" found, \nWould  you like to procceed?"));


if (ConfirmResult == EAppReturnType::No) return;
// Yes 라면 사용하지 않는 에셋 수집하고 삭제
else
{
	TArray<FAssetData> UnusedAssetDataArray;
	
    // 경로에 대한 탐색을 할때, 에디터에 손상이 안가게 Root 폴더는 꼭 제외
	for (const FString& AssetPathName : AssetsPathCount)
	{
		// make sure don't touch root folder
		if (AssetPathName.Contains(TEXT("Developers")) || (AssetPathName.Contains(TEXT("Collections"))))
		{
			continue;
		}

		if (!UEditorAssetLibrary::DoesAssetExist(AssetPathName)) continue;

		// 에셋에 대한 레퍼런스가 있는지 확인
		TArray<FString> AssetReferece =
		UEditorAssetLibrary::FindPackageReferencersForAsset(AssetPathName);

		// 레퍼런스가 없는 에셋들이 있다면 
		if (AssetReferece.Num() == 0)
		{
			const FAssetData UnusedAssetData = 
				UEditorAssetLibrary::FindAssetData(AssetPathName);
                // 사용하지 않는 에셋의 배열에 추가하기
			UnusedAssetDataArray.Add(UnusedAssetData);
		}
	}
    // 사용하지 않는 에셋의 배열이 1개 이상이면
	if (UnusedAssetDataArray.Num() > 0)
	{
    	// 에셋 지우고 다이얼로그 출력하기
		ObjectTools::DeleteAssets(UnusedAssetDataArray);
		ShowMsgDialog(EAppMsgType::Ok, TEXT(" Total Delete : ") + FString::FromInt(UnusedAssetDataArray.Num()));
	}
	else
	{
		ShowMsgDialog(EAppMsgType::Ok,TEXT("No Assets found under selected folder"));
	}

}
}

 

이런식으로 적용하게 되면, 

 

폴더 내에 있는 에셋들이 몇개인지 확인하고,

 

 

레퍼런스가 없는 에셋들을 찾아서 

 

 

갯수를 보여주면서

 

 

삭제가 되는 에디터 기능을 메뉴에 추가 할 수 있다.