Книга: Mastering Blender
Назад: Chapter 15: Making Things Happen in the Game Engine
Дальше: Appendix: The Bottom Line

Chapter 16

Python Power in the Blender Game Engine

As you saw in Chapter 14, “Creating Assets for the Blender Game Engine,” the Blender Game Engine (BGE) is a powerful tool for 3D interactive content creation and visualization. The logic brick system enables artists to quickly set up interactive logic without the need for programming knowledge. Nevertheless, the logic brick system has its limitations. When the logic becomes moderately complex, the stacks of connected blocks can become difficult to read and debug. External data cannot be accessed by logic bricks, and many commands do not have logic brick equivalents. Fortunately, Python can also be incorporated into your game engine creations, greatly expanding the possibilities of what you can do.

In this chapter, you will learn to

From Logic Bricks to Python

Blender’s logic bricks are a great way to define game logic in many cases. Among the advantages of working with logic bricks are that they are highly accessible for nonprogrammers, they contain numerous built-in functions that make logic bricks fast to work with even for programmers, and they are an entirely self-contained system in the BGE. However, many cases require a more powerful approach to creating game logic. Already you have seen that when the logic becomes moderately complex, logic bricks quickly become unwieldy and difficult to read. There are many functions that do not exist in logic bricks and need to be written specially. Finally, the self-contained nature of logic bricks also represents a limitation. If you want to work with certain kinds of external resources in your game, you have to turn to Python.

The BGE’s Python interpreter enables you to create game logic by programming directly in Python. Although a simple way to think of Python scripting in the BGE is as a replacement for logic bricks, it is more true to say that it is an enhancement of the logic brick system. You still must use logic bricks, but with Python you can use far fewer of them and stretch their functionality much further.

Controlling a Character with Python

A Python script in the BGE plays the role of the controller from the standpoint of logic bricks. However, rather than using simple Boolean logical operations such as AND, OR, and XOR, a script controller can take an arbitrary number of sensor inputs and arrive at the appropriate actuators to trigger based on instructions in the Python code. For this reason, all of the sensors that must be accessed by the script connect to the same controller, and likewise with actuators.

In an example in Chapter 14, four sensors were used to control a character’s movement: a Keyboard sensor for each of the four arrow keys: up, down, right, and left. The rigged character on its own can be found in the file character.blend on the website for this book. You can follow along with this section using that file. The result, with logic bricks and script, is in the file character_py.blend.

To duplicate the controls from Chapter 14 by using a Python script, the same four sensors are needed. Some actuators are also needed. Previously, you had separate Motion actuators for each motion: moving forward, turning right, turning left, and moving backward. With a Python script, you can set the specific values of an actuator within the script, so you won’t need four separate Motion actuators. Nevertheless, you will use one Motion actuator, which will handle all four cases. You will also use an Action actuator for the Walk action, although, again, the specific values for the actuator can be set within the script.

The resulting logic brick setup is shown in . As you can see in the left column, the situation with sensors is more or less identical to how it would be in a pure logic brick setup. Only the first sensor is shown unfolded, so you can see that Up Arrow is selected in the Key field. The other three sensors likewise have the appropriate key selected in the Key field. In the rightmost column of actuators, you see a Motion actuator and an Action actuator. However, you’ll notice that neither of these operators has any settings changed from the default, aside from their names.

The goal

c16f001.tif

Naming Your Logic Bricks
It is worth noticing that both the sensors and actuators have descriptive names. These names, as usual, are arbitrary, and the only thing that really matters is that each name be unique (this is enforced by Blender regardless). However, these names are how the Python script will access the logic bricks, so it is important to give them meaningful names that will make your code readable later.

The center column is where things are most different from the pure logic brick approach. Rather than a long stack of controllers, there is only one controller, with Python selected from the Controller Type drop-down menu and the name of the corresponding script, in this case move.py, displayed in the text field. If you try to enter the name of this script now, however, you will find that Blender will unceremoniously delete your entry from the field. You must enter the name of an existing text file in the text editor. So open a text editor window and select Add New from the header menu. Name the new text file move.py. Now go back and enter this name in the Script field on the Python controller.

After these preliminary steps have been completed and the necessary sensors and actuators have been plugged into the script controller, it is time to turn to the code itself.

The GameLogic Module

The module that distinguishes Blender’s in-game Python functionality is the GameLogic module. When a Python script is executed from within the BGE, GameLogic is automatically imported. Most of the game-engine-specific functionality you will need to use will be accessed via methods defined in this module. The GameLogic module is distinct from the main Python-Blender API. The game engine modules’ API documentation can be found here:

It is also convenient to take advantage of the OOP paradigm and rely on Python’s built in dir() command to find out what functions are available for each object, as this section describes.


