Save certain variables to different globalDict file?

So in my game I have a “new game” button that clears the dictionary so the save is erased. However, I don’t want this to affect the saved options. I would like for the options variables to be saved in a different bgeconf file. Is there a way to specify which file to save your variables to?

better to have cascading dictionaries:

bge.logic.globalDict = {“Default”:{}}

bge.logic.globalDict[“Profile1”] = {}

I’m confused about how I can setup multiple cascading dictionaries… Would “Profile1” be saved in default? How could I save it elsewhere?

What do you save?
-> You save a storage

What does a storage contain?
-> savable data

What can be saved?
-> you can store (and later save) the status of certain objects e.g. the position of an object, the frame of an animation, the name and value of a property

How can I organize my savable data?
-> this is totally up to you. Typically storage data is separated by purpose. The purpose decides when to store/restore what kind of data

Example:

  • game configuration: These options are setup once by installation and describe the general setup of the game (typically setup just once)
  • save point: Typically a snapshot of the status of the level (they change quite frequently, there can be many save points)
  • profile: These options are setup once per user and describe the general appearance of the game. Advanced games allow to switch between different profiles, while in game. Profiles can contain save points (they do not need to)

Typically game configuration is not kept together with save points. You can do it but you get the problems you describe in post #1.

How can I structure my storage to contain configuration and save points?
-> split the storage


storage = bge.logic.globalDict

 def getConfiguration():
    try:
        return storage["configuration"]
    except KeyError:
        storage["configuration"] = {}
        return storage["configuration"] 

def getSavePoints():
    try:
        return storage["save points"]
    except KeyError:
        storage["save points"] = []
        return storage["save points"] 

# Sample reading from configuration
isYAxisInverted = getConfiguration().get("inverted y", False)
locale = getConfiguration().get("locale", "EN")

# Sample adding/changing configuration
getConfiguration()["locale"] = "DE"

# Sample reading from save point
savePoint = getSavePoints()[0]
amountOfCollectedCoins = savePoint["collected coins"]
coinKeeper["coins"] = amountOfCollectedCoins
player["lifes"] = savePoint["lifes"]

# Sample creating new save point and writing to save point
savePoint = {}
getSavePoints().append(savePoint)

# Sample writing to save point
savePoint["collected coins"] = coinKeeper["coins"]
savePoint["lifes"] = player["lifes"]

# Sample clearing save point
savePoint.clear()

# Sample removing savepoint
del getSavePoints()[2]

# Sample replacing save point
getSavePoints()[2] = {}

The result can look like this:


