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

Saving a game with object instances

Hi


I'm trying to store my variables in a ds_map but I don't know how to go about saving an object inside an object.. For example, how to save the global alch_hourglass object in the code below:

Code:
global.alch_hourglass = noone;

        ds_map_add(instanceMap, "global-alchemist_timer", global.alchemist_timer);
        ds_map_add(instanceMap, "global-alchemist_lock", global.alchemist_lock);
        ds_map_add(instanceMap, "global-poison_all_splash", global.poison_all_splash);
        ds_map_add(instanceMap, "global-poison_all_splash", global.poison_all_splash);
 

Cameron

Member
Hi


I'm trying to store my variables in a ds_map but I don't know how to go about saving an object inside an object.. For example, how to save the global alch_hourglass object in the code below:

Code:
global.alch_hourglass = noone;

        ds_map_add(instanceMap, "global-alchemist_timer", global.alchemist_timer);
        ds_map_add(instanceMap, "global-alchemist_lock", global.alchemist_lock);
        ds_map_add(instanceMap, "global-poison_all_splash", global.poison_all_splash);
        ds_map_add(instanceMap, "global-poison_all_splash", global.poison_all_splash);
What do you mean you need to save the object, do you need to save the state of the object, such as specific variables in that object like how much progress a timer has made and restore it to be exactly the way it was when the game ended?
 
I need to save the state of the object. But I don't know if the object is instantiated, For example:

Code:
[/COLOR][/FONT][/LEFT]
[FONT=arial][COLOR=rgb(222, 222, 222)]
[LEFT]
///////save file names
global.save_file_name = noone;
global.load_file_name = noone;

////Alchemist Timer

        ds_map_add(instanceMap, "global-loading_flag", global.loading_flag);
        ds_map_add(instanceMap, "global-saving_flag", global.saving_flag);


Bascially my question really is how to save a variable with a no one value
 

chamaeleon

