Per my previous
In my early tests with hard-coded data, I was able to create a solar system by defining the star type and a number of planets of varying composition and position, which kind of satisfies the question of how to turn data into visuals. What I still didn’t know was how to get my Big Bang data into Godot, how to make it accessible, and then how to access it to generate scene content.
After spending some time (and some money), I think I have a solid plan of attack. Here’s the gist.
Bang Em If Ya Got Em!
I will continue to roll with my universe generation approach. Each new game will create a universe which is more or less like the previous, but content will be shuffled around. I could allow for the user to determine the number of systems, and eventually customize the presentation and number of major factions, but in the end the process will be the same: define the stars, the planets, the stations, the routes, and any other “static” data that will not be modified through gameplay.
The results of this process will be saved as a JSON file, or as several JSON files. This isn’t because I’m interested in allowing players to edit their own games (I’m not against it, but it’s not a priority right now), but because JSON is flexible and is my best option without a database. Plus Godot handles it like a champ, apparently.
One thing I liked about Unity was the scriptable object. This allowed dev/designers to codify in-game objects such as commodity items, weapons, or general loot as in-engine building blocks that can be used at design time as native objects. Godot also has scriptable objects in the form of resources. By defining a resource script, I can create an “EntityCommodity” or “EntityShipPart” script and use it to define objects which I can edit at design time. I intend to create commodity items as resources, for example, so I can set base values at design time.
My favorite abuse in Unity was the creation of a singleton script that I’d attach to a scene object that was never unloaded. This behavior would make that object and the contents of the singleton eternal, which was a “great” way to store data in memory and make it accessible everywhere in the game without having to worry about passing it between scenes.
Godot doesn’t use quotes around “great” and actually encourages singleton patterns by allowing developers to autoload scripts that derive from a SceneTree-able class such as Node. When the game starts, anything defined as an autoload will be instantiated once and once only across the entire game, and any public variables, properties, or functions therein can be accessed anywhere without having to instantiate class derivatives.
My goal is to have an autoload script StaticDataManager (or something like that) that loads the JSON file that defines the galaxy. This would put the top-level nodes of the JSON into a Dictionary object which, I hope, I could key using the System ID.
I would also need autoloaded scripts for data that isn’t immutable, such as player state, faction state, shop inventory, planetary production, etc., but a lot of that hinges on saving and loading game state, which I haven’t even considered just yet.
Tying It Together
Once the Big Bang generates its JSON, the autoload scripts have been defined and set, and the JSON loaded into the singleton objects, the player object would then decide which system needs to be loaded. As everything in the universe where the player is not is technically theoretical, the only system I have to really concern myself with is the one the player inhabits, and in my mind that is why the player’s System ID property — set either to a starting ID or the ID of the system the player was in when they last saved the game — is what I use to drive the solar system’s scene population.
Reaching into the StaticDataManager, I’d have to find the proper Dictionary reference for the correct solar system, and using that data, place the star at [0,0] and the planets and other items where they belong. The key is that unlike my previous test where I was choosing the star and planet textures based on an animation frame, I will have previously created resource-based scriptable objects for each star and each planet. This way, I could size stars according to their classification, and do the same for select planets (maybe having a small, medium, and large version of each, avoiding having to modify size properties at runtime for the sprite, collision body, and other aspects).
When a player docks at a station and accesses the commodity market, I would need to do more complex work. First, I’d get the base commodity list from disk; I’ve seen examples of just reading the contents of a directory and using the resource scriptable objects therein to populate a UI. Every station would have every commodity but whether the station buys or sells a given item varies. That particular data would be pulled from the StaticDataManager. Pricing would have to be dynamic as it would depend on factors such as faction control and player rep, system, region, and galactic conditions and events, and the production pipeline between planets that make the items which are being offered for sale. I have more to say on this, but that’s going to have to be saved for another post.
Of course, the end result is that some data is going to change either by player action or by background simulation. I believe it’s mainly a matter of keeping it all in singleton managers, and only writing out certain properties when the user saves the game. A player couldn’t erase an entire star system — I don’t think, but now that I say it out loud… — so things like solar system definitions would never be updated after the Big Bang. But say a plague reduces a major production planet’s population by 75%; that would absolutely have a chilling effect on the price of goods that were produced by that population. I’d have to store the data that such a plague happened, what its effects were and how and where they apply, and the fallout at the time the game was saved, so we don’t have cases where the population magically rebounds when the player loads the save file.
One of the last major question I have (to my personal knowledge, at least) is how to handle cases where the player is moving from one system to another. Technically all of the required data is stored in the StaticDataManager or in other managers dedicated to mutable data. Do I need to bounce between two scenes, pre-loading one as a player approaches a jumpgate? Can I throw up a shaded rectangle with a warp-speed animation to signal that the transition is taking place while I clear the stage and set up the next scene in the background? I haven’t seen many examples where the project being built actually uses a transition between scenes, so I have absolutely no idea what Godot’s best practices are surrounding this situation.
One thing that I have to get my head around is timing. It starts with the player firing up the game for the first time: there is no “universe” and there are no “saved games”, so there’s no data in the StaticDataManager and there’s no concept of the PlayerManager or other singleton object to act upon. A player would need to create a new universe whose initial state would be autosaved, and from there it’s “business as usual”. But then there’s the case of loading a saved game. I’d need to load the static data first, then the dynamic data, then would need to hit the ground running. I’d also have to have that answer on how to set the scene without the player being in the middle of it.
Overall I am pleased with the information that I am learning, but I have to say that it’s not been an easy road. Like pretty much any other technology out there, there is an overabundance of “getting started” tutorials, most of which focus (rightly so) on doing very specific things in very specific ways. I had to pay $45 to get an excellent series of videos which showcased how to use resource scriptable objects and some light data-driven design practices, which seems asanine to me; resources are a stupidly powerful feature, and I’d think that Godot should lean more into promoting them as such, but finding specifics was really difficult and once I did find specifics, they all dealt with very beginner-level examples. I then happened upon a video which showcased loading data from a JSON file and how to apply it to a scene, and that single, short explanation was my literal “a ha!” moment after weeks and weeks of the same tutorial focus across print and video sources.