[UE] Slate UI 2: 제거 마법사 - 에셋 리스트 띄우기
Content Menu Extender에 제거 마법사를 만들어 선택한 폴더에 들어있는 에셋들을 한 눈에 볼 수 있도록 만들것이다. 이번시간에는 Slate UI 내에 에셋리스트를 띄울 것이다. 다음 시간에는 띄운 에셋 리스트를 삭제할 수 있는 기능을 추가할 것이다.
목차
Widget을 생성하여 탭에 텍스트 띄우기
AdvanceDeletionWidget 생성


AdvanceDeletionWidget.h
#pragma once #include "Widgets/SCompoundWidget.h" /** Slate Widget을 위해 빈 클래스가 필요 * */ class SAdvanceDeletionTab : public SCompoundWidget { SLATE_BEGIN_ARGS(SAdvanceDeletionTab) {} SLATE_ARGUMENT(FString, TestString) SLATE_END_ARGS() public: void Construct(const FArguments& InArgs); };
AdvanceDeletionWidget.cpp
#include "SlateWidgets/AdvanceDeletionWidget.h" void SAdvanceDeletionTab::Construct(const FArguments& InArgs) { bCanSupportFocus = true; ChildSlot [ SNew(STextBlock) .Text(FText::FromString(InArgs._TestString)) ]; }
※ 참고사항: SWManager.cpp의 TSharedRef<SDockTab> FSWManagerModule::
OnSpawnAdvanceDeltionTab(const FSpawnTabArgs& SpawnTabArgs)

- 1개의 Widget == 1개의 Slot
- Widget은 하나의 slot을 차지한다.
- [ ] 는 slot을 의미한다.
WBP_SlateExample 생성



실행화면

