Python Scripting in Blender Gordon Erlebacher Fall 2012

Tuesday, October 2, 12

Audience • Python classes are for the programmers • Non-programmers should try to

understand, but will not be required to do homeworks based on Python

• Non-Programmers and Programmers must work together to develop the game, not in isolation

Tuesday, October 2, 12

Python Console

Tuesday, October 2, 12

Python Basics • From the command line (mac/linux/ windows with cygwin)

• type python

>>> 3+3 6 >>> a = “gor”; b = “fran” >>> a + b >>> ‘gorfran‘ # concatenation of strings

Tuesday, October 2, 12

Scripting Language • • • Tuesday, October 2, 12

Write Python in

• •

Blender Window Editor outside Python

An interpreter reads each line, checks for errors and executes the line



it is somewhat more complex than that

Allows complex interactions not possible in the game engine

Example print(5+6) exit()

# Stop the program

print("x = 6") # line is never executed

Tuesday, October 2, 12

Example print(5+6) exit()

# Stop the program

a = 3/; # Error (stops program from running) print("x = 6") # line is never executed

•First, the program is checked for syntactical errors •Second, the program is executed Tuesday, October 2, 12

When to use Python •

When the logic panel becomes too complex and hard to understand

• • • • • • • •

In situations with complex series of states

Tuesday, October 2, 12

When multiple objects should interact with each other When one wishes to create actions (animations) on the fly Fancy use of random numbers Program complex motions Handle the mouse Handle multiple cameras, multiple players, multiple viewports ... whatever your imagination dictates ...

Everything is an Object • Actuator, Controller, Sensor • Mesh • IPO • Texture, Material • Camera • etc. Tuesday, October 2, 12

Scenes • One can construct one or more scenes • Each scene contains a collection of objects • Objects can be shared between scenes • Scenes help with level design Tuesday, October 2, 12

Documentation • Documentation of 2.63 API • Code fragments (outside the game engine) • Thread on scripts (outside the game engine)

• 2.6 Manual (with section on game engine) • Blender Basics, 4th Edition Tuesday, October 2, 12

Interactive Console

Tuesday, October 2, 12

MacOSx/Linux cd /Applications/blender-2.63a-releaseOSX_10.6_x86_64/blender.app/Contents/ MacOS/ ./blender

# start blender

# errors will be printed out to the window blender is started in.

Tuesday, October 2, 12

Windows XP/Visa/7 When Blender is started on a Windows operating system, the Console Window is first created as a separate window on the desktop. Assuming that the right start-up conditions are met, the main Blender window should also appear and the Console Window will then be toggled off. This is unlike the 2.4x series where theConsole Window would remain visible while the main Blender window was open. To display the console in current versions of Blender, go to Help » Toggle System Console.When Blender is started on a Windows operating system, the Console Window is first created as a separate window on the desktop. Assuming that the right start-up conditions are met, the main Blender window should also appear and the Console Window will then be toggled off. This is unlike the 2.4x series where theConsole Window would remain visible while the main Blender window was open. To display the console in current versions of Blender, go to Help » Toggle System Console.

Tuesday, October 2, 12

Module bge • • •

Tuesday, October 2, 12

bge : Blender Game Engine First program:



import bge print(dir(bge))

Output



Blender Game Engine Started ['__doc__', '__name__', 'constraints', 'events', 'logic', 'render', 'texture', 'types'] ['__doc__', '__name__', 'constraints', 'events', 'logic', 'render', 'texture', 'types']



Blender Game Engine Finished

Simplest Program

Python script

Tuesday, October 2, 12