“Catchy” Class Names
As you browse the BGE Python API, you’ll no doubt notice that the classes’ names are all prefixed by the letters KX and an underscore. This is a remnant of the history of the BGE. When it was first implemented, it was given a catchy name—literally. The name given to the game engine at that time by its original creator, Erwin Coumans, was Ketsji, pronounced catchy. Erwin used the KX in the class names as a way to indicate that the classes belonged to the Ketsji game engine. The name Ketsji has since fallen out of use, but the KX prefix remains on the class names.

To set up the character controls, the first thing to do in the controller script is to import the logic module and assign it to a shorthand variable called GL. This is not so important in simple cases like this one, but it’s good practice for longer, more-complicated scripts, because this module gets called a lot. So the script begins with this line:

from bge import logic as GL 

It’s important to note that the BGE Python module is only visible while Blender is actively in Game mode, when you hit P. Because of this, trying to test this module by typing import bge or print(bge.__doc__) in Blender’s Python Console will not give you any info; it will just give you errors. Instead, one of this script's early goals will be to use the System Console for feedback.

The next step is to access the logic brick that the script is being called from and assign it to a variable. This is done with the getCurrentController() method. This line is always required in BGE scripts that use sensors or actuators. The logic brick returned by this method is passed to the variable cont in the line shown here:

cont = GL.getCurrentController()

Every code block has an owner, which is the object to which it is associated. The object that is active when you create a new code block will be that block’s owner. In this case, because the armature is the object that is affected by the logic, the code blocks must be associated with the armature, as you saw previously in . So, how do you return the owner of a controller in the Python code? There are two ways to find the answer to the question. One is to look it up in the API. Another way is with the dir() command mentioned previously. To do this, temporarily add the following line to your script:

print (dir(cont))

You now have a three-line script called move.py associated with a Python controller, which is in turn connected to the Keyboard sensors and actuators shown previously in . Place your Blender window in such a way that the terminal is visible, and enter game-play mode by pressing P. In game-play mode, press one of the four arrow keys: up, down, right, or left. When you do so, you will trigger the script to run, and the controller’s dir() value will be printed to the System Console as follows (without the line breaks):

