PART 5 · 강의 1/3

UFactory와 에셋 타입

완전히 새로운 에셋 타입을 정의하고, UFactory로 콘텐츠 브라우저에서 생성 가능하게 만듭니다

01

커스텀 에셋 아키텍처

에셋 타입 시스템의 구성 요소

UE5에서 새로운 에셋 타입을 만들려면 세 가지 핵심 요소가 필요합니다.

요소모듈역할
UObject 에셋 클래스 Runtime 에셋 데이터 정의 (직렬화)
UFactory 팩토리 Editor 에셋 생성 (콘텐츠 브라우저 메뉴)
FAssetTypeActions Editor 에셋 외관, 카테고리, 컨텍스트 메뉴
모듈 분리 원칙

에셋 데이터 클래스는 Runtime 모듈에, Factory와 AssetTypeActions는 Editor 모듈에 배치합니다. 이렇게 해야 에셋 데이터는 패키징된 게임에서도 사용할 수 있습니다.

02

에셋 데이터 클래스 정의

UObject를 상속하여 커스텀 에셋 데이터 구조 만들기

UDialogueTree.h (Runtime 모듈) #pragma once #include "CoreMinimal.h" #include "UObject/Object.h" #include "UDialogueTree.generated.h" USTRUCT(BlueprintType) struct FDialogueNode { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite) FString SpeakerName; UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (MultiLine)) FText DialogueText; UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray<int32> ChildNodeIndices; UPROPERTY(EditAnywhere, BlueprintReadWrite) FName ConditionTag; }; UCLASS(BlueprintType) class MYGAME_API UDialogueTree : public UObject { GENERATED_BODY() public: UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Dialogue") FString TreeName; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Dialogue") FText Description; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Dialogue") TArray<FDialogueNode> Nodes; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Dialogue") int32 StartNodeIndex = 0; // 런타임에서 노드 접근 UFUNCTION(BlueprintCallable, Category = "Dialogue") const FDialogueNode& GetNode(int32 Index) const; UFUNCTION(BlueprintCallable, Category = "Dialogue") int32 GetNodeCount() const; };
03

UFactory 구현

콘텐츠 브라우저에서 에셋을 생성하는 팩토리

UDialogueTreeFactory.h (Editor 모듈) #pragma once #include "Factories/Factory.h" #include "UDialogueTreeFactory.generated.h" UCLASS() class UDialogueTreeFactory : public UFactory { GENERATED_BODY() public: UDialogueTreeFactory(); // UFactory interface virtual UObject* FactoryCreateNew( UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; virtual bool ShouldShowInNewMenu() const override; };
UDialogueTreeFactory.cpp #include "UDialogueTreeFactory.h" #include "UDialogueTree.h" UDialogueTreeFactory::UDialogueTreeFactory() { SupportedClass = UDialogueTree::StaticClass(); bCreateNew = true; // "New" 메뉴에서 생성 가능 bEditAfterNew = true; // 생성 후 에디터 열기 } UObject* UDialogueTreeFactory::FactoryCreateNew( UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) { UDialogueTree* NewTree = NewObject<UDialogueTree>( InParent, InClass, InName, Flags); // 기본 데이터로 초기화 NewTree->TreeName = InName.ToString(); FDialogueNode StartNode; StartNode.SpeakerName = TEXT("NPC"); StartNode.DialogueText = LOCTEXT("Default", "Hello! How can I help you?"); NewTree->Nodes.Add(StartNode); return NewTree; } bool UDialogueTreeFactory::ShouldShowInNewMenu() const { return true; }
두 가지 팩토리 패턴

FactoryCreateNew: 콘텐츠 브라우저 "Add" 메뉴에서 새 에셋 생성. FactoryCreateFile: 외부 파일(JSON, CSV 등)을 드래그 앤 드롭으로 임포트. 용도에 따라 적합한 메서드를 오버라이드하세요.

04

FAssetTypeActions 등록

에셋 외관과 컨텍스트 메뉴 커스터마이징