How to use the output import bge print(dir(bge)) #['__doc__', '__name__', 'constraints', 'events', 'logic', 'render', 'texture', 'types'] print(dir( bge.events)) ['__doc__', '__name__', 'constraints', 'events', 'logic', 'render', 'texture', 'types'] ['ACCENTGRAVEKEY', 'AKEY', 'BACKSLASHKEY', 'BACKSPACEKEY', 'BKEY', 'CAPSLOCKKEY', 'CKEY', 'COMMAKEY', 'DELKEY', 'DKEY', 'DOWNARROWKEY', 'EIGHTKEY', 'EKEY', 'ENDKEY', 'ENTERKEY', 'EQUALKEY', 'ESCKEY', 'EventToCharacter', 'EventToString', 'F10KEY', 'F11KEY', 'F12KEY', 'F13KEY', 'F14KEY', 'F15KEY', 'F16KEY', 'F17KEY', 'F18KEY', 'F19KEY', 'F1KEY', 'F2KEY', 'F3KEY', 'F4KEY', 'F5KEY', 'F6KEY', 'F7KEY', 'F8KEY', 'F9KEY', 'FIVEKEY', 'FKEY', 'FOURKEY', 'GKEY', 'HKEY', 'HOMEKEY', 'IKEY', 'INSERTKEY', 'JKEY', 'KKEY', 'LEFTALTKEY', 'LEFTARROWKEY', 'LEFTBRACKETKEY', 'LEFTCTRLKEY', 'LEFTMOUSE', 'LEFTSHIFTKEY', 'LINEFEEDKEY', 'LKEY', 'MIDDLEMOUSE', 'MINUSKEY', 'MKEY', 'MOUSEX', 'MOUSEY', 'NINEKEY', 'NKEY', 'OKEY', 'ONEKEY', 'PAD0', 'PAD1', 'PAD2', 'PAD3', 'PAD4', 'PAD5', 'PAD6', 'PAD7', 'PAD8', 'PAD9', 'PADASTERKEY', 'PADENTER', 'PADMINUS', 'PADPERIOD', 'PADPLUSKEY', 'PADSLASHKEY', 'PAGEDOWNKEY', 'PAGEUPKEY', 'PAUSEKEY', 'PERIODKEY', 'PKEY', 'QKEY', 'QUOTEKEY', 'RETKEY', 'RIGHTALTKEY', Tuesday, October 2, 12

