What's the best way to pass data between scenes in a roguelite game?

Godot Version

4.4

Preface

I’m developing a roguelite RPG where players choose 4 characters from a randomly generated pool of 7. Each character is instanced with random stats in a Character Select scene, which then leads to a Combat scene where 4 random enemies are also instanced.

Originally, everything happened in one scene, but I’ve split it into separate Character Select and Combat scenes. Next, I want to add an intermediate scene for upgrades, healing, and events that can modify the player’s party before combat.

Question

What’s the best way to pass the player’s party between scenes in this setup?

I’m considering an autoload to store the party so it can be accessed between scenes, but I’m not sure if that’s the cleanest approach. I’ve also thought about not instancing characters/enemies and just creating scenes and overwriting them with the players party from the selected and randomly generated data for the enemies. But my main concern is persisting the party data reliably.

Any advice or examples would be appreciated!

I’d put it in a Resource, and just pass the resource as an argument in a signal personally. As long as this isn’t a multiplayer game, this will work fine.

3 Likes

The point of Resources is that they can be saved and loaded from the disk. You you only need to pass data between scenes, a simple RefCounted, Array or a Dictionary will suffice. And if you define a class in GDScript without extending anything, it’ll extend RefCounted by default.

@grulps That’s certainly one point of Resources. But there are other benefits to Resources especially when being used by someone who is not a programmer. Personally when answering questions in this forum I try not to assume an overabundance of knowledge in that area.

Here’s some other reasons I made the suggestion:

  1. It seemed like a good choice for the OP, because at some point I foresee them wanting to save that information to disk for various reasons. ResourceSaver is already there.
  2. Resources load up in the Godot editor and can easily be edited there in a visual style. This is not only helpful for non-programmers, but programmers who don’t want to dig through Dictionaries or Arrays when fine-tuning their game. This is especially useful when it comes debugging and testing. Resources are much easier to parse and change during runtime than primitives (in my experience).
  3. Resource is an Object. That means that it will be passed by reference. This means no matter how big it gets all that’s being passed underneath is a memory address. (RefCounted, Dictionary and Array are all also passed by reference in Godot.) So there is literally no processing time savings by using something more primitive and more difficult for the game developer to parse.
  4. A Resource can have code attached to values inside it and do data validation when values are set or changed. You can calso code additional interfaces to the data as needed that are user -friendly. (RefCounted can do this too, but not Array or Dictionary.)
  5. Array is a poor choice for transporting data in modern languages. For those of us who have been around for decades, we often have old habits of needing to save memory at the expense of convoluted code. Modern compilers and interpreters do a lot of optimization behind the scenes that we as developers don’t need to see. For example, in most modern languages ternary operators are no longer any faster than writing out an if-else statement. An array of disparate data is a nightmare to deal with in code and increases the cognitive load of developers for no significant performance gain.
  6. A Dictionary is a great choice transporting data, and that’s why JSON (JavaScript Object Notation) is so popular that it has transcended all languages since 2000 - it’s just a text representation of a Dictionary. However they are not necessarily needed in the context presented, and they are harder to read. And while Godot supports Variant dictionaries, they are a poor prgramming practice to inculcate in my opinion - with the exception of storing data on disk.
  7. A RefCounted is also a good option, but it is missing two things that could prove quite useful in the originally suggested scenario. First is that there is a Duplicate function for Resources. The second is a changed signal for when data is changed inside it. When dealing with the party makeup you may want to change the data and/or modify a previous party. This is much easier to do with this kind of functionality.

Ultimately, I think Resource is a great place to start for a new class storing complex data in Godot. It comes with a lot of benefits and very little overhead compared to RefCounted with a number of really useful features for data objects. There is no performance loss in using one for passing data inside a Godot game. (Multiplayer is different because you cannot pass Objects through RPC calls.) Also the memory footprint isn’t that much different, and in the given use case there aren’t going to be hundreds or thousands of them floating around in memory. So I think cognitive load of the developer is the most important metric and Resource has a lot more benefits to make it easy to use for complex data.

3 Likes

Oh man, thanks! That is a super detailed explanation on the different approaches. Useful info there for me.
I’ll definitely try doing resources for my use-case here. I’m already using it in a different for something else in the project, so might as well.

1 Like

Just to clarify on your approach. I want to make sure I am interpreting this correctly.

You’re saying to create resources of each individual player character’s data(their randomized stats) and accessing it later (in different scenes)?
i.e.
From Character Select, player selects 4 characters → each character’s stats are recorded to separate resources → Combat Scene uses saved resources and populates the characters objects in its scene.

or do you mean something else?

Based on the information you provided, yes I’d store the characters in a CharacterDetails Resource. (You can call it something else.)

class_name CharacterDetails extends Resource

Then you can pass them all around as an Array of CharacterDetails, because you always have four and an Array will keep them in the same order (and allow you to re-order them).

var character_list: Array[CharacterDetails]

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