In this post we’re going to create custom editor assets. In order to extend the UE4 Editor and add your own assets you need two classes:

One class that contains the various properties of your asset

One class (named XFactory where the X stands for the asset’s class name) that constructs the above class as an Editor asset (meaning a .uasset file).

For most assets, their factory classes are inside the EditorFactories files of the Editor. For example, when you’re creating a new Texture inside the Editor you will be presented with the various properties that are written in the Texture.h file. However, the class (named UTexture2DFactoryNew) that constructs this asset in the Editor is contained inside the EditorFactories.

So, let’s create our custom editor assets.

Creating the Asset class

In order to create a custom class, add a new C++ class (I’ve named my class as OrfeasCustomAsset) that inheirts the Object class and type the following code:

OrfeasCustomAsset header file #include "CoreMinimal.h" #include "UObject/NoExportTypes.h" #include "OrfeasCustomAsset.generated.h" /** * */ UCLASS() class CUSTOMASSET_API UOrfeasCustomAsset : public UObject { GENERATED_BODY() protected: //Just some properties to display on the Editor UPROPERTY(EditAnywhere) FString Description; UPROPERTY(EditAnywhere) int32 BonusCoins; }; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include "CoreMinimal.h" #include "UObject/NoExportTypes.h" #include "OrfeasCustomAsset.generated.h" /** * */ UCLASS ( ) class CUSTOMASSET_API UOrfeasCustomAsset : public UObject { GENERATED_BODY ( ) protected : //Just some properties to display on the Editor UPROPERTY ( EditAnywhere ) FString Description ; UPROPERTY ( EditAnywhere ) int32 BonusCoins ; } ;

Then, add a new C++ class that inherits the factory class:

and add the following code to its header file:

Orfeas Factory header file UCLASS() class CUSTOMASSET_API UOrfeasFactory : public UFactory { GENERATED_BODY() public: UOrfeasFactory(); /* New assets that don't override this function are automatically placed into the "Miscellaneous" category in the editor */ virtual uint32 GetMenuCategories() const override; /* Creates the asset inside the UE4 Editor */ virtual UObject* FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; }; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 UCLASS ( ) class CUSTOMASSET_API UOrfeasFactory : public UFactory { GENERATED_BODY ( ) public : UOrfeasFactory ( ) ; /* New assets that don't override this function are automatically placed into the "Miscellaneous" category in the editor */ virtual uint32 GetMenuCategories ( ) const override ; /* Creates the asset inside the UE4 Editor */ virtual UObject * FactoryCreateNew ( UClass * InClass , UObject * InParent , FName InName , EObjectFlags Flags , UObject * Context , FFeedbackContext * Warn ) override ; } ;

Let’s type the logic for these function inside our source file:

Orfeas Factory source file #include "OrfeasFactory.h" //The asset header file that we wish to create #include "OrfeasCustomAsset.h" //The asset type categories will let us access the various asset categories inside the Editor #include "AssetTypeCategories.h" UOrfeasFactory::UOrfeasFactory() { bCreateNew = true; bEditAfterNew = true; //Configure the class that this factory creates SupportedClass = UOrfeasCustomAsset::StaticClass(); } uint32 UOrfeasFactory::GetMenuCategories() const { //Let's place this asset in the Blueprints category in the Editor return EAssetTypeCategories::Blueprint; } UObject* UOrfeasFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) { //Create the editor asset UOrfeasCustomAsset* OrfeasEditorAsset = NewObject<UOrfeasCustomAsset>(InParent, InClass, InName, Flags); return OrfeasEditorAsset; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include "OrfeasFactory.h" //The asset header file that we wish to create #include "OrfeasCustomAsset.h" //The asset type categories will let us access the various asset categories inside the Editor #include "AssetTypeCategories.h" UOrfeasFactory :: UOrfeasFactory ( ) { bCreateNew = true ; bEditAfterNew = true ; //Configure the class that this factory creates SupportedClass = UOrfeasCustomAsset :: StaticClass ( ) ; } uint32 UOrfeasFactory :: GetMenuCategories ( ) const { //Let's place this asset in the Blueprints category in the Editor return EAssetTypeCategories :: Blueprint ; } UObject * UOrfeasFactory :: FactoryCreateNew ( UClass * InClass , UObject * InParent , FName InName , EObjectFlags Flags , UObject * Context , FFeedbackContext * Warn ) { //Create the editor asset UOrfeasCustomAsset * OrfeasEditorAsset = NewObject < UOrfeasCustomAsset > ( InParent , InClass , InName , Flags ) ; return OrfeasEditorAsset ; }

In order to access the AssetTypeCategories.h file you need to add the “AssetTools” in your project’s public dependencies:

PublicDependencyModuleNames . AddRange ( new string [ ] { "Core" , "CoreUObject" , "Engine" , "InputCore" , "AssetTools" } ) ;

At this point, save and compile your code. Then, restart the Editor and check out the Blueprints category:

Adding a custom thumbnail to your asset

The default thumbnail for your new asset is simply the name of the Asset’s class. In case you want to have a customized thumbnail you need to create a new Slate style and bind it with this asset’s class.