Member
@Ted Gress I am not sure what you are exactly trying to save and load but if it involves saving instance ids and later loading them and hoping they make sense, I would urge you to come to with something else and store data associated with the instances instead. If you think that storing the instance ids in a map makes it possible for the map serialization functions to do something with the instance variables that is not correct either (if that is not what you're doing my apologies, but there is very little code context to understand what you expect to happen).
 
@chamaeleon Yes. I need to save an object in a ds map and i'm not sure how to go about doin git. Do I break it down into its constituent variables? What then when I go to load the file, if I just create the object I broke down, it will be a new instance and I don't necessarily want that - it would reset all the objects.
 

chamaeleon

Member
@chamaeleon Yes. I need to save an object in a ds map and i'm not sure how to go about doin git. Do I break it down into its constituent variables? What then when I go to load the file, if I just create the object I broke down, it will be a new instance and I don't necessarily want that - it would reset all the objects.
As far as your ds_map is concerned, the instance you're storing is just a number (the id as returned by the create method), and that is what will be saved. It has no idea that the number happens to be instance rather than position floating point number, or a life counter, or .., etc. No introspection of the instance happens. If you need to save an instance's variables, you need to have code that goes through and saves those that are relevant for a save operation. Instance id numbers, data structure ids, etc., are just numbers with no meaning attached to them in their variables.
 
Ted, you can't save an instance and respawn it with all its variables being reconstituted using the instance ID. Once you destroy that instance, the instance ID for it means absolutely nothing. What you have to do is save the object ID AND all the variables you want saved. Then use that object ID to create a new instance of that object type and then fill that instance with the saved variables when you load it. I already showed you how to do it in the other thread.
 

chamaeleon

Member
Ted, you can't save an instance and respawn it with all its variables being reconstituted using the instance ID. Once you destroy that instance, the instance ID for it means absolutely nothing. What you have to do is save the object ID AND all the variables you want saved. Then use that object ID to create a new instance of that object type and then fill that instance with the saved variables when you load it. I already showed you how to do it in the other thread.
Just a note to say that storing an object id could invalidate a save file if an updated program is used that rearranges, adds, or deletes objects in the resource tree. Better to let the storage structure either implicitly represent the type of object a piece of data is representing, or store an identifier (string, number, whatever) that can be mapped to an object, or the name of an object and use asset_get_index() to get the object id that is valid for the executable running.
 
Good point, but the overall structure remains the same. Save the type of object you want (Ted: Use asset_get_index(object_get_name(object_index)) to get the object type, instead of simply the object_index) then create an instance of that object when you load and add in/modify the variables you have saved in the map to the newly created instance.
 

chamaeleon

Member
Good point, but the overall structure remains the same. Save the type of object you want (Ted: Use asset_get_index(object_get_name(object_index)) to get the object type, instead of simply the object_index) then create an instance of that object when you load and add in/modify the variables you have saved in the map to the newly created instance.
Another note... asset_get_index(object_get_name(object_index)) is the same as object_index... What one should do is save object_get_name(oid) to the file, and use asset_get_index(str) to get the object id upon load for instance creation.
 
RefresherTowel: So when I am saving these references I save the object id and all the enclosed variables? How do I do that with a ds_map?

Would it be something like:

ds_map_add(instanceMap, "global-object_index", object_get_name(object_index));
 
You can save and load variables for different instances by referencing that instance in the ini file you have created... use ini_open(name of save file) to create a new save file.

If your variable is using numbers, then use ini_write_real.
Code:
ini_write_real(Object name for instance, Variable being recorded, value of variable);
If it's using a string, then use ini_write_string.

Code:
ini_write_string(Object name for instance, Variable being recorded, value of variable);
So your done saving the variable. Now you wanna load it in. So, determine when the game should load in (at startup, when you press a button, etc.).

Then, you want to open that save file with ini_open(name of save file).
Now you can reference the variables in that file.

Let's say your object is obj_car. And the variable is color. The value you saved is red.

Code:
variable = ini_read_string(object name being referenced, variable being referenced, default value for the variable);

so...

obj_car.color = ini_read_string(Car, color, black); //the car's color, default is black (if the variable you are looking for isn't in the save file, then it will default to this).
If you're referencing the color from the obj_car itself, then you don't need to put obj_car.color but instead just color.
Likewise, if it's a global variable then just use global.color instead.
 
So something like this:

Ds_map(instancemap(object_index-variable,instancevariable)

Am I right with this?

Or would it be:for

dsmap "myobject" object_index
dsmap "myvar1" instance.var1
dsmap "myvar2" instance.var2
?

I know I'm going to catch flack for this but we are behind in development and its because I don't understand
how to do the following situation:

I need to save a global variable that contains a reference to an instance. This is what I have now:
Code:
ds_map_add(instanceMap, "global-current_collision_instance", global.current_collision_instance);

I need to save the object_index and the variables in the instance, I think. I don't know how to do that with a dsmap being that its a key-value pair.Can somebody jump in and give me a hand?


Duplicate topic merged. ~Tsuk
 
Last edited by a moderator:

jo-thijs

Member
So something like this:

Ds_map(instancemap(object_index-variable,instancevariable)

Am I right with this?

Or would it be:for

dsmap "myobject" object_index
dsmap "myvar1" instance.var1
dsmap "myvar2" instance.var2
?

I know I'm going to catch flack for this but we are behind in development and its because I don't understand
how to do the following situation:

I need to save a global variable that contains a reference to an instance. This is what I have now:
Code:
ds_map_add(instanceMap, "global-current_collision_instance", global.current_collision_instance);

I need to save the object_index and the variables in the instance, I think. I don't know how to do that with a dsmap being that its a key-value pair.Can somebody jump in and give me a hand?


Duplicate topic merged. ~Tsuk
That would only work if you have 1 instance per object you're saving.

We have too little information about your project to be sure what the best advice is.
For example, I'm wondering why you need to save entire instances in the first place.
And if so, what instances need to be saved?
Do you want to save every instance in the room?
Does this include, for example, wall instances that never change from the initial room configuration?
Or do you only want to save the state of a controller object?
This makes a huge difference in how you best save things.

The replies you've gotten before don't suggest you encode all the instance vaiables of every instance in the same "instancemap" variable.
They all suggested you just simply save the variables of the instances you want to save (somewhere in some files), but not necessarily encoded in a single datastructure.
Even though a ds_map can encode a list of intances with their variables, it is not the most logical choice for it.
It is far more logical to encode a list as a list.

You could construct a list where each element represents a single instance you desire to save.
You could represent an instance as a ds_map that maps variable names (as strings) to the respective instance variable value.
Along with the variables you want to save, you should also save the values of "object_get_name(object_index)" and "id".
This is then a datastructure you can easily save and contains sufficient information for later reconstruction.

When loading this datastructure, you'll need to iterate over the elements of the list and create an instance for every element in the list.
The object type of the instance to create depends on the value of "object_get_name(object_index)" you've saved and is "asset_get_index(...)" by default.
You then assign to every variable (that does not refer to an instance) of the newly created instance the saved value for that variable.
For every instance that you create while looping over this list, you add the id of this instance to a new list "newInstances" and a ds_map "idMap" that maps the saved value of "id" to the id of the newly created instance.

Then, when you're done iterating over the saved list (so you've recreated every instance), you start iterating over the elements of "newInstances".
Now you assign for each instance you iterate over a value to every variable that does hold a reference to an instance.
To figure out the value you need to assign, you look up the saved value for the variable of the corresponding instance in "idMap".
If this returns undefined, you just assign the saved value to the variable (because the reference probably points to an unsaved instance or is a keyword).
Otherwise, you assign the value returned by looking it up in "idMap".

There's a saving mechanism for you.
Of course, depending on your project this might still not suffice.
For example, you might have variables that hold references to object indices, which would require special attention as well when updating your game.
You might have instances you want to save without receating them when loading (controller objects for example).
Create events might be screwing you up when trying to load an instance (which could be solved by using instance_change).
You may want to destroy instances when loading a saved state and then destoy events may be a problem.
And there are tons of other possible issues I can think of right away.
However, we need more information about your project if we would want to tackle those issues.

PS: Just so you're aware, there is a game_save and a game_load function that circumvents this issue,
but if you were to use those, they'd come back to bite you in the ass eventually.
One of the reasons being that dynamic resources don't get saved (and it looks like you ar using those)
and another reason being that updating your game corrupts savefiles ceated this way.
 


Ok. I don't understand somet hing. Say I have three if-else statemetns each having a hp_minus instance variable. Then add on to that inside each if statement there is an instance of thte object "floater". How would I go about serializing that?
I thought:
Code:
if (global.active == true && collision_line(global., global.litorigin_y, global.lightning_1_x, global.lightning_1_y, SimpleBrain, false, false))
{
    lit_damage_timera--;
    show_debug_message("lit_damage_timera: " + string(lit_damage_timera));
    if (lit_damage_timera <= 0)
    {
       
        show_debug_message("Lit Timer A fire: ");
        hp_minus = irandom_range(100,400);
        damage = hp_minus;
        hp = hp - hp_minus;
        floater = instance_create_depth(x, y, -17000, DamageIndicatorObject)  
        floater.text = string(damage)
ds_list_add(list2, lit1, lit_damage_timera, hp_minus, floaterflag, );
 

jo-thijs

Member

Ok. I don't understand somet hing. Say I have three if-else statemetns each having a hp_minus instance variable. Then add on to that inside each if statement there is an instance of thte object "floater". How would I go about serializing that?
I thought:
Code:
if (global.active == true && collision_line(global., global.litorigin_y, global.lightning_1_x, global.lightning_1_y, SimpleBrain, false, false))
{
    lit_damage_timera--;
    show_debug_message("lit_damage_timera: " + string(lit_damage_timera));
    if (lit_damage_timera <= 0)
    {
      
        show_debug_message("Lit Timer A fire: ");
        hp_minus = irandom_range(100,400);
        damage = hp_minus;
        hp = hp - hp_minus;
        floater = instance_create_depth(x, y, -17000, DamageIndicatorObject) 
        floater.text = string(damage)
ds_list_add(list2, lit1, lit_damage_timera, hp_minus, floaterflag, );
I'm sorry, but I don't understand what you're asking for here.
What is the hypothetical code you're talking about and what are you trying to serialize?
 
I'm sorry, but I don't understand what you're asking for here.
What is the hypothetical code you're talking about and what are you trying to serialize?
The hypothetical code is the code that I posted right above you. And the variables I am trying to save in this case would be
hp_minus and floater. This whole if statement posted above is reapeated three times.

What I am asking is is how to save an instance variable when it occurs three times (or more) in a series of if-elses, Do you understand me? If it was just an instance variable I could save it in a map:

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

But because I have three possibilities which instance do I save? I hope that explains enough.
 

FrostyCat

Redemption Seeker
Why not save all of them? It's easy to see how this is done once you start nesting structures instead of trying to cram everything into the top level.

In JSON form, this is how it would look like (notice the nesting):
Code:
{
  ...
  "floaters": [
    {
      "x": x1,
      "y": y1,
      "hp_minus": h1
    },
    {
      "x": x2,
      "y": y2,
      "hp_minus": h2
    },
    {
      "x": x3,
      "y": y3,
      "hp_minus": h3
    }
  ]
}
First create a list to hold the potential instances and nest it into the top-level map. Then insert maps into it, one per instance.
Code:
var json_data = ds_map_create();
/* other serialization code here */
// List of floaters
var floaters = ds_list_create();
ds_map_add_list(json_data, floaters);
// Populate with floater instances
with (DamageIndicatorObject) {
  var subfloater = ds_map_create();
  subfloater[? "x"] = x;
  subfloater[? "y"] = y;
  subfloater[? "hp_minus"] = hp_minus;
  ds_list_add(floaters, subfloater);
  ds_list_mark_as_map(floaters, ds_list_size(floaters)-1);
}
var json_str = json_encode(json_data);
Then when you unserialize, simply apply JSON traversal logic and loop through the list at "floaters" > n (where n varies from 0 to 2).
Code:
var json_data = json_decode(json_str);
/* other deserialization here */
var floaters = json_data[? "floaters"];
for (var i = 0; i < ds_list_size(floaters); i++) {
  var floater = floaters[| i];
  with (instance_create_depth(floater[? "x"], floater[? "y"], -17000, DamageIndicatorObject)) {
    hp_minus = floater[? "hp_minus"];
  }
}
 
Top