SOLVED Combining strings to get a global variable?

hdarren

Member
So I have a bunch of global variables for individual things that need tracking, like.

GML:
global.var_Boots
global.var_Armour
global.var_Helmet
global.var_Gloves
global.var_Trousers
When equipping or removing the items I check to see what category the item is then change the according variable. Currently I do this one at a time, an if statement for each item category, but is it possible to combine a string to then form a global variable? I can't seem to get it working but I don't know if I'm doing something wrong.

So for example joining these strings: "global.var_" +"Boots" would allow me to change the "global.var_Boots" variable.
 

TsukaYuriko

☄️
Forum Staff
Moderator
The bad way to do exactly this is via variable_global_get. It's bad because reflection is rather performance-heavy.

The good way to do it is to use an array instead of multiple variables and index it via an enum.
GML:
boots = equipment[Equipment.BOOTS];
 

kburkhart84

Firehammer Games
It's your decision, but using enums or #macros is 100% worth it, and not actually that complicated once you try it out. It is also extremely much more performant.
 

Neptune

Member
... You sure you don't have that swapped 🤣
What are these 90%+ systems being made in GM that make enums & common DS all but useless?
 

kburkhart84

Firehammer Games
... You sure you don't have that swapped 🤣
What are these 90%+ systems being made in GM that make enums & common DS all but useless?
There are lots of struct based replacements for the DS stuff made by people. They are quite often much better than the native DS structures in several ways. They are typically made to easily be serialized(for JSON). They also garbage collect themselves(unlike native Data Structures). They also typically have other extra functions that you normally have to make separate functions for with native Data Structures.

Frosty is in general right, structs can all but replace data structures and many other things. They are simply much more robust and can do so much stuff that you have to make lengthy bits of code to do otherwise.
 

Neptune

Member
I guess I get it, and I knew JSON would be the answer... However, I don't see that being 90%, and common game-making advice.
Seems more like "if you're doing networking, and making phone apps... structs and json!!"

I've used JSON... Not once actually 🤔
I see some use for structs in my RPG, but... It hasnt been enough to make me want to convert.

I am genuinely curious what some of my larger data systems would look like using some struct method, and if it would really be super simple...
 

TsukaYuriko

☄️
Forum Staff
Moderator
While I generally agree with the sentiment that structs are better suited for a lot of cases than essentially the same thing constructed out of data structures simply due to being easier to visualize, especially for debugging purposes ("struct named inventory, has fields named shoes, hat, chest, gloves, pants" is a lot more descriptive than what essentially comes down to "list with id 31, indices 0~4 contain these values"), I suggested using an array and enums specifically because this seemed like one of those cases where structs aren't superior as they can't exactly be iterated over. It seemed to be a suitable way to handle the problem this topic is about - fill an array with enum elements (to iterate over), then use it to iterate over the other array that is indexed by the enum. You can technically iterate over a struct's fields via variable_struct_get_names, but then we're taking a step forward and another one backwards by addressing stuff via strings again.
 

kburkhart84

Firehammer Games
I guess I get it, and I knew JSON would be the answer... However, I don't see that being 90%, and common game-making advice.
Seems more like "if you're doing networking, and making phone apps... structs and json!!"

I've used JSON... Not once actually 🤔
I see some use for structs in my RPG, but... It hasnt been enough to make me want to convert.

I am genuinely curious what some of my larger data systems would look like using some struct method, and if it would really be super simple...
I personally re-wrote my inner working of my input system to use structs, because it was worth it for me. It also opened the way to being able to code some features that would have been too hard(or time-taking) to be worth doing. I also have some more features I'm going to add(once my audio system is done) that are also in that same category.

And the best part, I NEVER have actually used JSON despite it being recommended everywhere. It is just one of the benefits of using structs, not the main one in my opinion though.
 

FrostyCat

Member
... You sure you don't have that swapped 🤣
What are these 90%+ systems being made in GM that make enums & common DS all but useless?
Those 90%+ are use cases where enums and macros are merely being used as substitutes for categorical property labels. Here is a classic example:
GML:
enum EQUIPMENT {
    BOOTS,
    ARMOUR,
    HELMET,
    GLOVES,
    TROUSERS,
}
global.equipment[EQUIPMENT.BOOTS] = ...;
global.equipment[EQUIPMENT.ARMOUR] = ...;
global.equipment[EQUIPMENT.HELMET] = ...;
global.equipment[EQUIPMENT.GLOVES] = ...;
global.equipment[EQUIPMENT.TROUSERS] = ...;
GML:
my_glove = global.equipment[EQUIPMENT.GLOVES];
System written like the above are workarounds for when there was no garbage-collectible, string-labelled associative array in GMS 2.2 and lower. Below is the new GMS 2.3 way using untyped structs:
GML:
global.equipment = {
    boots: ...,
    armour: ...,
    helmet: ...,
    gloves: ...,
    trousers: ...,
};
GML:
my_glove = global.equipment.gloves;
Of the remaining use cases, most are only for compatibility with legacy data, and the last exceptions are for setups where the numeric value has special meanings (e.g. bitfields, scroll orders, etc.) and cannot just be swapped for a string label.

