[GUIDE] Easy way to Save / Load ALL Global / Scene Variables when ever you want, even if you didnt prepare!

Lets make this a 3 in 1.

  1. This is a Feature Request because having this option Native in GDevelop would be really helpful.

  2. If anyone wants to make this into an extension you have my blessing, just please keep it simple and efficient on performance.

  3. If none of that… Then heres a quick Guide on How to…

To start with… I never had a need to save my game before, so i hadent looked into this, now that i do, i decided to do research on how to save and load.

My go to info was the Official GDevelop YouTube video on how to save and load variables.

In it, you are given the example that if you make your Variables a Child of a Parent Structure / Array, things will go much smoother since you can get all your variables at once in one single event…

Issue… What if… you had over 100 Variables on a massive project and you only now decided to add Save / Load functionality?

Well… good luck sorting that mess out! :smiley: …that was untill now.

The moment i saw that i instantly went “NOPE”, theres got to be a better way… so i made a better way :smiley: (with the power of AI)

The Solution

  • I made Java Scripts that get ALL Variables and puts them in a single Scene Variable for you.
  • You can then save that Variable into a text and later on Load.
  • After Loading the text, you parse it onto a Scene Variable, this part is standdard.
  • Once you have you Scene Variable with all the information back, we run it trough a different Script that will break it apart and Restore all your variables back to where they were.

So heres the actual Guide and Scripts!

Saving and Loading ALL Scene Variables

  1. Create an event with something that will trigger the save once. In this example we will use “Key is Released”.

  2. Now add a JavaScript Sub Event, delete all contents and then Paste the following script inside:

Scene Variable Save Script

// Function to save scene variables into a scene variable
function saveSceneVariables(runtimeScene) {
    const sceneVars = runtimeScene.getVariables();
    const sceneSave = {};

    // Function to handle nested structures
    function saveVariable(variable) {
        const variableType = variable.getType();
        
        if (variableType === 'structure') {
            const structure = {};
            const children = variable.getAllChildren();
            for (const childName in children) {
                if (children.hasOwnProperty(childName)) {
                    structure[childName] = saveVariable(children[childName]);
                }
            }
            return structure;
        } else if (variableType === 'array') {
            const array = [];
            const children = variable.getAllChildrenArray();
            for (let i = 0; i < children.length; i++) {
                array.push(saveVariable(children[i]));
            }
            return array;
        } else if (variableType === 'number') {
            return variable.getAsNumber();
        } else if (variableType === 'boolean') {
            return variable.getAsBoolean();
        } else {
            return variable.getAsString();
        }
    }

    // Iterate through all scene variables and store their values
    const allVars = sceneVars._variables.items;
    for (const varName in allVars) {
        if (allVars.hasOwnProperty(varName) && varName !== "StoredSceneVariables") {
            sceneSave[varName] = saveVariable(allVars[varName]);
        }
    }

    // Convert the object to a JSON string and store it in the StoredSceneVariables scene variable
    sceneVars.get("StoredSceneVariables").setString(JSON.stringify(sceneSave));
}

// Call the function to save scene variables
saveSceneVariables(runtimeScene);

Screnshot example

  1. Now when the conditions are met, or in this case whe the Key is Released, all your scene variables will be saved into a Scene Variable called “StoredSceneVariables”.

  2. Next create a new Sub Event under the “Key is Released” and bellow the Java Scrip.

  3. This new event has No Condition, and the Action is “Save a text”.

  • In Storage Name, you can add something like you Game Name
  • In Group, put “Scene” so you know its Scene Variables
  • In Text type in ToJSON(StoredSceneVariables)

Awesome! Thats the Save part done!

Now for the Load part…

  1. Create an Event with the condition you want to use to trigger the Load, in this case ill use “Key Released” as the example.

  2. Add the Action “Load a text”

  • Fill in the fields with the same information you used on “Save a text”
  • On the Variable field type in StoredSceneVariables
  1. Add the Action “Convert JSON to a Scene Variable”
  • In JSON String type: VariableString(StoredSceneVariables)
  • In Variable where to store JSON Object type: StoredSceneVariables
  1. To finish, add a Java Script event and add it as a Sub Event of the one you just made, then delete all of its contents and Paste the following script inside:

Loading Scene Variables Script

// Function to load scene variables from the StoredSceneVariables variable
function loadSceneVariables(runtimeScene) {
    const sceneVars = runtimeScene.getVariables();

    // Get the JSON string from the StoredSceneVariables variable
    const sceneSaveString = sceneVars.get("StoredSceneVariables").getAsString();

    if (!sceneSaveString) {
        return;
    }

    // Parse the JSON string to an object
    const sceneSave = JSON.parse(sceneSaveString);

    // Function to handle nested structures
    function loadVariable(sceneVar, varValue) {
        if (typeof varValue === "object" && !Array.isArray(varValue)) {
            for (const key in varValue) {
                if (varValue.hasOwnProperty(key)) {
                    if (!sceneVar.getChild(key)) {
                        sceneVar.addChild(key);
                    }
                    const childVar = sceneVar.getChild(key);
                    loadVariable(childVar, varValue[key]);
                }
            }
        } else if (Array.isArray(varValue)) {
            for (let i = 0; i < varValue.length; i++) {
                if (!sceneVar.getChild(i.toString())) {
                    sceneVar.addChild(i.toString());
                }
                const childVar = sceneVar.getChild(i.toString());
                loadVariable(childVar, varValue[i]);
            }
        } else {
            if (typeof varValue === "number") {
                sceneVar.setNumber(varValue);
            } else if (typeof varValue === "boolean") {
                sceneVar.setBoolean(varValue);
            } else {
                sceneVar.setString(varValue);
            }
        }
    }

    // Iterate through the keys in the sceneSave object and restore the variables
    for (const varName in sceneSave) {
        if (sceneSave.hasOwnProperty(varName)) {
            const varValue = sceneSave[varName];

            // Check if the scene variable exists; if not, add it
            if (!sceneVars.has(varName)) {
                sceneVars.add(varName);
            }

            const sceneVar = sceneVars.get(varName);

            // Load the variable
            loadVariable(sceneVar, varValue);
        }
    }
}

