Save/Load Code

Status
Not open for further replies.
Hi

I'm trying to save "sprite_index" to a map but I keep getting this message:

Code:
############################################################################################
FATAL ERROR in
action number 1
of Draw Event
for object SaveCompleteObject:

Variable <unknown_object>.sprite_index(25, -2147483648) not set before reading it.
 at gml_Script_scr_drac_castle_save (line 23) -               ds_map_add(instanceMap, "sprite_index", instance.sprite_index);
############################################################################################
--------------------------------------------------------------------------------------------
stack frame is
gml_Script_scr_drac_castle_save (line 23)
called from - gml_Script_scr_castles_save (line 3) - scr_drac_castle_save();
called from - gml_Script_scr_save_game (line 10) - scr_castles_save();
called from - gml_Object_SaveCompleteObject_Draw_0 (line 8) - scr_save_game();
The savefile code is:

Code:
if(file_exists("drac_castle.sav"))
{
    file_delete("drac_castle.sav");
    
}

var saveFile = file_text_open_write("drac_castle.sav");


        if (!instance_exists(DraculasCastleObject))
        {
            show_debug_message("Draculas Castle Doesn't Exist");   
        }
        var instance = instance_find(DraculasCastleObject, all);
        
        //convert instance to a ds_map
        var instanceMap = ds_map_create();
        //var list1 = ds_map_create();
        //
            
        ds_map_add(instanceMap, "sprite_index", instance.sprite_index);
        ds_map_add(instanceMap, "image_speed", instance.image_speed);
        ds_map_add(instanceMap, "x", instance.x);
        ds_map_add(instanceMap, "y", instance.y);
        
        ds_map_add(instanceMap, "global-castle_hp", global.castle_hp);
        ds_map_add(instanceMap, "death_sound_flag", instance.death_sound_flag);
        ds_map_add(instanceMap, "global-non_usable", global.non_usable);
        ds_map_add(instanceMap, "castle_myTimer", instance.castle_myTimer);
        ds_map_add(instanceMap, "global-ghost_flag", global.ghost_flag);
        
        ds_map_add(instanceMap, "damage_audio_flag_1", instance.damage_audio_flag_1);
        ds_map_add(instanceMap, "damage_audio_flag_2", instance.damage_audio_flag_2);
        ds_map_add(instanceMap, "damage_audio_flag_3", instance.damage_audio_flag_3);
        ds_map_add(instanceMap, "damage_audio_flag_4", instance.damage_audio_flag_4);
        ds_map_add(instanceMap, "damage_audio_flag_5", instance.damage_audio_flag_5);
        ds_map_add(instanceMap, "damage_audio_flag_6", instance.damage_audio_flag_6);
        
        ds_map_add(instanceMap, "global-you_lose", global.you_lose);
        
        
        

        JSONInstance = json_encode(instanceMap);
        
        file_text_write_string(saveFile, JSONInstance);
        //seperate with pipe character
        file_text_write_string(saveFile, "|");
        
        ds_map_destroy(instanceMap);       
        
        
        

///close the file
file_text_close(saveFile);
 
D

Danei

Guest
I don't think you're supposed to use all as the 2nd argument in instance_find() like that. Try replacing it with 0 to find the first (and I assume only?) instance of DraculasCastleObject.
 
What Danei said - and if you need to save more than one DraculasCastleObject, you'll want to use 'instance_number', and a for loop.

E.g.
Code:
for (var i=0; i<instance_number(object); i++) {
    var _inst = instance_find(object, i);

    // DS MAP CODE
}
That's when you start nesting maps into lists. Effectively, all "saving a game" is in GameMaker, is reconstructing the GM instance tree in a JSON file. Or at least, it is for these purposes.
 
What Danei said - and if you need to save more than one DraculasCastleObject, you'll want to use 'instance_number', and a for loop.

E.g.
Code:
for (var i=0; i<instance_number(object); i++) {
    var _inst = instance_find(object, i);

    // DS MAP CODE
}
That's when you start nesting maps into lists. Effectively, all "saving a game" is in GameMaker, is reconstructing the GM instance tree in a JSON file. Or at least, it is for these purposes.
Yeah I know. I used all becauase for some reason instance_find(DraculasCastleObject, 0) is returning null.
 
D

Double7

Guest
I know when I do stuff like this I usually do something dumb. I don't assume you would make the same mistakes but this is what I would check.
1) Ensure the name of the object is exactly 'DraculasCastleObject'. (also, I'm not sure if instance_find will find child objects or not, so if this is a parent object I would try a child object directly and see if that works.
2) Make sure the code actually sees the objects. Just print out the count using instance_count() to see if it should be finding those objects. I would just use this instead of instance_exists() to grab more info

What exactly is "DraculasCastleObject"? is it a parent object?
Also I am curious why you are doing this in the Draw event
 
Uh, ok. So, what does 'null' mean? -1? -4? undefined? pointer_null? It says in the Docs that you can use 'all', and it substitutes the 'n' argument for 'instance_count-1'. Let's say you have 5 instances of that object. What this means is that you have an instance stored at index 4, and not 0. I'm not intimately familiar with the instance functions, but I'd hazard a guess that when you delete instances, the instance indexing moves to accommodate this change. If that's correct, then my next bet is that you're dealing with an inactive instance. Do you have instance_deactivate code anywhere?

