Sistema di salvataggi

Sistema di salvataggi

Salvare e caricare i progressi è una di quelle cose che, prima o poi tocca fare.
E anche se GameMaker offre già alcune funzioni base per gestire file (i file .ini ad esempio), spesso sono complicate da gestire e richiedono ripetizioni di blocchi di codice.

Per questo ho preparato uno script GML mono-file pensato per semplificare tutto il processo: un sistema di salvataggio basato su file JSON, facile da integrare in qualunque progetto.

Basta creare un nuovo script Storage.gml e incollare questo

global.local_storage = [];
global.local_storage_slot = 0;
global.local_storage_max_save_slots = 1;  // You can change this to the desired number of slots


for (var i = 0; i < global.local_storage_max_save_slots; i++) {
    var fname = "save_slot_" + string(i) + ".json";
    global.local_storage[i] = {
        fname: fname,
        data: {}
    };
}

if(storage_slot_exists(global.local_storage_slot))
    storage_load();

/**
* @desc Set the current active slot
* @param {Real} slot - The slot index to set as active
*/
function storage_set_slot(slot) {
    if (slot < 0 || slot >= global.local_storage_max_save_slots) {
        show_error("Invalid save slot.", false);
        return;
    }
    local_storage_slot = slot;
}


/**
* @desc Check if the save file exists for a given slot
* @param {Real} slot - The slot index to check
* @returns {bool} - Returns true if the save file exists, false otherwise
*/
function storage_slot_exists(slot) {
    if (slot < 0 || slot >= global.local_storage_max_save_slots) {
        show_error("Invalid save slot.", false);
        return false;
    }
    var fname = "save_slot_" + string(slot) + ".json";
    return file_exists(fname);
}

/**
* @desc Load the storage data from the current slot
*/
function storage_load() {
    var slot = global.local_storage_slot;
    var fname = global.local_storage[slot].fname;
    
    if (!file_exists(fname)) {
        show_error("Save file does not exist.", false);
        return;
    }
    
    var buf = buffer_load(fname);
    var json = "{}";
    if(buffer_get_size(buf) > 0) {
        buffer_seek(buf, buffer_seek_start, 0);
        json = buffer_read(buf, buffer_text);
        buffer_delete(buf);
    }
    
    try {
        global.local_storage[slot].data = json_parse(json);
    } catch (error) {
        show_error($"Failed to parse save data. Please move the corrupted file into another directory  {fname}: {json}", false);
    }
}

/**
* @desc Save the storage data to the current slot
*/
function storage_save() {
    var slot = global.local_storage_slot;
    var json = "";
    try {
        json = json_stringify(global.local_storage[slot].data, true);
    } catch (error) {
        show_error($"Failed to save a corrupted storage: {global.local_storage[slot].data}", false);
        return;
    }
    
    var buf = buffer_create(string_byte_length(json), buffer_fixed, 1);
    buffer_write( buf, buffer_text, json);
    try {
        buffer_save ( buf, global.local_storage[slot].fname);
    } catch(error) {
        print($"{error}: unable to save game");
    }
    buffer_delete( buf);
}


function storage_clear() {
    global.local_storage = [];
    for (var i = 0; i < global.local_storage_max_save_slots; i++) {
        var fname = "save_slot_" + string(i) + ".json";
        global.local_storage[i] = {
            fname: fname,
            data: {}
        };
    }
}


function storage_print() {
    show_debug_message(global.local_storage);
}

/**
* @param {String} key
* @param {any} value
*/
function storage_set(key, value) {
    var slot = global.local_storage_slot;
    global.local_storage[slot].data[$ key] = value;
}

/**
* @param {String} key
*/
function storage_delete(key) {
    var slot = global.local_storage_slot;
    struct_remove(global.local_storage[slot].data, key);

}

/**
* Get value from storage
* @param {String} key
* @param {any*} [default_value]
*/
function storage_get(key, default_value=undefined) {
    var slot = global.local_storage_slot;
    if (struct_exists(global.local_storage[slot].data, key))
        return global.local_storage[slot].data[$ key];
    return default_value;
}

Adesso abbiamo accesso a tutta una serie di funzionalità comode.

Cosa fa

In pratica, lo script crea una piccola struttura globale dove puoi salvare e caricare qualsiasi dato del tuo gioco posizione del giocatore, livello, opzioni, inventario, ecc.
All'occorrenza poi, viene memorizzato in un file .json per mantenere i nostri dati tra le varie sessioni di gioco o caricarli su cloud.

Puoi avere più slot di salvataggio, ognuno con il proprio file indipendente.
Perfetto se vuoi gestire profili diversi o più partite separate.

Come si usa

1. Inizializza lo storage

Dove farlo dipende dal tuo progetto, può essere all'avvio del gioco o al click di "Carica partita"

storage_set_slot(0);
if (storage_slot_exists(0)) 
  storage_load();

2. Metti i dati nello storage

storage_set("player_x", player.x);
storage_set("player_y", player.y); 

storage_set("coins", coins);

2. Leggi i dati dallo storage

var hp = storage_get("hp", 0); //il secondo parametro è il valore default nel caso non venisse trovato "hp" nello storage, utile per inizializzare
show_debug_message($"Player HP: {hp}");


var coins = storage_get("coins", 0);
show_debug_message($"Coins: {coins}");

var stats = {
 strength: 5,
 dexterity: 3,
 mana: 2
}
storage_set("player_stats", stats)

//per cancellare un elemento dallo storage
storage_delete("coins");

3. Salva i dati su disco

Quando il gioco deve salvare (autosalvataggio o salvataggio manuale da parte del giocatore), è sufficiente chiamare una funzione che si occuperà di salvare nello slot precedentemente selezionato

storage_save();

Ci sono altre funzioni utili, ma lascio a te il piacere di scoprirle.