GML How i can create and save a variable for every Instances of an objects in one room?

sadegh993

Member
Hello all.
I were thinking about creating and saving (in hdd or etc) a variable for every instance of an object in one room. i have thought about using of id or instance_id but i couldn't make it working.

So how i can create and save a variable for every Instances of an objects in one room?
For example: we have one Destructible Box Obj and we have 100 instances of that box in one room, how i can make it possible to don't load destroyed boxes after restating the game? (this is an example, i don't have this currently in the project)

Thanks a lot for any help.

Sorry but could anybody help me please or guide me?
Thanks a lot.
 

Psycho_666

Member
Hello.
Long answer short - parent object and arrays.
You need to check out any inventory tutorial you can get your hands on. There are like a bijillion on the internet. It's the same thing. Most of them will show you how to work with parent objects, how to index children objects and how to work with arrays. And that's pretty much all you need.
 

TheouAegis

Member
id should be fine for any instance placed in the room using the Room Editor. When the room starts, load the variable paired with that id and if it is set, destroy the instance.

A more abstract method is to store coordinates as destroyed status, then just loop through creating instances not yet destroyed until you reach the end of the save data.
 

sadegh993

Member
id should be fine for any instance placed in the room using the Room Editor. When the room starts, load the variable paired with that id and if it is set, destroy the instance.

A more abstract method is to store coordinates as destroyed status, then just loop through creating instances not yet destroyed until you reach the end of the save data.
How can i pair a variable with id of an instance and write or save that variable for every instance?
Thanks a lot.
 

Psycho_666

Member
How can i pair a variable with id of an instance
The easiest way is:
id=instance_create(x,y,object);
At that point ID will literally store the I stance ID.
Then again you may want some sort of data structure, be it list, grid, whatever, or as I love to do - simple array and store all the instances IDs.
Arrays being what they are you can literally store anything from numbers to even sprite indexes and stuff.
 

TheouAegis

Member
If you are using instance_create*() you shouldn't save the id. Only instances created in the IDE actually have consistent ids. There is thus no good reason to save ids of instances created via code. In other words, if using instances created via code, don't even think about using id for saving.
 

Joe Ellis

Member
I think you would really need to save the state of the room into a buffer or text file that can be read and then generate the room as it was during the time it was saved. There are quite a few options, one of the simplest would be to loop through the instance_id array, which is a built in global read only array that stores the id's of every active instance. You would then, for each instance save all it's variables that are important. They could just be the x, y, depth, sprite_index, image_index etc. In a lot of cases this would be substantial, but if you needed to save specific variables per object, you'd have to create a list of variables per object, plus the type of variable in order to save what's required. But hopefully just storing the positions and simple things will be enough for what you need
 

sadegh993

Member
If you are using instance_create*() you shouldn't save the id. Only instances created in the IDE actually have consistent ids. There is thus no good reason to save ids of instances created via code. In other words, if using instances created via code, don't even think about using id for saving.
I'm not using Instance_create() for creating that instances, i place them in room via room editor.
Thanks.

Active instances of an object have an id, is that possible to i write a few lines of codes in the object and create a variable for every active instance of that object and save that variables in a file like Savedata.ini ?and then after reloading the game variables for every instance will be loaded?
If this is possible can somebody please explain me it, how can i do that? (step by step or something like this)
Sorry if i'm asking a lot or for any other things that you didn't like from me.
Thanks a lot for your helps.
 

saffeine

Member
personally i think the best approach is what @Joe Ellis suggested. if you aren't familiar with buffers or text files, then there's no better time to learn about them.
i won't write out the code since it'd take a bit too much time, but i will point you in the right direction and give some pointers on how to do it.

manual links: buffers | file system | text files.

saving
in a controller object ( heck, even the player object will work ), under the game end event, or whatever event calls the end of the game or room / level, create a buffer.
using with( Destructible Box Object ), write the position of the boxes using buffer_write(), the way you do this is entirely up to you, i wouldn't worry about optimal ways to do it until you understand the process fully.
at the end of the writing process, save the buffer either by using buffer_save(), or a combination of buffer_base64_encode() and file_text_open_write(). again, the choice is yours.
don't forget to delete the buffer later using buffer_delete().