Regardless, this is likely going to be one of those times when contextual code is helpful. I don't know what returning 'null' means, but if you mean it returns '-4', it's returning the keyword 'noone', which means you don't have any instances there, and the problem is with your instance exist check. If it's just crashing, then we need to know more to help. Show us the code that returns null, like you said.
 
D

Danei

Guest
The maximum value for "n" in this function would be

For the keyword all: instance_count - 1
For an object index: instance_number(OBJ) - 1
http://127.0.0.1:51290/source/_build/3_scripting/4_gml_reference/instances/instance_functions/instance_number.html
I think this means that if you're using 'all' as the first argument, then the maximum value you ought to use as the second one is equivalent to instance_count -1, not that you can use 'all' as the second argument to make any substitutions.



 
Ok. First off, null is noone in just about most programming languag

My code that is coming back as no one is the following. It is coming back at no one but I don 't understand how that can be possible considering the object is sitting right in front of me.

Code:
if(file_exists("drac_castle.sav"))
{
    file_delete("drac_castle.sav");
  
}

var saveFile = file_text_open_write("drac_castle.sav");


        if (!instance_exists(DraculasCastleObject))
        {
            show_debug_message("Draculas Castle Doesn't Exist"); 
        }
        var instance = instance_find(DraculasCastleObject, 0); //returns no one even though there has to be an instantiation. Thhe can see the castle right in front of me
      
        //convert instance to a ds_map
        var instanceMap = ds_map_create();
        //var list1 = ds_map_create();
        //
          
        ds_map_add(instanceMap, "sprite_index", instance.sprite_index);
es.

//////

I'm getting a related runtime error that is associated with the DraculasCastleObject. Its the
followinjg:

Code:
___________________________________________
############################################################################################
FATAL ERROR in
action number 1
of Draw Event
for object SaveCompleteObject:

Variable <unknown_object>.sprite_index(25, -2147483648) not set before reading it.
Variable <unknown_object>.sprite_index(25, -2147483648) not set before reading it.
 at gml_Script_scr_drac_castle_save (line 23) -               ds_map_add(instanceMap, "sprite_index", instance.sprite_index);
############################################################################################
--------------------------------------------------------------------------------------------
stack frame is
gml_Script_scr_drac_castle_save (line 23)
called from - gml_Script_scr_castles_save (line 3) - scr_drac_castle_save();
called from - gml_Script_scr_save_game (line 10) - scr_castles_save();
called from - gml_Object_SaveCompleteObject_Draw_0 (line 8) - scr_save_game();

FATAL ERROR in action number 1 of Draw Event for object SaveCompleteObject:
 

samspade

Member
I think you've diagnosed your own problem. You're trying to reference an instance that doesn't exist. I also think this is very similar to a question you asked before.

https://forum.yoyogames.com/index.php?threads/not-set-before-reading-it.62660/#post-376505

The error message says it doesn't exist, so it doesn't exist. Your code says it doesn't exist, so it doesn't exist. I would figure out why it doesn't exist. I would strongly recommend learning to use the debugger and stepping through your code to figure this out.
 
samspade: Not sure where to look in the debugger. I know how to use it, I'm just sure how to narrow down the error using it. I'll give it a try....

Debugger says DraculasCastleObject exists

Look below. That is the profile in the save screen that is throwing an error.The screen throws an error when it tries to run a save script - the sprite_index specifically
 
Last edited by a moderator:

Evanski

Raccoon Lord
Forum Staff
Moderator
Make sure the object your trying to find is there in the same room

Before runing the save load code
 

FrostyCat

Redemption Seeker
How do I turn on instances? Doesn't say in the manual
The Manual's entry on debugging said:
You can reset the debugger layout at any time - or reopen closed windows - from the Debugger context menu at the top of the IDE:
The Manual's entry on debugging > Watches > All Instances said:
All Instances


Should you need to see all instances that are currently in the room, you can select this window and they will be listed. You can also see the state of any built in or instance variables that they have, and you can right click
on a value to set its type (see Data Types, below)
 
Yeah I have the All Instances window it just doesn't populate. I apologize ahead of time, I just can't find it in the manual.
If the All Instances window isn't populating it's because there are no instances.

EDIT: If you have Real-time Debugging enabled. Try enabling that.
 
The object, DraculasCastleObject wasn't in the same rom as the save room. So I made a flag that gets set to do the save when you return from save room. It doesn't crash but it doesn't save either.. And yeah, I had to enable real-time debugging.
 
Then DraculasCastleObject doesn't exist. For all intents and purposes, only objects in the current room are treated as "existing." Like EvanSki said:
Yeah I didn't know that only objects that are in the room are treated as existing. This is going to mnke writing save code a lot lot harder
 

Evanski

Raccoon Lord
Forum Staff
Moderator
Yeah I didn't know that only objects that are in the room are treated as existing. This is going to mnke writing save code a lot lot harder
Theres tutorials shaun spalding made a really easy .ini save system
 
I would but I've already got over 200 individual save files. And that woujld be a lot of work to recreate.

