Save Load - universal and simplified method (error)

Hello everybody
Do not tell me what is the problem with this script?

from bge import logic as G



G.loadGlobalDict()


def save():
    sce = G.getCurrentScene()
    
    for obj in sce.objects:
        if "ObjToSave" in obj:
            pos = obj.worldPosition
            orient = obj.worldOrientation
            props = {p:obj[p] for p in obj.getPropertyNames() if p != "PropToSave"}
            
            G.globalDict[obj.name] = pos, orient, props
    
    G.saveGlobalDict()
    
def load():
    sce = G.getCurrentScene()
    
    for obj in sce.objects:
        if obj.name in G.globalDict:
            sav = G.globalDict[obj.name]
            
            obj.worldPosition = sav[0]
            obj.orientation = sav[1]
            
            if sav[2]:
                for p in sav[2]:
                    obj[p] = sav[2][p]


In theory, this script can save a position, orientation, and all properties of objects with properties - ObjToSave, but neither of which does not work.

save_load_TEST.blend (494 KB)

Thank you for attention

When repairing the car, the masters threw out the old car, and made a new one.:yes:

Thank you for helping Leralasss and Charles_S

1.: i dont know what the hell is wrong with your script, but spaces somehow are not spaces there. I wrote a new one
2.: you cannot save obj.worldPosition because that is a Vector
3.: you cannot save obj.worldOrientation because there are Vectors in there

save_load_TEST.blend (492 KB)

2.: you cannot save obj.worldPosition because that is a Vector
3.: you cannot save obj.worldOrientation because there are Vectors in there

you can you just need to convert it like this:


            obj_data['position']    = list(obj.localPosition)
            obj_data['orientation'] = 
[list(vector) for vector in obj.localOrientation]

@TS
you can check my save/load(in my sig) and see how i’ve done it.

object.worldOrientation.to_euler() store quite well also.

yes i did that in the blend file

Rather than storing position, orientation and scale separately you can store the complete transformation matrix (converted indeed):


# store and convert it
storableTransformation = 
[list(row) for row in objectToStoreFrom.worldTransform.row]

...

# restore it (conversion is performed implicitly by the BGE)
objectToRestoreTo.worldTransform = storableTransformation 

Be aware you do not consider dynamically created objects (they share the same name). They need special handling.

  • OK man. This script is not mine, I got it from a guy. I did not check it and it was in the archive. Recently I began to finish the game, and the script does not work, so I asked… I just started to study python, for me the whole problem.
    And thanks for writing a universal script,:eyebrowlift: I’ll post an example later in Game Engine Resource.
    But how to write a sensor into this script so that it works if there are two sensors active (button with mouse over, and LMB)? Now the script works always with the mouse over.

Thank you again for your attention to me.

Here. . . (Should all be there.)

MySaveLoad.blend (1.28 MB)

Controls
[Arrow keys] move purple cube (the player).
[A] Saves, [S] Loads
[Q] & [W] drop “instanced objects”; objects that have been spawned from hidden layer.
[Z] & modify props, some that are set to save, some that are not (look at debug top left)

Notes:
Important The “Kill” module MUST be called before the “load” module, or all hell will break loose.

Only objects with the “save” property are saved, everything else is ignored.

This saves out to a file, (<name_of_scene>.save) So, even if you close and restart BGE, hitting [S] will return you to right where the save file says you were.

This doesn’t save physics constraints. If you set up, say, a hinged door, your hinge will likely break and the door will fall if you try and save then load it.

Some items in this demo are INTENTIONALLY not saved/loaded, to demonstrate that feature. The Suzanne is one of these, it’s props are also not restored.

This was made by me, fairly recently. (2015-2016)

No license on the code. Go nuts.

Edit2: Posted to Game Engine Resources, almost exactly as here, but with a bit more info.

  • Thank for the example, but for me it would be better to make a script when triggered with two sensors. And the sample script Leralasss as it seems much easier - its functionality is enough for me.If you know how to add a sensor to the previous script - help me.
    And where are the settings stored in which file?

Thank you for attention.

Thank for the example, but for me it would be better to make a script when triggered with two sensors.

