Hi, I’m pretty new to UE and I’m trying to undestand how UPackages work.and specifically the ::SavePackage() function.
What I want to do is:
- Define a UDataAsset in C++ (Plain Old Data)
- Populate it manually in the editor and Save it
- Feed it as input to a function in C++ that produces an output
- Save the output and a copy of the input from C++ to another UDataAsset visible from the editor
- Feed that resulting UDataAsset to an Actor (by UPROPERTY drag and drop from the editor)
So far all is good until step 4.
The two UAssetData definitions
UCLASS()
class UInputData: public UDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere)
uint16 some_int_var = 5000;
UPROPERTY(EditAnywhere)
float some_float_var= 0.10f;
};
UCLASS()
class UCombinedResult : public UDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(VisibleAnywhere)
UInputData* InputData;
UPROPERTY(VisibleAnywhere)
UCustomData CustomData;
};
And the c++ class that handles the generation of the output:
UCLASS()
class ATransformer : public AActor
{
public:
UPROPERTY(EditAnywhere)
UInputData* InputData;
private:
UCombinedResult* Output;
UPackage* Package;
UCustomData MyCustomData;
public:
void Init()
{
// Create the Package
FString PackageName = TEXT("/Game/Generated/MyOutput");
Package = CreatePackage(nullptr, *PackageName);
Package->FullyLoad();
Output = NewObject<UCombinedResult >(Package, TEXT("MyOutput"), RF_Public | RF_Standalone | RF_MarkAsRootSet);
if (InputData == nullptr) // Creates the default object
InputData = NewObject<UInputData>(Package, TEXT("InputData"));
else // Duplicates the object but this time inside the package
InputData = DuplicateObject<UInputData>(InputData, Package, TEXT("InputData"));
Package->MarkPackageDirty();
}
void Process()
{
MyCustomData = DoWork(InputData);
}
void Export()
{
Output->CustomData = MyCustomData;
Output->InputData = InputData;
Package->MarkPackageDirty();
FAssetRegistryModule::AssetCreated(Output);
FString PackageName = TEXT("/Game/Generated/MyOutput");
FString PackageFileName = FPackageName::LongPackageNameToFilename(PackageName, FPackageName::GetAssetPackageExtension());
UPackage::SavePackage(Package, Output, EObjectFlags::RF_Public | EObjectFlags::RF_Standalone, *PackageFileName, GError, nullptr, true, true, SAVE_NoError);
}
protected:
void BeginPlay()
{
Init();
Process();
Export();
}
};
So I basically create the new Output object and mark it as RF_Standalone and RF_Public , that to my understanding makes it visible to the editor and marks it as the “head” of the package
The SavePackage documentation says:
Save one specific object (along with any objects it references contained within the same Outer) into an Unreal package.
The Save operation is actually successful and I am able to see in the editor an asset of Type UCombinedResult and if I open it it correctly shows that it references a new UInputData object (not the one I gave to it as input but a copy of it not found anywhere else in the editor). My UCustomData object shows up but its broken (but thats probably due to improper serialization on my part, but that is another problem).
The result is that as long as I keep the editor alive I can use my UCombinedResult (output object) just fine and it actually holds the data that I need, but when I shut down and restart, even though I can see the .uasset file being created in the file explorer, the UCombinedResult asset is there but it doesnt load.
If I double click it in the console appears this
LogLinker: Warning: Unable to load MyOutput with outer Package /Game/Generated/MyOutput because its class does not exist
LogUObjectGlobals: Warning: Failed to find object 'Object /Game/MapData/MyOutput.MyOutput'
Thanks to everyone that read though all this. Hope you can give me some explaination around Unreal Packages (and specifically saving them) since you kinda find them everywhere in the engine but the Official documentation has nothing about them.