What is the best way to handle Saving/Loading an Array of Objects?

I’ve looked at tutorials, Tappy Chicken and Rama’s example, but can someone tell me what is the “recommended” way to save and load an array of objects?

More specifically, procedurally generated actors, which seem to break the FArchive derived routines in the engine (they use FindObject, but since they are procedurally generated, they are not assets). I can code up something custom, but I was wondering if I missed something built-in that would do the transforms, materials, etc.

1 Like

#The << Operator

“something built-in that would do the transforms, materials, etc.”

Check out ArchiveBase and Archive.h

If you have a UE4 type, try using the << operator on it!

Transforms, Materials, Etc should all be covered by the << operator already.

Then for your class you define your own use of the << operator, which uses the existing UE4 type << usage

Here’s an example:

//JSMA
USTRUCT()
struct FRedStruct
{
	GENERATED_USTRUCT_BODY()

	//Vars
	UPROPERTY()
	int32 					VictoryVibe;
	
	UPROPERTY()
	FTransform 			VictoryTransform;
	
	UPROPERTY()
	FName 				MeshPath;

	UPROPERTY()
	bool StartsDisabled;
	
	//default properties
	FRedStruct()
	{
		VictoryVibe = 0;	
		MeshPath = FName(TEXT(""));
		StartsDisabled = false;
	}
};


										//could use a class instead of struct
FORCEINLINE FArchive &operator <<(FArchive &Ar, FRedStruct& TheStruct )
{
	Ar << TheStruct.VictoryVibe;
	Ar << TheStruct.VictoryTransform;
	Ar << TheStruct.MeshPath;
	Ar << TheStruct.StartsDisabled;
		
	return Ar;
}

#Spawn and Load

Final step when loading is to spawn fresh copy of class and then use the << operator on it! (or fresh struct)

#Enjoy!

:slight_smile:

Rama

1 Like

Thanks Rama, I know about that operator. What I am looking for is the proper way to handle procedural objects. I had looked at the header files, and saw that everything derived from FMemoryArchive has the << operator disabled for objects. And the ones for UObject (in ArchiveUObject.h) require assets, for which there are none in procedural generated objects.

I have an entire in-game editor where I load and save characters, destructible meshes, static mesh actors, materials, material instances, and much more!

The process is pretty simple

  1. Saving: save the core data that you need that is unique to that actor / object
  2. Loading: load a fresh instance of the actor / object, and then reapply the core data you saved to hard disk.

For procedural mesh

save the triangle data to hard disk, and also the transform and material path

when loading, load a newly spawned procedural mesh with no data, then load in the triangle data from hard disk

it’s not hard :slight_smile:

I’ve already set the whole thing up for my own use of procedural meshes, thanks for doing all that research on this topic!

#Dynamic Load Object

to load materials you can use my wiki on the subject

Rama

Thanks again Rama, but it is not the answer I am looking for. I too have a save system that works the way you describe, but there is a whole object archiving infrastructure included in the engine. I want to know what the recommended way of using it is, instead of doing low-level archiving.

#Unresolved

hmm okay, good luck with that, you will always save space by doing more low-level though :slight_smile:

file sizes in my project are positively tiny as a result :slight_smile:

Rama

There is currently no good tutorial on how to do a custom save game setup for your game, so it will require a good bit of custom coding on your part. But here’s some info that may help out:

For fortnite, we’re using a system built on the SaveGame property flag. If you set the ArIsSaveGame archive flag on an archiver, it will only serialize properties that have that flag set on them. Some of the basic properties are set this way in the engine, and you can then tag your game specific properties.

So, you need to set up a savegame archive. For fortnite, we’re using a proxy wrapper based on the Objects-as-strings proxy wrapper:

struct FFortniteSaveGameArchive : public FObjectAndNameAsStringProxyArchive
{
    FFortniteSaveGameArchive(FArchive& InInnerArchive)
        :   FObjectAndNameAsStringProxyArchive(InInnerArchive)
    { 
        ArIsSaveGame = true;
    }
};

Then, you can use that proxy archive to serialize a record for an actor or object you wish to. Here’s our save code, that writes to a simple struct that has the actor’s class, transform, and a TArray storing the bytestream:

ActorRecord->ActorClass = Actor->GetClass();
ActorRecord->ActorTransform = Actor->GetTransform();
ActorRecord->ActorName = Actor->GetName();
 
FMemoryWriter MemoryWriter(ActorRecord->ActorData, true);
 
// use a wrapper archive that converts FNames and UObject*'s to strings that can be read back in
FFortniteSaveGameArchive Ar(MemoryWriter);
 
// serialize the object
Actor->Serialize(Ar);

And here’s our code that restores that actor:

FActorSpawnParameters SpawnParameters;
SpawnParameters.Name = ActorRecord->ActorName;
AFooActor *NewActor = GetWorld()->SpawnActor<AFooActor>(ActorRecord->ActorClass, TempVector, TempRotator, SpawnParameters);
 