My code is generic, triggering it with two sensors is as simple as replacing the keyboard sensors on the camera with the sensors you want.

To make it so both sensors must be active at the same time, then just do…


def save(cont):
    if cont.sensors['&lt;some_sensor&gt;'].positive & cont.sensors['&lt;some_other_sensor&gt;'].positive:
        . . .
        #do the save code . . .
        . . .

And the sample script <script?> as it seems much easier - its functionality is enough for me.

Is it really fair to call it easier/enough if it doesn’t even work?

Sure, my code may seem scary to you because it’s larger, but it’s about as simple as working code gets. 127 lines of code is hardly big either. (Hell, 21 lines of that is just comments, which only makes it easier AND smaller.)

And where are the settings stored in which file?

A file will be made in the same location you run the blend. It will have the name <scene>.save, where <scene> is the name of your scene. So, in my demo, the save file is named “Level-1.save” because the scene is named “Level-1.”

lets say you have 137218371298 sensors attached to your script (that would be a bit much) run in module mode (save.load or save.save)


import bge

def save():cont = bge.logic.getCurrentController()

#go through each connected sensor
for sens in cont.sensors:


[INDENT=2]#if one sensor is not positive (active), then stop the script
[/INDENT]
[INDENT=2]
if not sens.positive:
[/INDENT]
[INDENT=3]return None #this will stop the script (actually it will stop the Function)
[/INDENT]

#Now do the rest of the script. Will only run if all connected sensors are positive




just use this in front of the modules and then it will check for all connected sensors to be true.

you could also do this as a separate module, like this:


import bge

def and_controller():cont = bge.logic.getCurrentController()

for sens in cont.sensors:

[INDENT=2]if not sens.positive:[/INDENT]
[INDENT=3]return False[/INDENT]

return True


def save():#check if all sensors are True
if not and_controller(): # and_controller() returns True or False

[INDENT=2]return None
[/INDENT]

#now do rest



Indeed, if you have more than a few sensors that must ALL be the same, then one of the post #11 methods is the way to go.

  • sorry that I did not answer for a long time, I was busy. Everything worked according to the example of the second code - I thank for the help.
    And so I decided to make a simple example for everyone - intro, menu, pause, save \ load. I just live by the principle - if you were helped, share this with others. Link to the project - It turned out to be stylish and simple …


    *many balls :rolleyes:
    And here’s another question, if it’s not difficult for you to answer - how to make the second option save only for settings, what would be saved in a separate file?
    I would like to of course expand the functionality, as in your version of the save script - that would be and the objects appearing from other layers were saved … add a pause for sound, as in this script:
from bge import logic
from bge import render
cont = logic.getCurrentController()
own = cont.owner


scene = logic.getSceneList()[0]
objs = scene.objects


width = render.getWindowWidth()
height = render.getWindowHeight()


sen = cont.sensors["Keyboard"]
pause = own["pause"]
if sen.positive:
    for obj in objs:
        for act in obj.actuators:
            if act.name.startswith("Sound")
                try:
                    if pause:
                        if act.time &gt; 0.00:
                            act.startSound()
                    else:
                        if act.time &gt; 0.00:
                            act.pauseSound()
                except:
                    print("Name '??' in not sound actuator in object '??' in main scene." \ (act.name, obj.name))


    if pause:
        own["pause"] = False
        render.setMousePosition(int(width/2)), int(height/2))
        render.showMouse(False)
        scene.resume()
        own.visible = False
    else:
        own["pause"] = True
        render.setMousePosition(int(width/2)), int(height/2))
        render.showMouse(True)
        scene.suspend()
        own.visible = True

*there are errors in the script

Thank you for attention.

Thank you, everything is already working -my project, Although I would like to expand the functionality, even make a kit for everyone - #13

how to make multiple save files without writing/reading to text files: (just using globalDict)


import bge

cont = bge.logic.getCurrentController()
owner = cont.owner

#somehow decide which save slot you are using and set a property in owner
slot = owner["slot"] #can be a number, string, tuple (but best just use a number), so owner["slot"] = 1 or something

gd = bge.logic.globalDict