[EDIT] Do you have a link for that sav routine? I think I"m g oing to cut my losses if this is easy to set up
 
Last edited by a moderator:
So, I watched his videos. Everything looks to be straightforward, except saving multiple instances of objects. I have an example here of my problem. I know how to save to file using a ds_map etc.. I just don't know how to partition the instances.The tutorial code I was looking at used | pipes as delimiters. That's fine for saving, but I don't know how to unwrap them.

Code:
Fading Gem

var fading_gem_count = instance_number(FadingGemObject);

if (file_exists("fading_gem.sav"))
{
    file_delete("fading_gem.sav");
    
}

var saveFile = file_text_open_write("fading_gem.sav");



for (var i = 0; i< fading_gem_count;i++)
{
        var instance = instance_find(FadingGemObject, i);
        
        //convert instance to a ds_map
        var instanceMap = ds_map_create();
        
        ds_map_add(instanceMap, "fade_duration", instance.fade_duration);
        ds_map_add(instanceMap, "alpha_step", instance.alpha_step);
        ds_map_add(instanceMap, "movement_speed", instance.movement_speed);
        ds_map_add(instanceMap, "image_alpha", instance.image_alpha);
        ds_map_add(instanceMap, "alpha", instance.alpha);
        
        
            
        
        
        JSONInstance = json_encode(instanceMap);
        
        file_text_write_string(saveFile, JSONInstance);
        //seperate with pipe character
        file_text_write_string(saveFile, "|");
        
        ds_map_destroy(instanceMap);       
        
        
        
        

        
}
///close the file
file_text_close(saveFile);

The Load FIle:
Code:
var gem_counter  = instance_number(FadingGemObject);


var loadFile = file_text_open_read("fading_gem.sav");

if (!file_text_eof(loadFile))
    {
        //masetrData one long string
        masterData = file_text_read_string(loadFile);
        
        var SentryArray  = scr_split(masterData, "|");
        var SentryCount = array_length_1d(SentryArray);
        
        if (SentryCount > 0)
        {
            //freach sentry
            for (var i = 0; i < SentryCount; i++)
            {
                var SentryJSON = sentryArray[i];
                //deserialize
                var sentryMap = json_decode(SentryJSON);
    

                for (var i = 0; i< spit_count;i++)
                {
                        var instance = instance_find(FadingGemObject, i);
        
                        //convert instance to a ds_map
                        var instanceMap = ds_map_create();
        
                            inst = instance_create_depth(_x,_y, -1100, FadingGemObject);   
        
        
        
                        var _fade_duration = ds_map_find_value(instanceMap, "fade_duration")
                        var _alpha_step = ds_map_find_value(instanceMap, "alpha_step")
                        var _movement_speed = ds_map_find_value(instanceMap, "movement_speed")
                        var _image_alpha = ds_map_find_value(instanceMap, "image_alpha")
                        var _alpha = ds_map_find_value(instanceMap, "alpha")
                        
                        inst.fade_duration = _fade_duration;
                        inst.alpha_step = _alpha_step;
                        inst.movement_speed = _movement_speed;
                        inst.image_alpha = _image_alpha;
                        inst.alpha = _alpha;
        
        
                    
            
                        ds_map_destroy(instanceMap);       
                }
            }
        }
    }
        


///close the file
file_text_close(loadFile);
 

Evanski

Raccoon Lord
Forum Staff
Moderator
I would but I've already got over 200 individual save files. And that woujld be a lot of work to recreate.

[EDIT] Do you have a link for that sav routine? I think I"m g oing to cut my losses if this is easy to set up
 
So, I watched the video on JSON and Save/Load. My problem is that I need some kind of delimter to separate the instances in the map.I have code to save the instances but I don'ot have code to
load them.


Save

Code:
var fading_gem_count = instance_number(FadingGemObject);

if (file_exists("fading_gem.sav"))
{
    file_delete("fading_gem.sav");
    
}

var saveFile = file_text_open_write("fading_gem.sav");



for (var i = 0; i< fading_gem_count;i++)
{
        var instance = instance_find(FadingGemObject, i);
        
        //convert instance to a ds_map
        var instanceMap = ds_map_create();
        
        ds_map_add(instanceMap, "fade_duration", instance.fade_duration);
        ds_map_add(instanceMap, "alpha_step", instance.alpha_step);
        ds_map_add(instanceMap, "movement_speed", instance.movement_speed);
        ds_map_add(instanceMap, "image_alpha", instance.image_alpha);
        ds_map_add(instanceMap, "alpha", instance.alpha);
        
        
            
        
        
        JSONInstance = json_encode(instanceMap);
        
        file_text_write_string(saveFile, JSONInstance);
        //seperate with pipe character
        file_text_write_string(saveFile, "|");
        
        ds_map_destroy(instanceMap);       
        
        
        
        

        
}
///close the file
file_text_close(saveFile);
Load

Code:
var gem_counter  = instance_number(FadingGemObject);


var loadFile = file_text_open_read("fading_gem.sav");