Ideally, you will place your new assets in a new module or plugin. For the sake of this example, I’m going to use a plugin’s startup module in order to create a new slate style for my custom assets. At this point, I’ve added a blank plugin named “OrfeasPlugin” into my project and I’m going to use its default icon as a new thumbnail. Go inside your plugin’s header file and add the following code:

OrfeasPlugin header file #include "CoreMinimal.h" #include "ModuleManager.h" #include "SlateStyle.h" class FOrfeasPluginModule : public IModuleInterface { public: TSharedPtr<FSlateStyleSet> StyleSet; /** IModuleInterface implementation */ virtual void StartupModule() override; virtual void ShutdownModule() override; }; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include "CoreMinimal.h" #include "ModuleManager.h" #include "SlateStyle.h" class FOrfeasPluginModule : public IModuleInterface { public : TSharedPtr < FSlateStyleSet > StyleSet ; /** IModuleInterface implementation */ virtual void StartupModule ( ) override ; virtual void ShutdownModule ( ) override ; } ;

Then, inside your plugin’s source file, add the following code:

OrfeasPlugin source file #include "IPluginManager.h" #include "SlateStyleRegistry.h" #define LOCTEXT_NAMESPACE "FOrfeasPluginModule" void FOrfeasPluginModule::StartupModule() { // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module StyleSet = MakeShareable(new FSlateStyleSet("OrfeasStyle")); //Content path of this plugin FString ContentDir = IPluginManager::Get().FindPlugin("OrfeasPlugin")->GetBaseDir(); //The image we wish to load is located inside the Resources folder inside the Base Directory //so let's set the content dir to the base dir and manually switch to the Resources folder: StyleSet->SetContentRoot(ContentDir); //Create a brush from the icon FSlateImageBrush* ThumbnailBrush = new FSlateImageBrush(StyleSet->RootToContentDir(TEXT("Resources/Icon128"), TEXT(".png")), FVector2D(128.f, 128.f)); if (ThumbnailBrush) { //In order to bind the thumbnail to our class we need to type ClassThumbnail.X where X is the name of the C++ class of the asset StyleSet->Set("ClassThumbnail.OrfeasCustomAsset", ThumbnailBrush); //Reguster the created style FSlateStyleRegistry::RegisterSlateStyle(*StyleSet); } } void FOrfeasPluginModule::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. //Unregister the style FSlateStyleRegistry::UnRegisterSlateStyle(StyleSet->GetStyleSetName()); } #undef LOCTEXT_NAMESPACE IMPLEMENT_MODULE(FOrfeasPluginModule, OrfeasPlugin) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include "IPluginManager.h" #include "SlateStyleRegistry.h" #define LOCTEXT_NAMESPACE "FOrfeasPluginModule" void FOrfeasPluginModule :: StartupModule ( ) { // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module StyleSet = MakeShareable ( new FSlateStyleSet ( "OrfeasStyle" ) ) ; //Content path of this plugin FString ContentDir = IPluginManager :: Get ( ) . FindPlugin ( "OrfeasPlugin" ) -> GetBaseDir ( ) ; //The image we wish to load is located inside the Resources folder inside the Base Directory //so let's set the content dir to the base dir and manually switch to the Resources folder: StyleSet -> SetContentRoot ( ContentDir ) ; //Create a brush from the icon FSlateImageBrush * ThumbnailBrush = new FSlateImageBrush ( StyleSet -> RootToContentDir ( TEXT ( "Resources/Icon128" ) , TEXT ( ".png" ) ) , FVector2D ( 128.f , 128.f ) ) ; if ( ThumbnailBrush ) { //In order to bind the thumbnail to our class we need to type ClassThumbnail.X where X is the name of the C++ class of the asset StyleSet -> Set ( "ClassThumbnail.OrfeasCustomAsset" , ThumbnailBrush ) ; //Reguster the created style FSlateStyleRegistry :: RegisterSlateStyle ( * StyleSet ) ; } } void FOrfeasPluginModule :: 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. //Unregister the style FSlateStyleRegistry :: UnRegisterSlateStyle ( StyleSet -> GetStyleSetName ( ) ) ; } #undef LOCTEXT_NAMESPACE IMPLEMENT_MODULE ( FOrfeasPluginModule , OrfeasPlugin )

In order to access the IPluginManager.h header file you need to add the “Projects” dependency on your plugin’s dependencies:

OrfeasPlugin.Build.cs PrivateDependencyModuleNames.AddRange( new string[] { "CoreUObject", "Engine", "Slate", "SlateCore", "Projects" // ... add private dependencies that you statically link with here ... } ); 1 2 3 4 5 6 7 8 9 10 11 PrivateDependencyModuleNames . AddRange ( new string [ ] { "CoreUObject" , "Engine" , "Slate" , "SlateCore" , "Projects" // ... add private dependencies that you statically link with here ... } ) ;

As a last step, make sure to mark your plugin’s type as Runtime instead of Developer so you can ship your project successfully.

Save and compile your code. Then, restart the Editor.

Here is the end result: