Saving and loading

Hi,

I am currently trying to save the state of the actors within my level to file for a little RPG I am working on. For this I read through Rama’s save-system-wiki-article(https://wiki.unrealengine.com/Save_System,Read%26_Write_Any_Data_to_Compressed_Binary_Files), which has answered me some questions on how I would achieve that. I believe I would be able to write variables to a file and read them back in, so far so good. But unfortunately it’s not as easy as saving a few integers and then at one point loading them back in. There’s something about the whole saving-process, I just can’t wrap my head around: How would I repopulate the actors with their respective variables? Especially, when I don’t directly spawn in the actors myself but load a level I built with the editor, but I still want to save the old actors (Or the fact they were destroyed) and any new ones. Or pointers, how would saving pointers work? I am completely unsure where to start with this, as there seems to be so much I’d need to do to just to restore a few simple actors. The only thing I can think of right now is to start with an empty level and literally write out every single actor to file, spawn it in manually and then somehow keep track of which actor needs a pointer to which actor (Not even sure how that would work because I can’t know beforehand which actors I’d need pointers to)… Nope, doesn’t sound right to me. There’s probably some better way, some kind of magic UE4-thing that makes this a little simpler. Or have I just missed something while reading through Rama’s article?

I’ll just make a specific case up that should cover most of my problems:

Let’s say I had everything set up as explained by Rama to save and load variables. Let’s imagine I were to create a game where the player just places some sorts of waypoints in the world (To create streets or something along those lines. I’m horrible at making up scenarios): Every waypoint is a visible actor with a mesh, location and rotation, but also it would have an array of pointers that contains all of the adjacent waypoints. Now because it wouldn’t be difficult enough as is, there are prebuilt levels in this game that already contain some of those waypoints and various movable meshes whose position and rotation also need to be saved.

To me this currently seems like a completely impossible thing to do to be honest. I don’t have any idea where to even start with this. I think I don’t understand something about the very core-concept of saving with UE4 and there doesn’t seem to be a lot of examples either. So to sum my questions up: How, would I use Rama’s saving-technique to save the level in the case posted above?

Hopefully somebody can clear this up a little bit :slight_smile:

Thank you in advance!

I would suggest looking into the ‘FObjectAndNameAsStringProxyArchive’ class in the engine. I use it to serialize oodles of data! The only is, I don’t know how it’ll handle those references, but I thiiiiink that will actually patch them together if the object exists and is of the same name. Not sure. If you need waypoints that referenced each other, use the archive (or a save object) to save that transform data, and make some kind of loose id setup for all waypoints in the world. Save systems are one of the most aggravating parts of game dev (in my opinion).

I find it depends entirely on the exact situation how you save the data, I’ve yet to find a ‘one size fits all’ solution. I have a mixture of archive serialization and save game objects to achieve a decent result.

Hopefully that points you in the right direction!

I’m sorry it took me so long to write back. Thanks for your answer first of all!

I ended up realising, that in my case I don’t need to save a lot of different actors. I just ended up saving all of my items and when loading first destroying all of the items in the level and spawn them in again.

Most likely I’m not using an ideal solution here, since I just let USaveGame do everything for me. I derived from USaveGame, gave it a few structs for items, the player and doors (Those are pointed to by buttons, thus I decided not to destroy them but just save the necessary variables and the names of the actors since I don’t spawn in new doors at runtime [Yet]) and then used the following functions to save and reload everything (As explained here: https://docs.unrealengine.com/latest/INT/Gameplay/SaveGame/Code/index.html):

UGameplayStatics::CreateSaveGameObject
UGameplayStatics::SaveGameToSlot
UGameplayStatics::LoadGameFromSlot

I figured it would be good to have something working first of all. I guess serializing the actors would be more efficient harddrive-space-wise, but I think for the moment this works just fine. :slight_smile:

Hi again,

I am currently trying to redo my savesystem since the way I currently do it, the more things I need to save the messier it gets (All I currently save are the player, doors, items and harvestable plants, and already it’s a mess). Saving a single new variable is just too much extra work every time. But I’m struggling with this for multiple days now and I can’t think of any good solution, thus here I am again.

Let me first explain what I want to achieve:

Ideally in my new savesystem every saveable actor would handle it’s saving himself. I feel it is bad practice if I handle all the saving to the saveobject and loading from it from a single function that iterates all the actors and has special code for different types. First of all I feel this is counterintuitive when working with an OOP language. From what I learned, in OOP you want your objects to be self-sufficient if possible. Also problems would arise when inheriting from a class that I already save - and even if no problems would arise, it would still be inefficient iterating through some actors multiple times. Now I began to think about leveldesign, and especially puzzles. I would rather not have to create entirely new classes for basic puzzles that need to save some very basic states. Let’s for example imagine a puzzle where, in order to open a door, the player has to press a button exactly 3 times. Of course I would want to save how many times the button was pressed, but it doesn’t seem like a good solution to create an entirely new subclass just to make a simple int saveable. That means, ideally the system would be able to save variables per-instance and at-runtime.

So to sum it up: I need to have every saveable actor handle it’s own saving, and the saving has to be dynamic enough to be used with blueprints and without having to use a new class if all I want is to save a new variable.

What I am doing so far (Which works… kind of… But not really :slight_smile: ):

First of all I decided to give my actors a SaveComponent that will handle saving and loading and before saving and after loading call a PreLevelSave(UMySaveGame* SaveObj) and PostLevelLoad(UMySaveGame* SaveObj) function that have a pointer to the SaveObject as parameter that I want to save/that I just loaded. So far so good, but now is the point where serious flaws in my plan arise: How can I store any type of variable in my SaveClass without knowing what I’m going to need beforehand? So far, I used TMaps filled with structs to store the actors. For example I had a TMap that stored structs which held a few variables I needed to save the state of the door: bool IsOpened, FVector Location etc., or a Playerstruct. But how can I store an arbitrary amount of variables of arbitrary type? TMaps and TArrays, of course, can only contain specific types.

Soooo… The only solution I could come up with was to create a TMap for every type I need and have About 200 lines of code just for getters and setters for a few basic types. I do realize this solution is horrible of course, but I can’t think of any better way frankly. But I’m assuming somewhere out there, there is someone who had to deal with the same problems but was actually competent enough to solve it :slight_smile: I appreciate any help. At this point I don’t think I will find a good solution myself, I pretty much spent the last few days trying to figure this out.

To sum my question up:

How can I store an arbitrary amount of completely arbitrary typed variables in my SaveClass to later retrieve them after loading (Which means there has to be some kind of index which I can use to find them)?

Thank you for reading!

Hello, here’s a snippet on how I save objects

Here’s the struct I’m going to save, it has a class and a name for the object


struct FSaveGameActorDesc
{
	TSubclassOf<AUnit> UnitClass;
	FString UnitName;
};


Here’s one of the functions I call during saving


void USaveGame::SaveHeroRecords(FArchive& Ar)
{
	if (PlayerState)
	{
		// make hero records, grab all the heroes' class and name and put them in the struct
		TArray<FSaveGameActorDesc> HeroRecords;
		if (Ar.IsSaving())
		{
                        // Only do this when saving!
			HeroRecords.AddDefaulted(PlayerState->Heroes.Num());
			for (int32 i = 0; i < PlayerState->Heroes.Num(); ++i)
			{
				HeroRecords*.UnitClass = PlayerState->Heroes*->GetClass();
				HeroRecords*.UnitName = PlayerState->Heroes*->GetName();
			}
		}
		// save/load the struct!
		Ar << HeroRecords;
		TArray<AUnit*> Heroes;
		if (Ar.IsLoading())
		{
                        // When loading, just go through the records and respawn every unit
			for (int32 i = 0; i < HeroRecords.Num(); ++i)
			{
				FActorSpawnParameters p;
				p.Name = *HeroRecords*.UnitName;
				p.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
				p.bNoFail = true;
				p.Owner = GameTeam;
				AUnit* Unit = World->SpawnActor<AUnit>(HeroRecords*.UnitClass, p);
				PlayerState->Heroes.Add(Unit);
			}
		}
                // During loading, at this point, your unit's properties (HP, mana, etc.) will be at default, so the below serialize code will "override" them to the saved values
                // Save/load unit's properties
                for(int32 i = 0; i < PlayerState->Heroes.Num(); ++i)
                {
                        Ar << PlayerState->Heroes*->Hp;
                        Ar << PlayerState->Heroes*->Mana;
                        Ar << PlayerState->Heroes*->Hunger;
                        // ... etc
                }
	}
}

Hope this help!

It is a really useful snippet, thanks!

Looks interesting so far :slight_smile: From what I can tell this is more Ramas approach. Just to clear things up a bit though: In the archive I don’t have to specify the variabletypes I’m going to store with it, do I? I could just, without touching anything else, save a new variable now by saying Arr << MyVar? And what variable is taken out of Arr just depends on the order I read them in? If that were all the case that would be great and something I misunderstood before. I’m assuming the variables are immediately serialized, so there is no need anymore to store them as a specific type in the archive? I’ll need to look a little more into this then the next days, but at least now I understand why this approach is better than just using UE’s SaveGame-object and have a lead as to what I can do to improve my current system. Thanks you for posting, your snipped cleared up a few things indeed it seems :slight_smile:

HammelGammel,

You’re exactly right, you don’t have to worry about primitive variables when doing “Ar << MyVar”, because FArchieve already understood them.
You’re also right that read order must match write order exactly! if you change your MyVar to another type of different size, you’ll also be in trouble when loading a save!

Saving UClass*

After looking into this way of saving a little bit more, I’ve pretty much got it working (And I really like this apporach a lot more!). One problem I am having though is my inventory. Saving an array of my FInventoryStruct throws me a compilererror, so I’m assuming saving structs, or at least arrays of structs isn’t supported by the archive. My next idea was to split the array in two. The struct contains an integer for the amount of the item and a TSubclassOf which holds the itemclass. Unfortunately trying to save/load a TSubclassOf or UClass* to/from the archive crashes the game with the following error:

Assertion failed: 0 [File:D:\Program Files\Epic Games\4.11\Engine\Source\Runtime\Core\Public\Serialization\Archive.h] [Line: 54]

UE4Editor_Core!FDebug::AssertFailed() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\core\private\misc\outputdevice.cpp:430]
UE4Editor_MyProject_751!FMemoryArchive::operator<<() [d:\program files\epic games\4.11\engine\source\runtime\core\public\serialization\archive.h:55]
UE4Editor_MyProject_751!AMyProjectCharacter::SaveLoad() [c:\users\daniel\documents\unreal projects\myproject\source\myproject\myprojectcharacter.cpp:863]
UE4Editor_MyProject_751!AMyProjectCharacter::SaveTemp() [c:\users\daniel\documents\unreal projects\myproject\source\myproject\myprojectcharacter.cpp:827]
UE4Editor_MyProject_751!AMyProjectCharacter::Quicksave() [c:\users\daniel\documents\unreal projects\myproject\source\myproject\myprojectcharacter.cpp:762]
UE4Editor_Engine!FInputActionUnifiedDelegate::Execute() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\engine\classes\components\inputcomponent.h:181]
UE4Editor_Engine!UPlayerInput::ProcessInputStack() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\engine\private\userinterface\playerinput.cpp:1211]
UE4Editor_Engine!APlayerController::ProcessPlayerInput() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\engine\private\playercontroller.cpp:2310]
UE4Editor_Engine!APlayerController::TickPlayerInput() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\engine\private\playercontroller.cpp:3878]
UE4Editor_Engine!APlayerController::PlayerTick() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\engine\private\playercontroller.cpp:1980]
UE4Editor_Engine!APlayerController::TickActor() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\engine\private\playercontroller.cpp:3954]
UE4Editor_Engine!FActorTickFunction::ExecuteTick() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\engine\private\actor.cpp:110]
UE4Editor_Engine!FTickFunctionTask::DoTask() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\engine\private icktaskmanager.cpp:228]
UE4Editor_Engine!TGraphTask<FTickFunctionTask>::ExecuteTask() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\core\public\async askgraphinterfaces.h:886]
UE4Editor_Core!FNamedTaskThread::ProcessTasksNamedThread() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\core\private\async askgraph.cpp:779]
UE4Editor_Core!FNamedTaskThread::ProcessTasksUntilQuit() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\core\private\async askgraph.cpp:526]
UE4Editor_Core!FTaskGraphImplementation::WaitUntilTasksComplete() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\core\private\async askgraph.cpp:1534]
UE4Editor_Engine!FTickTaskSequencer::ReleaseTickGroup() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\engine\private icktaskmanager.cpp:486]
UE4Editor_Engine!FTickTaskManager::RunTickGroup() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\engine\private icktaskmanager.cpp:1373]
UE4Editor_Engine!UWorld::RunTickGroup() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\engine\private\leveltick.cpp:702]
UE4Editor_Engine!UWorld::Tick() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\engine\private\leveltick.cpp:1187]
UE4Editor_Engine!UGameEngine::Tick() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\engine\private\gameengine.cpp:989]
UE4Editor!FEngineLoop::Tick() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\launch\private\launchengineloop.cpp:2644]
UE4Editor!GuardedMain() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\launch\private\launch.cpp:142]
UE4Editor!GuardedMainWrapper() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\launch\private\windows\launchwindows.cpp:126]
UE4Editor!WinMain() [d:\buildfarm\buildmachine_++ue4+release-4.11\engine\source\runtime\launch\private\windows\launchwindows.cpp:200]
UE4Editor!__scrt_common_main_seh() [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl:264]
kernel32
ntdll

