GM Version: Studio 2.3+
Target Platform: Windows / All
Download: n/a
Links: n/a
[THEME]
Today we will be talking about types, this will be a short tutorial that I hope will help you understand how GMS2 handles types.
We will also talk about memory leak problems that can appear if you are not careful using data structures and other elements.
I'll do my best to keep it clean and simple to follow
[EXPLANATION]
Lets start our tutorial, first we will talk about types, as it will prepare you to understand the rest.
(1) WHAT ARE TYPES?
This will look like a very common-sense talk but believe me, that there are a LOT of people that actually don't know what those are.
But it's okay, part of the community is here to learn so we've got you covered. Let's start simple and with some "game talk".
When we think "games" we most often think about:
player stats need to be another kind of container but with each stat is given a certain value.
When we use the words number, text, container we are actually talking about types, in this case respectively: reals/int, string, array/struct (I won't use the term list right now... I'll explain later)
(2) GMS 2.3 TYPES
So now lets look at what types are there in GMS2.3+, for that we will look at he code below and debugger information:
So,
You might, however, notice that I placed a (*) in the code above for the last three variables and looking into the debugger they are actually kind of strange.
The values you see in the Value column for those, are actually memory addresses. So as an analogy:
(2.1) COMPOSED TYPES (CONTAINERS)
One of the type examples we used in the "let's talk games" list was the container type and this subsection is related to those.
On the debugger image above we can spot a [+] sign next to both the
This simple indicator means that they are containers in other words they are boxes where you can store other variables.
Let's look deep into the array/struct type with another code example and subsequent debugger image:
In the examples above we just populated both our array and struct with some variables, making it possible to expand both and look at their contents.
One thing to notice right away is that the type of the array or struct (memory address) is independent from the type of the contents.
The main difference you can clearly see between both is that:
(3) PSEUDO-TYPES (RESOURCES)
Now that we have looked at the main types in GMS2 you might be asking yourself:
I'm sorry to be the one giving you the bad news (if it is news at all), but those aren't actually types on GML.
To explain that we will look into another code example with yet another cute debugger image.
Has you can see above all of the examples we have buffers/surfaces/sprite/objects are actually numbers (or int to be specific).
I call these pseudo types, we kind of work with them as if they were types even though they are not. You might be asking
(3) THE HIDDEN TABLE
In order to explain and solve the problem above we will need to imagine an excel spreadsheet.
Hmmm.. well I'll actually place one below no need to imagine
So how GMS2 handles these pseudo-types is by using something similar to an hidden table.
This table has a column for each corresponding pseudo-type and as you might have guessed each column has rows and the values we get in the debugger are actually the row number where the actual buffer/surface/sprite is.
Let's fill in the blanks for the data we have created:
Above we can see that the types actually exist and there are also addresses associated with them, but GML doesn't allow us access to them.
We are only given the handle (number of the row) for the respective element we are creating.
(4) IMPLICATIONS
There are some good and bad things about this approach now we will be looking at some:
Let's look at this code below for a simple example:
As you can see
(1) WHAT ARE THOSE?
People usually get frighten with the term memory leak even though sometimes they do not know what it actually is.
If you look at the CONS list in the section above you can read:
(2) WHAT RESOURCES CAUSE MEMORY LEAKS?
Here is the list of all the resources you need to manually manage and delete upon finishing using them.
I'll try to keep the list as complete as possible:
of some instance you probably better of creating it in the [create event] and destroying in the [destroy/cleanup event].
(3) SOLUTION
We already understand that to deal with this problem we need to manual remove dynamic resources from memory but how can we do that?
As well as GameMaker provides function to create resources it also provides built in functions to destroy those same resources when we are finished with them.
For example if you take a look at the autocomplete while trying to create a list, one or the suggestions that come up in the list is
This will require a handle (row number) of the list and will tell GameMaker Studio to remove the resource from memory.
These
Keep an eye on them.
(4) MEMORY MONITORING
You probably might be asking yourselves how can you monitor memory usage while your game is running. And that is actually pretty simple you can do so
using the debugger. Running the project in debug mode and then going to through the menu Debugger > Windows > Graph, will bring up a graphic where you
can control the memory being used.
If you spot an increase of the memory being used over time then your code is probably suffering from a memory leak problem
I considered adding this section because there are a lot of comments in the forums/discord stating that "x function is broke and not working for me".
This will be address some of those functions as you might now understand what the problem is. So I'm essentially talking about:
row numbers got reused and that is exactly the case. When you destroy a ds_list for example that row in the "hidden table" is now free and available and
GMS2 will recycle those rows and reuse them for the next ds_list you create. Let's look how this leads to a problem in the code below:
The same applies to all the
We can actually overcome this particular problem with a little line of code:
This is not ideal nor is 100% bullet proof as we still have a problem with variables being interchangeable but at least will help reducing the bugs.
[CONCLUSION]
And that's it, types/memory leaks in a nutshell!
Here xD from xDGameStudios,
Good coding to you all.
Target Platform: Windows / All
Download: n/a
Links: n/a
[THEME]
Today we will be talking about types, this will be a short tutorial that I hope will help you understand how GMS2 handles types.
We will also talk about memory leak problems that can appear if you are not careful using data structures and other elements.
I'll do my best to keep it clean and simple to follow
[EXPLANATION]
Lets start our tutorial, first we will talk about types, as it will prepare you to understand the rest.
NOTE: The code examples throughout this tutorial can and should be followed so you can better understand the concepts.
[TYPES]
(1) WHAT ARE TYPES?
This will look like a very common-sense talk but believe me, that there are a LOT of people that actually don't know what those are.
But it's okay, part of the community is here to learn so we've got you covered. Let's start simple and with some "game talk".
When we think "games" we most often think about:
- Gun with ammo
- NPC with name
- Player with a bunch of stats (health, age, weight, attack, ...)
- Inventory with some items
player stats need to be another kind of container but with each stat is given a certain value.
When we use the words number, text, container we are actually talking about types, in this case respectively: reals/int, string, array/struct (I won't use the term list right now... I'll explain later)
(2) GMS 2.3 TYPES
So now lets look at what types are there in GMS2.3+, for that we will look at he code below and debugger information:
GML:
a_real = 1; // this is a number or 'real'
a_string = "Hello"; // this is text or 'string'
an_array = []; // this is a collection or 'array' (*)
a_struct = {}; // this is a key:value group or 'struct' (GMS2.3 only) (*)
a_function = function() {}; // this is a 'function' (GMS2.3 only) (*)
So,
a_real
and a_string
are pretty straight forward. You look at their value and you know the first is a number and the second is text.You might, however, notice that I placed a (*) in the code above for the last three variables and looking into the debugger they are actually kind of strange.
The values you see in the Value column for those, are actually memory addresses. So as an analogy:
memory is kind of a big city where your variables are in and that strange looking number is... well... it's the address of the house where they live
NOTE: Don't get fooled by the first two types (reals/strings) they are actually also stored inside an address however to simplify things GML doesn't
allow us to know the address they are stored in. Also it is actually more readable to see a value than it is to see an address for those types.
NOTE: You will read the word real, int and string from now on. So I'll explain them better: a real is a number as we talk in mathematics real numbers
so it includes all numbers whole/fractions; on the other hand int is also a number but refers to a integer/whole number without fraction part. The
word string refers to a variable that contains text.
(2.1) COMPOSED TYPES (CONTAINERS)
One of the type examples we used in the "let's talk games" list was the container type and this subsection is related to those.
On the debugger image above we can spot a [+] sign next to both the
an_array
and a_struct
variablesThis simple indicator means that they are containers in other words they are boxes where you can store other variables.
Let's look deep into the array/struct type with another code example and subsequent debugger image:
GML:
a_struct = {
name: "player",
age: 30,
attack: 10
}
an_array = [
"apples",
"oranges",
"rocks"
];
In the examples above we just populated both our array and struct with some variables, making it possible to expand both and look at their contents.
One thing to notice right away is that the type of the array or struct (memory address) is independent from the type of the contents.
The main difference you can clearly see between both is that:
- the struct has variables within itself and those variables have values.
- the array is a pure ordered container where each entry is represented by a number (index).
NOTE: Inside the
a_struct
we can see a variable we didn't defined ourselves - toString
. This variable is a functionand is added by default to all defined structs. This function is responsible for converting the struct to a string when we use the
string()
or show_debug_message()
functions for example.
NOTE: You can see that structs can hold values of different types, but the same can happen with arrays even though in the
example above I only provided an example with strings, arrays can hold any other type and also mixtures of types.
(3) PSEUDO-TYPES (RESOURCES)
Now that we have looked at the main types in GMS2 you might be asking yourself:
Some of you with more advanced programming skills might even add more stuff to the question above like buffers/surfaces.What about objects/sprites/sound?
I'm sorry to be the one giving you the bad news (if it is news at all), but those aren't actually types on GML.
To explain that we will look into another code example with yet another cute debugger image.
GML:
a_buffer = buffer_create(1, buffer_fixed, 1);
a_surface = surface_create(1, 1);
a_sprite = sprEnemy;
an_object = objGame;
Has you can see above all of the examples we have buffers/surfaces/sprite/objects are actually numbers (or int to be specific).
I call these pseudo types, we kind of work with them as if they were types even though they are not. You might be asking
Well to explain this we will need to look at the next topic as we will be talking about "the hidden table".How can three different things (buffer/surface and an object) all be 0?! How does it work?
(3) THE HIDDEN TABLE
In order to explain and solve the problem above we will need to imagine an excel spreadsheet.
Hmmm.. well I'll actually place one below no need to imagine
So how GMS2 handles these pseudo-types is by using something similar to an hidden table.
This table has a column for each corresponding pseudo-type and as you might have guessed each column has rows and the values we get in the debugger are actually the row number where the actual buffer/surface/sprite is.
Let's fill in the blanks for the data we have created:
Above we can see that the types actually exist and there are also addresses associated with them, but GML doesn't allow us access to them.
We are only given the handle (number of the row) for the respective element we are creating.
NOTE: The addresses you see above were randomly created by me with a purpose of explaining how GMS2 handles pseudo-types under the hood.
There is actually no way for us to know the address where the data is stored.
NOTE: I'll be calling the "returning numbers" from the functions above - handles - since, even though they are not the actual element, they allow us to manipulate it.
(4) IMPLICATIONS
There are some good and bad things about this approach now we will be looking at some:
PROS:
- Prevents memory corruption (by inexperienced users)
- Light on the engine (no need to automatically track and collect the unused elements)
CONS:
- There is no way to differentiate between elements (in the example above
a_buffer
anda_surface
are indistinguishable by the engine) - You need to manually destroy the element (setting a handle to
undefined
will not destroy it's representation)
Let's look at this code below for a simple example:
GML:
buffer_read(a_buffer, buffer_u8); // This WORKS reads buffer
buffer_read(a_surface, buffer_u8); // This also WORKS reads buffer
buffer_read(0, buffer_u8); // This also WORKS read the buffer
a_buffer
and a_surface
are interchangeable and this can lead to messy code and bugs.[MEMORY LEAKS]
(1) WHAT ARE THOSE?
People usually get frighten with the term memory leak even though sometimes they do not know what it actually is.
A memory leak is leak on resources (lists/grids/buffers/...) that result in an continuous increase of memory usage that leads to a decrease in system performance and eventually crash. These leaks are often related with incorrect memory management.
But what does that big sentence means? Let's look at the spreadsheet example again, if we keep creating resources one after the other eventually the spreadsheet will have hundreds or thousands of entries. This entries need memory space so we will reach a point where the system memory is not enough leading to a crash. The solution would be correctly managing the memory and that means removing resources from that hidden table as they cease to be used.If you look at the CONS list in the section above you can read:
- You need to manually destroy the element
NOTE: When an resource needs to be destroyed we say that it is NOT garbage-collected (on the other hand arrays & structs ARE garbage-collected)
(2) WHAT RESOURCES CAUSE MEMORY LEAKS?
Here is the list of all the resources you need to manually manage and delete upon finishing using them.
I'll try to keep the list as complete as possible:
- Buffers
- Surfaces
- Data Structures (list/map/grid/stack/queue/priority)
- Cameras
- Particles (part_emitter/part_system/part_type)
- Dynamically Created Assets (rooms/sprites/timelines/paths/fonts/sequences/animcurves) (created through code)
- Audio Emitters
of some instance you probably better of creating it in the [create event] and destroying in the [destroy/cleanup event].
NOTE: Instances are not in the list above, but you still need to destroy them when no long in use. However, differently from the ones on the
list above, when leaving a room, all active instance get automatically destroyed. There is an exception for the deactivate instances since these
will not be destroyed and can lead to memory leaks (extra management is needed if you use instance deactivation).
(3) SOLUTION
We already understand that to deal with this problem we need to manual remove dynamic resources from memory but how can we do that?
As well as GameMaker provides function to create resources it also provides built in functions to destroy those same resources when we are finished with them.
For example if you take a look at the autocomplete while trying to create a list, one or the suggestions that come up in the list is
ds_list_destroy
.This will require a handle (row number) of the list and will tell GameMaker Studio to remove the resource from memory.
These
*_destroy
, *_free
(surfaces) and *_delete
(sprites, paths, timelines) are the ones to look for Keep an eye on them.
(4) MEMORY MONITORING
You probably might be asking yourselves how can you monitor memory usage while your game is running. And that is actually pretty simple you can do so
using the debugger. Running the project in debug mode and then going to through the menu Debugger > Windows > Graph, will bring up a graphic where you
can control the memory being used.
If you spot an increase of the memory being used over time then your code is probably suffering from a memory leak problem
[OTHER PROBLEMS]
I considered adding this section because there are a lot of comments in the forums/discord stating that "x function is broke and not working for me".
This will be address some of those functions as you might now understand what the problem is. So I'm essentially talking about:
- ds_exists
- buffer_exists
- surface_exists
- ...pretty much all the
exists
functions...
row numbers got reused and that is exactly the case. When you destroy a ds_list for example that row in the "hidden table" is now free and available and
GMS2 will recycle those rows and reuse them for the next ds_list you create. Let's look how this leads to a problem in the code below:
GML:
var _list = ds_list_create(); // let's imagine the returned handle is 0.
ds_list_destroy(_list); // this will make row 0 available.
var _newList = ds_list_create(); // As row 0 is available GMS2 will return it.
// At this point _list == 0 and _newList == 0
ds_exists(_newList, ds_type_list); // This returns true
ds_exists(_list, ds_type_list); // This will also return true
// Because what you are actually doing is:
ds_exists(0, ds_type_list); // The line is occupied so the list exists.
*exist*
functions rendering them a little useless.. OR maybe not, completely!We can actually overcome this particular problem with a little line of code:
GML:
ds_list_destroy(_variable); // after destroying a variable
_variable = -1; // Set it to an invalid row number
[CONCLUSION]
And that's it, types/memory leaks in a nutshell!
- GML true types are reals/int/string/array/struct/function
- All of the other types are actual pseudo-types
- When you create a pseudo-type resource you are given an handle to it.
- The handle is actually a number that represents the row where GMS can find the resource inside an "hidden table".
- Pseudo type resources need to be managed manually so you need to destroyed them to avoid memory leaks.
Here xD from xDGameStudios,
Good coding to you all.
Last edited: