saving a game

T

teamrocketboyz

Guest
hello all, i have never really created a project where a save game is needed and im currently trying to create a small template project where i can open my game, place objects, press escape to close the game. re open the game and the objects will still be there.

how would i even start going about doing this?
 

Bearman_18

Fruit Stand Deadbeat
Start by learning about .ini files from the documentation. It's a quick, easy way to store variables outside the program itself, and you call on them whenever you need to. My personal favorite, though there are other methods.
 

Joe Ellis

Member
Here's a basic way of doing it with a buffer:

Code:
///save_game(filename)

var i = 0, l, b = buffer_create(1, buffer_grow, 1),
num_objects, obj;
with all
{l[++i] = id}

num_objects = i

buffer_write(b, buffer_u16, num objects)

i = 0
repeat num objects
{
obj = l[++i]

buffer_write(b, buffer_f32, x)
buffer_write(b, buffer_f32, y)
buffer_write(b, buffer_u16, obj.object_index)

//then write sprite_index and anything else it needs
}

buffer_save(b, argument0)
Code:
///load_game(filename)

var b = buffer_load(argument0), px, py, obj_index, obj;

repeat buffer_read(b, buffer_u16)
{
px = buffer_read(b, buffer_f32)
py = buffer_read(b, buffer_f32)
obj_index = buffer_read(b, buffer_u16)
obj = instance_create(px, py, object_index)

//then for other variables:
obj.variable = buffer_read(b, required_type)
}

However this isn't very versatile and if you've got specific variables for different objects,
you'd have to do: with(each object_index) and save the certain variables differently, which would end up in alot of code.
Instead I would build some lists of variables for each object_index, then you can use this when reading and writing, eg.
Code:
///save_game(filename)

var i = 0, l, b = buffer_create(1, buffer_grow, 1),
num_objects, obj, var_names, var_types, j, var_name, var_type;
with all
{l[++i] = id}

num_objects = i

buffer_write(b, buffer_u16, num objects)

i = 0
repeat num objects
{
obj = l[++i]
buffer_write(b, buffer_f32, x)
buffer_write(b, buffer_f32, y)
buffer_write(b, buffer_u16, obj.object_index)
buffer_write(b, buffer_u16, global.object_num_vars[obj.object_index])
var_names = global.object_var_names[obj.object_index]
var_types = global.object_var_types[obj.object_index]
j = 0
repeat global.object_num_vars[obj.object_index]
{
var_name = var_names[++j]
var_type = var_types[j]
buffer_write(b, buffer_string, var_name)
buffer_write(b, buffer_u8, var_type)
buffer_write(b, var_type, variable_instance_get(obj, var_name))
}
}

buffer_save(b, argument0)
Code:
///load_game(filename)

var i = 0, b = buffer_load(argument0), px, py, obj_index, num_vars, obj, var_name, var_type;

repeat buffer_read(b, buffer_u16)
{
px = buffer_read(b, buffer_f32)
py = buffer_read(b, buffer_f32)
obj_index = buffer_read(b, buffer_u16)
num_vars = buffer_read(b, buffer_u16)

obj = instance_create(px, py, obj_index)

repeat num_vars
{
var_name = buffer_read(b, buffer_string)
var_type = buffer_read(b, buffer_u8)
variable_instance_set(obj, var_name, buffer_read(b, var_type))
}
}

This would be really versatile and also backwards compatible,
ei. if you change what variables an object uses, it will still load the old ones it was saved with and ones that weren't saved will get the default value set in it's create event, then next time you save it will save the new variables and forget about the old ones it no longer needs.
The only thing that would cause an error is if the variable was saved as a string and you changed it to be a number instead,
but you'd just have to avoid doing this, and its probably really unlikely that you'd ever do this anyway.

The only thing that this method is lacking is the ability to save variables that are resource indices (scripts, objects etc.)
cus they'd get saved as a number and once you change its place in the resource tree it'd load the wrong one.

A way to get around this would be saving the name of the resource and using asset_get_index when loading,
but you would have to add a couple of extra things:
  1. An enum containing each asset type
  2. A conditional inside the repeat loop checking if the var_type is one of these asset types
  3. A script which gets the name of an asset of a certain type, which will have to be a switch statement or a series of ifs ordered most likely to least likely eg.

    Code:
    ///asset_get_name(asset_index, type)
    
    if type = asset_type.object
    {return object_get_name(asset_index)}
    
    if type = asset_type.script
    {return script_get_name(asset_index)}
    
    //etc.

The conditionals and extra stuff in the repeat loop are:

Saving:
Code:
repeat global.object_num_vars[obj.object_index]
{
var_name = var_names[++j]
var_type = var_types[j]
buffer_write(b, buffer_string, var_name)
buffer_write(b, buffer_u8, var_type)
if var_type >= asset_type.object
{buffer_write(b, buffer_string, asset_get_name(variable_instance_get(obj, var_name), var_type))}
else
{buffer_write(b, var_type, variable_instance_get(obj, var_name))}
}