loading
when the same room starts again, check to see if the file exists where you saved it using file_exists().
if the file does exist you need to delete all the boxes from the room using with( Destructible Box Object ) and instance_destroy().
then load the buffer from the file by using either buffer_load() or buffer_base64_decode(), depending on how you saved it.
once the buffer is loaded, use while( buffer_tell( buffer ) < buffer_get_size( buffer ) ), and buffer_read() to read all the positions, and create a box at each position read.
again, once all this is done, don't forget to delete the buffer.

to be quite honest, once you get a grasp of the functions i bolded and how buffers / files work within gamemaker, this whole process is a cake walk.
it might seem intimidating at first since there's a bunch of things to remember and make a note of, but all of these functions can be used for other things too like save files and networking ( later ).

hope this is enough information for you, feel free to ask if something doesn't make sense, but i do encourage you to read through the manual links to try and figure it out first. good luck!
 

sadegh993

Member
Hello again. Thanks a lot for your detailed and long explanation.
saffeine
Sorry for late reply i have tried to execute the way that you have written but i think i haven't understood it correctly.
can you please take a look at below image? what is wrong with this? i have tried it for block object (Game End event). when i opened Player_Save.sav there was only a few characters written which i guess somethings are wrong.
also i have tried with(o_block) before buffer_write but i got an error related to variable.
Any help is appreciated.
Thank you again.
pcgmsp01.png
 

TheouAegis

Member
Your code only saves the coordinates for one block. You need to loop through all the blocks and have them write their coordinates to the buffer.

The save code should not be in a block, it should be in the player object or in a control object so the save code is run only once.
 

saffeine

Member
Hello again. Thanks a lot for your detailed and long explanation.
saffeine
Sorry for late reply i have tried to execute the way that you have written but i think i haven't understood it correctly.
can you please take a look at below image? what is wrong with this? i have tried it for block object (Game End event). when i opened Player_Save.sav there was only a few characters written which i guess somethings are wrong.
also i have tried with(o_block) before buffer_write but i got an error related to variable.
Any help is appreciated.
Thank you again.
View attachment 34263
first of all, you'll need to let a single object do it and let the o_block object simply pass on its respective variables.
you're off to a good start, but the way you're doing it is going to have each box overwrite the last, leaving you with data for just one object.

secondly, you have no reason to be opening the .sav in a text editor unless it's to confirm that it's writing anything at all.
when buffers are encoded / saved, they're translated into unreadable strings that represent the values that are passed into them, and can only be read through buffer_load or an equivalent. hence why it looks like that.

create a controller object and place it in the room. in the Room End / Game End event, put something like this:
GML:
var buff = buffer_create( 16384, buffer_fixed, 2 );
buffer_seek( buff, buffer_seek_start, 0 );

with( o_block ){
    // note that you're writing two values per object, this will be important when loading.
    buffer_write( buff, buffer_s16, x );
    buffer_write( buff, buffer_s16, y );
}

buffer_save( buff, room_get_name( room ) + ".sav" ); // we save and load with the room name because you don't want it loading from the wrong level ( if you have levels that is ).
buffer_delete( buff );
in the same object, put this code in the Room Start / Game Start event:
GML:
if( file_exists( room_get_name( room ) + ".sav" ) ){
    // if there is a save file associated with the room, destroy the boxes to load the new ones in.
    with( o_block ){ instance_destroy(); }

    var buff = buffer_load( room_get_name( room ) + ".sav" ); // we save and load with the room name because you don't want it loading from the wrong level ( if you have levels that is ).

    // here we run a while loop to read the buffer until we reach the end.
    while( buffer_tell( buff ) < buffer_get_size( buff ) ){
        // here we read those two values in sequence each time.
        var xx = buffer_read( buff, buffer_s16 );
        var yy = buffer_read( buff, buffer_s16 );
    
        // and we create an instance where those values point to.
        var inst = instance_create( xx, yy, o_block );
    }

    buffer_delete( buff );
}
you may have to tweak some bits and pieces of this. i coded it in the post editor rather than the ide, and some functions differ very slightly between gamemaker versions, but the idea is the same.
that being said, i'd honestly surprise myself if this works at all since it looks so simple to me, but i think i covered enough of the bases. the task itself isn't complicated, it's just knowing how it works.
i was hesitant to give you explicit code in my first reply since people tend to just take it and run without really learning anything, but you gave it a shot, so why not šŸ˜

