Devlog: Preserving squad data across levels - Ivan Shyika


Hi! I am Ivan Shyika, our Team Leader, Project Manager, and Engine Developer.

Today, I would like to share about the issue of transitioning data between maps in our game and my solution to it.

PROBLEM: Data Persistence

First of all, why keeping data between maps is a problem? There must be ways to store it, right?

And you would be correct. Some objects exist from when a game opens and until it is closed, regardless of whether you are in the menu or in a "real" game level. The issue, however, is the specific data you likely want to store - for example, the status of your squad - exists not in those perpetual objects but in transient ones such as "actors" located at the game level. When one map/level is unloaded, those transient objects are deleted with all their data. The only way to preserve that data is to copy it into a perpetual object before the transient owner is destroyed.

Our Case for Data Persistence

In our game, a squad of heroes travels from chamber to chamber (our term for "level"). There is a lot of information to keep track of: gold, playthrough time, the count of completed chambers; and above all, information about the squad itself, which is an arbitrary composition of Heroes of different classes in arbitrary conditions (each hero of the squad has their own health and may have died in one of the previous encounters).

Because the squad can be customized, we have to remember the squad composition (i.e., which Hero classes the squad consists of) in such a perpetual object so that the squad can be recreated. In addition, there are also things like health upon completion of a chamber, and we also must have a way not to confuse data between heroes.

So, if the squad is essentially forgotten on chamber completion, how can we recreate the squad at the next chamber seamlessly?

SOLUTION

Data Organization

We can use a series of specialized containers that will be stored within one another. Think of it like a box in a box in a van:

  • Hero Data contains information relevant to one particular hero in the squad: their class, current HP, whether they are dead, and a pointer to the Hero entity in the world. It is like a profile of you at this point in your life, and the pointer to the entity to be an indication at you as a human being. The profile describes you, and the pointer/indication can be used if someone wants to refresh or deepen their knowledge about you.
  • Squad Data is a container of Hero Datas - simply put, a catalog of profiles.
  • Run Data contains all the information pertaining to the playthrough: Squad Data plus all the other variables like current gold, health potions count, playthrough time, and game progression statistics such as a list of encounters completed so far. It is like the principal spreadsheet with *all* the info that needs to be tracked.

This idea of compartmentalization is often referred to as modularity in software development. In essence, a complex system is broken down into smaller, individual chunks ("modules"), which can function independently. 

That topmost spreadsheet (Run Data) will be stored in a perpetual object. And since the catalog of profiles (Squad Data containing individual Hero Datas) is stored within it, we have all this data stored safely where it will not be deleted (until the game is closed).

Visualization of the data setup for data persistence

Visualization of the data setup for data persistence.

Such a modular setup allows for a straightforward implementation of data persistence and save file systems: if you want to recreate a squad, you only need an instance of Run Data. And if you want to have a history of playthroughs, you would only need to load a list of Run Datas.

Applying and Updating Data

There are two key times when the data persistence system is essential: recreating squad on chamber load, and updating squad data on chamber completion.

Recreating a squad

  1. On encounter start, we consult the principal spreadsheet (Run Data) to recreate the squad as it was at the end of the previous encounter.
  2. We go through the entire profile catalog (Squad Data), retrieve individual profiles (Hero Datas), and recreate alive heroes based on their hero class.
  3. We update Hero variables (like current health) to their last known values.
  4. Finally, we set the pointer inside of the profile (Hero Data) to point at the newly created Hero.

Recreating squad

Instantiating a Hero as an entity based on his HeroData profile.

Updating squad data

On the encounter end, we need to update Run Data with up-to-date information on the squad. Updating information is very easy as we have the pointer to every hero stored in their profile (Hero Data).

  1. We retrieve the profile catalog (Squad Data) and walk over each profile (Hero Data).
  2. The Hero pointer inside of each profile (Hero Data) will tell us which hero to consult to update this specific profile (Hero Data). Using that pointer, we set the variables in the profile to the values of the alive hero.
  3. Once we are done with actualizing the info, we proactively invalidate the pointer because the chamber is about to unload, destroying the Hero object in the process.

Updating squad information

Updating squad information. The last line means "set the health in HeroData to match current health of the Hero".

Handling hero death

You may point out, "But what if the hero dies and no longer exists?" That's an important consideration.

If the hero is dead, it does not make sense to recreate them in the next encounter. Besides, their profile will not change after their death, so there will be no need to update their profile either.

And it is accounted for: when the hero is created, the system proactively asks them to let the system know when they die so that the system can mark them as such and invalidate their pointer.

The code that asks the newly created Hero to notify its HeroData when the Hero dies
This is what HeroData does when the hero associated with it dies.

You may be surprised, but in our game, Heroes are so obliging that they always inform the system about their death right before they die, even despite the inconvenience of it. They are true examples of commitment and human decency. Fills you with hope for humanity, doesn't it?

Conclusion

We can now rest assured that any squad data persists across the in-game chambers. Furthermore, you know an example of modularity applied in a real-world case.

I hope you found this breakdown useful, and I thank you for your attention.

Best,
Ivan Shyika
Team Leader, Project Manager, and Engine Developer of Good Faith Team

Get Return to Divinity

Leave a comment

Log in with itch.io to leave a comment.