['__class__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'activate', 'actuators', 'deactivate', 'executePriority', 'invalid', 'mode', 'name', 'owner', 'script', 'sensors', 'state', 'useHighPriority'] 

This is a list of all the methods defined for Controller objects. As you can see, just reading this list will give you a few ideas of what kinds of things you can request from controllers. Because you’re interested in accessing the Armature object that is the owner of the controller in this example, it is intuitively clear that the owner is what you want. The line of code to retrieve the owner is as follows:

own = cont.  owner

Sensors and Actuators

You now have access to the controller and to the object that owns it. The next thing to do is to retrieve the necessary sensors. Again, the output from the dir() command showed that cont has a sensors method. You can see what sensors you have active with this command:

print(cont.sensors)

Next, you want to keep the sensors distinct and assign them to meaningfully named variables, as shown in the following code:

#Sensors  forward = cont.sensors['forward']  right = cont.sensors['right']  left = cont.sensors['left']  back = cont.sensors['back']

The retrieval of the actuators works in exactly the same way:

#Actuators  motion = cont.actuators["motion"]  action = cont.actuators["action"]

Game Logic in Python

Now all the necessary logic brick components are accessible within the script, and the code for the movement itself can begin. Start by setting some variable values. Set the variables speed and rspeed with the values you want to use for forward-walking speed and rotation speed when the character turns. The default values for walk and turn are 0, because the character should not be moving when the script is first executed.

speed = .07  bspeed = -.07  rspeed = 0.02  walk = 0  turn = 0

As you saw, the Motion and Action actuators in the logic bricks did not have any of their fields filled in. These values can be provided in the script itself, and this is the point where that is done for the Action actuator. Review the discussion of the Action actuator in Chapter 14 if you need a reminder of what the individual fields mean, and print the output of print (dir(action)) to find out what other methods are available for accessing an Action actuator.

The Action actuator should use the Walk action created in Chapter 14. The start frame should be 1, and the end frame of the action loop should be 20. Finally, the Loop Stop play mode should be selected, which is done using the mode property. The play modes are represented by integers: 0 represents the Play play mode, 1 represents the Property play mode, 2 represents the Flipper play mode, 3 represents the Loop Stop play mode, and 4 represents the Loop End play mode.

The code for setting these various Action actuator parameters is as follows:

action.action = 'Walk'  action.frameStart = 1  action.frameEnd = 20  action.mode = 3

The actual logic part of the code is simple. Once again, use the dir() command to print out a list of methods appropriate for sensors. The positive property is used to return whether the sensor is currently being activated. For example, in this code, forward.positive will return a True value if the up arrow is being pressed. The variables defined previously are used here to assign the appropriate values to walk and turn, according to the input from the sensors.

if forward.positive:       walk = speed  if back.positive:       walk = bspeed  if left.positive:       turn = rspeed  if right.positive:       turn = -rspeed

Another if conditional is needed to set the Action activator as active when the character is moving and inactive if the character is not moving. Anytime an actuator’s parameters are changed, the activate() method must be called from the controller in order for the new parameters to be realized in the game. When the deactivate() method is called, the actuator is rendered inactive. So in order to have the character walk when the movement sensors are positive, the following conditional is used:

if forward.positive or back.positive or left.positive or right.positive:       cont.activate(action)  else:       cont.deactivate(action)

The activate() method needs to be called again to activate the Motion activator with the correct parameters for making the character move. Once again, dir() will tell you what the possible properties are that can be set on a Motion actuator. In this case, you use dLoc and dRot to set the location and rotation speed. The order of the arguments represents the order of the fields on the Motion actuator logic brick. The code for setting these parameters and adding the active animator is as follows:

motion.dLoc = [0,0,walk]  motion.dRot = [0,turn,0]  cont.activate(motion)

And with that, the movement controls for the character are finished. Run your game by pressing P and test it out. Be sure to have your console open as described in Chapter 12, “The Blender-Python Interpreter,” to watch for errors or warnings from Python.

Python Power for the BGE

Now that you know the basics of how to control a character with Python, you should be on track to understanding how to mimic much of the core functionality of logic bricks with Python. The API and the dir() command will be your friends as you explore this further. This section describes various ways you can use Python to get effects that are difficult or impossible to achieve with only logic bricks.

Creating a Teleportation Machine with Python

Using Python, it is simple to place an object anywhere in the 3D space. The next example shows you how to “teleport” objects from one place to another when a sensor is triggered. The setup here is as shown in , and you can find the .blend files on the website for this book. The completed file is called portals.blend, and the starting file with no logic, physics, or script attached is called portals_nologic.blend.

Rings and a ball for “teleportation”

c16f002.tif

Objects and Logic Bricks

In the scene, there are five Torus primitive objects and one Icosphere object. They are placed on the Y origin plane at Z,X coordinates (beginning with the sphere) [-10,10], [-10,0], [0,10], [0,0], [10,10], and [10,0]. The Sphere is directly above a ring, and the other two pairs of rings are aligned vertically. The objects’ names are Sphere, Torus1, Torus2, Torus3, Torus4, and Torus5, and they are arranged as shown in .

The positions and object names of the rings

c16f003.tif

The effect that will be created in this example is to have the sphere fall as a rigid body, affected by gravity, and when it “passes through” the ring below it, it will be “teleported” to the top ring in the column to the right. It will fall to the ring below that one and again be teleported to the top ring in the next column. It will be caught in the last ring.

Before going on, make sure that your render option has been changed from Blender Render to Blender Game in the drop-down menu of the Info bar header at the top of your Blender desktop. Set the sphere to be a rigid body object by selecting Rigid Body in the Physics Type drop-down menu in the Logic buttons area, as shown in . You can leave all the other values at their defaults.

Setting the sphere to be a Rigid Body object

c16f004.tif

If you press the P key now, the Sphere will drop and be caught by the first ring, Torus1. This collision is what should trigger the script to handle the teleportation functionality, so the next step is to set that up.

The teleportation script will be called via a controller and sensor on the Sphere object. When the sphere senses that it has collided with a ring, it will run the script. As you saw in Chapter 15, “Making Things Happen in the Game Engine,” the way for an object to be recognized by other objects is to use properties. For this reason, it is necessary to create properties for each of the rings. Add an int type property called port to each ring, as shown in . Give the properties integer values that correspond with the object names. Torus1 should have a prop value of 1; Torus2 should have a prop value of 2; Torus3 should have a prop value of 3; and so on.

Adding a port property to Torus1

c16f005.tif

Open a text editor and add a new text file called portal.py. To the Sphere object, add a Collision sensor and a Python controller, as shown in . The Collision sensor is called hitportal, and it is activated on collision with objects with property port. The Python controller calls the portal.py script.

Logic for the sphere

c16f006.tif

Code for the Teleportation Machine

Now that the objects and logic bricks are in place, you can turn your attention to the code. The code begins by assigning the GameLogic module to a shorthand variable, as in the previous case. Then, the controller and owner are retrieved and assigned to variables as shown here:

from bge import logic as GL  cont = GL.getCurrentController()  scene = GL.getCurrentScene()  own = cont.owner

Aside from the owner object, which is the Sphere, it is also necessary to retrieve the other objects from the scene. The GL.getCurrentScene() method will return the current scene. The scene, in turn, has a property objects that contains a collection of the objects in the scene in the form of a Python dictionary.

The objects property can be accessed by the 3D object names as keys; use print (scene.objects) if you want to see this list in the System Console. The following code assigns the actual 3D Torus objects to appropriately named variables:

torus1 = scene.objects["Torus1"]  torus2 = scene.objects["Torus2"]  torus3 = scene.objects["Torus3"]  torus4 = scene.objects["Torus4"] 

The next lines of the code retrieve the Collision sensor from the controller and then query the Collision sensor for the object that was hit. As always, the dir() command can be relied on to find out the name of the appropriate methods. Calling dir() on a Collision sensor object will display a list of methods that includes the property hitObject. This is the property to use to retrieve the object that was collided with. In the following code, the object is passed to a variable called hit:

col = cont.sensors["hitportal"]  hit = col.hitObject

To make sure that the property calls are well defined in the subsequent code, add an if clause to ensure that hit has an actual value. What happens next depends on the port property value of the hit object. If the port value is 1, the object’s location is set to a location just below the second upper ring. If the port value is 3, the location is set to just below the rightmost upper ring. Note that in both of these clauses, place[2] refers to the z-axis coordinate of the position dealt with. Subtracting 0.8 has the effect of placing the ball just below the upper rings:

if hit:       print(hit.getPropertyNames())       if hit['port'] == 1:            place = list(torus2.position)            place[2] = place[2] - 0.8            own.position = place       if hit['port'] == 3:            place = list(torus4.position)            place[2] = place[2] - 0.8            own.position = place     

If you run the BGE now, the results might not be as you expect. With the settings described so far, the ball will not fall straight through the series of rings but rather will collide with the first ring it strikes, and the forces from that collision will affect its trajectory as it emerges from the next ring in the series. To remedy this, set the ring dynamics to Ghost in the Logic buttons area.

Creating a Login Screen

In this section, you’ll walk through an implementation of a simple login screen with name and password fields. Python is necessary here to process the name and password to ensure that they match and also to handle other functions of the login screen. In the example, the name and password data is stored in a simple dictionary within the script itself, but you could also load the data from an external database (to learn how to do this, you will need to refer to general Python documentation for accessing the database of your choice).

The starting setup for this section, without the Python code or logic, can be found on the website for this book, in the file login_nologic.blend. The finished login screen is in the file login.blend.

Objects and Logic Bricks

To get started, I placed a number of objects in the 3D space to create a login screen, as shown in . The login screen is made up of three vertex-painted planes that compose the text fields themselves. Above the fields are textured planes for dynamic text exactly like the ones described in Chapter 15. Two small circles are placed over the left side of each text field, which will be used to indicate which text field is active to type into.

The initial setup for the login screen

c16f007.tif

A separate scene called Welcome was also created, as shown in . This scene contains only a single dynamic text plane, as described in Chapter 15.

A separate scene for the Welcome screen

c16f008.tif

The Username and Password dynamic text objects need to have several properties set up for them, as shown in . Each needs a text property to contain the text itself, which in both cases should be initialized with empty values. The Username object also has a property called username, and the Password object has a property called password. The other dynamic text objects in the scene need only text properties with the desired text strings as values.

Properties for Username and Password dynamic text objects

c16f009.tif

The main logic for processing the input will be attached to the camera. This is a completely arbitrary decision. It would be just as easy to associate this logic with another object in the scene or to create a new object such as an empty specifically to associate the logic with. The camera seems as intuitive a place as any to associate this logic, so that’s what I chose. Because the camera will take the logic for processing the input, it will be necessary to create some properties on the camera relating to the input, shown in . Specifically, there needs to be a property called input of type String, which will be used to receive text strings typed in from the keyboard. The other property necessary is a Boolean property I called keystrokelog that will be used to tell the Keyboard sensor to interpret input as a string. It should be set to the value True.

Properties on the camera

c16f010.tif

The full logic for the password-processing functionality is shown in . It includes the required five sensors, a script controller that calls a script named Control.py, and a Scene actuator that will set the scene to the Welcome scene when the password matches the username. You’ll also need to switch the scene to Welcome and make sure its camera has the same logic setup, with Control.py as its controller.

Logic bricks on the camera

c16f011.tif

Before moving on to the code of Control.py, take a closer look at those five sensors connected to the script controller. You can see all five sensors in their unfolded state in .

Always The first sensor is an Always sensor that ensures that the script runs constantly. This sensor is given the name init by default because one of its purposes is to ensure that the initialization code is executed immediately, but this sensor is not accessed directly in the code, so it does not really matter what its name is.
Mouse The next sensor is a Mouse sensor of type Mouse Over Any, and its name is mouse. As you might guess, this sensor will register as positive if the mouse is moved over any object from the perspective of the camera in the game-play environment.
Keyboard The next sensor is a Keyboard sensor. Instead of choosing a specific key, the All Keys option is selected. Furthermore, the value keystrokelog is entered into the Log Toggle field. As you recall, the keystrokelog property is a Boolean type property with a value of True. Setting Log Toggle to True enables keyboard input to be interpreted as a string. The Target field determines which property the string is passed to. The value input is entered into this field so that the string will be passed to the input property.
Property To print the input to the screen letter by letter, you need to add a sensor that fires each time a letter is entered. This is done using a Property sensor of type Changed with a Property value of input. This means that the sensor will fire every time the input property changes, which happens every time a letter is pressed, thanks to the Keyboard sensor.
Mouse Finally, another Mouse sensor registers when the mouse is left-clicked. The name of this sensor is mouseclick.

A closer look at the sensors

c16f012.tif

Accessing Keyboard Information Directly in Python
The example here uses the sensor logic brick to set the keyboard key for the Keyboard sensor. It is also possible to work with keyboard information directly in Python by using the GameKeys module. This is one of the three modules that make up the BGE API, the other two being GameLogic and Rasterizer, which is discussed later in this chapter. In the BGE API, you will find a list of the key codes for accessing individual keys with GameKeys.

Python Code for a Login Screen

As in the previous examples, the code begins by creating a shorthand for the GameLogic name. After that, the Python hasattr() function is called to assess whether GameLogic has an attribute called init. GameLogic attributes are how values can be persisted and accessed throughout a game-play session. Upon the first iteration of this script, GameLogic does not have any such attribute, so the code in the if clause will be executed. This results in several initialization steps.

First, the showMouse() method of the Rasterizer module is imported and set to 1, which causes the mouse pointer to be visible within the game-play environment. Next, the GL.password and GL.name attributes are created and given empty string values. These attributes will now persist throughout the game-play session. Finally, GL.init is created and given a value of None. This will prevent this code from being executed again, because the hasattr() function will result in a True value the next time it is called.

from bge import logic as GL  if not hasattr(GL, "init"):      from Rasterizer import showMouse      showMouse(1)      GL.init = None      GL.password = ""      GL.name = ""

Once-off Execution and Persistent Variables in the BGE
BGE Python scripts are executed when the associated sensors are activated. Scripts associated to Always sensors are executed over and over again in rapid succession. Of course, variable values do not generally persist beyond a single execution of a normal Python script, which means that when scripting for the BGE, it is necessary to have another way to ensure that values can be made to persist throughout an entire game session. The solution to this problem is also the solution to the problem of how to execute a given piece of code only once, at the start of a game-play session. The way to do this is to assign attributes to the logic object itself. You can create arbitrarily named attributes for logic and assign them values, which will be accessible by any script run at any time during a single game-play session. The syntax for this is logic.attributename = value. To execute a particular section of code once only, use Python’s hasattr() function to determine whether the logic object has a particular attribute associated with it. If hasattr() returns 0 (False), execute the once-off code, and then define the attribute for GameLogic, so that the hasattr() function will not return 0 the next time it is called. An example of this technique is the use of the init attribute in the login screen script in this chapter.

As in the previous examples, the current controller, its owner, and its associated sensors and actuators are retrieved and sent to aptly named variables, which is done with the following code:

cont = GL.getCurrentController()  current_scene = GL.getCurrentScene()  own = cont.owner  mouse = cont.sensors["mouse"]  mouseclick = cont.sensors["mouseclick"]  welcome = cont.actuators['welcome']

Once again, the collection of objects (not strictly a Python list) is retrieved by accessing objects on the current scene:

object_list = current_scene.objects

The next block of code is a function definition. This definition must precede calls to the function. The function to define here is validate_pass(name, password), which, as the name suggests, will return a True value if the name/password combination checks out.

The first line of the validate_pass function creates a dictionary called passwords, which has key-value pairs of usernames and passwords. For the present purposes, I’ve created a toy example by defining the dictionary directly. If you want to retrieve username/password pairs from a database or other external resource, you can write a function to do this. It’s beyond the scope of this book to cover how to access a database from a Python script, but such information is widely available in general-purpose Python documentation.

The remainder of the function is a try/except structure in which Python attempts the try clause and if this fails executes the except clause. The try clause tests the name and passwd values to see if they correspond to a key-value pair in the passwords dictionary. If so, the current scene is set to the Welcome scene by activating the setScene actuator. The except clause here simply clears the values of the password and name properties and blanks the two text fields.

def validate_pass(name, passwd):      passwords = {"myname":"mypass",                   "bob":"bobspass"}      try:          if passwords[name] == passwd:              print(current_scene.name)              cont.activate(welcome)              print(current_scene.name)      except:          GL.password = ""          GL.name = ""          password_input['Text'] = ""          username_input['Text'] = ""

The main body of the code follows. The code is divided into two conditions. The first condition is if the current scene is the default scene called Scene. The second condition, toward the end of the script, deals with the case in which the scene has changed to the Welcome screen scene.

If the current scene is the main scene, the first thing that is done is that its objects are retrieved and assigned to variables. In addition, an empty string is assigned to a variable called mask: if current_scene.name == "Scene".

    field1 = object_list["field1"]      field2 = object_list["field2"]      button = object_list["button"]      circle1 = object_list["Circle1"]      circle2 = object_list["Circle2"]      username_input = object_list["UsernameText"]      password_input = object_list["PasswordText"]      cam = object_list["camera"]      mask = ""

Next, the case in which the mouse is moved over an object is considered, with the if mouse.positive conditional clause. This block of code will be executed anytime the mouse is over any object. The hitObject property is called on the Mouse sensor to retrieve the specific object that the mouse is over. This object is passed to the hit_object variable:

      if mouse.positive:          hit_object = mouse.hitObject

The idea here is for the field that has the mouse over it to be active. This should be indicated by the appropriate white circle becoming visible, and it should result in the text input going to the appropriate variable. If the username field has the mouse over it, the white circle on that field should be visible and any text the user types should be interpreted as username input. Likewise, if the password field is active, typed text should be interpreted as password input. There is one more thing that these clauses do; if the mouse has been moved from another place, the input is cleared. This has the effect of clearing fields when the mouse has been moved away and then returned to the field, which may not be ideal behavior. This can be changed, but for the sake of keeping it simple for this example, I will accept this minor idiosyncrasy. Note a difference between the case of field1, the username field field2, and the password field. In the first case, the cam.input value is passed to both GL.name (the GameLogic property storing the username) and username_input.Text (the dynamic text object representing the username text field). In the password case, the value is passed from cam.input to GL.password but not directly to the dynamic text object. Rather, a variable called mask is introduced, and a string of asterisks the same length as the password value is passed to this variable. This string is passed to the dynamic text object, so the password is concealed.

if hit_object == field1:              if field1['active'] == 0:                  cam['input'] = ""              circle1.setVisible(1)              circle2.setVisible(0)              field1['active'] = 1              field2['active'] = 0              username_input['Text'] = GL.name = cam['input']          elif hit_object == field2:              if field2['active'] == 0:                  cam['input'] = ""  circle1.setVisible(0)              circle2.setVisible(1)              field1['active'] = 0              field2['active'] = 1              GL.password = cam['input']              for x in range(len(GL.password)):                  mask = mask + "*"              password_input['Text'] = mask

The else portion of the clause deals with the case in which the mouse is not over any object. This sets both circles to be invisible and both fields to be inactive:

    else:          circle1.setVisible(0)          circle2.setVisible(0)          field1['active'] = 0          field2['active'] = 0

Another, much shorter conditional clause checks to see whether the mouse-click event is positive. If so, the script checks whether the object under the mouse is the Button object. If so, the previously defined validate_pass() function is called to validate whether the username text input matches the input password:

    if mouseclick.positive:          if hit_object == button:              validate_pass(username_input['Text'], GL.password)

The top-level if clause checked whether the current scene was the login screen scene. If the scene has been changed to the Welcome scene, the only thing that needs to be done is to access the OBWelcome text and pass the GL.name value to the text, so that the user is welcomed by name:

elif current_scene.name == "Welcome":      list = GL.getCurrentScene().objects      list["Welcome"]['Text'] = "Welcome, "+GL.name+"!"    

Sound Effects and Multiple Viewports

In this section, you will learn how to create unique game environments with sound effects and multiple viewports.

Working with Viewports

In addition to GameLogic and GameKeys, the third module that makes up the BGE API is Rasterizer. This module deals with how the visible pixels of the game are arranged on the screen. Using this module, it is possible to enable the display of multiple viewports.

You can see a simple example of this by setting up an imaginary two-player game. In the file viewports_nopython.blend, you’ll find the initial setup for this game, with the complete game logic. In , you can see the setup illustrated. This figure is reproduced in color in the color insert of the book. There is a red cube and a blue cube, named RedCube and BlueCube, respectively. Each cube has a camera associated with it, likewise named RedCamera and BlueCamera. You can add cameras to a scene in the same way you add other objects, by pressing the spacebar and choosing Add > Camera from the menu.

Two cubes and corresponding cameras

c16f013.tif

The game logic is simple, along the same lines of the motion control logic you read about in Chapter 15. Each cube can go forward and turn right and left. The keys to control the blue cube are E, S, and D, and the keys to control the red cube are O, K, and L. Each camera has a Camera actuator associated with it, targeted on the appropriate cube. You can see the logic for the red cube and its camera summarized in .

Logic bricks for RedCube and RedCamera

c16f014.tif

Finally, there is one more camera, OverheadCamera, placed above the entire scene, as shown in the upper-left corner of .

OverheadCamera placed above the board

c16f015.tif

The bird’s-eye view of the game board from OverheadCamera is shown in . To look through this camera, hold down the Ctrl key while you press 0 on the number pad to make it the active camera. After you do that, pressing the 0 key on the number pad will always shift the view to this camera.

Finally, a new Text object called viewports.py is created, and a script controller is connected to an Always sensor on the game board Plane object, as shown in . Again, the choice of the object to attach this logic to is arbitrary. The plane is as sensible a place as any.

The view from OverheadCamera

c16f016.tif

The viewports.py script controller

c16f017.tif

The content of viewports.py is of course where the real action is in this example. The first thing you need to do is to import the module that handles putting the game imagery on the screen. That module is called render. Import the module just as you would with any other module, along with importing logic, as shown here:

from bge import logic as GL  from bge import render

To divide the game view area between viewports, you first need to retrieve the height and width of the total view area. The render module enables you to access this information as follows:

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

The next few lines of code should be familiar from previous examples in this chapter, in which the scene and the objects in the scene are retrieved and assigned to variables:

scene = GL.getCurrentScene()  obList = scene.objects  redcam = obList["OBRedCamera"]  bluecam = obList["OBBlueCamera"]  overview = obList["OBOverviewCamera"]

To set viewports dimensions, use the setViewport() method on the appropriate Camera object. The method takes four arguments. They represent, in order, the following parameters: the position of the left side of the viewport, counting left to right from zero to the width of the full view area; the position of the bottom edge of the viewport, counting bottom to top from zero to the height of the full view area; the right side of the viewport, counting left to right from zero to the width of the viewing area (if this number is smaller than the first argument, the viewport will not be visible); and finally, the top edge of the viewport, counting bottom to top from zero to the height of the full view area (if this number is smaller than the second argument, the viewport will not be visible). The overview camera is set to be visible over the other two viewports by using the setOnTop() method. Finally, you need to enable each of the viewports by setting the property useViewport to True for each Camera object individually:

bluecam.setViewport(0,0,width/2,height)  redcam.setViewport(width/2,0,width,height)  overview.setViewport(width/4,0,width*3/4,height*1/3)  overview.setOnTop()  bluecam.useViewport = True  redcam.useViewport  = True  overview.useViewport = True

When you run the game-play mode now, you will see a split-screen view with an overlaid viewport, as shown in . This figure is reproduced in color in the color insert of the book.

Playing the game with multiple views

c16f018.tif

Accessing Sound with Python

In Chapter 14, you saw how to work with basic BGE sound controls, including the 3D sound option. In this section, you’ll see how to access this data with Python and gain a greater level of control over it.

Begin with a logic brick setup associated with the Blender default cube, as shown in . The actuator is attached to a Python script controller for a script called volume.py (as always, this must be created in the text editor first). Three sensors lead into the controller. The first sensor is an Always sensor. The second sensor is a Keyboard sensor triggered on the U key, which will raise the volume. The third sensor is a Keyboard sensor triggered on the D key, which will decrease the volume.

Sensors and actuators connected to the volume.py controller

c16f019.tif

Unlike the example in Chapter 14, the 3D button on the Sound actuator panel should not be selected.

The code of volume.py is as follows. To keep the volume consistent over repeated calls of the script, you need to store the value in a GameLogic attribute. The way to do this, as you saw previously in this chapter, is to assign a value GL.vol in the context of an initialization loop using hasattr(). Possible volume values range from 0 to 1, so set GL.vol to mid-volume, at 0.5.

from bge import logic as GL  if not hasattr(GL, "init"):      GL.vol = 0.5      GL.init = None

As always, the controller, owner, actuators, and sensors must be retrieved, as you see here:

cont = GL.getCurrentController()  own = cont.owner  play = cont.actuators['playsound']      volumeup = cont.sensors['volumeup']  volumedown = cont.sensors['volumedown']

Just as in the previous cases of working with sensors in Python, the positive property is called to assess whether the sensor is being triggered. The code here simply sets up two if conditionals: one for the case of increasing the volume and one for the case of decreasing the volume:

if volumeup.positive:      if GL.vol < 1.0:          GL.vol = GL.vol + 0.01  if volumedown.positive:      if GL.vol > 0.01:          GL.vol = GL.vol - 0.01

The new volume value is set to the Sound actuator by using the volume property. Finally, as in all cases in which an actuator must be refreshed, the activate() method is called with the Play actuator as its argument:

play.volume = GL.vol  cont.activate(play)

When you press P and enter the game-play environment, you will be able to control the sound of the bubbles by using the U and D keys. The finished example can be found in the file soundcube_volume.blend on the website for this book.


realworld.eps
Visualizing Fish Population Data with the BGE
An extraordinary example of the practical usefulness of Python and the BGE can be seen in the collaborative fish population visualization project carried out by the University of British Columbia (UBC) Fisheries Centre and students in the UBC Masters of Digital Media program. The ambitious project focused on taking large amounts of complex statistical data describing projected populations of marine life and the results of fishing policies on the populations. Although the collected data was compelling, the researchers of the Fisheries Centre needed a way to make it accessible to nonbiologists and nonstatisticians.
The result of the collaboration is a dynamic 3D underwater environment with animated marine life in densities reflecting actual projections of the data. The user can select marine species and set parameters for fishing policy in real time, and the projected effect on the fish population will become immediately visible. The team at UBC hopes that this unique marriage of game technology and intuitive data visualization can help to influence how fishing policy is made in the future.
You can read more about the project at
The UBC team’s full results can be seen on their YouTube page, which includes videos of the interface such as this:

Further Resources

If you’ve followed this entire book to the end, you should now have a solid grounding in many of the most advanced areas of Blender functionality. But even a quick glance at the Python-Blender API or the GameLogic API is enough to know that there is a great deal out there still to learn. Blender’s functionality is almost endless and expanding all the time. Even the relatively limited corner of functionality discussed in this chapter is impossible to do justice to in such a small amount of space. For this reason, you will want to turn to other resources to deepen your knowledge.

Because the API is so new, a lot of the tutorials and resources that people have used over the years for learning the BGE have become largely obsolete. A very promising upcoming book is Mastering the Blender Game Engine by Mike Pan and Dalai Felinto (2012, Course Technology PTR). Although the book has not been published at the time that I write this and I haven’t seen it, I feel confident recommending it on the strength of the authors’ reputations alone. Both Mike and Dalai are known in the Blender community as some of the most skilled and knowledgeable users of the BGE around. If you’re interested in delving deeper into the BGE, I expect their book to be a must-read.

The forum at BlenderArtists.org remains one of the most important resources available to any Blender user. In the Game Engine section of the forum, you’ll find gurus who will answer the most obscure questions and give you tips for creating almost any effect.

Many other resources pop up around the Web from time to time. Keep an eye on BlenderNation.com for the latest developments and tutorials to be made available. At the same time, if you create a useful tool, an interesting visualization, a helpful tutorial, or a killer game, don’t be shy about letting the community know about it, either through the forums at BlenderArtists.org or by submitting a news item at BlenderNation.com.

One of the best ways to learn is by digging around in the .blend file of a skilled user. Many such files are available under Creative Commons at , and if you make a project worth sharing, you can post it there as well. The Blender community is always supportive and always eager to hear about the interesting uses to which people are putting this extraordinary software. In the meantime, with what you’ve learned from this book, you should be able to confidently forge ahead on your own advanced Blender projects.

Happy Blendering!

The Bottom Line

Replace multiple logic brick setups with a few lines of Python code. By using a Python script controller to coordinate your sensors and actuators, you can accomplish the same things as with logic bricks but much more efficiently and with much less clutter. For larger, complex projects, using Python makes it vastly easier to keep your logic organized.
Master It Add some more movements to the walking character from this chapter. Create a new action and implement it in the BGE with Python so that pressing the W key will make him wave.
Use Python to create effects in the BGE that would not be possible using logic bricks alone. Not every possible game engine behavior is encoded in logic brick form. Setting an object’s location is a simple example of something that requires Python to do. More-complex operations requiring variables or data structures also require the power of a fully functional programming language like Python.
Master It Implement a Python script that makes the default cube follow the mouse around the viewport.
Create unique game environments with sound effects and multiple viewports. Effects like real-time control of sound volume and split-screen views can help to make your interactive 3D environment more engaging and immersive. With Python, these effects are easy to achieve.
Master It Create an environment with one stationary object and another object that can be moved around similarly to the cube in the viewports example. Have the stationary object emit a sound such that the volume of the sound increases to one when the objects are at their closest together and decreases to zero as the objects get farther apart.
Назад: Chapter 15: Making Things Happen in the Game Engine
Дальше: Appendix: The Bottom Line

lookforrent
Буду знать! Оцените туристический портал lookfor.rent
JbnvJinge
12 month loans cash now cash loans in winchester tn cash advance in dubai
androidinfoSa
Знать достаточно свежие публикации у сфере планшетов и наблюдать презентации планшетов Андроид пользователи смогут на разработанном сайте запись телефонных звонков , который окажет помощь для Вас находиться в теме последних выпусков мировых марок в операционке Android и продажи задекларированной устройств. Популярный ресурс выдает потребителям совершенно популярные предметы обсуждения: мнение экспертов про телефоны, оценка пользователей, обновление, апки для персональному смартфону, ОС Андроид, ответы на популярные вопросы также различные основные содержание, какими интересуются регулярно. Стоит коротко увидеть новый телефон и выделить уникальные характеристики? Вовсе не AndroidInfo.Ru преград - у основной строчке возможно кликнуть модель либо ключевое слово затем одержать с вашего задания подходящую параграф совместно с фотоотчетом плюс описанием преобладающего функций. В случае если юзер есть несомненного ценителя выпусков смарт устройств по операционке Андроид Android , здесь регистрация поможет юзерам ни разу не выпустить каждую единую добавленную новость у области умных систем. Будет изобилие всего увлекательного также развивающего для всем ценителей инноваций новой эры.
Anciwhish
buying paper custom written papers
DbgvAmurn
dissertation research research methodology dissertation