hopefully this works for you and makes sense, be sure to keep us posted.
 

sadegh993

Member
saffeine
Thanks you very much for your helps.
Also thank you TheouAegis and others.
saffeine Thanks to your codes and your explanations i have been able to recreate objects that i want to create again after reloading the game but i have an important problem or problems, i don't know why number of o_block instance are lot's of time higher than previous. i can't understand correctly. for example instance count of o_block was about 200 but now after creating instance via code in Room Start event i have about 4000 instances of that object. it's important because amount of o_block instance are important for me.
Thanks a lot.
 

saffeine

Member
it's possible that the Room Start code is being run before the o_block objects have actually been created, which would lead to it not deleting any objects, and still loading in the previous ones.
i have a solution, but it isn't the prettiest. move the Room Start code to an Alarm event instead, and in the Room Start event, set the alarm to a small number ( 1 should be fine, but you may need it to be longer ).
by doing it like this, you're giving the project time to create all of the instances, and then when the Alarm event is triggered, it will delete the objects properly.

i'm hoping this will solve the problem. make sure to delete the previous .sav file before running the project again after the changes, i can't imagine how many instances it'll try to create once loaded.
 

sadegh993

Member
it's possible that the Room Start code is being run before the o_block objects have actually been created, which would lead to it not deleting any objects, and still loading in the previous ones.
i have a solution, but it isn't the prettiest. move the Room Start code to an Alarm event instead, and in the Room Start event, set the alarm to a small number ( 1 should be fine, but you may need it to be longer ).
by doing it like this, you're giving the project time to create all of the instances, and then when the Alarm event is triggered, it will delete the objects properly.

i'm hoping this will solve the problem. make sure to delete the previous .sav file before running the project again after the changes, i can't imagine how many instances it'll try to create once loaded.
Thanks for replying.
I have check and there wasn't that controller object in previous room, so it first loads in considered room.
I have given that code to the alarm even but it seems it didn't help. i have also adjusted timer value of alarm. the other strange things is for me is that it creates other types of o_block objects too. (there are types of o_block)
Thank you again.
gmsp2.png
 

saffeine

Member
yeah, it'll do that. along with the x and y, you need to be storing the type of object too.
when you write to a buffer, all it does is store information. it's down to you at that point to read the information back and do what needs to be done with it.
a simple solution would be to have each type of o_block store what type of block it is along with its x and y coordinates, and when you read it back it creates that type of block.

saving:
GML:
buffer_write( buff, buffer_string, object_get_name( object_index ) );
buffer_write( buff, buffer_s16, x );
buffer_write( buff, buffer_s16, y );
loading:
GML:
// here we read those ( now ) three values in sequence each time.
var name = buffer_read( buff, buffer_string );
var xx = buffer_read( buff, buffer_s16 );
var yy = buffer_read( buff, buffer_s16 );

// and we create an instance where those values point to.
var inst;
switch( name ){
    case "o_block": inst = instance_create( xx, yy, o_block ); break;
    case "o_block1": inst = instance_create( xx, yy, o_block1 ); break;
    case "o_block2": inst = instance_create( xx, yy, o_block2 ); break;
}

as far as why you might be having so many objects being created when loading, i tested it in a clean project and it seems to work completely fine for me. do you have any code left over that might be causing it?
another thing to check is that the instance number is actually accurate. how are you setting / incrementing it? are you using instance_number() or just counting when a block is created?