I am genuinely curious what some of my larger data systems would look like using some struct method, and if it would really be super simple...
The struct and array fixes in GMS 2.3.2 have basically squeezed the last life out of DS data structures. Structs and arrays can now do everything that DS data structures can do, plus being trivially nestable, garbage-collectable, and easy to set up with inline literal syntax.
  • Arrays are now fully "stretchy" just like DS lists, with associated functions for inserting and deleting entries without leaving gaps. They also now have push and pop utilities emulating DS stacks and queues.
  • Structs can now have keystring-value pairs added and removed at will without leaving undefined slots, the same way DS maps can.
  • Arrays can now be nested, so the extra dimension no longer warrants a separate DS grid use case. They also now come with a customizable array_sort function that can sort contents by specific criteria, so there goes another key use case for DS grids and priority queues.
As a result of these updates, I have NOT touched DS structures at any capacity in my production-level code since GMS 2.3.2's release. I do not miss the ID type mismatches, the manual cleanup, or the verbose setup that DS structures have made standard in legacy GM code for the past two decades.
 

Neptune

Member
@FrostyCat So this code is really old, and using grids was pointless... could of just used lists, but anhow -- This defines a bunch of data, and finally stores it as written strings, so not constant access.

Is this a completely ridiculous approach, or a better way with structs!?! A lot of my global game data is stored in this manner at runtime 😅
1620156923465.png