// Call the function to load scene variables
loadSceneVariables(runtimeScene);

  • Thats the whole precess! Seems a bit complicated written down, but its essentially just a couple events with a couple sub events, easy stuff!

For the Global Variables

  1. Repeat Everything the same as for the Scene Variables.

  2. Change “StoredSceneVariables” for “StoredGlobalVariables”

  3. In “Groups” swap “Scene” for “Global”, so you know its for Global variables.

  4. As for the Scripts, heres the Global Variable equivalents:

Save Script for Global Variables

// Function to save global variables into a scene variable
function saveGlobalVariables(runtimeScene) {
    const globalVars = runtimeScene.getGame().getVariables();
    const globalSave = {};

    // Function to handle nested structures
    function saveVariable(variable) {
        const variableType = variable.getType();
        
        if (variableType === 'structure') {
            const structure = {};
            const children = variable.getAllChildren();
            for (const childName in children) {
                if (children.hasOwnProperty(childName)) {
                    structure[childName] = saveVariable(children[childName]);
                }
            }
            return structure;
        } else if (variableType === 'array') {
            const array = [];
            const children = variable.getAllChildrenArray();
            for (let i = 0; i < children.length; i++) {
                array.push(saveVariable(children[i]));
            }
            return array;
        } else if (variableType === 'number') {
            return variable.getAsNumber();
        } else if (variableType === 'boolean') {
            return variable.getAsBoolean();
        } else {
            return variable.getAsString();
        }
    }

    // Iterate through all global variables and store their values
    const allVars = globalVars._variables.items;
    for (const varName in allVars) {
        if (allVars.hasOwnProperty(varName)) {
            globalSave[varName] = saveVariable(allVars[varName]);
        }
    }

    // Convert the object to a JSON string and store it in the StoredGlobalVariables scene variable
    const globalSaveString = JSON.stringify(globalSave);
    runtimeScene.getVariables().get("StoredGlobalVariables").setString(globalSaveString);
}

// Call the function to save global variables
saveGlobalVariables(runtimeScene);

Load Script for Global Variables

// Function to load global variables from the StoredGlobalVariables scene variable
function loadGlobalVariables(runtimeScene) {
    const globalVars = runtimeScene.getGame().getVariables();

    // Get the JSON string from the StoredGlobalVariables scene variable
    const globalSaveString = runtimeScene.getVariables().get("StoredGlobalVariables").getAsString();

    if (!globalSaveString) {
        return;
    }

    // Parse the JSON string to an object
    const globalSave = JSON.parse(globalSaveString);

    // Function to handle nested structures
    function loadVariable(globalVar, varValue) {
        if (typeof varValue === "object" && !Array.isArray(varValue)) {
            for (const key in varValue) {
                if (varValue.hasOwnProperty(key)) {
                    if (!globalVar.hasChild(key)) {
                        globalVar.addChild(key);
                    }
                    const childVar = globalVar.getChild(key);
                    loadVariable(childVar, varValue[key]);
                }
            }
        } else if (Array.isArray(varValue)) {
            for (let i = 0; i < varValue.length; i++) {
                if (!globalVar.hasChild(i.toString())) {
                    globalVar.addChild(i.toString());
                }
                const childVar = globalVar.getChild(i.toString());
                loadVariable(childVar, varValue[i]);
            }
        } else {
            if (typeof varValue === "number") {
                globalVar.setNumber(varValue);
            } else if (typeof varValue === "boolean") {
                globalVar.setBoolean(varValue);
            } else {
                globalVar.setString(varValue);
            }
        }
    }

    // Iterate through the keys in the globalSave object and restore the variables
    for (const varName in globalSave) {
        if (globalSave.hasOwnProperty(varName)) {
            const varValue = globalSave[varName];

            // Check if the global variable exists; if not, add it
            if (!globalVars.has(varName)) {
                globalVars.add(varName);
            }

            const globalVar = globalVars.get(varName);

            // Load the variable
            loadVariable(globalVar, varValue);
        }
    }
}

// Call the function to load global variables
loadGlobalVariables(runtimeScene);

Thats all for Global Variables!! :smiley:

If you want you can combine the two like shown on the screenshots bellow!

Saving Both

Loading Both

2 Likes

If you like this sort of stuff, check out the YouTube Channel!