SListView를 활용하여 Slate Widget에 Asset을 TArray타입으로 넘기
SListView 설명
- ListView Widget은 일련의 데이터 항목을 관찰하고 이러한 항목를 시각적 생성한다.
- TSharedPtr 과 UObject* 형식으로만 작동한다. 데이터를 성공적으로 읽으려면 TSharedPtr 또는 UObject* 형식을 사용해야만 한다. 다른 형식으로는 사용할 수 없다.
- Slate Widget에 Asset을 TArray타입으로 넘긴다.
- List View를 생성하기 위해 이 TArray 변수의 Asset을 사용한다.
- SNew()
- SListView< TSharedPtr <FAssetData > >
- ListSourceItems
- OnGenerateRow()
https://docs.unrealengine.com/5.1/en-US/API/Runtime/Slate/Widgets/Views/SListView/
SListView
Copyright Epic Games, Inc. All Rights Reserved.
docs.unrealengine.com
Build.cs
SWManager.Build.cs
// Copyright Epic Games, Inc. All Rights Reserved. using UnrealBuildTool; public class SWManager : ModuleRules { public SWManager(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; PrivateIncludePaths.AddRange( new string[] { System.IO.Path.GetFullPath(Target.RelativeEnginePath) + "/Source/Editor/Blutility/Private" } ); PublicDependencyModuleNames.AddRange( new string[] { "Core", "Blutility", "EditorScriptingUtilities", "UMG", "Niagara", "UnrealEd", "AssetTools", "ContentBrowser", "InputCore" } ); PrivateDependencyModuleNames.AddRange( new string[] { "CoreUObject", "Engine", "Slate", "SlateCore", } ); } }
PublicDependencyModuleNames.AddRange( new string[]
- " InputCore " 추가
- SListView 사용 시 필
AdvanceDeletionWidget
AdvanceDeletionWidget.h
#pragma once #include "Widgets/SCompoundWidget.h" /** Slate Widget을 위해 빈 클래스가 필요 * */ class SAdvanceDeletionTab : public SCompoundWidget { SLATE_BEGIN_ARGS(SAdvanceDeletionTab) {} SLATE_ARGUMENT(TArray< TSharedPtr <FAssetData> >, AssetsDataToStore) SLATE_END_ARGS() public: void Construct(const FArguments& InArgs); private: TArray< TSharedPtr <FAssetData> > StoredAssetsData; TSharedRef<ITableRow> OnGenerateRowForList(TSharedPtr<FAssetData> AssetDataToDisplay, const TSharedRef<STableViewBase>& OwnerTable); };
변수 추가
- TArray< TSharedPtr <FAssetData> > StoredAssetsData;
함수 추가
- TSharedRef<ITableRow> OnGenerateRowForList(TSharedPtr<FAssetData> AssetDataToDisplay, const TSharedRef<STableViewBase>& OwnerTable);
AdvanceDeletionWidget.cpp
#include "SlateWidgets/AdvanceDeletionWidget.h" #include "SlateBasics.h" void SAdvanceDeletionTab::Construct(const FArguments& InArgs) { bCanSupportFocus = true; StoredAssetsData = InArgs._AssetsDataToStore; // Slate Widget이 생성(Construct)될 때 변수에 InArgs._AssetsDataArray 넣음으로써 에셋 데이터를 Slate Widget 안에 담는다 FSlateFontInfo TitleTextFont = FCoreStyle::Get().GetFontStyle(FName("EmbossedText")); // 글꼴 TitleTextFont.Size = 30; // 글자 크기 ChildSlot [ SNew(SVerticalBox) // Main vertical box // First Vertical slot - Title text + SVerticalBox::Slot() .AutoHeight() [ SNew(STextBlock) .Text(FText::FromString(TEXT("제거 마법사"))) .Font(TitleTextFont) // 글꼴, 글자 크기 설정 .Justification(ETextJustify::Center) // 글자 중앙 정렬 .ColorAndOpacity(FColor::White) ] // 2번째 Slot - drop down to specify the listing condition and help text + SVerticalBox::Slot() .AutoHeight() [ SNew(SHorizontalBox) ] // 3번째 slot - 에셋 리스트 + SVerticalBox::Slot() .AutoHeight() [ SNew(SScrollBox) // 사용하려면 #include "SlateBasics.h" 필요 + SScrollBox::Slot() [ SNew(SListView<TSharedPtr<FAssetData>>) .ItemHeight(24.0f) .ListItemsSource(&StoredAssetsData) .OnGenerateRow(this, &SAdvanceDeletionTab::OnGenerateRowForList) ] ] // 4번째 slot - 3 buttons + SVerticalBox::Slot() .AutoHeight() [ SNew(SHorizontalBox) ] ]; } // 매개변수 AssetDataToDisplay로 TArray< TSharedPtr <FAssetData> > StoredAssetsData배열변수의 원소 하나하나 들어온다 TSharedRef<ITableRow> SAdvanceDeletionTab::OnGenerateRowForList(TSharedPtr<FAssetData> AssetDataToDisplay, const TSharedRef<STableViewBase>& OwnerTable) { const FString DisplayAssetName = AssetDataToDisplay->AssetName.ToString(); // 에셋의 이름을 FString 형태로 변수에 저장 TSharedRef< STableRow<TSharedPtr<FAssetData>> > ListViewRowWidget = SNew(STableRow<TSharedPtr<FAssetData>>, OwnerTable) [ SNew(STextBlock) .Text(FText::FromString(DisplayAssetName)) // FString을 FText형태로 변환 ]; return ListViewRowWidget; }
함수 정의
- void SAdvanceDeletionTab::Construct(const FArguments& InArgs)
- 3번째 slot - 에셋 리스트 안에 .OnGenerateRow(this, &SAdvanceDeletionTab::OnGenerateRowForList)
- TSharedRef<ITableRow> SAdvanceDeletionTab::OnGenerateRowForList(TSharedPtr<FAssetData> AssetDataToDisplay, const TSharedRef<STableViewBase>& OwnerTable)
SWManager
SWManager.h
#pragma once #include "CoreMinimal.h" #include "Modules/ModuleManager.h" class FSWManagerModule : public IModuleInterface { public: /** IModuleInterface implementation */ virtual void StartupModule() override; virtual void ShutdownModule() override; private: #pragma region ContentBrowserMenuExtention void InitCBMenuExtention(); TArray<FString> FolderPathsSelected; TSharedRef<FExtender> CustomCBMenuExtender(const TArray<FString>& SelectedPaths); void AddCBMenuEntry(class FMenuBuilder& MenuBuilder); void OnDeleteUnsuedAssetButtonClicked(); // 사용하지 않는 Asset 제거 void OnDeleteEmptyFoldersButtonClicked(); // 사용하지 않는 Folder 제거 void OnAdvanceDeletionButtonClicked(); // 제거 마법사 void FixUpRedirectors(); // Fix Up Redirectors #pragma endregion #pragma region CustomEditorTab void RegisterAdvanceDeletionTab(); TSharedRef<SDockTab> OnSpawnAdvanceDeltionTab(const FSpawnTabArgs& SpawnTabArgs); // Tab 스폰 TArray< TSharedPtr <FAssetData> > GetAllAssetDataUnderSelectedFolder(); // 선택한 폴더에 포함된 모든 에셋 불러오기 #pragma endregion };
함수 추가
- TArray< TSharedPtr <FAssetData> > GetAllAssetDataUnderSelectedFolder();
- 선택한 폴더에 포함된 모든 에셋을 불러오는 함수
SWManager.cpp
#include "SWManager.h" #include "ContentBrowserModule.h" #include "DebugHeader.h" #include "EditorAssetLibrary.h" #include "ObjectTools.h" #include "AssetRegistryModule.h" #include "AssetToolsModule.h" #include "SlateWidgets/AdvanceDeletionWidget.h" #define LOCTEXT_NAMESPACE "FSWManagerModule" void FSWManagerModule::StartupModule() { // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module // SWManager.uplugin에서 설정한 PreDefault 이후 실행됨 InitCBMenuExtention(); RegisterAdvanceDeletionTab(); // Tab 스폰 시키기 } #pragma region ContentBrowserMenuExtention void FSWManagerModule::InitCBMenuExtention() { FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser")); // ContentBrowser 모듈 로드하여 변수에 담음 TArray<FContentBrowserMenuExtender_SelectedPaths>& ContentBrowserModuleMenuExtenders = ContentBrowserModule.GetAllPathViewContextMenuExtenders(); // ContentBrowserModule의 모든ContentMenuExtenders를 TArray변수에 담음 /*FContentBrowserMenuExtender_SelectedPaths CustomCBMenuDelegate; CustomCBMenuDelegate.BindRaw(this, &FSWManagerModule::CustomCBMenuExtender); ContentBrowserModuleMenuExtenders.Add(CustomCBMenuDelegate); //아래의 한줄과 같은 의미 */ // ContentBrowserModuleMenuExtenders에 항목을 추가하고 FContentBrowserMenuExtender_SelectedPaths에 this(=FSWManagerModule)를 생성하고 CustomCBMenuExtender함수를 Bind 한다. 다른말로 표현하면, 현재 존재하는 모든 delegates들에 내가 만든 CustomCBMenuExtender 델리게이트를 추가한다. ContentBrowserModuleMenuExtenders.Add(FContentBrowserMenuExtender_SelectedPaths::CreateRaw(this, &FSWManagerModule::CustomCBMenuExtender)); } TSharedRef<FExtender> FSWManagerModule::CustomCBMenuExtender(const TArray<FString>& SelectedPaths) // 우클릭 시 ContentBrowserMenu에 노출시켜질 위치를 정의해주기 { TSharedRef<FExtender> MenuExtender(new FExtender()); if (SelectedPaths.Num() > 0) { // MenuExtender의 "Delete"위치 뒤에 UICommandList를 추가하고 AddCBMenuEntry함수를 Bind 한다. MenuExtender->AddMenuExtension(FName("Delete"), // Extension hook, 삽입할 위치 EExtensionHook::After, // Delete 뒤에 들어가게 After 설정 TSharedPtr<FUICommandList>(), // Custom hot keys FMenuExtensionDelegate::CreateRaw(this, &FSWManagerModule::AddCBMenuEntry)); // Second binding, Menu Entry에 노출될 디테일을 정의해줄 AddCBMenuEntry를 바인딩 FolderPathsSelected = SelectedPaths; // 유저가 현재 선택한 폴더에 접근할 수 있도록 SelectedPaths를 FolderPathsSelected변수에 담는다 } return MenuExtender; } void FSWManagerModule::AddCBMenuEntry(FMenuBuilder& MenuBuilder) // ContentBrowserMenu에 노출되는 버튼에 함수 바인딩 { MenuBuilder.AddMenuEntry ( FText::FromString(TEXT("사용하지 않는 에셋 제거하기")), // Menu Entry의 이름 FText::FromString(TEXT("폴더에서 사용하지 않는 에셋들을 안전하게 지우는 기능")), // Tooltip 설명 FSlateIcon(), // Custom Icon FExecuteAction::CreateRaw(this, &FSWManagerModule::OnDeleteUnsuedAssetButtonClicked) // 실행될 함수 ); MenuBuilder.AddMenuEntry ( FText::FromString(TEXT("사용하지 않는 빈 폴더 제거하기")), // Menu Entry의 이름 FText::FromString(TEXT("빈 폴더를 안전하게 지우는 기능")), // Tooltip 설명 FSlateIcon(), // Custom Icon FExecuteAction::CreateRaw(this, &FSWManagerModule::OnDeleteEmptyFoldersButtonClicked) // 실행될 함수 ); MenuBuilder.AddMenuEntry ( FText::FromString(TEXT("제거 마법사")), // Menu Entry의 이름 FText::FromString(TEXT("탭에 제거할 에셋 목록 띄우기")), // Tooltip 설명 FSlateIcon(), // Custom Icon FExecuteAction::CreateRaw(this, &FSWManagerModule::OnAdvanceDeletionButtonClicked) // 실행될 함수 ); } void FSWManagerModule::OnDeleteUnsuedAssetButtonClicked() // 에셋 삭제 { if (FolderPathsSelected.Num() > 1) // 2개 이상의 폴더가 선택된 경우 { DebugHeader::ShowMsgDialog(EAppMsgType::Ok, TEXT("한 개의 폴더에서만 진행할 수 있습니다")); return; } TArray<FString> AssetsPathNames = UEditorAssetLibrary::ListAssets(FolderPathsSelected[0]); // 선택된 폴더(FolderPathsSelected[0]) 내에 있는 모든 에셋들을 AssetsPathNames변수에 담는다 if (AssetsPathNames.Num() == 0) // 폴더 내에서 에셋을 찾지 못한 경우 { DebugHeader::ShowMsgDialog(EAppMsgType::Ok, TEXT("선택한 폴더에서 아무런 에셋을 찾을 수 없습니다."), false); return; } EAppReturnType::Type ConfirmResult =DebugHeader::ShowMsgDialog(EAppMsgType::YesNo, TEXT("총 ") + FString::FromInt(AssetsPathNames.Num()) + TEXT(" 개의 에셋을 찾았습니다.\n사용하지 않는지 체크 후 삭제를 진행하시겠습니까?"), false); if (ConfirmResult == EAppReturnType::No) return; // 유저가 No를 누르면 삭제를 진행하지 않고 바로 종료 FixUpRedirectors(); // 경로가 문제되지 않도록 체크 후 업데이트 TArray<FAssetData> UnusedAssetsDataArray; // 사용되지 않는 에셋들을 담는 TArray변수 for (const FString& AssetPathName : AssetsPathNames) { // Root 폴더는 건드리면 안 된다. 프로젝트 파일 Content 밑의 Developer, Collection 폴더일 경우 continue if (AssetPathName.Contains(TEXT("Developers")) || AssetPathName.Contains(TEXT("Collections")) || AssetPathName.Contains(TEXT("__ExternalActors__")) || AssetPathName.Contains(TEXT("__ExternalObjects__"))) { continue; } // 에셋이 없다면 continue if (false == UEditorAssetLibrary::DoesAssetExist(AssetPathName)) continue; // AssetReferencer변수에 referencers들의 package path를 담는다. 에셋 데이터가 아닌 path를 담는것이다. TArray<FString> AssetReferencers = UEditorAssetLibrary::FindPackageReferencersForAsset(AssetPathName); if (AssetReferencers.Num() == 0) // 해당 에셋이 사용되고 있지 않다면 { // 에셋 데이터를 UnusedAssetsDataArray(=사용되지 않는 에셋들을 담는 TArray변수)에 추가 const FAssetData UnusedAssetData = UEditorAssetLibrary::FindAssetData(AssetPathName); UnusedAssetsDataArray.Add(UnusedAssetData); } } if (UnusedAssetsDataArray.Num() > 0) // 사용되지 않는 에셋이 0이상이라면 { ObjectTools::DeleteAssets(UnusedAssetsDataArray); // UnusedAssetsDataArray를 삭제 } else { DebugHeader::ShowMsgDialog(EAppMsgType::Ok, TEXT("선택한 폴더에서 사용하지 않는 에셋을 찾을 수 없습니다. 삭제할 수 없습니다.\n 에셋이 없거나 사용중입니다."), false); } } void FSWManagerModule::OnDeleteEmptyFoldersButtonClicked() // 사용하지 않는 Folder 제거 { FixUpRedirectors(); // 경로가 문제되지 않도록 체크 후 업데이트 TArray<FString> FolderPathsArray = UEditorAssetLibrary::ListAssets(FolderPathsSelected[0], true, true); // 선택된 폴더(FolderPathsSelected[0]) 내에 있는 모든 에셋+폴더들을 FolderPathsArray변수에 담는다 uint32 Counter = 0; FString EmptyFolderPathsNames; TArray<FString> EmptyFoldersPathsArray; // 빈 폴더들을 담을 TArray변수 for (const FString& FolderPath : FolderPathsArray) { // Root 폴더는 건드리면 안 된다. 프로젝트 파일 Content 밑의 Developer, Collection, __ExternalActors__, __ExternalObjects__폴더일 경우 continue if (FolderPath.Contains(TEXT("Developers")) || FolderPath.Contains(TEXT("Collections")) || FolderPath.Contains(TEXT("__ExternalActors__")) || FolderPath.Contains(TEXT("__ExternalObjects__"))) { continue; } // 폴더가 없다면 continue if (false == UEditorAssetLibrary::DoesDirectoryExist(FolderPath)) continue; // FolderPath에 에셋이 없다면 if (false == UEditorAssetLibrary::DoesDirectoryHaveAssets(FolderPath)) { EmptyFolderPathsNames.Append(FolderPath); EmptyFolderPathsNames.Append(TEXT("\n")); EmptyFoldersPathsArray.Add(FolderPath); // EmptyFoldersPathsArray배열에 빈 폴더의 FolderPath를 추가 } } if (EmptyFoldersPathsArray.Num() == 0) // 빈 폴더가 없다면 { DebugHeader::ShowMsgDialog(EAppMsgType::Ok, TEXT("선택한 폴더들 중 빈 폴더를 찾지 못 했습니다."), false); return; } EAppReturnType::Type ConfirmResult = DebugHeader::ShowMsgDialog(EAppMsgType::OkCancel, TEXT("빈 폴더 ") + EmptyFolderPathsNames + TEXT(" 를 찾았습니다. \n 모두 삭제 하시겠습니까?"), false); if (ConfirmResult == EAppReturnType::Cancel) return; // Cancel를 누르면 삭제하지 않고 종료 for (const FString& EmptyFolderPath : EmptyFoldersPathsArray) { UEditorAssetLibrary::DeleteDirectory(EmptyFolderPath) ? ++Counter : DebugHeader::Print(TEXT("폴더 " + EmptyFolderPath + " 삭제를 실패했습니다."), FColor::Red); // 디버깅 메시지 띄우기 } if (Counter > 0) { DebugHeader::ShowNotifyInfo(TEXT("폴더 ") + FString::FromInt(Counter) + TEXT("개를 성공적으로 삭제했습니다.")); } } void FSWManagerModule::OnAdvanceDeletionButtonClicked() // 제거 마법사 { FGlobalTabmanager::Get()->TryInvokeTab(FName("AdvanceDeletion")); // Menu Entry에 "AdvanceDeletion"란 Tab ID를 가진 탭 부르기. "AdvanceDeletion"가 오타나면 실행되지 않는다. } void FSWManagerModule::FixUpRedirectors() { TArray<UObjectRedirector*> RedirectorsToFixArray; FAssetRegistryModule& AssetRegistryModule = FModuleManager::Get().LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")); FARFilter Filter; Filter.bRecursivePaths = true; // subfolder에 접근이 가능하도록 true 설정 Filter.PackagePaths.Emplace("/Game"); // 어떤 폴더에 접근할 지 경로 설정 Filter.ClassNames.Emplace("ObjectRedirector"); // 필터 하기를 희망하는 클래스의 이름 TArray<FAssetData> OutRedirectors; // 결과를 저장할 TArry 변수 AssetRegistryModule.Get().GetAssets(Filter, OutRedirectors); // Filter한 결과를 OutRedirectors에 담는다 for (const FAssetData& RedirectorData : OutRedirectors) { if (UObjectRedirector* RedirectorToFix = Cast<UObjectRedirector>(RedirectorData.GetAsset())) // Redirect 할게 있다면 { RedirectorsToFixArray.Add(RedirectorToFix); // RedirectorsToFixArray에 RedirectorToFix 담음 } } FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>(TEXT("AssetTools")); AssetToolsModule.Get().FixupReferencers(RedirectorsToFixArray); } #pragma endregion #pragma region CustomEditorTab void FSWManagerModule::RegisterAdvanceDeletionTab() { FGlobalTabmanager::Get()->RegisterNomadTabSpawner(FName("AdvanceDeletion"), // Tab ID: "AdvanceDeletion" FOnSpawnTab::CreateRaw(this, &FSWManagerModule::OnSpawnAdvanceDeltionTab)) .SetDisplayName(FText::FromString(TEXT("제거 마법사"))); } TSharedRef<SDockTab> FSWManagerModule::OnSpawnAdvanceDeltionTab(const FSpawnTabArgs& SpawnTabArgs) { return SNew(SDockTab).TabRole(ETabRole::NomadTab) [ SNew(SAdvanceDeletionTab) .AssetsDataToStore(GetAllAssetDataUnderSelectedFolder()) // 선택한 폴더에 포함된 모든 에셋 불러오기 ]; } TArray<TSharedPtr<FAssetData>> FSWManagerModule::GetAllAssetDataUnderSelectedFolder() { TArray< TSharedPtr <FAssetData> > AvaiableAssetsData; // 유효한 에셋들 TArray<FString> AssetsPathNames = UEditorAssetLibrary::ListAssets(FolderPathsSelected[0]); // 선택된 폴더(FolderPathsSelected[0]) 내에 있는 모든 에셋들을 AssetsPathNames변수에 담는다 for (const FString& AssetPathName : AssetsPathNames) { // Root folder들은 건드리지 않는다 if (AssetPathName.Contains(TEXT("Developers")) || AssetPathName.Contains(TEXT("Collections")) || AssetPathName.Contains(TEXT("__ExternalActors__")) || AssetPathName.Contains(TEXT("__ExternalObjects__"))) { continue; } // 에셋이 없다면 continue if (false == UEditorAssetLibrary::DoesAssetExist(AssetPathName)) continue; // 에셋을 찾아 Data변수에 담는다 const FAssetData Data = UEditorAssetLibrary::FindAssetData(AssetPathName); // MakeShared<FAssetData>(Data)를 AvaiableAssetsData에 추가 AvaiableAssetsData.Add(MakeShared<FAssetData>(Data)); } return AvaiableAssetsData; // 유효한 에셋들 리턴 } #pragma endregion void FSWManagerModule::ShutdownModule() { // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, // we call this function before unloading the module. } #undef LOCTEXT_NAMESPACE IMPLEMENT_MODULE(FSWManagerModule, SWManager)
헤더 추가
- #include "SlateWidgets/AdvanceDeletionWidget.h"
함수 정의
- TArray<TSharedPtr<FAssetData>> FSWManagerModule::GetAllAssetDataUnderSelectedFolder()
실행화면

'⭐ Unreal Engine > UE Plugin - Custom Editor Tool' 카테고리의 다른 글
[UE] Texture로 Material 생성하기 (0) | 2023.12.07 |
---|---|
[UE] Content Browser Extender Menu에 아이콘 등록하기 (0) | 2023.12.07 |
[UE] Slate UI 1: Tab 띄우기 (0) | 2023.12.01 |
[UE] ContentBrowser Menu: 사용하지 않는 에셋/빈 폴더 제거 (0) | 2023.11.25 |
[UE] Asset Editor Tool 3: 사용하지 않는 Asset만 제거하기 (0) | 2023.11.25 |
댓글
이 글 공유하기
다른 글
-
[UE] Texture로 Material 생성하기
[UE] Texture로 Material 생성하기
2023.12.07언리얼 엔진에서는 Material과 Material Instance를 사용해서 Texture들을 묶어서 관리한다. Texture의 종류로는 Metalic, Roughness, Normal, Ambient Occulution, BaseColor 등 다양하다. 이번에는 이런 다양한 Texture들을 선택한 후 버튼 클릭 하나로 Material를 생성하고 Material 내의 Texture들의 노드를 자동으로 이어주도록 할 것이다. 목차 Material 생성 버튼 WBP로 만들기 UEditorUtilityWidgets를 상속받는 위젯클래스 QuickMaterialCreationWidget UEditorUtilityWidgets를 상속받는 위젯클래스를 만들어 사용할 것이다 UEditorUtilityWidgets… -
[UE] Content Browser Extender Menu에 아이콘 등록하기
[UE] Content Browser Extender Menu에 아이콘 등록하기
2023.12.07Content Browser Extender Menu에 아이콘 등록하기 아이콘 등록하기 구현방법 1. Custom FSlateStyle Set에서 할 일 Custom FSlateStyle Set으로 사용할 SWManageStyle 클래스를 만들고 아래 작업들을 수행한다. Style set 이름 설정하기 static FName StyleSetName; Custom Style set 생성하기: static TSharedRef CreateSlateStyleSet(); 파일 경로를 가져와서 Icon 이미지를 로드 TSharedRef CustomStyleSet = MakeShareable(new FSlateStyleSet(StyleSetName)); const FString IconDirectory = IPlug… -
[UE] Slate UI 1: Tab 띄우기
[UE] Slate UI 1: Tab 띄우기
2023.12.01Slate UI를 사용하여 탭을 생성하고 폴더 내에 있는 에셋 목록을 한번에 보여주게 만들 것이다. 에셋 목록을 조작하여 필요시 선택한 에셋들을 지우는 기능을 추가할 것이다. 일단, 탭을 생성하고 띄워주는 것부터 하겠다. 목차 Smart Pointer Smart Pointer 스마트 포인터 객체를 소유 삭제 예방 Unique Property TSharedPtr O O Reference Counting 메서드 사용 NULL을 가리킬 수 있음 TSharedRef O O NULL을 가리킬 수 없음 무조건유효한 객체를 가르켜야 함 TWeakPtr X X Reference cycle을 부순다 TSharedRef타입의 함수는 return 값이 무조건 NULL이 아닌 유효한 값이다. 이 점을 알고 있으면 코드 파악이… -
[UE] ContentBrowser Menu: 사용하지 않는 에셋/빈 폴더 제거
[UE] ContentBrowser Menu: 사용하지 않는 에셋/빈 폴더 제거
2023.11.25Content Browser Menu에 사용하지 않는 에셋과 폴더를 제거하는 기능을 추가하였다. 폴더를 우클릭하여 해당 기능을 눌러 실행한다. 해당 에셋이 사용하고 있는지 체크하고 사용하지 않을 경우에만 삭제를 진행한다. 폴더 역시 빈 폴더인지 체크 후에 빈 폴더이면 삭제한다. 목차 사용하지 않는 에셋 제거 Delegate 선언 정리 DECLARE_DELEGATE( DelegateName ) void Function() ex. DECLARE_DELEGATE( OnDelegateButtonClicked ) DECLARE_DELEGATE_OneParam( DelegateName, Param1 Type ) void Function( Param1 ) ex. DECLARE_DELEGATE_OneParam( OnD…
댓글을 사용할 수 없습니다.