Storing actor variable information from levels that stream in and out

I hope there is a simple solution, but I just can’t seem to find it.

Problem: I want to create a large map of several rooms and hallways, utilizing level streaming per room. I intend to have doors be either locked or unlocked, as you progress, stored in the “bp_DoorActor” as a simple boolean variable (“locked” = true). Obviously this doesn’t work as simply as changing the values, because the moment a door actor is unloaded and reloaded, it would return to it’s default values. E.g. the door is initially locked, the player finds a key, unlocks the door, travels down a hallway (unloading the door), then returns to find the door is once again at it’s default value- locked = true.

One possible solution I have thought of would be to put all of the doors into the persistent level, but I would assume there is a more elegant solution. It seems like a problem that many games, from rpgs to almost anything with open world exploration has already tackled, but for the life of me I cannot come up with a means for doing this.

Perhaps storing them in an array in the game instance, and calling their values through their indexes?
Any help would be great.

I been wondering about that too since my game has thousands of variables in it, I think You need to store it in the game state, but I can’t even get the gamestate to work, it won’t fire event play…

I don’ think i’ve used the game state much for storage. I’ve used the game instance a lot, but no he gamestate so I can’t help with using that.
I’ll keep posting my ideas on a solution though- sort of my way of solving these kinds of issues… it seems like in cases of a simple door, or chest, it seems like just placing it into the persistent level would be a solution, although not he best I’d assume- but i’m sure that if you want to mark major actors/AI as permanently dead or something like that, that really doesn’t seem like he way to go about it. Bu I’m no sure. Storing an actor location is easy enough, and stats inside of a struct is easy enough, but what about a collection of actors that change from their default throughout play, but spawn through streaming?

You need to store them in the save game. using some kind of guid.

See Rama Save System in Code Plugins - UE Marketplace for an example

1 Like

my thoughts at his point:
Mostly tutorials seem to show how to save your character (located in the persistent level) location at a checkpoint. I figure a very similar system is used for finding and loading the streaming level, etc, but my problem is this doors stream in and out of existence.
So would this mean I would have to make an event as soon as the door is loaded to check the game slot for whether or not it has been been unlocked? As well how would I store a persistent reference in the save slot? Saving the door into the slot should run invalid after I unload and reload the level because it now has a new actor.
I guess I could expose a unique variable, a simple int (“doorNum”), for each door, and the run a check when loading from the game slot? e.g from a for each loop for an array of doors, find if actor reference is valid and is equal to the same DoorNum of the door loading, and if so find the lock state?

Hi @Rgerson, I think you are on the right track. Here is how I would tackle it:

Use a custom Game Instance and create a Map with an Actor Reference as the unique key and a boolean as the second value. This Map will hold a reference to all doors with a boolean representing whether they are open or closed. Call the map: MapDoorStatus. The boolean will be “Opened?” Now change your variable on your door from “Locked” to “Open” instead, so the default value is False (same as the Map boolean).

Now on your bp_DoorActor, create two custom events:

EventRegisterStatus and EventCheckStatus

When the Player opens the door, call EventRegisterStatus.

EventRegisterStatus -> Get Game Instance and add a new value to MapDoorStatus. Use the ‘Self’ node to enter the key (actor reference) and the value of Opened? for the boolean. This event effectively creates a new row for this door on the map and marks the boolean True (not locked).

Now on your bp_DoorActor add EventCheckStatus to OnBeginPlay

EventCheckStatus -> Get Game instance and search MapDoorStatus for the actor reference (use Self here). If the reference is NOT found, it means the door hasnt been opened, so do nothing (Opened = false) If reference IS found, then get the value from the boolean and SET Opened to this value.

If I explained everything correctly (going by memory here as Im not on my dev machine to check) this is how things should work:

OnBeginPlay the doors that are loaded (from whatever sublevel you load first) will check the Map on the game instance to see if they can find themselves; they will not, so they will do nothing - boolean remains False (not opened). Then when the Player opens the door, EventRegisterStatus will go to the Map, add itself and update the boolean to True (Opened is now true). Then when you unload the sublevel with this door and then load it again, this same door will run EventCheckStatus on BeginPlay again. This time, it WILL find itself and will then set Opened to the value of the boolean on the Map (True). This door is now opened no matter how many times you load or unload the sublevel.

Now to tie everything together, you want to save this map, so create a SaveGameBP and create a Map with similar parameters. When you save your game, copy the Map from the Game Instance to your SaveGameBP and then save to disk. Then find a way to load this Map from disk and copy it to the Game Instance when you first play the game (figure out where it makes sense to do this).

I know this seems rather long and complicated, but its really not :). All you are doing is saving the value of Opened? on your Game Instance (keeps data from level to level) and then having each door check whether its opened or not when it loads.

Try it out and let me know if it works :slight_smile:

Mostly this makes sense, but I am a bit confused by your meaning behind creating a map- is this basically another level, which is constant? it loads in on start, holding only the single storage actor, and is never unloaded, or is there another meaning behind map that I am unfamiliar with?
I’m going to try out testing different ways to tackle this, and see if I hit anything. I think you’ve given me a few ideas, so I will see if I can make it work.

Even if you don’t buy Rama’s plugin and roll your own, it is definitely worth watching several of his videos.

He has thought of many things and you can copy his architecture and approach…

Highly recommended

I found a solution!
Inside of my BP_Door actor I created an identifier variable (int), set to public/editable in editor. Each locked door I assigned a unique number. “doorNum”
Using the game instance, I created an array of int that would store the numbers for the doors once they were unlocked. “doorsUnlocked”

When a door is unlocked it casts to the game Instance and adds the unique integer, “DoorNum” to “doorsUnlocked”.
When a door is created, on event begin play, I cast to the game instance and get the array "doorsUnlocked. I check if it contains the Bp_Door’s unique “doorNum”.
If true, it sets the door to locked
If false, it does nothing/ runs the rest of the script.

Foreseeable problem:
mostly just a design headache. Need to make sure that every door has a unique number, as the moment any two doors have the same value, they will both read unlocked when they are loaded in, even if only one door has been unlocked.

Possible benefit:
If I ever want to force lock a door, all I need to know is the unique “doorNum” I assigned to it, and by deleting that from the array, when the door streams in it will be locked.


Hi Rgerson,

I should have specified. What I meant was a variable of type ‘Map.’ Check out this vid to know what I mean:

However, you understood the concept and it looks like you made it work :). A quick change to your solution - instead of saving a unique integer, how about you save the actor reference in an array? The reference is unique (unique name in the level) and you wont have to worry about assigning values yourself :). So cast to the Game Instance and see if the door reference is found in the array - if found, door is open, if not, its closed.

Hope this helps!

I tried making it an array of actor references before i tried the unique integer system, and what happened kind of surprised me.
I set up a test input, that when i pressed “z” on a for each loop, using the doors (an array stored in the game instance), it would display the display names of the actors in the array. What was interesting is that the array would actually remove the door from the level that would unstream. I assumed it would run invalid, saying it couldnt find the actor being referenced to, since that was now destroyed, but instead, without running any kind of removal command, it simply removed the actor reference the moment it stopped existing.

Thats interesting…never seen that before. But then again, Ive never tried this method :). One more thing to try before you give up on it…instead of doing a For Loop, how about you simply do a “Contains” on the array and try to find the actor reference for the door. This way only the doors that are loaded will check the array (So no door will search for a reference that is invalid because they themselves exist)…If not, then at least you have it working now, its just a bit tedious that you have to manually assign an ID to each door.