if (!file_text_eof(loadFile))
    {
        //masetrData one long string
        masterData = file_text_read_string(loadFile);
        
        var SentryArray  = scr_split(masterData, "|");
        var SentryCount = array_length_1d(SentryArray);
        
        if (SentryCount > 0)
        {
            //freach sentry
            for (var i = 0; i < SentryCount; i++)
            {
                var SentryJSON = sentryArray[i];
                //deserialize
                var sentryMap = json_decode(SentryJSON);
    

                for (var i = 0; i< spit_count;i++)
                {
                        var instance = instance_find(FadingGemObject, i);
        
                        //convert instance to a ds_map
                        var instanceMap = ds_map_create();
        
                            inst = instance_create_depth(_x,_y, -1100, FadingGemObject);   
        
        
        
                        var _fade_duration = ds_map_find_value(instanceMap, "fade_duration")
                        var _alpha_step = ds_map_find_value(instanceMap, "alpha_step")
                        var _movement_speed = ds_map_find_value(instanceMap, "movement_speed")
                        var _image_alpha = ds_map_find_value(instanceMap, "image_alpha")
                        var _alpha = ds_map_find_value(instanceMap, "alpha")
                        
                        inst.fade_duration = _fade_duration;
                        inst.alpha_step = _alpha_step;
                        inst.movement_speed = _movement_speed;
                        inst.image_alpha = _image_alpha;
                        inst.alpha = _alpha;
        
        
                    
            
                        ds_map_destroy(instanceMap);       
                }
            }
        }
    }
        


///close the file
file_text_close(loadFile);
This is the saving of the json string


Code:
        JSONInstance = json_encode(instanceMap);
        
        file_text_write_string(saveFile, JSONInstance);
        //seperate with pipe character
        file_text_write_string(saveFile, "|");

