I have watched some videos, read some articles, and even visited a very pleasant corner of reddit, and I have come away wiser. Let me regale you with tales of one data management technique, courtesy of PixelatedPope.
First, there were properties
Every object in the game has properties like X and Y and the state of the sprites. These are the kinds of things we want to save when we are saving a game.
Because we only concern ourselves with the current game room we can see, we only need to save data that exists in the current room. This cuts down on the number of things we have to look at, which is good.
Next, came the grid
Game Maker 2 doesn’t use object-oriented programming. You can instantiate objects, which is one of the tentpoles of OOP, but that’s about as far as it goes.
This means it doesn’t support my usual method for data storing, which is a collection of some sort. Usually, I’ll create a data object, which is a named container that has several properties exposed. I’ll create dozens, hundreds, or thousands of these things and store them into a collection, and then I’ll convert that collection to JSON and write it to disk. On a new game or to load a game, I’ll call up the JSON file, convert it back into the collections of data objects, assign the data where appropriate, and we’ll be off to the races.
The closest thing GM2 has is called a ds_grid. This is a data structure (ds_) which is basically what it sounds like: an in-memory grid, complete with X and Y coordinates, and where those coordinates meet we find a data bucket. The problem here is that a ds_grid is unformed. We can’t tell anything about it when it’s new, so we have to shape it into something we can use.
We do this by creating an enumeration or enum. These are immutable structures that put friendly names on values so instead of remembering that column stores the color, we can refer to an enum named “ObjectProps” to get the number of the “color” column:
ObjectProps.color = 5;
By creating an enum that holds named properties of the object we want to save to the database, we associate a name with an X value in the ds_grid — sort of — because we can just use the enum instead of the number. This allows us to create our ds_grid with friendly name references and store property values into this grid, one row per object in the room. Note, however, that each ds_grid stores a single object type. We couldn’t store the state of a jump gate and the state of an orbital station in the same ds_grid because the properties of the two prototypes are different. We’d need one ds_grid for each.
Lo! A map approaches!
Another data structure is called the ds_map. This is more like a traditional dictionary in that is a collection where every record is comprised of two parts: a key, which can be a string or a number, and a value, which in this case is a string (or a number recognized as a string).
We can create a ds_map full of one-off data points that we want to save, so this is a good way to store player-character info or non-instance-specific info like time played or the current score.
To disk with you and your kind!
The ds_map is what we want to write to disk because it has built-in functions to do so. However, we need to get the ds_grid and the ds_map together. Thankfully, we can — by converting the ds_grid into a string and storing it in a row in the ds_map with an appropriate key. This conversion is one of the tricks of ds_grid, so we don’t have to stoop to writing low-level code to convert it ourselves.
The resurrection is at hand!
Finally, we would want to load this data from disk. We first load the file into a ds_map, and then using the specific key pull the room data into a new instance of ds_grid. We take the data from each row and assign the values from each column to the proper columns of the proper items in the room, and they’ll inherit the state they had when the game was last saved.