print(bge.logic) ['BL_DST_ALPHA', 'BL_DST_COLOR', 'BL_ONE', 'BL_ONE_MINUS_DST_ALPHA', 'BL_ONE_MINUS_DST_COLOR', 'BL_ONE_MINUS_SRC_ALPHA', 'BL_ONE_MINUS_SRC_COLOR', 'BL_SRC_ALPHA', 'BL_SRC_ALPHA_SATURATE', 'BL_SRC_COLOR', 'BL_ZERO', 'CAM_POS', 'CONSTANT_TIMER', 'CONSTRAINT_IK_COPYPOSE', 'CONSTRAINT_IK_DISTANCE', ... many more items ... EWMATRIX_TRANSPOSE', '__doc__', '__name__', '__package__', 'addScene', 'endGame', 'error', 'expandPath', 'getAverageFrameRate', 'getBlendFileList', 'getCurrentController', 'getCurrentScene', 'getExitKey', 'getLogicTicRate', 'getMaxLogicFrame', 'getMaxPhysicsFrame', 'getPhysicsTicRate', 'getRandomFloat', 'getSceneList', 'getSpectrum', 'globalDict', 'keyboard', 'loadGlobalDict', 'mouse', 'restartGame', 'saveGlobalDict', 'sendMessage', 'setExitKey', 'setGravity', 'setLogicTicRate', 'setMaxLogicFrame', 'setMaxPhysicsFrame', 'setPhysicsTicRate', 'startGame']

Tuesday, October 2, 12

Do not sweat the details

Tuesday, October 2, 12

Example 1 Make a cube move with the Akey

Tuesday, October 2, 12

bge.logic.getCurrentScene() ['__class__', '__contains__', '__delattr__', '__delitem__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'active_camera', 'activity_culling', 'activity_culling_radius', 'addObject', 'cameras', 'dbvt_culling', 'drawObstacleSimulation', 'end', 'get', 'invalid', 'lights', 'name', 'objects', 'objectsInactive', 'post_draw', 'pre_draw', 'replace', 'restart', 'resume', 'suspend', 'suspended']

Tuesday, October 2, 12

Scene objects import bge control = bge.logic.getCurrentController() scene = bge.logic.getCurrentScene() objs = scene.objects print(objs) #Output [Cube.001, Cube, __default__cam__]

Tuesday, October 2, 12

What are keyboard sensor properties? cube = objs[‘Cube.001’] sensor = cube.sensors[‘mykey’] print(sensor) ['__class__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'events', 'executePriority', 'frequency', 'getKeyStatus', 'hold1', 'hold2', 'invalid', 'invert', 'key', 'level', 'name', 'neg_ticks', 'owner', 'pos_ticks', 'positive', 'reset', 'status', 'tap', 'targetProperty', 'toggleProperty', 'triggered', 'useAllKeys', 'useNegPulseMode', 'usePosPulseMode'] Tuesday, October 2, 12

Tuesday, October 2, 12

Press and release akey import bge control = bge.logic.getCurrentController() scene = bge.logic.getCurrentScene() objs = scene.objects

The printout occurs twice.

cube = objs['Cube.001']

WHY?

sensor = cube.sensors['mykey'] print(dir(sensor))

Tuesday, October 2, 12

Controllers and pulses • The python controller gets called every time a pulse hits it

• The keyboard sensor sends a positive pulse when the key is pressed

• The keyboard sensor sends a negative pulse when the key is released

• How to prevent the negative pulse from having an effect?

Tuesday, October 2, 12

Avoid negative pulse import bge control = bge.logic.getCurrentController() scene = bge.logic.getCurrentScene() objs = scene.objects cube = objs['Cube.001'] The printout only occurs sensor = cube.sensors['mykey'] when the key is pressed if (sensor.positive): print(dir(sensor)) Notice the “:” in the “if statement” Also: lines inside “if statement” all have the same indentation (imposed by Python)

Tuesday, October 2, 12

Now, avoid keyboard sensor

• It is possible to have python capture keyboard events, without a sensor

• Advantage: you can give the Python code to your friend

• More complicated, so we will avoid this for now

• Working with Python sensors/actuators is

faster then writing your own, in most cases

Tuesday, October 2, 12

Two Cameras • Create a cube and a cone • camCube and camCone are two cameras,

which point to the cube and to the cone

• “akey” will select the camCube • “bkey” will select the camCone • First step: create the scene (two_cams_python.blend)

Tuesday, October 2, 12

Add two camera actuators, one tracking the cube, one tracking the cone. Tracking constraints are not maintained in the game engine

Tuesday, October 2, 12

Object • bge is an object • bge has the following variables/subclasses ['__doc__', '__name__', 'constraints', 'events', 'logic', 'render', 'texture', 'types']

• Each subclasses has its own subclasses and so on

• Check out the API documentation Tuesday, October 2, 12

Control of the Mouse • Use the MouseLookup.py module • Download from class website (Oct.2/Oct. 4)

• How does it work?

Tuesday, October 2, 12

Steps • • • • • Tuesday, October 2, 12

Parent a camera to a character object

• •

The camera is the child of the character The character is the parent of the camera

The character is a dynamic object



rigid might make the object rotate

Create flow logic for the camera All objects can have any name Example on next slide

Properties that control the Mouselookup script Code in file: mouselook.py Execute main function, which is the entry point: mouselook.main The source code is not loaded with Python. If the blender file is transferred elsewhere, remember to transfer the source files as well

Tuesday, October 2, 12

Try it out! • • • •

http://www.sc.fsu.edu/~gerlebacher/gd/

• • •

0key to see the view from the camera

Tuesday, October 2, 12

mouseCamera.blend Activate layer 2 only Layer 1 contains the example that comes with mouselook.py package. pkey to start the game use mouse to rotate the view, then ASWD to move the dynamic object around (and the parented camera will follow)

Object Properties

How are they used from within Python? Let us do something not possible without Python ...

Tuesday, October 2, 12

Object Rotation • Start an object to rotate slowly (keyboard Rkey)

• Every time the Rkey is pressed, the object will rotate a little faster

• Without Python, the rotation value in the motion actuator is a fixed value, which cannot be changed.

Tuesday, October 2, 12

Constant Rotation

Key ups are ignored Once the motion actuator is turned on, motion only stops when it is deactivated

Tuesday, October 2, 12

No Deactivation Script: rotate_start_nostop

Better: cube = cont.owner Usually bad idea to use specific object names. If the object name changes, strange things might happen The motion actuator is never deactivated

Tuesday, October 2, 12

Properties

Get property ‘acc’ Use default -.01 if non-existent The property value is always the same when accessed. How to change it?

Tuesday, October 2, 12

Update Property Every time the key is pressed, rotz increases by 0.01 Keeping the key pressed does not increase the acceleration Increase the acceleration by keeping a key pressed: simply use positive frequency button

Tuesday, October 2, 12

Global Variables • I would like to keep track of the total number of enemies: nb_enemies

• Multiple objects would like to access this property

• How is this done? Tuesday, October 2, 12

Method 1 • • • •

Tuesday, October 2, 12

Choose an object in the game, and give it a property: ‘nb_enemies’



cube[‘nb_enemies’] = 5

Another object: hero, needs access to ‘nb_enemies’ Inside a script for hero, cube = objects[‘cube’] nb_en = cube[‘nb_enemies’] Disadvantage: Why should this variable be associated with ‘cube’? If all objects need access to this variable, better to have a more centralized location. Thus the game dictionary.

Method 2: Game Dictionary • • •

Tuesday, October 2, 12

bge.logic.gameDict Initially, the dictionary is empty Initialize variables in the game:

• • •

bge.logic.gameDict[‘nb_enemies’] = 3 update as needed access: nbe = bge.logic.gameDict[‘nb_enemies’]

Always sensor: only gets executed once. Useful for one-time initializations

save and load globalDict Save variables between game restarts When is game restarted? - when player gets killed (same level) - when moving from one level to the next

Tuesday, October 2, 12

Without save/load

Tuesday, October 2, 12

Change Scenes • • • • Tuesday, October 2, 12

Create two scenes Scene1: a cube, light, camera



health [0-100] property

Scene2: a cone, light, camera



Same health property

Store health in globalDict.



maintained between scenes

Cube Scene

Cube.001

cube.camera

Tuesday, October 2, 12

Change scene

Cone Scene Cone.001

cone.camera Change scene

Tuesday, October 2, 12

Notes • •

Camera trackers are required



The globalDict is not destroyed when changing scenes, however,



Object properties are reset to initial values when changing scenes. Thus, the advantage of the globalDict

• •

What about when a game is restarted?

Tuesday, October 2, 12

A TrackTo contraint outside the game engine is not respected inside the game engine

Object variables can be initialized from the dictionary in an initialization python script (once per game, or for each object)

Blender 2.49a

Tuesday, October 2, 12

Anatomy of a program (blender 2.49b)

• • • • • • Tuesday, October 2, 12

Import GameLogic # not required cs = getCurrentScene() objs = cs.objects gd = GameLogic.globalDict; gd[‘house’] = ‘number of bullets’ gd[‘nb_bul’] = 30



I stored two variables in the Global Dictionary

Any object can access the global dictionary, from any script, from any scene

Anatomy of a program (blender 2.59)

• • • • • • Tuesday, October 2, 12

Import bge # not required cs = bge.logic.getCurrentScene() objs = cs.objects

# list of all objects in the scene

gd = bge.logic.globalDict; gd[‘house’] = ‘number of bullets’ gd[‘nb_bul’] = 30



I stored two variables in the Global Dictionary

Any object can access the global dictionary, from any script, from any scene

Tuesday, October 2, 12

Printing variables •

• Tuesday, October 2, 12

print(objs)



parenthesis are very important!!! Not necessary in the past

• •

printout can be seen on the console



on the Mac, one must start Blender from the command line within a window, which becomes the console

on Windows, the console is behind the Blender window

Printing simplifies debugging

Arrays • • • • • •

Tuesday, October 2, 12

How to store objects and information In Python, use lists a = [4,5,6]

(all the same type)

b = [73,‘gordon’, -45.34] (different types) a[0]+b[2] ==> 4-45.35 = -41.35



different types, but integer converted to float

a[0]+b[1] ==> error (different types)



string ‘gordon’ cannot be converted to integer

Accessing properties con = bge.logic.getCurrentController() sc = bge.logic.getCurrentScene() print(sc.objects)

# prints to console

ob = sc.objects['Cube'] print dir(ob)

Tuesday, October 2, 12

Hash tables • •

Tuesday, October 2, 12

Special kind of array Array index can be any type (object, string, ...)

• • • • •

a = {}

# dictionary creation

a[‘gordon’] = 3 a[5] = ‘blender’ a[6] = 23.5 a[‘gordon’] + a[6] ==> 26.5

Dictionary is a hash table GameLogic = bge.logic con = GameLogic.getCurrentController() sc = GameLogic.getCurrentScene() print(sc.objects) ob = sc.objects[‘Cube’] print(dir(ob)) a = {} a[ob] = 3 # argument can be list, integer, float, string a[sc] = ‘gordon‘ # probably will not work print(a) Tuesday, October 2, 12

Example

demonstrate_simple_python_script.blend

Tuesday, October 2, 12

Program name: test_program import bge GameLogic = bge.logic

con = GameLogic.getCurrentController() sc = GameLogic.getCurrentScene() print(sc.objects) ob = sc.objects['Cube'] print(dir(ob))

a = {}

Tuesday, October 2, 12

Tuesday, October 2, 12

Storing information in objects



# ob name is ‘Cube’ ob = objects[‘Cube’]



# to see this variable, create obj property ob[‘newvel’] = 3.4 print dir(ob) # ‘newvel’ appears in the list



To see object in debug mode (blender 2.49b)

Tuesday, October 2, 12

• • •

changed in blender 2.59b Game menu -> Debug Properties Add Property (in property panel)

Tuesday, October 2, 12