Unreal Custom Editor

What do you get after reading?

Export, import, overwrite custom resources

The effect is as follows
Export, you can see an additional file suffixed with simpAnim on the desktop

Import, import the file we exported above, you can see the value inside or 18 after import

Overwrite, we will modify the Value in the original file and drag in the first gif exported file, you can see that the Value of the file has changed to 18 again

1. Export

#pragma once

#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Exporters/Exporter.h"
#include "SimpleAnimationAssetExporter.generated.h"

UCLASS()
class USimpleAnimationAssetExporter : public UExporter
{
	GENERATED_UCLASS_BODY()

	virtual bool SupportsObject(UObject* Object) const override;
	virtual bool ExportBinary(UObject* Object, const TCHAR* Type, FArchive& Ar, FFeedbackContext* Warn, int32 FileIndex = 0, uint32 PortFlags = 0) override;
};

We need to inherit UExporter and override these two methods, which represent:
SupportsObject: Objects that support export
ExportBinary: The form of data (or what kind of data is saved) that an export object stores.

#include "SimpleAnimationAssetExporter.h"
#include "SimpleAnimationAsset.h"

USimpleAnimationAssetExporter::USimpleAnimationAssetExporter(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	SupportedClass = USimpleAnimationAsset::StaticClass();
	PreferredFormatIndex = 0;
	FormatExtension.Add(TEXT("simpAnim"));
	FormatDescription.Add(TEXT("Simple Animation Asset"));
}

bool USimpleAnimationAssetExporter::SupportsObject(UObject* Object) const
{
	bool bSupportsObject = false;
	if (Super::SupportsObject(Object))
	{
		bSupportsObject = IsValid(Cast<USimpleAnimationAsset>(Object));
	}
	return bSupportsObject;
}

bool USimpleAnimationAssetExporter::ExportBinary(UObject* Object, const TCHAR* Type, FArchive& Ar, FFeedbackContext* Warn, int32 FileIndex, uint32 PortFlags)
{
	USimpleAnimationAsset* Asset = CastChecked<USimpleAnimationAsset>(Object);

	if (!IsValid(Asset))
	{
		return false;
	}
	Ar << Asset->IntValue;
	return true;
}

It is important to note that in the constructor, we define the "Export Supported Resource Types", SupportedClass, with the following parameters representing:
1. Priority index using the suffix, file description defined below
2. File Suffix
3. Document description
The suffixes and instructions are shown below in Save as Type.
In the ExportBinary function, simply save the value of IntValue in bytes to a file, so you'll see that str doesn't actually save it and can't load it.

2. Import

#include "CoreMinimal.h"
#include "Factories/Factory.h"
#include "SimpleAnimationAssetFactoryImport.generated.h"

UCLASS()
class USimpleAnimationAssetFactoryImport : public UFactory
{
	GENERATED_UCLASS_BODY()
public:
	virtual UObject* FactoryCreateFile(UClass* InClass, UObject* InParent,
		FName InName, EObjectFlags Flags, const FString& Filename, const TCHAR* Parms,
		FFeedbackContext* Warn, bool& bOutOperationCanceled) override;
};

Importing is equivalent to recreating a new resource by using it, so it inherits from the "UFactory" as we originally defined it, and we need to rewrite its FactoryCreateFile to create the corresponding UE internal resource when importing

#pragma once

#include "SimpleAnimationAssetFactoryImport.h"
#include "SimpleAnimationAsset.h"
#include "Containers/UnrealString.h"
#include "Misc/FileHelper.h"

USimpleAnimationAssetFactoryImport::USimpleAnimationAssetFactoryImport(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
	Formats.Add(FString(TEXT("simpAnim;")));
	SupportedClass = USimpleAnimationAsset::StaticClass();
	bCreateNew = false;
	bEditorImport = true;
}

UObject* USimpleAnimationAssetFactoryImport::FactoryCreateFile(UClass* InClass, UObject* InParent,
	FName InName, EObjectFlags Flags, const FString& Filename, const TCHAR* Parms,
	FFeedbackContext* Warn, bool& bOutOperationCanceled)
{
	USimpleAnimationAsset* SimpleAnimationAsset = nullptr;
	TArray<uint8> Bytes;

	if (FFileHelper::LoadFileToArray(Bytes, *Filename) && Bytes.Num() >= sizeof(int32))
	{
		SimpleAnimationAsset = NewObject<USimpleAnimationAsset>(InParent, InClass, InName, Flags);
		for (uint32 i = 0; i < sizeof(int32); ++i) {
			SimpleAnimationAsset->IntValue |= Bytes[i] << (i * 8);
		}
	}

	bOutOperationCanceled = false;

	return SimpleAnimationAsset;
}

It is important to note that in the constructor, we define the "Import Supported Resource Type", which is SupportedClass, with several other parameters representing:
1. Supported File Suffixes (List of Factory Supported Formats)
2. Whether to return default values from CanCreateNew() when importing
3. Allow factories to import objects from files

In the FactoryCreateFile method, simply read the value of IntValue in bytes into the UE and assign it to IntValue

3. Coverage

#pragma once

#include "CoreMinimal.h"
#include "Factories/Factory.h"
#include "EditorReimportHandler.h"
#include "SimpleAnimationAssetFactoryImport.generated.h"

UCLASS()
class USimpleAnimationAssetFactoryImport : public UFactory
	, public FReimportHandler
{
	GENERATED_UCLASS_BODY()
public:
	virtual UObject* FactoryCreateFile(UClass* InClass, UObject* InParent,
		FName InName, EObjectFlags Flags, const FString& Filename, const TCHAR* Parms,
		FFeedbackContext* Warn, bool& bOutOperationCanceled) override;
	virtual bool CanReimport(UObject* Obj, TArray<FString>& OutFilenames) override;
	virtual EReimportResult::Type Reimport(UObject* Obj) override;

};

We need to inherit an additional class FReimportHandler in import, which is used for re-importing, that is, overwriting

Several new import-related functions have been added:
CanReimport: Is it possible to re-import
Reimport: Actual data operations imported

bool USimpleAnimationAssetFactoryImport::CanReimport(UObject* Obj, TArray<FString>& OutFilenames)
{
	return true;
}

EReimportResult::Type USimpleAnimationAssetFactoryImport::Reimport(UObject* Obj)
{
	if (IsValid(Obj)) {
		bool OutCancel = false;
		UObject* NewObj = ImportObject(Obj->GetClass(), Obj->GetOuter(),
			Obj->GetFName(), Obj->GetFlags(), PreferredReimportPath,
			nullptr, OutCancel);
		if (!OutCancel) {
			USimpleAnimationAsset* SimpleAnimationAsset = Cast<USimpleAnimationAsset>(NewObj);
			return EReimportResult::Succeeded;
		}
	}
	return EReimportResult::Failed;
}

Whether import can be re-imported or not, we define it as True, where import restrictions can be increased depending on requirements

In Reimport we convert the imported object to the resource type we defined, USimpleAnimationAsset

Tags: C++ unreal

Posted on Fri, 03 Sep 2021 00:11:14 -0400 by sstoveld