{
    "configuration": {
        "inverted y": False,
        "locale": "EN",
    }
    "save points": [
        {
            "coins": 28,
            "lifes": 3,
        },
        {
            "coins": 0,
            "lifes": 5,
        },
}

Be aware dictionaries are pretty useful in such a situation, but due to their general usage, it is very very easy to confuse what is where. This means your game will not fail when it writes configuration data into a save point, or even the storage itself. Therefore I strongly suggest to avoid concatenated access like this:


coins = bge.logic.globalDict["save points"][1]["coins"]

You might think that looks short and very professional. But:

  • if there is an error, you do not know where it is
  • if you forget an access (e.g. the [1]) you access the wrong dictionary
  • it is hard to read
  • it leads to a lot of code replication
  • it does not focus on the current operation (as it includes operations on higher abstraction level such as finding the storage)
  • it does not decouple high from low abstraction (high abstraction = storage, low abstraction = save point)

Better step down the abstraction layers, you can even hide them and focus on what you want to do


def storeStats():
   savePoint = getSavePoints()[0] # encapsulated - here we do not know where the save points come from

   #focus on storing stats
   savePoint["coins"] = getCoinKeeper()["coins"]
   player = getPlayer()
   savePoint["lifes"] = player["lifes"]
   savePoint["location"] = player.worldPosition.xyz

Remark: This is just an example. There are many other ways to organize the storage including custom classes.

So this is basically just making two different dictionaries inside the global dictionary? Thanks for the example, Monster.

you can make use of the dict(GD) you can make as much savefiles as you like in it.


GD = logic.globalDict

GD['level_1_save'] = all data

#or deeper:
    
GD['level_1_save']['settings'] = general level 1 settings
GD['level_1_save']['game_info']= game info like hp, coins, etc
GD['level_2_save']['settings'] = general level 1 settings
GD['level_2_save']['game_info']= game info like hp, coins, etc

#and you can repeat all steps for more levels, or data you wanna store in the GD.

#to load its just:

settings    = GD['level_1_save']['settings']
game_info   = GD['level_1_save']['game_info']

#or easier:

save_file = GD['level_1_save']

test    = save_file['settings']
test2   = save_file['game_info']

But you can also separate/combine the two, save game info in the dict like above/monster.
and save the settings to a text file.

It al depends on how you want to handle it, here is an example to write/load a textfile.


from bge import logic
import os
import ast

# change to your liking
file_name   = 'save.sav'
path        = 'C:/Game name/Saves/'

def write_save_file():
    
    #the global dict you want to save in the file
    GD = logic.globalDict['save_file']
    
    if GD:
        os.makedirs(path, exist_ok=True)

        file = open(path + file_name, 'w')

        file.write(str(GD))
        file.close()

        print('FILE SAVED')  
    else:
        print('
*** NO GD FOUND! ***
')  


def read_save_file():

    if os.path.isfile(path + file_name):

        file = open(path + file_name, 'r')

        # read and convert to Dict
        data = ast.literal_eval(file.read())

        file.close()

        return data
    
    else:
        print('
*** NO SAVE FILE FOUND! ***
')  

Is that separate files or just embedded dictionaries?

Is that separate files or just embedded dictionaries?

if you use the script, and make a function to adjust the path and or filenames then you can make as much seperate dicts as you like, else it will be 1 file with all the info in it.

Here’s an example of a saved game from my BGMC entry:

{"items": ["my_journal_a", "my_journal_b", "newspaper_0", "post_it_a", "teddy", "hammer", "post_it_6", "metal_key", "newspaper_3", "rusty_key", "my_letters", "loads_of_money", "my_journal_11", "newspaper_2", "lighter_2", "candle", "sledge_hammer", "missing_poster", "newspaper_9", "quarantine_poster_1", "my_journal_3", "quarantine_poster_2", "post_it_5", "scrap_2", "my_journal_5"], "volume": 0.3, "last_location": "the_teaching_room", "used_items": ["sledge_hammer", "machine_2"], "location": "the_blocked_stairs"}

You can see there are several entries in the dictionary, and some of them are lists.
It would be easy for some of them to be dictionaries too.

How can I clear just one dictionary in it?

I can’t understand that, lol. Could you give me an example of how I could clear just one dictionary in the globalDict?

You can clear any dictionary or dictionary entry by setting it:

Globaldict[“items”] ={}
Globaldict[“location”] =None

Globaldict ={}

GD = {"items": ["my_journal_a", "my_journal_b", "newspaper_0", "post_it_a", "teddy", "hammer", "post_it_6", "metal_key", "newspaper_3", "rusty_key", "my_letters", "loads_of_money", "my_journal_11", "newspaper_2", "lighter_2", "candle", "sledge_hammer", "missing_poster", "newspaper_9", "quarantine_poster_1", "my_journal_3", "quarantine_poster_2", "post_it_5", "scrap_2", "my_journal_5"], "volume": 0.3, "last_location": "the_teaching_room", "used_items": ["sledge_hammer", "machine_2"], "location": "the_blocked_stairs"}

is a dictionary { , , , , }

that dict contains a list [ , , , , ] called ‘items’ that contains the following items

<b>"items":</b> ["my_journal_a", "my_journal_b", "newspaper_0", "post_it_a", "teddy", "hammer", "post_it_6", "metal_key", "newspaper_3", "rusty_key", "my_letters", "loads_of_money", "my_journal_11", "newspaper_2", "lighter_2", "candle", "sledge_hammer", "missing_poster", "newspaper_9", "quarantine_poster_1", "my_journal_3", "quarantine_poster_2", "post_it_5", "scrap_2", "my_journal_5"]

and in that same dict are below strings and lists as well

<b>"volume":</b> 0.3,<b>"last_location":</b> "the_teaching_room",<b>"used_items":</b> ["sledge_hammer", "machine_2"],<b>"location":</b> "the_blocked_stairs"

And to empty it you can use smoking_mirror example, just totally clear a dict by: GD = {}

Why didn’t you use code tags.

Because it isn’t actual code, its incomplete only the top quote can be in code tags, for your liking i will set it to code :stuck_out_tongue:

Thankyou it looks better.

Thank you. I’ll definintely try this.

Thanks guys. It took awhile, but with all your help I finally separated the game saving and options saving.