This is how my code looks:


	TArray<int32> ItemAmounts;
	TArray<UClass*> ItemClasses;

	if (Ar.IsSaving())
	{
		for (int i = 0; i < Inventory.Num(); i++)
		{
			ItemAmounts.Add(Inventory*.Amount);
			ItemClasses.Add(Inventory*.Item);
		}
	}

	Ar << LevelName;
	Ar << Location;
	Ar << Rotation;
	Ar << Health;
	Ar << MaxHealth;

	// -- Save/load inventory -- //
	Ar << ItemAmounts;
	Ar << ItemClasses; // <--- This causes the crash


Just saving ItemAmounts works fine and it also crashes if it’s not an array, but just a single TSubclassOf/UClass* that I’m trying to save. Apparently in order to save those, with this approach I have to do something different. Sorry if it’s obvious, but I’m really not sure what I can do to get this working. Ideally I would just be able to somehow save the TArray of my custom struct, but if that’s just not possible, knowing a way to save UClasses would also work.

Thank you for reading :slight_smile:

Sorry, I miss pasting a piece of code!
Here comes the magic of operator overloading!


FArchive &operator <<(FArchive &Ar, FSaveGameActorDesc& Struct)
{
	Ar << Struct.UnitClass;
	Ar << Struct.UnitName;
	return Ar;
}