( i apologise in advance if there are issues in a few functions as i reply. i work / tested in GMS2.3 and a few functions were changed since GMS1. i trust you know what the equivalent is however, they're similar enough. )
 
Last edited:

sadegh993

Member
as far as why you might be having so many objects being created when loading, i tested it in a clean project and it seems to work completely fine for me. do you have any code left over that might be causing it?
another thing to check is that the instance number is actually accurate. how are you setting / incrementing it? are you using instance_number() or just counting when a block is created?

( i apologise in advance if there are issues in a few functions as i reply. i work / tested in GMS2.3 and a few functions were changed since GMS1. i trust you know what the equivalent is however, they're similar enough. )
There are lots of instances. i have tested without of creating o_blcok2 via buffer and numbers of o_blcok2 were correct but when i try to use buffer for creating o_block2 i see a large number also I'm using from instance_number().
At this time i want to only create instances of o_block2 object via buffer or loading system.
Thanks a lot.
Your welcome for GMS1, Thanks again.
 
Last edited:

sadegh993

Member
There are lots of instances. i have tested without of creating o_blcok2 via buffer and numbers of o_blcok2 were correct but when i try to use buffer for creating o_block2 i see a large number also I'm using from instance_number().
At this time i want to only create instances of o_block2 object via buffer or loading system.
Thanks a lot.
Your welcome for GMS1, Thanks again.
I have also understood something, it seems the number of o_block2 is related to the buffer size, if i change buffer size to quarter of previous value then it seems i have 1024 o_block2.
Thanks again.
 

saffeine

Member
I have also understood something, it seems the number of o_block2 is related to the buffer size, if i change buffer size to quarter of previous value then it seems i have 1024 o_block2.
Thanks again.

in that case, you'll be better off creating a 'grow' buffer. that way, your buffer will lengthen based on how much you write to it.
GML:
var buff = buffer_create( 1, buffer_grow, 2 ); // you may have to set a different initial size, but give it a go.
i'll be honest, i don't think i've ever used buffer_save, so i wasn't really sure how much of the buffer would actually get saved. as it turns out, it's the whole buffer.
i do believe with buffer_save_ext you can specify a size, so that's an option too. buffer_tell will let you know how far along a buffer you are, so combining the two is probably what you want.
 

sadegh993

Member
i'll be honest, i don't think i've ever used buffer_save, so i wasn't really sure how much of the buffer would actually get saved. as it turns out, it's the whole buffer.
i do believe with buffer_save_ext you can specify a size, so that's an option too. buffer_tell will let you know how far along a buffer you are, so combining the two is probably what you want.
Sorry but i didn't understand the bold part very good, how should i do that? pardon me, may i know what is your comment about ds_list or one dimensional array, however i haven't also good knowledge of this things. are this things easier to use? could i know are you more familiar with this or you can help me? sorry.
Thank you very much.
 

saffeine

Member
Sorry but i didn't understand the bold part very good, how should i do that? pardon me, may i know what is your comment about ds_list or one dimensional array, however i haven't also good knowledge of this things. are this things easier to use? could i know are you more familiar with this or you can help me? sorry.
Thank you very much.
whenever you write to a buffer, the position you're writing to will shift forward, as you'd expect. buffer_tell will tell you how far along the buffer you are, regardless of size.
if you create a buffer of 4096 bytes and write 2 bytes, then another 2, then another 2, buffer_tell will return 6(?). alignment might affect this, i don't know the technical stuff all too well, but whatever it returns is where your current position is in the buffer, and in most cases it'll be at the end, where you last read / wrote to. this can be useful for knowing how much of a buffer you've used, and how much to save / send over a network without excess.

buffer_save_ext asks for 4 arguments ( buffer, filename, offset, size ), and by using the offset and size arguments, you can save just the part of the buffer that you need. set the offset to 0 and the size to the value of buffer_tell( buff ), and it will only save the parts of the buffer that actually contain values for the blocks, rather than writing more than that, and then giving you issues when reading it back ( since it appears to be reading the rest of the buffer which doesn't contain anything of use, and is creating instances based on that ).


also, to answer your question about lists and arrays, they're just used to hold data in groups. is there anything specific you'd like to know about them?
 

TheouAegis

Member
I never had issue with loading a buffer from a file. One possibility to look into is the old save file isn't deleted and GM just appends the new data to it.

How many manager objects are in the room?

...Are you creating your buffers with a size of 1 and using buffer_grow or are you still declaring your buffers with a minimum size?
 
Last edited:

sadegh993

Member
whenever you write to a buffer, the position you're writing to will shift forward, as you'd expect. buffer_tell will tell you how far along the buffer you are, regardless of size.
if you create a buffer of 4096 bytes and write 2 bytes, then another 2, then another 2, buffer_tell will return 6(?). alignment might affect this, i don't know the technical stuff all too well, but whatever it returns is where your current position is in the buffer, and in most cases it'll be at the end, where you last read / wrote to. this can be useful for knowing how much of a buffer you've used, and how much to save / send over a network without excess.

buffer_save_ext asks for 4 arguments ( buffer, filename, offset, size ), and by using the offset and size arguments, you can save just the part of the buffer that you need. set the offset to 0 and the size to the value of buffer_tell( buff ), and it will only save the parts of the buffer that actually contain values for the blocks, rather than writing more than that, and then giving you issues when reading it back ( since it appears to be reading the rest of the buffer which doesn't contain anything of use, and is creating instances based on that ).


also, to answer your question about lists and arrays, they're just used to hold data in groups. is there anything specific you'd like to know about them?
Thanks a lot for your helps.
I have tested with buffer_save_ext, befor testing i delete that save file.
Here are codes for Saving and Loading:
02.png
01.png
Again i wasn't successful for creating correctly o_blcok2 instances. not only their count number is wrong also they aren't correctly created.
Regarding to ds_list and arrays, i want to know if i use them for saving and loading o_block2, would that be easier or not? however i'm not good at either of them.
Thanks a lot.
 

sadegh993

Member
I never had issue with loading a buffer from a file. One possibility to look into is the old save file isn't deleted and GM just appends the new data to it.

How many manager objects are in the room?

...Are you creating your buffers with a size of 1 and using buffer_grow or are you still declaring your buffers with a minimum size?
Thanks for your reply.
I delete it manually. i use this codes:
02.png
01.png
Thank you.
 

saffeine

Member
chrome_gBvL7V9C27.png
chrome_wJgIJT0pkz.png

you're writing three values and only reading back two. note that when i provided the code i also added a 'name' variable when loading, along with a switch statement to determine what object was loaded.

as far as your question about arrays and lists: yeah, they're probably a good idea for keeping track of the objects you have in your room, but you need to remember that you need to save them too.
ultimately, once you've added all your objects to a list or array, how do you plan on saving and loading them, if not in the exact same way you're attempting to do it now? :p

it's really important to realise that when you're programming, you're responsible for how the data is being handled by the computer. there are functions to help you, but you need to be aware of what's happening.
any type of data structure ( arrays, lists, maps, grids, etc ) is great for storing data, but you still have to figure out what to do with that data in the end, because the computer will never just know what you want to save.

fix the issue i mentioned at the start of my reply and you should be a few steps closer. you can either remove the first buffer_write ( the string ), or add another buffer_read before the other two ( also a string ).
 

sadegh993

Member
@saffeine Thanks to your helps, i think that problem is solved or almost solved.
I have another question, how i can delete that save file (room_name.save or something like this) with codes in game maker studio? i mean if i don't want to delete it manually how i can do that by for example pressing a button.
Thanks a lot for all of your helps.
 

TheouAegis

Member
What's the last code you tried? If you can create the save file, you should be able to delete it. And how do you know it's not working for you? What issue reared its head after using file_delete()?
 

sadegh993

Member
@TheouAegis
I have checked file from AppDate folder and it exists.
Yes i can create .sav file via:
buffer_save_ext( buff, room_get_name( room ) + ".sav",0,buffer_tell( buff ));
and almost recently i have also tried to delete that file via below code:
file_delete("level_1_test_new.sav");

Thanks a lot.
 

TheouAegis

Member
Make sure your game isn't running, then go into the AppData folder and delete any of the old folders. You want to make sure you aren't looking at leftover files. Next, put a show_message("File deleted?") where you delete the file so you can verify that block of code actually ran. Then run your game, create a new save, then delete it right away before you close the game. If the message doesn't pop up, then you never delete the file. Otherwise, check if the file still exists after that. If creating the file and deleting the file during the same session doesn't delete the file and you have verified that file_delete() really is running, you might need to file a bug report. But like I said, take steps to make sure you aren't accidentally looking at an old file. Finally in the bug report means you will have to make a small, relatively empty project that does the same thing as your project and verifies that the bug exists even in the small project. They don't want your full large project for bug testing.
 
Top