This is the loading of the jason string
Code:
 //masetrData one long string
        masterData = file_text_read_string(loadFile);
        
        var SentryArray  = scr_split(masterData, "|");
        var SentryCount = array_length_1d(SentryArray);
        
        if (SentryCount > 0)
        {
            //freach sentry
            for (var i = 0; i < SentryCount; i++)
            {
                var SentryJSON = sentryArray[i];
                //deserialize
                var sentryMap = json_decode(SentryJSON);
    

                for (var i = 0; i< spit_count;i++)
                {
                        var instance = instance_find(FadingGemObject, i);
        
                        //convert instance to a ds_map
                        var instanceMap = ds_map_create();
        
                            inst = instance_create_depth(_x,_y, -1100, FadingGemObject);

 
So the latest video by Shaun Spalding uses JSON like I do above. I'm all set except for how to use a delimiter to separate the instances of the objects. For example I have a Simple Brain object and I have 5 instances of it. I need to save the object variables for each individual instance. I'd like to use a pipe to separate them but I don't know how that works with JSON.I basically need a "split" script for JSON
 
H

Homunculus

Guest
Why would you want to use a delimited string when you can just use a json structure? Keep in mind that ds_maps and ds_lists can be nested generating a nested json object or array respectively, much better than a string, and definitely easier to manage.

Just add a ds_list to your save data with your instances in it saved as ds_maps (with every variable as key / value pair in the map)
 
Nevermind that for right now. I just found a major issue/logic error in my code. I want to save the current state of the game. To do that I was saving the variable instances variables. But there are a lot of variables and I was trying to save them all at once. Let me give you an example. There are eight bats you can choose from to have on the playing field. If I go to save the state of the game at any one time, there coujld be any oneo f those 8 bats on the play field as well as various enemies. But since there is no guarantee there is a bat on the field, there is no guarantee the bats variables are instantiated. So, I'm getting the following error for example (on a global variable):

Code:
global variable name 'wind_drop' index (100259) not set before reading it.
 at gml_Script_scr_wind_save (line 24) -               ds_map_add(instanceMap, "global-wind_drop", global.wind_drop);
######################################################################################
######
--------------------------------------------------------------------------------------------
I need a solution that doesn't involve me prototyping all the variables. The game easily has 100 instance variables that need to be saved.
 
Why would you want to use a delimited string when you can just use a json structure? Keep in mind that ds_maps and ds_lists can be nested generating a nested json object or array respectively, much better than a string, and definitely easier to manage.

Just add a ds_list to your save data with your instances in it saved as ds_maps (with every variable as key / value pair in the map)
I am using a JSON String. I used the delimited string because that was what the tutorial I went through used.
I'm not sure how to decode JSON into a string and maintain the hierarchy.
 
H

Homunculus

Guest
I can see that you used a delimiter, but it's totally unnecessary when you have better and built in ways to do the same thing. When you decode (and encode) to json your hierarchy is automatically maintained (in the form of nested data structures) as long as you added your nested data structures using the appropriate functions (ds_map_add_map, ds_list_mark_as_map, ds_map_add_list, etc...).

I don't remember Shaun's video about JSON suggesting using a delimiter, assuming we are talking about the same one.
 
No. He didn't go beyond explaining how to save more than one instance of an object. But more importantly, did you see my other post? The JSON string isn't that big of an issue now that I can't even save to my ds_map
 
H

Homunculus

Guest
No. He didn't go beyond explaining how to save more than one instance of an object.
You will never find the specific and exact code you need for everything you are trying to do, but we are here to help if you are stuck. I'm trying to tell you that the part you miss is related to generating a JSON using nested structures. In the specific: a root ds_map, containing a ds_list of instances (as ds_maps, you already have this part).
Try reading about nesting data structures in the manual, and eventually come up with something or every question you have in this regard.

But more importantly, did you see my other post? The JSON string isn't that big of an issue now that I can't even save to my ds_map
That's another problem, if that's preventing you from testing the json part, solve that first. I'm just replying to the latest of your questions, I figured that since you were asking for something else the other problem was solved.
 
Last edited by a moderator:
You will never find the specific and exact code you need for everything you are trying to do, but we are here to help if you are stuck. I'm trying to tell you that the part you miss is related to generating a JSON using nested structures. In the specific: a root ds_map, containing a ds_list of instances (as ds_maps, you already have this part).
Try reading about nesting data structures in the manual, and eventually come up with something or every question you have in this regard.


That's another problem, if that's preventing you from testing the json part, solve that first. I'm just replying to the latest of your questions.
It is preventing me from testing out the json part. I understand that you are just trying to help and I appreciate it. The latest problem kind of struck me and I can't work with a ds_map, ds_list, JSON, etc. without this problem with the variable scope. Any help you could provide would be greatly appreciated.
 
H

Homunculus

Guest
As far as I can tell, you were trying to save instances belonging to another room (since you move to a different room to save) and you solved this by calling the save routine when coming back from the save room.

Given the above, if the instances are not saved, there are a couple of things that come to mind that could cause this:

- The code for saving is never run. Add a show_debug_message at the top to verify that
- The code is run, but there are no instances at the time of the save. This could happen if the instance calling the save routine is created before any dracula castle (even in the same step, instances are not created simultaneously). Verify that by using the debugger or a show_debug_message at the top of the script checking the existence of an instance at least (you already did that at some point in a code you posted)
- Everything runs fine, but there's a problem writing to file or generating the json string. Use a show_debug_message on the json string and check if it's empty or generated as expected.
 
As far as I can tell, you were trying to save instances belonging to another room (since you move to a different room to save) and you solved this by calling the save routine when coming back from the save room.

Given the above, if the instances are not saved, there are a couple of things that come to mind that could cause this:

- The code for saving is never run. Add a show_debug_message at the top to verify that
- The code is run, but there are no instances at the time of the save. This could happen if the instance calling the save routine is created before any dracula castle (even in the same step, instances are not created simultaneously). Verify that by using the debugger or a show_debug_message at the top of the script checking the existence of an instance at least (you already did that at some point in a code you posted)
- Everything runs fine, but there's a problem writing to file or generating the json string. Use a show_debug_message on the json string and check if it's empty or generated as expected.


This is a big problem. Lets say I have four player characters, two castles, and five enemies. But they aren't all on the screen at the time. In reality its more like 8 pc s , 10 enemies, and two towers. If the variables for the sprites etc, have to be in the room to save, how in the world can I tell when they are in the room or not. Especially if they are global variables?
 
H

Homunculus

Guest
I think you are very confused about variables, instances and their scope.

The instances have to exist in the room you are saving them, their variables is just a consequence. By global variables I imagine you are referring to the instance name in the room editor, but if the instances are not there, it doesn’t really matter if the name is a global or not.

Moreover, by existing I don’t mean visible. An instance can exist even if it’s outside of the screen or invisible. As soon as you change room though they get destroyed. So, if you change room before saving, all instances in the current room are destroyed (unless persistent).

There are a lot of ways to solve a problem like this, a couple:

1. save the instances BEFORE going to the save room.
2. Prepare the json (or the ds_map) before going to the save room and store it in a global. Then, only in the save room, store it to file.
 
Last edited by a moderator:
I think you are very confused about variables, instances and their scope.

The instances have to exist in the room you are saving them, their variables is just a consequence. By global variables I imagine you are referring to the instance name in the room editor, but if the instances are not there, it doesn’t really matter if the name is a global or not.

Moreover, by existing I don’t mean visible. An instance can exist even if it’s outside of the screen or invisible. As soon as you change room though they get destroyed. So, if you change room before saving, all instances in the current room are destroyed (unless persistent).

There are a lot of ways to solve a problem like this, a couple:

1. save the instances BEFORE going to the save room.
2. Prepare the json (or the ds_map) before going to the save room and store it in a global. Then, only in the save room, store it to file.
Ok. So global means global variable. As in: global.myCounter++
Sorry visible was the wrong word. What I am saying is there are multiple objects that can be instantiated or not instantiated and I don't know how to save them in the main room before switching to say, the save room, when I don't know what objects are saved or what objects to save.

I think you might not be understanding me. Here is the process. I have a scr_dracula_castle script. It gets called by the master save script, scr_save_game. I enter the main room. I go into the save room which sets a flag to save the game when in the main room. I return to the main room and the save flag begins the save script. The first script to execute has two types of floating brains, they both need to be saved. In this case, one of the floating brains is instantiated, but the other isn't - but could be.

So let me summarize. I have a room with multiple objects that can be instantiated at various times. How do I write a script to handle that. In addition, I have global variables that cross rooms: may need to be accessed in the main room or both the main room and second room. How do I store that? Say I'm in my save script, I think what I should do is declare an instance of those globals in the create event for every time I use it in an Object. Is that right? I don't see how I can store a global in a ds_map without that variable being instantiated.
 
D

Danei

Guest
Why not create an obj_saveable object, and make it the parent of every object whose instances will need to be saved. Then, using with, you can loop through each instance that exists, put its relevant variables into a ds_map, and then nest that ds_map inside the larger ds_map that you will ultimately encode into JSON.

Hypothetical example using made-up variable names:

Code:
overarchingSaveMap = ds_map_create();
var instanceSaveID = 0;

with (obj_saveable){
  var instanceMap = ds_map_create(); //create a submap
  instanceMap[? "objIndex"] = object_index; //save the object index so we know what to create upon load.


  switch (object_index) {
  case obj_brain:
  
    instanceMap[? "x"] = x;
    instanceMap[? "y"] = y;
    instanceMap[? "hp"] = hp;
   //etc.
  break;

case obj_bat:
 
instanceMap[? "x"] = x;
    instanceMap[? "y"] = y;
    instanceMap[? "wingspan"] = wingspan;
   //etc.
  break;

case obj_draculas_castle:
 
instanceMap[? "x"] = x;
    instanceMap[? "y"] = y;
    instanceMap[? "interiorDecorStyle"] = interiorDecorStyle;
    //Obviously I'm just making up variables here, but you can choose which ones each type of object needs to save in a switch like this
  break;
  }

ds_map_add_map(overarchingSaveMap, "instance"+string(instanceSaveID), instanceMap);
instanceSaveID ++; //put the instance map into the larger map ensuring each one will have a unique key

}
then you will end up with a ds_map of ds_maps, and each of those submaps will contain whatever variables you want them to. You can then encode that as a JSON, and when you decode it upon load, you can iterate through the overarching map with ds_map_find_first() and ds_map_find_next() and run instance creation code every time you pull out a key that starts with "instance".

As for global variables, you should initialize all of them, even if it's just as noone. That way you'll never try to save a variable that isn't defined. You can just stick them into the same overarching map that you put the instances into, or nest them all in their own submap too, whichever you find easier to work with.
 
Hi Danei

Can you tell me if this code is what you are talking about or if I have to change anything, etc...

Code:
overarchingSaveMap = ds_map_create();
var instanceSaveID = 0;

with (obj_saveable){
  var instanceMap = ds_map_create(); //create a submap
  instanceMap[? "objIndex"] = object_index; //save the object index so we know what to create upon load.


  switch (object_index) {
  case SentryBatObject:
 
 
      ds_map_add(instanceMap, "global-wind_drop", global.wind_drop);
        ds_map_add(instanceMap, "global-wind_drop_myTimer", global.wind_drop_myTimer);
        ds_map_add(instanceMap, "sprite_index", instance.sprite_index);
        ds_map_add(instanceMap, "image_speed", instance.image_speed);
        ds_map_add(instanceMap, "x", instance.x);
        ds_map_add(instanceMap, "y", instance.y);
        
        
        ds_map_add(instanceMap, "snap", instance.snap);
        ds_map_add(instanceMap, "flight", instance.flight);
        ds_map_add(instanceMap, "alarm[0]", instance.alarm[0]);
        ds_map_add(instanceMap, "hp", instance.hp);
    
        ds_map_add(instanceMap, "damage", instance.damage);
        
        ds_map_add(instanceMap, "killed_a_brain", instance.killed_a_brain);
        ds_map_add(instanceMap, "opponent", instance.opponent);
        ds_map_add(instanceMap, "under_attack", instance.under_attack);
        
        
        
        
        ds_map_add(instanceMap, "colliding_flag", instance.colliding_flag);
        ds_map_add(instanceMap, "global-wind_myTimer", global.wind_myTimer);

        ds_map_add(instanceMap, "wind_brain_refresh_myTimer", instance.wind_brain_refresh_myTimer);

        ds_map_add(instanceMap, "global-push_back_trigger", global.push_back_trigger);
        ds_map_add(instanceMap, "global-push_back_enemy", global.push_back_enemy);
        ds_map_add(instanceMap, "global-old_path", global.old_path);


        ds_map_add(instanceMap, "play_myTimer", instance.play_myTimer);
        ds_map_add(instanceMap, "played", instance.played);

        ds_map_add(instanceMap, "target_placement_x", instance.target_placement_x);
        ds_map_add(instanceMap, "target_placement_y", instance.target_placement_y);
        ds_map_add(instanceMap, "wind_counter", instance.wind_counter);
        
        ds_map_add(instanceMap, "new_bat", instance.new_bat);
    
    
        


        ds_map_add(instanceMap, "wind_counter", instance.wind_counter);
        
        
        ds_map_add(instanceMap, "brainx", instance.brainx);
        ds_map_add(instanceMap, "brainy", instance.brainy);
        ds_map_add(instanceMap, "sprite_myTimer", instance.sprite_myTimer);
        ds_map_add(instanceMap, "global-old_path", instance.old_path);
        
        ds_map_add(instanceMap, "path_speed", instance.path_speed);
        ds_map_add(instanceMap, "speed", instance.speed);
break;
  }

ds_map_add_map(overarchingSaveMap, "instance"+string(instanceSaveID), instanceMap);
instanceSaveID ++; //put the instance map into the larger map ensuring each one will have a unique key

}
I made a parent object obj_saveable then added all the stuff I want to save as children. But I thought I would have to
do an instance_exists() somewhere in the code to see if the instance exists in the room to save.


-Ted
 
D

Danei

Guest
The with (obj_saveable) should obviate any need to use instance_exists(), because it's only going to run in instances that exist.

you don't need instance.x, instance.y, etc. the with puts you in the instance's scope. Which is also why you shouldn't be saving global variables inside the with, or putting them in the instance's sub-map at all. Either give all globals their own sub-map, or just put them in the larger map by themselves. I assume you'll be adding more cases to your switch statement as well?
 
D

Danei

Guest
There are so many instances I’d like to do multiple Ds maps. How would they worked
That code already does 1 ds_map per instance...Or do you mean multiple overarching maps to house different sets of instances?? If the latter, you'd do it the same way as the above, just with a different overarching map name, and a different set of object index cases inside the switch. I don't see how doing that would help you much, though. It's not like your ds_map is going to run out of room for instances.
 
@Danei:

How would I go about doing the load part? Also I still don't know how to handle globals?? Also like I was saying, for each case statement there is a TON of code and I'd like to split up that switch statement into multiple files so it is readable.

Also, finally, could you tell me what is wrong with this code? It is creating a save file:

{ "instance0": { "objIndex": 19.000000 } }

Code wise I have:

Code:
overarchingSaveMap = ds_map_create();

var instanceSaveID = 0;

with (obj_saveable){
  var instanceMap = ds_map_create(); //create a submap
  instanceMap[? "objIndex"] = object_index; //save the object index so we know what to create upon load.


  switch (object_index) {
  case SentryBatObject:
 
        ds_map_add(instanceMap, "target_placement_x", instance.target_placement_x);
        ds_map_add(instanceMap, "target_placement_y", instance.target_placement_y);
        ds_map_add(instanceMap, "wind_counter", instance.wind_counter);
        
        ds_map_add(instanceMap, "new_bat", instance.new_bat);
    
    
        


        ds_map_add(instanceMap, "wind_counter", instance.wind_counter);
        
        
        ds_map_add(instanceMap, "brainx", instance.brainx);
        ds_map_add(instanceMap, "brainy", instance.brainy);
        ds_map_add(instanceMap, "sprite_myTimer", instance.sprite_myTimer);
        ds_map_add(instanceMap, "global-old_path", instance.old_path);
        
        ds_map_add(instanceMap, "path_speed", instance.path_speed);
        ds_map_add(instanceMap, "speed", instance.speed);

    break;
  }
/*
case obj_bat:
 
instanceMap[? "x"] = x;
    instanceMap[? "y"] = y;
    instanceMap[? "wingspan"] = wingspan;
   //etc.
  break;

case obj_draculas_castle:
 
instanceMap[? "x"] = x;
    instanceMap[? "y"] = y;
    instanceMap[? "interiorDecorStyle"] = interiorDecorStyle;
    //Obviously I'm just making up variables here, but you can choose which ones each type of object needs to save in a switch like this
  break;
  }
*/
}
        ds_map_add_map(overarchingSaveMap, "instance"+string(instanceSaveID), instanceMap);
        instanceSaveID ++; //put the instance map into the larger map ensuring each one will have a unique key



        JSONInstance = json_encode(overarchingSaveMap);
        show_debug_message("WINDJSON" + JSONInstance);
        //JSONInstance2 = json_encode(list1);
        file_text_write_string(saveFile, JSONInstance);
        
        ds_map_destroy(instanceMap);       
        

///close the file
file_text_close(saveFile);
 
Last edited by a moderator:
H

Homunculus

Guest
What have you improved and / or solved during this week? What are you still having trouble with?
 
Catan:

Welll, I've changed things so so I'm using a switch on a parent object now so I don't have to worry about if an object is instantiated in the room or not. I'm putting the individual maps into a overarching instance map that gets turned to a JSON string. I learned where the working directory is.

I learned in cases like thus:

instanceMap[? "x"] = x;

I don't have to type

instanceMap[? "x"] = instance.x

according to the way the code is structured.

And lastly, I learned something pretty obvious, to save an instance it has to be present and in the room otherwise it is out of scope. So I the following is a short snippet version of my save code if anybody is willing to take a look at it. When the game saves to the working directory, it writes this file:

Code:
{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }
And this is the save code snippet:


Code:
if (argument0 == 0)
{
    
    var saveFile = file_text_open_write(working_directory+"save_working1.sav");
    
    
    if (file_exists(working_directory+"save_working1.sav"))
    {
        file_delete(working_directory+"save_working1.sav");
        
    }
}

show_debug_message("SaveWorkingFile executing")



var overarchingSaveMap = ds_map_create();

var instanceSaveID = 0;

with (obj_saveable){    //sort of like a for loop iterating over the objects with this signature
  var instanceMap = ds_map_create(); //create a submap
  instanceMap[? "objIndex"] = object_index; //save the object index so we know what to create upon load.


  switch (object_index) {
  case SentryBatObject:
 
 
            //ds_map_add(instanceMap, "global-wind_drop", global.wind_drop);
            //ds_map_add(instanceMap, "global-wind_drop_myTimer", global.wind_drop_myTimer);
            //ds_map_add(instanceMap, "sprite_index", sprite_index);
            //ds_map_add(instanceMap, "image_speed", image_speed);
            instanceMap[? "x"] = x;
            instanceMap[? "y"] = y;
            instanceMap[? "hp"] = hp;
            break;
 

        ds_map_add_map(overarchingSaveMap, "instance"+string(instanceSaveID), instanceMap);//add this with instance to the overarchingSaveMap
        instanceSaveID ++; //put the instance map into the larger map ensuring each one will have a unique key
  }

        JSONInstance = json_encode(overarchingSaveMap);
        show_debug_message("WINDJSON" + JSONInstance);
        //JSONInstance2 = json_encode(list1);
        file_text_write_string(saveFile, JSONInstance);
        
        ds_map_destroy(instanceMap);   
 } // completed processsing of with statement


///close the file
file_text_close(saveFile);
Thanks. I hop eto hear from someone soon.
 
H

Homunculus

Guest
Ok overall your are going in the right direction. I see a few problems with your code, some more critical than others:

1. You are creating and saving the JSON string inside the with statement, which is wrong. You only want to create and fill the instance map and add it to the main map, the rest should be outside for obvious reasons. Then, outside the with statement, you encode to json, write it to file, and destroy the main map. No need to destroy every singe internal instance map if using ds_map_add_map, since the internal ones are cleared automatically. This should probably solve your weird output problem.

2. You are saving the object_index of every instance, which seems right since you need this information in order to know which object you are referencing when loading the data back. The problem though is that the object_index (which is not the object name but a numeric value) may change during development, so it's not a reliable way to do that. Use the actual object name instead by saving object_get_name(object_index) instead. On load then, convert it to the object index using asset_get_index. It is not required but may save some headache when refactoring code, and you also get a nice human readable name in the JSON.

3. You'll need a way to loop through your instances when loading the data back. While you can loop through every entry of a ds_map, it's a slow process and not ideal in my opinion. After solving the points above and got a meaningful JSON output, consider adding a ds_list of instance maps to the main map, instead of the instance maps directly.
 
Last edited by a moderator:
Hmmm....where did I screw up?

Output save:

Code:
{ }
For the following code:

Code:
if (argument0 == 0)
{
   
    var saveFile = file_text_open_write(working_directory+"save_working1.sav");
   
   
    if (file_exists(working_directory+"save_working1.sav"))
    {
        file_delete(working_directory+"save_working1.sav");
       
    }
}

if argument0 == 1
{
    var saveFile = file_text_open_write(working_directory+"save_working2.sav");

    if (file_exists(working_directory+"save_working2.sav"))
    {
        file_delete(working_directory+"save_working2.sav");
   
    }
}

if argument0 == 2
{
    var saveFile = file_text_open_write(working_directory+"save_working3.sav");

    if (file_exists(working_directory+"save_working3.sav"))
    {
        file_delete(working_directory+"save_working3.sav");
   
    }
}

if argument0 == 3
{
    var saveFile = file_text_open_write(working_directory+"save_working4.sav");

    if (file_exists(working_directory+"save_working4.sav"))
    {
        file_delete(working_directory+working_directory+"save_working4.sav");
    }  
}

if argument0 == 4
{
    var saveFile = file_text_open_write(working_directory+"save_working5.sav");

    if (file_exists(working_directory+"save_working5.sav"))
    {
        file_delete(working_directory+"save_working5.sav");
   
    }
}
show_debug_message("SaveWorkingFile executing")



var overarchingSaveMap = ds_map_create();

var instanceSaveID = 0;

with (obj_saveable){    //sort of like a for loop iterating over the objects with this signature
  var instanceMap = ds_map_create(); //create a submap
  instanceMap[? "objIndex"] = object_index; //save the object index so we know what to create upon load.


  switch (object_index) {
  case SentryBatObject:
 
 
            //ds_map_add(instanceMap, "global-wind_drop", global.wind_drop);
            //ds_map_add(instanceMap, "global-wind_drop_myTimer", global.wind_drop_myTimer);
            //ds_map_add(instanceMap, "sprite_index", sprite_index);
            //ds_map_add(instanceMap, "image_speed", image_speed);
            instanceMap[? "x"] = x;
            instanceMap[? "y"] = y;
            instanceMap[? "hp"] = hp;
            instanceMap[? object_get_name(object_index)] = object_get_name(object_index)
            break;
 

        ds_map_add_map(overarchingSaveMap, "instance"+string(instanceSaveID), instanceMap);//add this with instance to the overarchingSaveMap
        instanceSaveID ++; //put the instance map into the larger map ensuring each one will have a unique key
  }
}

JSONInstance = json_encode(overarchingSaveMap);
show_debug_message("WINDJSON" + JSONInstance);
//JSONInstance2 = json_encode(list1);
file_text_write_string(saveFile, JSONInstance);
       
ds_map_destroy(instanceMap);  
// completed processsing of with statement


///close the file
file_text_close(saveFile);
If you can't help point me in the right dreiction please/
 
Status
Not open for further replies.
Top