• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Accessors - how to remember their symbols, how to use them

gnysek

Member
GM Version: 2.3+
Target Platform: ALL
Download: N/A
Links: N/A

Summary:

GameMaker Studio have several accessors, which are shorter way to read data from data structures and structs.

Tutorial:

In GameMaker Studio, we have several data types, which can be read in alternative - shorter - way, after they are created. Those are:
- lists
- maps
- grids
- structs
- arrays (to modify array passed to function, instead modifying it's copy under variable named as function argument)


How to remember them?

All of those have symbols, which weren't chosen randomly, and you can easily associate them.

Lists: Accessor for lists is [| index ] . Imagine | symbol as column with numbers in lists, seen from some distance, so it became a straight line. You can also think about it as margin on page with shopping list torn from notebook.

Maps: Accessor for maps is [? key ] . Imagine ? symbol, as treasure on pirates map.

Grids: Accessor for grids is [# a,b ] . Imagine # symbol, as tic tac toe grid.

Structs/Instances: Accessor for struct properties is [$ propname ] . Imagine $ symbol as letter s - s for struct :) This way variables with space in it's name could be created too for both structs and instances.

Arrays: Accessor for arrays is [@ key ]. Imagine @ as letter a - a for array. Also remember, that this is only used in functions, when you want to override original array passed to script, instead array which which will remain only under function argument name. For 2022.1+ this feature applies only when "copy on write is enabled" in project "Main" options.

akcesory.png


How to use them?

Lists [| ]:

ds_list_set(list, pos, val) can be written as list[| pos] = val. Pos need to be integer.
var a = ds_list_find_value(list, pos) can be written as var a= list[| pos]
You can find some more interesting info about ds_list_set and filling it when pos is higher than list size in manual (in short - it will fill gaps with 0).

Maps [? ]:
ds_map_set(map, key, value) can be written as map[? key] = val. Key can be integer or string.
var a = ds_map_find_value(map, key) can be written as var a = map[? key].

Grids [# ]:
ds_grid_set(grid, x, y, val) can be written as grid[# x, y] = val. X and Y need to be integers.
var a = ds_grid_get(grid, x, y) can be written as var a = grid[# x, y].

Structs / Instances [$ ]:
When having a struct, var a = {}; var custom_name = "points";
Both a.points = val and variable_struct_set(a, custom_name, val) can be written as a[$ custom_name] = val. As argument need to be a string, you can also put either a variable, or quoted name, as you wish (a[$ "points"] is also valid).
Both var b = a.points and var b = variable_struct_get(a, custom_name) can be written as var b = a[$ custom_name].

Arrays [@ ]: (only when Copy on write is enabled for 2022.1+ versions)
When having an array: var a = [1, 5, "string"];, and function:
Code:
function test(val) {
    val[1] = 2;
}
If you perform test(a);, then on first write a copy of array "a" will be made under temporary variable "val" which will be freed from memory after function execution ends, not changing original value. As per manual, until there's no write, original array is used.

To change original array value, there are two ways:

- bad one: change function to
Code:
function test(val) {
    val[1] = 2;
    return val;
}
and then calling it by a = test(a);.
The problem here is, that we're getting copy of whole "a" created (having two copies now), then we're writing it back to a, making another copy, so there's several copy/free operations on memory.

- good one: to save number of times that we're copying and freeing arrays in memory, we can write:
Code:
function test(val) {
    val[@ 1] = 2;
}
and still call it with regular test(a), and "val" will in fact gonna write/read on original array instead of making a copy.


Chained accessors:

As per GMS 2.3+ update you can also put one type of variable/struct in another, so writing something as a[? "key"][| 5][# 10, 12][$ "prop"][? "level"] is valid as long, as every value type on each level match corresponding data structure or struct.


----

Edit: 24th Oct 2022
Added info about Copy on write behavior after 2022.1, and info that structs accessor works on instances too.

Edit: 8th Nov 2022
Added image with accessors associations.
 
Last edited:

FoxyOfJungle

Kazan Games
Very useful! Congratulations.
I still have trouble understanding what @ does when using arrays, I need to do tests to practice...
 

samspade

Member
Very nice.

Very useful! Congratulations.
I still have trouble understanding what @ does when using arrays, I need to do tests to practice...
A slightly longer version of the above is that as long as only one variable (anywhere) references an array, it doesn't matter. However, if two variables refence the same array and you try to change something in that array through either variable, WITHOUT the accessor, GameMaker first copies the array and then makes the change in the new copied array. The most obvious place this occurs is with functions. If you pass an 'array' into a function, what you're actually passing is a reference to the array and that reference gets assigned to a new argument variable inside of the function. Now you have two variables referencing the same array. But the same is true anywhere else in GM. You can always reference an array without the accessor regardless how many variables hold a reference to the same array.

So short version is the accessor is only needed if you are changing a value in an array when more than one variable holds a reference to that array (which always occurs when passing an array as an argument in a function and can occur elsewhere).

I take it back? Maybe this is no longer true. I decided to test it as I hadn't done this since 2.3 and it doesn't seem to change anything. I take back my tack back - it's true but possibly only for later created variables? I don't even know.

GML:
array_a = [0, 1, 2];
array_b = array_a;

//this will not trigger the copy on write
array_a[0] = 10;

//this will trigger the copy on write and make a new array
array_b[0] = 20;
Either way, basically don't worry about it. Use the @ for arrays when writing to an array passed into a function, otherwise ignore it (unless you start getting unexpected copy on write behavior).
 
Last edited:

gnysek

Member
I've made an update about copy on write behavior after 2022.1, and info that structs ([$ "varname"]) accessor works on instances too.
 
Top