Designing a progress storage system

Hi, I’m making an rpg game that focuses on character interactions. I have it set up so my character can walk around a map and interact with npc characters. I also have a whole dialogue system that is set up already, but I want to know what is the best way to track the player’s progress during runtime.

Obviously a save state is what I would use to track the player’s progress between play sessions, but I want to know where to store all the variables in the meantime so I’m not saving, loading, and casting from the save state every time I want to reference a game state variable. The way the game works is like a tree of storylines so I need to be able to check the value of variables quite often.

Currently I am storing the variables in my player character blueprint since I know that it will always be present when I need to reference the variables, plus I already have a reference to my player character in the dialogue system bp that I can reuse to refer to the variables. However, I feel that this method will make my player char bp get cluttered quite fast. I was considering changing to a system where I store all of the game state variables in an editable struct instead.

I want to hear other people’s thoughts since I feel like there’s probably a better way that I haven’t used yet.
Thank you so much for your time!

Do you consider viable using a persistent singleton like game instance (for example) to save / load and have every other class read directly from the save game object?

1 Like

if visual clutter is the only issue i wouldn’t necessarily make a separate class.
once you make a separate class then you introduce potential for communication issues, and just in general more separation = hard to reason about code.

Of course, often times using different class is needed for collaboration concerns, or making code more modular if that is needed.

But if visual clutter is the issue to fix, I recommend take a closer look at many of the organizational features you get with blueprint. Categorization for functions and variables, and for the code in graph you can make separate event graphs, or use collapsed graphs.
Because Ctrl+F search also picks up names of collapsed graphs I find this to be the most preferable way to organize code - rather than just using comment blocks. You can make your bluepritn graphs so that you can easily find categories of code very quickly, and also condense all of the code so that it reads like a book.
Be aware that CTRL+F search also picks up category names, so that is a way you can easily find a group of variables, similar to if you collected them to a struct.
Like this:





That said, I think most people would use something like the Game Mode or Game Instance as place to store game state related data, especially if many different actors may want to query that data, it makes sense to put it in a place that is easy to access. In my case, because I make a single player only game and am solo developer (no collaboration concerns), I just use the player control as the primary manager class for most of the games systems.

1 Like

the way you can store a value “meaningfully” to persist across save/load, and still be referenced without “checking the save” would be in any of the classes you have set as your games “defaults” (in the project settings “Project”->“Maps and Modes”) most of these can be trusted for casting, and can be trusted to “exist” in the current game state. the PlayerCharacter is “easiest” at least to start because almost everything is saved.

so you could put these values in GameMode which is a “one at a time” singleton, or GameInstance which is an absolute Singleton for each Engine instance, and in the worst case you can mark almost any object or variable as “savegame” for persistence (the more things you tag this way the bigger the save file will be).

the Object lifecycle of anything derived from UObject goes like this: the class defaults are read in the order of the “C++ header”->“C++ Constructor”->“BP defaults”->preinitialize()->Initialize()->PostInitialize()->ConstructionScript), then the SaveGame is applied to marked objects and variables, and then BeginPlay() is called on Everything that is currently in existence, and then the GameLoop takes over.

in the worst case, unless you are doing data obfuscation (lying about what a data value actually is, or storing it in multiple places to prevent corruption/manipulation), you are just doing a Get() and Set() operations on specific variables. you might need to validate on a Set() though that is data security things

how often to access the Save game object is more of a redundancy, and frequency of save question. does the game need to do a save 5 times in a given conversation, is the game state going to be persistent for long enough where checking the Save Game 5 times in a few seconds is even meaningful. A Save Game is “meant” to be a snapshot of the current Game State at that moment to prevent progress loss, you can do things like progress locking (the player attacked CharacterX they will remember that and your save file is now marked forever, kind of thing), and the save game is “intended” to be referenced at the point of load, and written too when “enough” has changed to be “meaningful”. It is up to you what the “meaningful” is, but the nature of games interactivity means game state can/will/should change.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.