FDialogueTreeTypeActions.h #pragma once #include "AssetTypeActions_Base.h" class FDialogueTreeTypeActions : public FAssetTypeActions_Base { public: // 표시 이름 virtual FText GetName() const override { return LOCTEXT("Name", "Dialogue Tree"); } // 에셋 색상 (콘텐츠 브라우저에서 표시) virtual FColor GetTypeColor() const override { return FColor(0, 200, 255); // 시안 } // 지원하는 UClass virtual UClass* GetSupportedClass() const override { return UDialogueTree::StaticClass(); } // 에셋 카테고리 virtual uint32 GetCategories() override { return EAssetTypeCategories::Gameplay; } // 더블클릭 시 커스텀 에디터 열기 virtual void OpenAssetEditor( const TArray<UObject*>& InObjects, TSharedPtr<IToolkitHost> EditWithinLevelEditor) override; // 에셋 이름에 표시되는 접두사 virtual bool HasActions(const TArray<UObject*>& InObjects) const override { return true; } // 우클릭 컨텍스트 메뉴 항목 추가 virtual void GetActions( const TArray<UObject*>& InObjects, FMenuBuilder& MenuBuilder) override; };

에디터 모듈에서 등록

C++ - AssetTypeActions 등록 #include "AssetToolsModule.h" void FMyGameEditorModule::StartupModule() { IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>( "AssetTools").Get(); // 커스텀 에셋 카테고리 등록 (선택) MyAssetCategory = AssetTools.RegisterAdvancedAssetCategory( FName(TEXT("MyGame")), LOCTEXT("MyGame", "My Game")); // AssetTypeActions 등록 DialogueTreeActions = MakeShareable( new FDialogueTreeTypeActions); AssetTools.RegisterAssetTypeActions( DialogueTreeActions.ToSharedRef()); } void FMyGameEditorModule::ShutdownModule() { if (FModuleManager::Get().IsModuleLoaded("AssetTools")) { IAssetTools& AssetTools = FModuleManager::GetModuleChecked<FAssetToolsModule>( "AssetTools").Get(); AssetTools.UnregisterAssetTypeActions( DialogueTreeActions.ToSharedRef()); } }
SUMMARY

핵심 요약

  • 커스텀 에셋은 UObject 데이터 클래스(Runtime) + UFactory(Editor) + FAssetTypeActions(Editor) 세 요소로 구성됩니다
  • UFactory::FactoryCreateNew를 오버라이드하여 콘텐츠 브라우저의 "Add" 메뉴에서 에셋을 생성합니다
  • FAssetTypeActions_Base를 상속하여 에셋의 색상, 카테고리, 컨텍스트 메뉴, 더블클릭 동작을 정의합니다
  • RegisterAdvancedAssetCategory로 커스텀 에셋 카테고리를 등록하여 콘텐츠 브라우저 필터에 표시합니다
  • 에셋 데이터 클래스를 Runtime 모듈에 배치하면 패키징된 게임에서도 데이터에 접근할 수 있습니다
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: UFactory로 커스텀 에셋 생성

UFactory를 상속받아 커스텀 데이터 에셋(예: UMyQuestData)을 생성하는 팩토리를 구현하세요. FactoryCreateNew()를 오버라이드하고, 콘텐츠 브라우저의 'Add' 메뉴에서 커스텀 에셋 타입이 나타나는지 확인하세요.

실습 2: 임포트 팩토리 구현

UFactory를 상속받아 외부 파일(.csv, .json 등)을 UE5 에셋으로 임포트하는 팩토리를 구현하세요. FactoryCreateFile()을 오버라이드하여 CSV 파일을 파싱하고 UDataTable 에셋으로 변환하세요.

심화 과제: Re-import 지원 팩토리

FReimportHandler를 구현하여 소스 파일이 변경되면 자동으로 에셋을 업데이트하는 Re-import 팩토리를 만드세요. 소스 파일 경로 추적, 변경 감지, 자동 리임포트 기능을 구현하세요.