#here is the previous code with changes so save slots can be used
def and_controller():
    cont = bge.logic.getCurrentController()

    for sens in cont.sensors:
        if not sens.positive:
            return False

    return True

def save():
    if not and_controller():
        return None
    
    scene = bge.logic.getCurrentScene()
    
    gd[slot] = {} #if there is no saved file in that slot then need this
    #if already a saved file, then overwrite
    
    sdict = gd[slot] #save to dict in global dict
    
    for obj in scene.objects:
        if "ObjToSave" in obj:
            pos = list(obj.worldPosition)
            
            orient = list(obj.worldOrientation)
            myor = []
            for vec in orient:
                myor.append(list(vec))
            
            props = {}
            for pname in obj.getPropertyNames():
                if pname == "ObjToSave":
                    continue
                props[pname] = obj[pname]
            
            sdict[str(obj)] = [pos,myor,props] #changed "gd" to "sdict"
    
    bge.logic.saveGlobalDict()

def load():
    if not and_controller():
        return None
    
    if not slot in gd:
        #dont do anything if there is nothing saved in the selected slot
        return None
    
    bge.logic.loadGlobalDict()
    
    scene = bge.logic.getCurrentScene()
    
    sdict = gd[slot]
    for name in sdict: #changed gd to sdict
        if not name in scene.objects:
            continueslist = sdict[name] #changed gd to sdict

        
        obj = scene.objects[name]
        
        obj.worldPosition = slist[0]
        obj.orientation = slist[1]
        
        for pname in slist[2]:
            obj[pname] = slist[2][pname]

#def save_options():#i used tabs here, not spaces like above (i copied above code from blender so there were 4 spaces as tabs)
#save options as:

[INDENT=2]#gd["options"] = {}
#doesnt interfere with save slots
[/INDENT]


so in the end the global Dict will look something like:


globalDict = {

1: { "myobj":[ pos, orientation, props ], "nextobj"...., }, #for each save slot object names with pos,scl...
2: { ... },

"options": { ... } #whatever you want to put in there

}

  • Dear Leralasss, you in your example wrote a very professional script. But alas, the Laws are such that you need to specify copyrights - or this is a free-of-charge reference for BGE, or CC BY 3.0 - otherwise the copyright for the finished scene is entirely up to you.

I studied your code, wrote for myself “my” script - and I want to put it for everyone under the CC BY 3.0 license, with a path for clear code (used as a reference).
This code is one more step towards the ease of BGE.

My script, based on the study of your code:

import bge

cont = bge.logic.getCurrentController()
scene = bge.logic.getCurrentScene()
own = cont.owner

globalDict = bge.logic.globalDict #connecting globalDict

oneKey = cont.sensors['ONEKEY']
twoKey = cont.sensors['TWOKEY']

if oneKey.positive:
      
    for obj in scene.objects:
        if "to_save" in obj:        
            orientation = list(obj.worldOrientation)
            position = list(obj.worldPosition) 
            speed = list(obj.worldLinearVelocity) 
            objOrient = []
            for vectOrient in orientation:
                objOrient.append(list(vectOrient))
        
            property = {} #get property names and data
            for propName1 in obj.getPropertyNames():
                if propName1 == 'to_save':
                    continue
                property[propName1] = obj[propName1]
                
                globalDict[str(obj)] = [objOrient, position, speed, property]    
                
    bge.logic.saveGlobalDict()           
                
if twoKey.positive:
    bge.logic.loadGlobalDict()
    for names in globalDict:
        objList = globalDict[names]
        objScene = scene.objects[names]
        
        objScene.worldOrientation = objList[0]
        objScene.worldPosition = objList[1]
        objScene.worldLinearVelocity = objList[2]
        
        for propName2 in objList[3]:
            objScene[propName2] = objList[3][propName2]

So I helped here to solve this issue, but without your permission … there may be extra questions:eyebrowlift2:.

Forgive me for disturbing you.
Thank you for attention.

p.s if you deny my request, I do not even know what to do with my brains and memories - they all remember:o.

  • Hey Leralasss, are you somewhere?
    This code is just a free reference for the BGE API (free answer to my question), are there any … nuances?
    I’m sorry to bother you.