When to use singletons, resources, and classes?

I am new to Godot and wanted to try making a game somewhat similar to a sports team management game. I am just trying to figure out the overall structure for now. The basic idea would be that there are players with their own stats like speed, accuracy, etc. and teams composed of players. Players could be traded from one team to another.

My first thought was to just make a dictionary of players and each player would have their own dictionary with their stats. I could then either put the player dictionaries inside of a team dictionary or keep them separate and just have the team dictionary reference the dictionaries of the players on the team.

But I am having trouble figuring out the best way to make the data accessible and able to be saved.

Would I just have a singleton holding the dictionaries and then I could access the data from anywhere in the game and then when the game is saved/quit, I could save the data in the singleton to a resource? Do I even need to use a singleton at all? Could I just put the dictionaries directly into a resource and be able to access and modify the data? Or is it not possible/less convenient to work with data in a resource vs a singleton?

And would I want to make a custom class for players and teams or is it not needed since the structure would come from the dictionary? Or could I just have all the players be an instance of a player class with their stats in the class and then put all the player instances in a dictionary or something? Or is that not how classes are meant to be used?

I’ve tried reading up and watching tutorials on these things but just can’t wrap my head around it all and I would appreciate any advice on when to use what or how to structure it all.

You are probably going to want to use a database to store your data. It depends somewhat on the scale of your game but if you look at something like the FM series, they can have potentially 100k+ players. You won’t be able to load all the data into RAM at once, it will need to be stored on disk in some way. Resources may not be suitable depending on how you want to be able to retrieve the data. You could probably use resources if you are just trying to do things like “get all players on team x” or “get all players in league x” but you won’t be able to do something like “get me all free-agents in England who are goal keepers taller than 200cm and average stats above 80”.

edit: In terms of how to structure the data, I don’t personally have experience with solving this problem but I’d suggest searching for something along the lines of “relational database for sports league” to get an idea of how you might store the various team/players/etc

2 Likes

I will 2nd looking into relational db method. So you would have tables relating to each other. E.g. player, teams, a stats. However the issue is linking these tables. You need unique keys for each player to join all the tables and unless you have something that guarranteed to never change that poses an issue as you move them around. Maybe their name and dob.

Now if you want there are other styles of db similar to resources to consider. They are called, NoSQL. Their strength is that they are much less structured and dont require joining of tables. Its more like document oriented, yet you can query them like a database. Not gonna sell you on either db type but maybe make a mockuo of all the data you want to track. Then you get a better picture how uit should he organized.

Google sql vs nosql. Either way you need some form of db to easily manipulate large data.

1 Like

OK going by all your questions it seems like you getting information overload. So first thing I’ll say is take a step back. Don’t worry about saving data yet, you’re not there yet. Try to work on and learn about one thing at a time.

First thing you need to work out is what data you need. Keep it to the bare minimum for now, but keep in mind what you might want to add. Something like:

[Teams] Name, Players
[Player] Name, Speed
[Matches]HomeTeam, AwayTeam, FinalScore

Now you know your data create a class for each group (so in my example you’d have Teams, Player and Matches), and then start adding some basic game logic to use it. Rinse and repeat adding more detail and logic until you’re happy. If you find you’re creating a lot of very similar classes, or adding a lot of variables that are only used in certain circumstances, then look into inheritance (a class based off of another class).

Once you’ve got all that done then worry about saving it all.

1 Like

Thanks for the replies!

I didn’t think it would be easy to use a database with Godot, but it seems I can do it pretty easily with an extension like Godot-SQLite - Godot Asset Library

I think SQLite should be fine and I have a little bit of experience with it. And I think for unique keys I could just have an auto-incrementing id field, right?

Even though I will be fine using SQLite for this game I still want a little bit better understanding of when to use the other things.

For a resource, is it something that is somewhat static but can be changed when needed? Things like save data that you load once and then save at the end, but it’s not updated a ton during the game? Since player stats would need to be updated often it wouldn’t be ideal to store them in a resource right? If I had premade teams that the player couldn’t edit or change that would be a good thing to have in a resource since I just need to load it once and use the data as is without changing it? And static things like team logos and jerseys and stuff would also be fine as a resource?

For singletons I could put stuff like team records in there since they would need to be updated after every game and then when the game is saved/exited I could save it to a resource and load that resource back into a singleton next time the game is launched?

The main one I am still confused by is classes. I understand when to use them for things like enemies or resources that can be harvested or items you can equip, but do I need/want to have classes when I am only dealing with the data side? I can’t figure out how it would fit in between generating the data and saving it to a dictionary/sqlite database. Would I only need the class when I need to use the data to instantiate a player in the game?

1 Like

Classes are essentially custom data types. Think of them like your standard built-in data types like int, string, float, Array. They describe some data and methods (functions that operate on that data).

For example String is a string of characters (ie the data), let’s say “Hello, world!”.
capitalize is a method on the string class.
var caps: String = "Hello, world!".capitalize() # caps = "HELLO, WORLD!"

All Godot’s Node types are examples of more complicated classes.

Using classes for the different kinds of data you will be handling will make it easier to work with in a few ways. Take this example.

You could store your player in a dictionary like so:

var player: Dictionary = {
	"name": "John Doe",
	"position": "Striker",
	"age": 25,
	"team": "Godot United",
	"country": "Godotland",
	... # More player data
}

This will work fine, and you can write functions that work with this structure and program your whole game that way. The downsides are that you could run into errors if your data isn’t formatted exactly how you expect. What happens if you have a player var that is missing a name? What if their age is stored as a string?

If you use a custom class:

class_name Player

var name: String
var position: String
var age: int
var team: String
var country: String

func _init(name: String, position: String, age: int, team: String, country: String) -> void:
	self.name = name
	self.position = position
	self.age = age
	self.team = team
	self.country = country

Then you know that every time you create a player with Player.new() it must have all those values, and they will be the types you expect. This means that you can write functions that operate on the player data more safely,

func youngest(player_a: Player, player_b: Player) -> Player:
    return player_a if player_a.age < player_b.age else player_b

will always work. The types ensure that both players have an age. If they were Dictionaries you wouldn’t be able to guarantee this.

In regards to updating resources. You are right that they are suitable for static values. For players you could store their initial values as resources but I would probably try doing something like on creating a save, generate the DB in the user:// directory and copy all players, teams etc. there. Then when playing always use that copy of the data which you can write back to.

1 Like

Then you know that every time you create a player with Player.new() it must have all those values, and they will be the types you expect.

That made it click for me, thanks!

1 Like

This video was good on how to save/load and manage Json data. They even use a program that makes dictionaries easy.

2 Likes

Several people in this thread have suggested databases. But Godot doesn’t have a database table editor.

So what happens when you want to reference resources (via file paths I guess) in your data, and have that be robust to change, like renaming and folder restructuring?

All databases have external database editing tools. Also you should make your file paths relative to your application or provide a way to find them via environment, paths or some other configuration settings.