Download

Keeping inventory items and other data between levels

I have an inventory, but all my items are gone after loading a new level. What’s the best way to keep them? I also would like to save equipment (inventory items) of all my ‘party members’ ally NPCs, all the stats for all characters (HP, strength, agility, stuff like that), learned skills (array of ‘Skill’ actors), and many more…

I know that there is a GameInstance, which is persistent. So should we transfer (using some function) all the data to this class before loading new level? (like GameInstance.playerItems] = MyPlayer.inventory.items], etc…)? And then load everything back when level is loaded? What about, for example all the actors on level 1 (like regular trees, barrels, physics objects, everything), when player e.g. destroys some of them, or they change their position, how to keep that information? So when we load level 2 and go back to level 1, all will remain the same as we left it? That would be A LOT of variables in GameInstance for every object… And I don’t even know how would we gather them, Get All Actors of Class?

The GameInstance is one way. Having your Data gathered in structs will make it easier to save it.

You can also use a SaveGame and just save and reload the SaveGame data. This wouldn’t require so much
logic in the GameInstance. You will just want to have some Functions that control loading and saving the SaveGame.
If you need information about how to use SaveGames, please google and look on youtube. There are a lot of videos
showing how to use them!

One last way could be using seamless travel and adding actors to a list of surviving level change actors. But i don’t
know if this is a solution for you and i also don’t know how exactly that works. You may want to search a bit or
wait for someone who used Seamless travel for things like this.

I would be pushing all of the data over to the game instance constantly and depending on your desire simultaneously saving it with a save game. Working with structs and arrays would be your way to keep the load low with this.

Thanks! :slight_smile: Now i know how to transfer values between levels and save game, but how to transfer an object? For example, I have an “Inventory” object (spawned Actor) on my Player. It has variables like “currentItems” which is array of “Item” Actors, etc… I want to save this object and load on different level. Do I have to spawn a new “Inventory” Actor and copy all the properties (so it would involve spawning all “currentItems” actors again from their saved classes?) one-by-one to it? Or maybe is there some way to duplicate an object?

To make this more clear:
I have an “Inventory” Actor that is spawned on Begin Play and attached to player.
It has properties like “currentItems” array which is array of spawned “Item” actors.
How to transfer something like this to different level/save game?

Keeping actors alive would need the seamless travel. Otherwise you will need to save the ItemProperties,
respawn them after the level change and fill the ItemProperties back in.

But with seamless travel you wouldn’t need to save these ItemProperties at all.

If you want to respawn them, you might want to have all your ItemProperties in a Struct, so you just
need to save the Struct instead of every single variable.
And then you also want to save the ItemClasses. Not the blue Reference type, but the purple one.
So you convert your ItemActor Array into one Array of the ItemProperties Struct and one Array of the ItemClass.

Then, in the new level, you can take the ItemClasses Array, loop through it and spawn them back into the inventory.

Thanks, I’ll try to implement something like this.
If I understood correctly, I don’t need to change my “Item” class to have a “ItemProperties” struct variable instead of separated variables, but create “ItemProperties” struct array just for data-transfer/savegame purposes? Because for now I can easily access “Item” properties like Sword.damage, Shield.defense, etc., and with struct I would need to Sword.ItemProperties → Break struct (a lot of pins there) → pick certain value … Which would be a little pain :slight_smile:

PS. Congratulations on 1k posts :smiley:

Edit/Update: I’ve managed to get this to work in the fashion that you’ve described, thanks for the hints eXi & ZoltanJr!

… Going deeper into creating save/load system, I’m wondering how to handle all monsters/scenery objects that were killed/destroyed but they respawn at each level load, because they are placed in level editor. For now I see only one option: Destroy all spawned monsters/scenery objects on Begin Play and then spawn only these from save game data. Is there a better way to handle this?

You could give the monsters a unique identifier and when killed store that identifier in an array within the game instance. When the level is reloaded get all monsters and loop through the game instance identifier array and destroy any monster that matches (or flip this logic and reference the array when creating your enemies).

I thought about this a few times. The identifier is a good thing, like a unique ID. You would save the ID and the state if the monster.
You could have things in your level that aren’t destroyed but used, so i would try to give each actor a State (an ENUM for example)
which tells you if it is destroyed, used or available. Then you save the ID and the State and later on iterate through them.

I think if the monsters are already placed in the level from the editor and not spawned during gameplay then you should be able to get away with just an ID as you could simply get all actors of class and match on ID then do whatever you want with that subset (although this could prove false if the get all actors of class is executed before the monsters are placed!). However, if you are spawning monsters during gameplay a second criteria would definitely help. This is where I would put the ID into a monster struct with the aforementioned state and any other monster related info that would be useful to persist.

I’m sure if you play around with this approach you’ll be able to achieve what you want.

Thanks guys! Your version seems better, it would not involve 1. Spawning all monsters, 2. Destroying all monsters, and 3. Spawning the alive ones again :slight_smile: - but just destroying dead monsters + spawning the extra ones, not placed in editor originally (e.g. spawned at runtime).
But indeed, in this case an unique indentifier is needed. It would be nice if UE had some built-in unique ID-assign for every spawned actor. Maybe Get Object Name will do the job as an unique identifier? If I’m not mistaken, each object name is unique.

So…

  1. On BeginPlay, iterate through all monsters spawned by level editor placement and destroy the ones with its ID saved with “Destroyed” state
  2. (Optionally) Spawn an extra monsters that were spawned during runtime (not placed by level editor placement)

PS. Woo, we can upvote posts now!

Place this logic in your GameState. It is the place for the current State of the Game, so it would fit he most
using its BeginPlay to reload the SaveGame file.

For the UniqueID: I don’t think you can use the ObjectName, since it will be different on every LevelLoad or?
Unique in the current Level, but you need an identifier that doesn’t change.

I don’t know if you need to assign a unique ID by hand. Would be a lot of work x)

@PS: :smiley: Yeah!

Was thinking about the UniqueID thing, doing by hand is super tedious! Perhaps a combination of data fed in via a csv file and creating monsters could be used? Admittedly I need to play around with this a lot myself!

The problem is, that you can’t assure that the Monsters spawn with the exact some timing every time, can you?
Because otherwise you could create a ManagerActor Class that you spawn in your GameState and in the BeginPlay,
you let your Monsters call back to this actor (can be accessed over the GameState then) and the manager
gives them unique ids.

But if they spawn differently, at least 2 of them swap the position and one is alive while the other one would be dead.

PS: You could really use a manager, but that would need some more code. You could create a manager that makes everything for you.
You will give him SpawnPoints and classes it should spawn. It will also take care of the SaveGame file and instead of
dragging the Monster into the scene, you use the Spawnpoints (could create an array of Vectors) to spawn them at BeginPlay of the
Manager. Then you could make sure that the IDs are all the time the same by looping through the Spawnpoint array and spawning them
one by another, giving them the Index of the Spawnpoint array.

Hmm so generating unique ID is quite tricky… An actor spawn manager class is a good idea.
Considering my first idea: Maybe i miss something, but this would not require unique IDs:

  1. On SaveGame (for example when exiting a level) save all monsters with (state != dead) to SaveGameObject.(currentLevelName).monsters] (along with info like position, stats, etc.)
  2. On BeginPlay in GameState, destroy ALL monsters and for each item in SaveGameObject.(currentLevelName).monsters] spawn a monster.

Yeah, but then you could directly use the Manager and create SpawnPoints. That would result in the same
thing, but way cleaner, since you only need to save the ID and the State and the Manager does the rest.

I tell you, with things like this (the manager for example) you feel way better about your code xD