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.
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.
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.
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.
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
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)
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
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)
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
: FObjectAndNameAsStringProxyArchive(InInnerArchive, false)
ArIsSaveGame = true;
TArray<uint8> ObjectSaver(UObject * SaveObject)
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
// serialize the object
void ObjectLoader(UObject * LoadObject, TArray<uint8> ObjectData)
if (ObjectData.Num() <= 0) return;
//TODO how to check if valid binary?
FMemoryReader MemoryReader(ObjectData, true);
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
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