It looks like structs would shortcut accessing the data ... But storing/managing it remains as laborious and robust as ever 🤔
GML:
var data = global.DS_items[# 0, ITEM.gloves];
//or
var data = global.DS_items[| ITEM.gloves];
Would become
GML:
var data = global.ITEM.gloves;
 
Last edited:

Neptune

Member
Perhaps I'm mistaken, but I'm not seeing the use of structs here with "realistic" or large multi-dimensional data 🧐
 
Perhaps I'm mistaken, but I'm not seeing the use of structs here with "realistic" or large multi-dimensional data 🧐
What? You just nest the data. I don't see how the use of structs negatively affect nested data. I only see how it positively affects nested data, since it's more legible when accessing and significantly easier to do so.
fire_def += global.itemdata[# 0, ITEM.gloves][| ITEMDATA.element_def][| ELEMENT.fire];
vs.
fire_def += global.ITEM.gloves.element_def[ELEMENT.fire]
 

kburkhart84

Firehammer Games
Perhaps I'm mistaken, but I'm not seeing the use of structs here with "realistic" or large multi-dimensional data 🧐
You would be mistaken. Structs can hold all of that just fine. They can not only hold arrays, but hold functions, and other types of data, all in the same place. You could have a struct that contains a few simple numbers for stats for a weapon. Then you could have a struct that contains a 2d array(technically two nested 1d arrays) if you want a 2d grid to store items. You could then have the first struct be part of the second struct, and handle moving stuff around that grid in your game easily.

That struct for the weapon could easily contain a function for determining base damage as well. You just set the numbers and then when you need the damage, just call the function and it can do it all for you right then and there, all inside the struct itself, nice and organized.

Another nice part, the struct can easily contain different kinds of data. An RPG character could be a struct. The character needs to know things like current stats(like HP, MP), it would be able to have an array or group of variables for weapons/equips. You could store sprite information. Then you could have a few handy functions as part of the struct for doing "helper" stuff with that data.
 

Neptune

Member
So this has become more of a "it's possible" and not "it's profoundly better and should be done 90%+ of the time"...

Can you write a struct? I'm assuming it's all constant access, in that you'd have to unpack the whole thing to save it to a string?
 

kburkhart84

Firehammer Games
Can you write a struct? I'm assuming it's all constant access, in that you'd have to unpack the whole thing to save it?
If you just use the JSON functionality you don't have to do any unpacking, etc... to save it. That's part of the advantage.

Another part is how all this data of all different types is stored in a single variable reference.
 

Neptune

Member
So it's all constant access in RAM? Idk I'm not sold that structs are a cure all for this 🤖
I fear if I converted all of my stowed away written data (for some reason?) to constant access structs, the low-end laptop players would be screeeeewed.

I appreciate the structs and the cool stuff they can do as light-weights and with functions, but eh... Idk about replacing enum/DS.
 

kburkhart84

Firehammer Games
So it's all constant access in RAM? Idk I'm not sold that structs are a cure all for this 🤖
I fear if I converted all of my stowed away written data (for some reason?) to constant access structs, the low-end laptop players would be screeeeewed.

I appreciate the structs and the cool stuff they can do as light-weights and with functions, but eh... Idk about replacing enum/DS.
The only time I've heard of performance issues with structs is if you consistently create lots of them. In all normal usage I've only heard of them being faster. I've never done actual testing on it myself since I've never had performance problems and the simple code organization I get from them is 100% worth it.
 

Neptune

Member
Makes sense, I don't doubt they're faster, but I wonder if it would consume more static RAM at any given time?
I should probably learn some computer science... That fact I don't know if a constant access would take more or less memory than if it was written and stored as a string 😕
I imagine constant access takes many more "cells" of RAM, than a single string 🤔

(and I was referring to low-end's RAM capping out, which is really the only issue performance-wise ive encountered)
 
Last edited:

kburkhart84

Firehammer Games
If you are saving numbers instead of strings, you in general will save RAM. Numbers are more space efficient than equivalent strings. Strings are also slower overall. They have their uses, but comparing numbers to strings will show numbers being faster in a general sense.
 
Makes sense, I don't doubt they're faster, but I wonder if it would consume more static RAM at any given time?
I should probably learn some computer science... That fact I don't know if a constant access would take more or less memory than if it was written and stored as a string.
I imagine constant access takes many more "cells" of RAM, than a single string 🤔
Unless you've completely butchered it (you haven't), there is no way you're using enough RAM solely through an inventory system to affect performance. I'm going to create a worst-case scenario here. You have 50k unique items. Each item has 1k stats. I'll assume every stat is taking up 8 bytes. Even in this completely absurd scenario, RAM usage from the items would take only 400MB. Even the cheapest budget laptops available nowadays have 5x that amount of RAM to spare.

Not to mention that that same inventory system would use the same amount of RAM regardless of whether it's in ds_* form, struct, array, or anything else. I don't know why you'd store things as a string if you're worried about RAM, though. As @kburkhart84 brought out, it's incredibly inefficient.
 

Neptune

Member
I'm talking about 90% of my games data, which is stowed away in strings (not just the items), but as you said probably not an issue... Although RAM does grow as people play (because of the nature of the game) so losing 1 Gig is a lot in my case.
Regardless, a variable containing a written DS (a string) takes the SAME memory as a open / live / constant access DS. This is true?
A variable containing 1000x1000 grid (written) == 1000x1000 constant access?
 

kburkhart84

Firehammer Games
Regardless, a variable containing a written DS (a string) takes the SAME memory as a open / live / constant access DS?
Naw, the string that contains all that data will be much more memory than a DS with the numbers. Each character in a string is a byte, each number(in GM, since they are all doubles), is 8 bytes. Then consider the other things that go with it, map/key relations, indices, etc... I don't know what all goes into making the string that represents a data structure, but those strings are quite long.

Technically, a smaller number, like 1000, could theoretically be be represented by 4 bytes instead of 8 if you used a string "1000" but even in those cases, you lose performance every time you want to do any math with those values. And worse, a larger number like 100,000,000,000 already takes 12 bytes as a string "100000000000" compare to the same 8 bytes as a number. Furthermore, if you really had to save space in storage(not in RAM), you could use buffers and convert the numbers to smaller spaces, like using ints(4 bytes). In general, you shouldn't have to over-optimize like this though, unless you are saving something like a 3d model that has tons of vertex information or something similar.

A variable containing 1000x1000 grid (written) == 1000x1000 constant access?
If you have an array instead of a grid(DS Grid), that array will be much faster, yes. It will theoretically take the same amount of RAM, except that Data Structures have some slight overhead in the engine, don't know what that is or how much it could be.
 

FrostyCat

Member
This defines a bunch of data, and finally stores it as written strings, so not constant access.

Is this a completely ridiculous approach, or a better way with structs!?!
It is completely ridiculous. With DS data structures, you convert to and from strings because there was no built-in way to nest lists in grids (or any other heterogenous pairing other than map-list). With structs and arrays, they can nest inside each other in their native live states, without converting to and from strings.

It looks like structs would shortcut accessing the data ... But storing/managing it remains as laborious and robust as ever 🤔
Serializing structs and arrays has NOT been difficult since the introduction of json_stringify and json_parse in GMS 2.3.1.

Often these two functions are coupled with these corresponding helper script functions for use in files:
GML:
function json_export(data, filename) {
    var _jsonStr = json_stringify(data);
    var _f = file_text_open_write(filename);
    file_text_write_string(_f, _jsonStr);
    file_text_close(_f);
}
function json_import(filename) {
    var _buffer = buffer_load(filename);
    var _jsonStr = buffer_read(_buffer, buffer_string);
    buffer_delete(_buffer);
    var _jsonData = json_parse(_jsonStr);
    return _jsonData;
}
Given that your top-level data consists only of structs, arrays, reals, strings, undefined and boolean inside, saving it to a file is a single function call:
GML:
json_export(global.ITEM, working_directory + "items.json");
And so is loading it from a file:
GML:
global.ITEM = json_import(working_directory + "items.json");
Robust, but not at all "laborious". At this point, saving or loading data with any more than a single line now looks ridiculous to any genuine post-2.3 GML developer.

Perhaps I'm mistaken, but I'm not seeing the use of structs here with "realistic" or large multi-dimensional data
I don't understand this sentiment. Every dimension is just a level of nesting. How good something is for multi-dimensional data depends on how well it supports nesting and serializing nested data.

Structs and arrays excel at nesting and deep serialization. They can be nested at will in their native live states (i.e. WITHOUT having to transform to/from a string the way you did in your example), have a convenient literal syntax, and can be deep-serialized using a single function call. They are ideal for multi-dimensional data, and have since become the medium of choice for informed post-2.3 GML developers.

Data structures suck at nesting and deep serialization. They need manual marking for lists and maps, don't nest properly any other way (e.g. lists in grids), and their opaque serialization format is only one layer deep (e.g. you can't ds_grid_write a grid of lists without manually handling the lists inside). They are a pig to use for multi-dimensional data, and their utility is completely nullified for experienced users who have spent time studying post-2.3 GML.

So it's all constant access in RAM? Idk I'm not sold that structs are a cure all for this 🤖
I fear if I converted all of my stowed away written data (for some reason?) to constant access structs, the low-end laptop players would be screeeeewed.
You are much more likely to screw low-end users with code related to graphics or sound, than code related to structs.

I appreciate the structs and the cool stuff they can do as light-weights and with functions, but eh... Idk about replacing enum/DS.
DS data structure abandonment is happening, and the only people not noticing it are legacy GML dinosaurs and novices using tutorials too old for their own good. In addition to structs and arrays becoming more full-featured, an unintended change in the DS read/write format broke a range of commercial projects in the GMS 2.3.1 timeframe. Everyone who used DS data structures for saving data lost user data, and it could not be easily remediated because of the opaque DS read/write format. The more modern JSON-reliant demographic was largely unaffected.

Enums and macros will still have their uses, but the "arrays-pretending-to-be-structs" use case will be losing its halo.

Regardless, a variable containing a written DS (a string) takes the SAME memory as a open / live / constant access DS?
Absolutely not. A live DS data structure will always be smaller than its DS-write string equivalent. It is like how a buffer is always smaller than its Base64 representation, or how a struct/array is always smaller than its JSON representation.

On top of that, the current DS read/write format is opaque and notoriously bloated. See for yourself how much extra goes into it:
GML:
var list = ds_list_create();
ds_list_add(list, "foo", "bar", "baz");
show_debug_message(ds_list_write(list));
ds_list_destroy(list);
Code:
2F010000030000000100000003000000666F6F0100000003000000626172010000000300000062617A
 
Regardless, a variable containing a written DS (a string) takes the SAME memory as a open / live / constant access DS. This is true?
A variable containing 1000x1000 grid (written) == 1000x1000 constant access?
Are you talking about using ds_*_write? You can test it yourself using the debugger. Initial results say written ds_* takes more RAM.
 

Neptune

Member
I mean if this is true, then my life is a lie!! lol
Huh, so I might as well say "**** it" and leave everything strewn about in constant access... So weird to me that would be the same size or MORE 🤯

I'm thinking I had all my stuff neatly folded in the dresser, and now you're telling me "naw unfold them and hang em outside!".
Next game I suppose 🤣
 
Last edited:
Top