FMemoryReader MemoryReader(ActorRecord->ActorData, true);
FFortniteSaveGameArchive Ar(MemoryReader);
NewActor->Serialize(Ar);

So we’re using this system for Fortnite, which has a complicated system of procedural generation. Basically there’s a state machine that handles either generating the actors randonly, or restoring from a record like above. We also use a similar technique to save a player’s inventory, but instead of saving a whole actor we just serialize a specific Struct, which is a great place to put your array of objects:

FMemoryWriter MemoryWriter(PlayerRecord->BackpackData, true);
FFortniteSaveGameArchive Ar(MemoryWriter);
Inventory->Serialize(Ar);
1 Like

I’ve been searching everywhere, reading and re-reading the documentation to figure out how to most easily serialize all actors when saving and then spawning them again when loading. This is the best answer, but still only shows part of the solution and the only way I’ve managed to get save/load working is using Rama’s code as a starting point and then writing a SaveLoad function for every class which then handle spawning actors when loading. This is a lot of work and very error prone, so I would LOVE to see a working example of saving a bunch of actors.

I understand that ActorRecord is a struct, but is ActorData:

TArray< uint8 >  ActorData?

When you call Actor->Serialize(Ar); does that write to this ActorData?

Then on load you use the ActorRecord to create the SpawnParameters, but I’m not sure how the ActorRecord is itself loaded?

A small, but complete save/load example serializing objects would really help me get going.

Hey Rama,

I’ve been following your Custom Save to Binary Files post on the wiki the past couple days: A new, community-hosted Unreal Engine Wiki - Announcements - Unreal Engine Forums

I’m having trouble figuring out how to dynamically save a whole set of objects at runtime based on the ‘SaveGame’ bool that’s a UPROPERTY specifier (or found within a blueprint variable). As it looks right now, it seems like your method needs to explicitly state what kinds of variables will be loaded into the function (unless I’m missing something).

I’ve implemented your approach into a base class inheriting from Actor, so that I can drag it into the scene in order for my UI blueprints to call it. I’ve created Save, Load and SaveLoad functions just like the ones you provided, as well as an additional function that gets called from blueprint (since it didn’t like accepting a SaveBuffer as an input for a UFUNCTION). Currently I’d like to just grab the player controller’s location to save and load it. I can do this in a more convoluted way by passing in the actor through blueprints, grabbing it’s world location in code and storing that vector into the save and load functions. Doing this for every individual variable is highly unperformative, as you could imagine.

How can I pass variables into these functions with explicitly stating all of their types? Is there a way to store all UPROPERTIES under the ‘SaveGame’ specifier into some sort of array that’s passed into this class?

Currently I’m looking into this solution:

The issue is that I don’t understand how to combine your binary method with these other methods of using LoadSaveFromSlot, etc. I’d love to continue using binary conversion because it seems like a much cleaner way to store and load variables.

I apologize if this reply doesn’t really have to do with the original post, but these forums contain no way to private message users from what I can tell

Yes, ActorData is TArray. And you can save the ActorRecord in a save game object.

Hi Guys, I was struggling with this for a bit and still learning best way to do it. I’ve create a wiki page showing what i’ve setup so far if it might help with your setup in anyway, posting here because this is one of the most complete answers and most helpful for me so just thought I could add to it if it helps anyone else:

Feel free to offer any suggestions to update the page or clarify anything if you want :slight_smile:

I’d still have a question if possible. For example if a character is in middle air swinging his weapons around will that be serialized too?

For my example I have an RPG, however at the moment I’m using an workaround so that when the player is in combat a save is not possible, because I don’t know how to serialize the animation state.

My question is, if I’m using something like yours, can I also serialize the state of animated objects? That is is a character is right in the middle of an attack will that be serialized/saved? That requirement is quite standard for all games, however I’m not sure it’s easily do-able in Unreal?

Thanks & regards!

I believe everything can be serialized if you just use the right property to record it. Just like the animation, you can use a float or other StateMachine things to record how long it has been after the last state. And when loaded from the disk you can just set the state to exactly where it was. But I highly recommend you check the necessity of all this.

I know it’s been a couple years so maybe some things have changed, but I was wondering something.
Howe is it that you are able to a generated file (specifically your save game archive struct) without having a GENERATED_BODY() and USTRUCT() macros?

This also works perfectly for when you want to serialize custom UObjects.

I use this method to serialize a custom composite UObject (a parent UObject consisting of a variety of child UObjects) then send it via TCP to the client and deserialize the object there.

Thanks for showing us this method thumbsup x10

The link was lost after the migration. I found similar topic (maybe the same) in the new wiki:
SaveGame Pointers and Structs

Posting it so it may help other lost souls.

1 Like