If you stick that in the same place you declare FSaveGameActorDesc, then


Ar << HeroRecords;

will work without you splitting the variables.

As for the crash, I’m just guessing the reason it crashes may be caused by trying to save unsupported type.
You may have to wrap your “Item class” like so TArray<TSubclassOf<AItem>>, not a raw naked pointer TArray<UClass*> like you declared at the top.

Thank you for answering first of all. I really appreciate it! That explains how you saved your struct (Which I did notice today when looking through your snippet again and was wondering if you overloaded the operator for this to work :smiley: ). Still no luck with the crash though. I did try it with TSubclassOf too. The following causes a crash for me every time I call the function (That is empty apart from this):



void AMyProjectCharacter::SaveLoad(FArchive& Ar)
{
    TSubclassOf<AActor> Test = GetClass();

    if (Test)
        Ar << Test;
}

This is the line of the error in Archive.h:



virtual FArchive& operator<<( class UObject*& Res )
{
    // Not supported through this archive
    check(0); // <-- This is the line
    return *this;
}

Looks to me like this would just always halt execution. But why? O.o
Removing the check(0) of course gets rid of the crash and for some reason seems to work flawlessly and saves/loads my TSubclassOf as it should (Seems to anyways, haven’t done a lot of testing), but I have to wonder: Why is it there? Epic must have had a reason to put it there.