Loading:
Code:
repeat num_vars
{
var_name = buffer_read(b, buffer_string)
var_type = buffer_read(b, buffer_u8)
if var_type >= asset_type.object
{
asset = asset_get_index(buffer_read(b, buffer_string)
if asset != -1
{variable_instance_set(obj, var_name, asset))}
}
else
{variable_instance_set(obj, var_name, buffer_read(b, var_type))}
}

Here's the scripts with these added:
Code:
///save_game(filename)

var i = 0, l, b = buffer_create(1, buffer_grow, 1),
num_objects, obj, var_names, var_types, j, var_name, var_type;
with all
{l[++i] = id}

num_objects = i

buffer_write(b, buffer_u16, num objects)

i = 0
repeat num objects
{
obj = l[++i]
buffer_write(b, buffer_f32, x)
buffer_write(b, buffer_f32, y)
buffer_write(b, buffer_u16, obj.object_index)
buffer_write(b, buffer_u16, global.object_num_vars[obj.object_index])
var_names = global.object_var_names[obj.object_index]
var_types = global.object_var_types[obj.object_index]
j = 0
repeat global.object_num_vars[obj.object_index]
{
var_name = var_names[++j]
var_type = var_types[j]
buffer_write(b, buffer_string, var_name)
buffer_write(b, buffer_u8, var_type)
if var_type >= asset_type.object
{buffer_write(b, buffer_string, asset_get_name(variable_instance_get(obj, var_name), var_type))}
else
{buffer_write(b, var_type, variable_instance_get(obj, var_name))}
}
}

buffer_save(b, argument0)
Code:
///load_game(filename)

var i = 0, b = buffer_load(argument0), px, py, obj_index, num_vars, obj, var_name, var_type;

repeat buffer_read(b, buffer_u16)
{
px = buffer_read(b, buffer_f32)
py = buffer_read(b, buffer_f32)
obj_index = buffer_read(b, buffer_u16)
num_vars = buffer_read(b, buffer_u16)

obj = instance_create(px, py, obj_index)

repeat num_vars
{
var_name = buffer_read(b, buffer_string)
var_type = buffer_read(b, buffer_u8)
if var_type >= asset_type.object
{
asset = asset_get_index(buffer_read(b, buffer_string)
if asset != -1
{variable_instance_set(obj, var_name, asset))}
}
else
{variable_instance_set(obj, var_name, buffer_read(b, var_type))}
}
}
 
C

Catastrophe

Guest
Just in case we're all jumping the gun with high end solutions, a simple game_save()/game_load() will work for very simple projects.
 

CloseRange

Member
Just in case we're all jumping the gun with high end solutions, a simple game_save()/game_load() will work for very simple projects.
just like how using speed and direction and score and health and game_restart and a lot of built in variables are not recommended the same goes with game_save and game_load.
There is so much that goes on under the hood with those functions that it could become very problematic. If you ever have an error associated with those functions it will be very hard to tell that that is the problem and if you do happen to figure out that's the problem and you come to the forums to ask how to fix it, chances are everyone will tell you to stop using game_save and game_load.
Might as well learn standard ways now rather than shooting yourself in the foot later down the line.
Personally for me it doesn't matter how new someone is to game maker or programming at all they should never use game_save and load, just creates bad habits.
 

FrostyCat

Redemption Seeker
Just in case we're all jumping the gun with high end solutions, a simple game_save()/game_load() will work for very simple projects.
You have to add "that you won't ever touch again" to your statement, then you wouldn't be lying.

The biggest catch with game_save() and game_load() is that old saves will stop working the instant you change anything in the project. This makes it unsuitable even for simple projects, and also makes it imperative for novices to learn general data modelling as soon as technically possible. Unfortunately data modelling isn't exactly as sexy as "this is how you make an RPG!" and "this is how you make things jump!", so most YouTube-mongering rookies will flounder on or flat out ignore this essential skill if left to their own devices.
 
What I would suggest is to use data structures and JSON.

You can represent each of your objects using a ds_map.

Then, you can add all these maps to a ds_list and mark them as maps.

Finally you can encode everything as JSON and save to a file.
This is the best all-around way to handle saving. It's hardly any more complex than saving to a .ini, you don't have to deal with the weird restrictions of the .ini file format, and it's easier to protect data from casual editing.
 

CloseRange

Member
Just a note here: On HTML5 platform, you cannot use the traditional ways above.

You can save data to Firestore(An online Database by Google) via my extension here (obsolete links removed)
Or you can save the game data locally by my Web Local Storage extension here (obsolete links removed)
OP is someone who has never created a save system before. Not sure he is on html 5 let alone should be looking into firestore / firebase or dealing with anything server related (I kinda consider databases to be server related)
Though I'm sure the extensions are great!
 
Last edited by a moderator:
Just a note here: On HTML5 platform, you cannot use the traditional ways above.

You can save data to Firestore(An online Database by Google) via my extension here -
Or you can save the game data locally by my Web Local Storage extension here -
The links now redirect to adsites some of which spread malware, so if Mert is not available a mod might want to edit those out of the post and the quote.
 
Top