Saving Whole Actors in SaveGame

How do you save actors and complex objects in SaveGame?
I can easily save simple variables such as bool, int, or FString, but I fail at saving instances of various objects.

Example:
I create an actor from class ABottle with a property Capacity set to 10. I check the SaveGame flag on that property.
I then create a variable holding an instance of that bottle in the SaveGame blueprint (and set the flag on that variable too).
Now if I save that bottle, the value in property Capacity is just ignored. When I reload the actor, its Capacity is 0.

I would think that SaveGame flag is here to choose which properties are to be serialized when the object is (so during a save/load).
Having to store each object’s properties in a separate variable in SaveGame is just a no-go, I need to be able to save objects in cascade.

I’m interested too with this question.

I just tried in c++, same failure.

Thing that work:

  • start the game
  • create a ACharacter (MyCharacter)
  • store it into MySaveGame
  • call SaveGameToSlot (savegame created on disk)
  • call LoadGameFromSlot

MySaveGame->MyCharacter is ok.

Thing that fails:

  • start the game
  • load the savegame into MySaveGame (savegame is still on disk)

Here MySaveGame->MyCharacter is null.

It’s like LoadGameFromSlot can only load objects that already exist … Note that MyCharacter is stored as a reference (actually, a pointer) in MySaveGame.
Maybe I should ask in the c++ forum about that.

Aww, do you mean the SaveGame system in the engine is bugged?

No; I mean doing things by hand is going to be annoying and time consuming. Like the experiences you already had.

The problem here is assigning the data back. You can’t just save the whole actor, you would need to save the relevant data (in form of single integer, float, bool, etc variables) and fill them back in after loading.
This needs some kind of ID system, because you need to make sure that an actor is identified.

Imagine you have 3 bridges in your game that can be raised and lowered. If you save all 3 with a simple boolean (bIsDown), you would have no idea which bridge to match these to.
If you give each bridge a unique ID, you can easily tell which one needs which boolean.

You could now say “Hey, but if I spawn my Actors runtime, I could just save the data and spawn them back into the level with the save data instead of the default data.”
That is kinda true, but you should not forget that this Actor could be referenced by something.
Imagine it’s an AI that is currently reference by another AI. You spawn both, so you need to tell each AI what other AI they are reference with. So you, again, need to work with unique IDs.

That’s interesting but I’m not yet at the point where I can feed my saved object back into the game.
For now, I’m just receiving null from loading a savegame.

Put an int into SaveGame, call SaveGameToSlot, relaunch the game and call LoadGameFromSlot, you get your int back.
Put a complex object into SaveGame, do the same, you’ll get null in the end. That’s what puzzles me. It works perfectly with ints, FString, and TArray, but fails on most UObjects.
It’s like the reference of the object is stored/serialized rather than the object itself.

1 Like

You can save the blueprint Class, which will give you all the default variables, but not the changed variables, as Exi said above ID and variable is what you need.

A simple solution would be to create the following

  1. a struct containing any variables that may change during game play, and you want to retain.(like location, unlocked abilities, health, inventory, weapons equipped etc)
  2. a savegame object containing above struct type and an ID integer (if you have multiple objects (get all items of class and save to array feed into save game) use array of above structs and on load do a for each loop to breakout and assign to objects using the ID == ID of individual object branch.
    so you should be able to save as normal, then on load set the above struct from savegame, when needed feed variables from struct into your character or objects,

doing the above you can save an Array of Structs containing arrays to get around not being able to save Arrays of Arrays :slight_smile:

I did the above to save items in multiple storage chests in level, so same objects and amounts remain in specific chests between game play sessions, 4.15 no issues.

No need to use ID numbers; the name of an object is the ID itself, Unreal will not allow you to have two objects or actors of same name in the game world. If they have the same display name, the real name is different under the hood.

You convert the object to a binary array, then save that in the save game.
(I know you were looking for a BP solution, but I’ve included the following C++ solution because you said you’d already tried C++ too)

Here is the link I think I based my system on
https://answers.unrealengine.com/questions/35618/savingloading-an-array-of-objects.html

and it looks like there might be some useful info on this page too
https://wiki.unrealengine.com/Save_System,Read%26_Write_Any_Data_to_Compressed_Binary_Files

It’s actually much simpler than it seems from those links. I’ll include my functions to help you, just don’t expect any polish (I basically got it working at minimum level and haven’t gone back for a while)
I’ve changed some names to be more generic.


struct MySaveGameArchive : public FObjectAndNameAsStringProxyArchive
{
	MySaveGameArchive(FArchive& InInnerArchive)
		: FObjectAndNameAsStringProxyArchive(InInnerArchive, false)
	{
		ArIsSaveGame = true;
	}
};

TArray<uint8> ObjectSaver(UObject * SaveObject)
{
	TArray<uint8> ObjectData;
	if (SaveObject == nullptr) return ObjectData;

	FMemoryWriter MemoryWriter(ObjectData, true);
	// use a wrapper archive that converts FNames and UObject*'s to strings that can be read back in
	MySaveGameArchive MyArchive(MemoryWriter);
	// serialize the object
	SaveObject->Serialize(MyArchive);

	return ObjectData;
}

void ObjectLoader(UObject * LoadObject, TArray<uint8> ObjectData)
{
	if (ObjectData.Num() <= 0) return;
	//TODO how to check if valid binary?

	FMemoryReader MemoryReader(ObjectData, true);
	MySaveGameArchive MyArchive(MemoryReader);
	LoadObject->Serialize(MyArchive);
}

edit: to clarify, I’m calling both those functions from BP
To save an object you just pass in the object, it returns an array of bytes that you can store in SaveGame like any other variable
To load an object, you spawn an actor of the proper class, then pass it into this function along with the previously saved data.

So if I understand correctly, instead of storing references to the objects you want to save into MySaveGame (which seems to fail), you store their serialized form (TArray<uint8>). That’s clever :slight_smile:
Thanks!

That is not the “full actor” though.
It only serializes exposed UProperty; you can’t serialize whole objects like you do with .NET binary serialization.

Indeed. That actually only serializes fields that are UProperty, with the SaveGame flag on, and that are not pointers/references.
But that’s ok. I only care about those properties I explicitely mark as SaveGame. As for pointers, each object can handle its own serialization by calling its fields “serialize” method, so it’s done in cascade. Should be fine for my needs :slight_smile:
Thanks again!

If you want to save actor references, I found a good article about this in the new wiki: SaveGame Pointers and Structs

Important notes:

  • You need to recreate dynamic actors and UObjects before actually start deserializing, so references can be properly restored.
  • UObject “outer” parameter needs to be stored as well. The FObjectAndNameAsStringProxyArchive archive uses Obj->GetPathName(), which includes the name and outer names.

I know this is an old thread, but it’s in the top search results and I couldn’t find good guides about it, till now. Hope this helps other lost souls.