Saving files and loading files in relative path and custom path without specifying user's name manually. We'll be saving some game data and loading that data. The data we'll be saving is the player's location.
Fire up the engine, add two keyboard sensor, bind them as F1 and F2 repectively, add two python controllers set their script type to module and name them. First one's module will be written as save and second as load.
Create a text data-block and name it as whatever you want, I've named it as mycode.py, import our game engine module, get our current controller, get the owner data and create a function that does nothing.
try:
import bge
except:
import Range as bge
cont = bge.logic.getCurrentController()
own = cont.owner
def save(cont):
pass
Now we need to get our player object's name and position. We can keep this outside the function, so there won't be any need to define them two times.
cube = own.scene.objects["Cube"]
cube_pos = cube.worldPosition
Now we need to save our player's position as a file. It can be any file, we will be saving as .mysave file, and define the relative path. So we do the following
# Relative path
rel_path = bge.logic.expandPath("//")
with open(rel_path+".mysave", "w") as f:
f.write(str(cube_pos)) # !!! NOT YET DEFINED !!!
f.close()
Here we get the relative path and do the in-built python file writing
operation using with.
Altough f.close() is automatically called,
its a good practice to do so.
And note, we have not defined the cube's
position. We need to save it as a dict.
x = cube.localPosition[0]
y = cube.localPosition[1]
z = cube.localPosition[2]
cube_pos={
"X":x,
"Y":y,
"Z":z
}
The above code might look tricky, the localPosition returns us as Vector, which might be difficult to load the position. We get the X, Y, Z data seperatly by accessing element by key and store them as a dictionary. So now the full save function and other defenitions above looks like this.
try:
import bge
except:
import Range as bge
cont = bge.logic.getCurrentController()
own = cont.owner
rel_path = bge.logic.expandPath("//")
cube = own.scene.objects["Cube"]
x = cube.localPosition[0]
y = cube.localPosition[1]
z = cube.localPosition[2]
cube_pos={
"X":x,
"Y":y,
"Z":z
}
def save(cont):
with open(rel_path+".mysave", "w") as f:
f.write(str(cube_pos))
f.close()
print("File Saved.")
Now to load the the saved data, we need to use the with like before, but instead of writing we're gonna read file. Passing the argument "r" to the second parameter of open(rel_path, mode='r') is optional, but for your comfort you can set it.
# This code wont work
with open(rel_path+".mysave") as f:
read = (f.read())
print(type(read))
cube.localPosition[0] = read["X"]
cube.localPosition[1] = read["Y"]
cube.localPosition[2] = read["Z"]
If you try running the game, you'll be encountering an error like below.
Error: Python.002 (Cube), Python script error Traceback (most recent call last):
File "C:\Users\[user name]\Downloads\[file name].blend\mycode.py", load
line 31, in
ValueError: dictionary update sequence element #0 has length 1; 2 is required
For some reason you can't convert string from a file to a dict, and to solve this problem we use ast.literal_eval() function. So in the code we do something like this.
with open(rel_path+".mysave") as f:
read = ast.literal_eval(f.read())
print(type(read))
cube.localPosition[0] = read["X"]
cube.localPosition[1] = read["Y"]
cube.localPosition[2] = read["Z"]
And this must be working for you. So far we created a relative dir with logic.expandPath, created a .mysave file saved it and loaded it. Here is the full working code.
try:
import bge
except:
import Range as bge
import ast # Can't convert str to dict directly, so we need ast
cont = bge.logic.getCurrentController()
own = cont.owner
rel_path = bge.logic.expandPath("//") # Get relative path
cube = own.scene.objects["Cube"]
# Split the coordinates as X,Y,Z
x = cube.localPosition[0]
y = cube.localPosition[1]
z = cube.localPosition[2]
#Cube Position as dict
cube_pos={
"X":x,
"Y":y,
"Z":z
}
def save(cont):
with open(rel_path+".mysave", "w") as f:
f.write(str(cube_pos))
f.close()
print("File Saved.") # Just making sure its saved
def load(cont):
with open(rel_path+".mysave") as f:
read = ast.literal_eval(f.read())
print(type(read)) # Just making sure its a dict
# Assign the position
cube.localPosition[0] = read["X"]
cube.localPosition[1] = read["Y"]
cube.localPosition[2] = read["Z"]
print("File Loaded.") # Making sure its loaded
coming soon...