Looks to me like I have to use another subclass of FArchive for this that implements this specific operator-overload properly (Even if this works, I’m still curious why it works for you and not for me. And I don’t like messing around with Epic’s source). Currently I am passing my SaveLoad-Function a FBufferArchive when saving and a FMemoryReader when loading. Is that correct?

Are you passing around that FBufferArchieve ? If so, that could be the problem, I found that you need to wrap it with something like FObjectAndNameAsStringProxyArchive like so


TArray<uint8> ObjectBytes;
FMemoryWriter MemoryWriter(ObjectBytes, true);
FObjectAndNameAsStringProxyArchive Ar(MemoryWriter, false);


Basically saves all your objects and classes as string in the memory archive or buffer archieve (which is technically the same) in your case.

That did the trick, thank you yet again. I feel very stupid… Thanks for all the help :slight_smile: The rest I should hopefully get working by myself.

Jup, it’s me again. Sorry for reviving the thread. I feel though, that this is the best place to ask:

Not long after having the savesystem implemented I noticed that sometimes after changing the level via OpenLevel some actorclasses were not properly restored (Their UClass was null). Took me a while, but I tracked it down as far as I seem to be able to: I noticed that it’s always the same classes that are affected, but which classes exactly depends on the level. Essentially it depends on whether or not they are somehow referenced in the level or by my character. Classes that are placed in the level or are referenced in my characterclass are always restored (In my case the default-inventory of the player), others are not. This seems to reproduce it 100%. I’m unsure what to make of it though. Somehow the classes don’t yet exist for UE it seems and thus the deserialization fails. I would rather not have to place every actor whose class I want to save in the level once. That doesn’t seem like a good solution. Do you maybe have an idea what I can do in this case? I’m calling my load-functionality from the BeginPlay-function of my character btw.

Thanks again for reading :slight_smile:

I am still having trouble with this. I even tried calling my reload-function with a timer after opening the level, but it seems if the items aren’t placed in the level, they can never be deserialized. I’ll push this real quick, hoping somebody knows what’s happening here :slight_smile:

I haven’t worked with FArchive yet, but looking at the code for that archive:



FObjectAndNameAsStringProxyArchive Ar(MemoryWriter, false);


The second parameter in the constructor is called ‘bLoadIfFindFails’. So I’m guessing you need to set this to true, so that if the serialized class happens not to be in memory when you try to deserialize, it will load the class.

O.O

That was fast… And worked, and such a simple thing I missed too. I looked up everything I could think of relating to this, but for some reason I didn’t think of looking up what the second parameter is actually used for. I feel stupid.

Thank you so much kamrann :smiley: