BGE: Using saved values retrieved with the configparser

In my last tutorial, many moons ago, we took a look at using the configparser to load data into the BGE and printing them to the console. This next part takes that a step further by using the values to reposition a cube and loading the player score. The ultimate goal is to build a save/load system using the configparser. The completed files are included at the bottom of this tutorial.

Scene setup

We’ll start with a default cube and add some logic bricks to it:

Cube brick set up

The first 4 bricks are linked to a simple motion actuator so we can move the cube around. The 5th brick is tied to a python controller, for now, leave this blank. Set the cube’s physics to dynamic and create a plane underneath it and scale the plane up. Finally, reposition the camera above the plane and cube so we can see what’s going on. Press P to test it and your cube should move around the plane.

Cube brick set up

Now create some text and position that so that it can be seen by the camera and create a smaller cube in the path of the first cube created. Select both the text and the smaller cube and create the following logic brick set up:

Cube brick set up

Then duplicate the smaller cube a few times, space them out and move one duplicate to an inactive layer. Give it a test, all going well, when the big cube hits a smaller one the score should go up and delete the smaller cube. Next, save the file in its own folder, close Blender and open it by clicking on the saved file.

Why did we do this? Well, the configparser module is going to be looking for a file in the current working directory. For a newly created blender file, this will be in the directory where Blender is installed. To ensure that the python script looks in the right place, we need to open the .blend from within the same folder as the file we’re going to be looking for.

The final step is to create the .cfg file we’re going to read the values from. So, just like in the previous tutorial, open note pad and copy in:

[player]
xpPos: -1.5
yPos: -1.5
score: 10

[cubes]
smallCube1: 0.0, 3.0, 0.0
smallCube2: 6.6, 7.0, 0.0
smallCube3: -8.3, 2.2, 0.0
smallCube4:
smallCube5: 7.4, -5.5, 0.0

Then save it with the extension .cfg in the same location as the .blend.

The python bits

Create a new text file in Blender and fill it with the following code:

import bge
import configparser

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

player = scene.objects['Cube']
score = scene.objects['ScoreText']

def Load():
    config = configparser.ConfigParser()
    config.optionxform = str
    try:
        config.read_file(open('settings.cfg'))
    except IOError:
        print("file could not be found.")
        return
    if config.has_section('player'):
        player.position.x = config.getfloat('player', 'xPos', fallback=0.0)
        player.position.y = config.getfloat('player', 'yPos', fallback=0.0)
        score['Text'] = config.getint('player', 'score', fallback=0)
    if config.has_section('cubes'):
        #reset all the cubes
        #first delete them all
        for obj in scene.objects:
            if 'smallCube' in obj.name:
                obj.endObject()
        #then recreate the cubes in play
        for cube in config.options('cubes'):
             if config.get('cubes', cube):
                obj = scene.addObject('smallCube', 'Plane', 0)
                pos = config.get('cubes', cube)
                pos = [float(x) for x in pos.split(',')]
                obj.position = pos

Ok, lets break this down. I’ll cover the code that’s new from the previous tutorial in more detail. We start by importing the usual BGE stuff and the configparser module and declaring some BGE objects. Then we create our load function. Line 13 (config.optionxform = str) is used to override the configparser’s default operation. This is need because by default it ignores capital letters in the config file, this ensures that it preserves them. There is no need to preserve capitalisation in our example, but it’s useful to know.

Before moving to loading and using values, we go through the process of error handling if the config file is not found. In the except block we could include some default values, but in this example the function returns and the game goes on as if nothing has happened.

We then load the player’s saved values (score and position). Previously, to retrieve values from the configparser we used the .get() function, returning the options value in the form of a string. This was fine when we were only printing values, but if we want to use them we need them in the correct format. Now, we could use .get() then just use something like int() or float() to convert them into something usable, but the configparser has a method to do that for us. We start with the player’s position and call .getfloat() to get the x and y co-ordinates and assign the returned float to the objects position. Just like with .get() the same parameters apply (section, option, fallback value). The score is recalled with .getint(). There is also a .getbool() method, but this example does not use it

Now comes the trickier part: updating the collected cubes. There are numerous ways this can be done, and the method used here is not the best but highlights a few useful functions. We start by deleting all the small cubes in the scene then running through the list of small cubes stored in the config file. Line 31 checks if the cube has a position value, cubes without one will be skipped. If the cube has a position then we need to recreate it.

When adding new objects through the KX_Scene object (line 32) we need to specify a game object to use it’s centre to position the newly added cube. Using the player object would result in an immediate collision and affect the score, so the ground plane is used instead. Note, that if you’ve scaled the plane you need to apply it (ctrl+A) otherwise the same scaling will be applied to the added cubes.

The configparser cannot handle more complex data types, like a list, and storing each cube’s position as a option-value pair would defeat the point of it being a nice and easy to read file. So what we do is store the position as a string with each value separated by a comma (smallCube1: 0.0, 3.0, 0.0). The .get() function is then used to return this string and store it in the variable posAsString. Using python’s list comprehension (line 34) we cycle through posAsString and use the inbuilt method .split() to seperate it out into a list. If no agreements are provided to .spilt() then spaces will be used. In this example we provide the arguement ‘,’ to indicate that a comma is separating the values. .split() also returns a string, so we convert that to a float using float() and then store it. This list can then be used to update the added cubes position to the correct one.

Putting it all together

The final step is to get the script working in the game we created at the beginning. Remember the 5th keyboard logic brick we created on the player’s cube? Set the python controller to module and type in the box (without quotation marks): “loader.Load”. Then, test it out. If nothing’s working, use the console to check for python errors and make sure all your object names match up with object names in the script. Also, download the completed tutorial files below and compare the set up in them to yours.

You might notice that if you fall off the edge of the plane hitting load will not bring the player back onto the plane. I’ll leave that one for you to solve!

Overall, this method can be used in many games to store which objects are still in play and load their layout. It is not the most efficient way of doing things and might struggle with larger scenes. Additionally you need to store the object positions manually in the config file, which would take time when developing a game. When we come to look at saving we’ll find a way around this. The main thing the config parser is useful for is storing player setup details (mouse look sensitivity, keyboard layouts, etc.). The same processes used to recall the player’s position and score can be applied here.

Tutorial Resources

Here’s the previous tutorial

Here’s the .blend file

Here’s the config file

Advertisements

~ by Jay on April 21, 2014.

One Response to “BGE: Using saved values retrieved with the configparser”

  1. […] this tutorial looks at the default section in INI sytle files. We take a slight departure from our previous example of saving/loading  player data to explore how default sections can be used to create an in-game […]

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

 
%d bloggers like this: