Search Results

Sunday, January 23, 2011

Using Python in the Blender Game Engine - Part 2

The debug console is an invaluable tool in using the Blender Game Engine.







Hey, there. So, here's the second part in a tutorial about using the Blender Game Engine to make a game using the Python scripting language.


Last tutorial in the series, we had something like this to the left. In this screenshot, we see that the Cube is running a script that prints out a string to the console, which is the black window to the left of the main Blender window. This window is exceptionally useful in debugging as it allows us the opportunity to view values of variables and the general state of the game, as well as see if there are any errors that the game is encountering during execution. Now, then, how do we take this further? Well, we now can begin to implement user interaction with this, allowing us to control the Player.

The first thing you'll need to do is add a Motion actuator to our Player object. Make sure that it's named 'Motion', otherwise the script won't pick the actuator up from our list of actuators in the script below. Attach the newly created actuator to the Python controller running the script.

The Blender Game Engine (as of 2.56) has built-in modules that we can use to access the keyboard and mouse. Here's the source code for input recognition:
from bge import logic

from bge import events

def Player():
cont = logic.getCurrentController()
obj = cont.owner
motion = cont.actuators['Motion']
key = logic.keyboard.events
kbleft = key[events.LEFTARROWKEY]
kbright = key[events.RIGHTARROWKEY]
mx = 0.0
my = 0.0
if kbleft > 0:
mx = -0.1
elif kbright > 0:
mx = 0.1
motion.dLoc = [mx, my, 0.0]
cont.activate(motion)
So this isn't too much different from Part 1's source code - just a few extra lines. Let's check out the differences, shall we?
from bge import events
So, first, we have a new import function that imports a new portion of the Blender Game Engine's programming library that we haven't used yet - events. The events module contains keybinding constant values that we can use to check for input from specific keys. For example, rather than trying to find the exact key code value for the left arrow key, we can just use events.LEFTARROWKEY. Simple, right?
motion = cont.actuators['Motion']
This line creates a simple local variable, motion, that corresponds to the Python controller's actuators and finds a specific one, a Motion actuator named... well, Motion. The Motion actuator, as you probably well know, controls motion in the Blender Game Engine. While we could just alter the object's position values using Python very easily, this is a bit more useful as we get a bit more ease of control.
key = logic.keyboard.events

kbleft = key[events.LEFTARROWKEY]

kbright = key[events.RIGHTARROWKEY]
As you can see, the logic submodule (I throw 'submodule' around a lot - there are really just a 'submodule' because it's a 'child' of the bge top module) of the bge module is quite useful - it even gives us a pre-made keyboard and mouse to access. Here in the first line, we access the keyboard variable of the logic module and it's input events by creating a variable (key) that references logic.keyboard.events, which is a dictionary of each key on the keyboard and its input status. Using that key variable, we can find specific keys and whether they are being held down or not. Useful, right? In the other two lines, we create two other variables, kbleft and kbright that point to the Left Arrow and Right Arrow keyboard keys, respectively. How? Well, thanks to our previously imported module, the events module, we can now refer to specific keys using simple constants, like XKEY or RIGHTARROWKEY, like above.
mx = 0.0

my = 0.0
In these two lines, we create two variables, both for moving our object on the X and Y axes. We will use these to apply a simple speed to the Player object to get it to move on our command.
if kbleft > 0:
mx = -0.1
elif kbright > 0:
mx = 0.1
EDIT: Note that these keyboard variables will return 3 when just released, but this is so minute a time frame that for simplicity, I used ' > 0' rather than '... == 1 or ... == 2'. Also, note that I use an elif statement for the right key check, meaning that either the left key or the right key can be pressed at a given time - if the left key is pressed, then the right key doesn't register a response. Whether or not you wish to pursue this method of input-checking as opposed to checking for either is up to you.

These lines are very simple. We just evaluate to see if the left arrow key is being pressed (greater than 0); if so, then we move -0.1 Blender units to the left. Otherwise, if the right arrow key is pressed, then we move 0.1 Blender units to the right. The keyboard events dictionary works very simply - if the key binding's value is 0, that means that the key isn't being pressed. 1 means that the key has just been pressed, 2 means that the key is being held down, and 3 means that the key is being released. The actual correct way of doing the lines above would be if kbleft == 1 or kbleft == 2 and elif kbright == 1 or kbright == 2, but I'm not trying to split hairs.
motion.dLoc = [mx, my, 0.0]

cont.activate(motion)
The first lines are referencing our motion actuator's dLoc variable - this is a small Python list, or array, of how much to move the object. As can be seen above, we assign the mx and my movement variables to dLoc. If you follow the line of logic, you can see that if you press the left or right arrow keys, then the mx variable gets assigned a small value, which gets put into the motion actuator at the end. Because of this, the Player cube will move 0.1 Blender units to the right or left depending on which key you press. The last line makes the Python controller running this script activate the motion actuator, which finally affects the object, moving our Player cube if you press the keyboard keys. Play the game, and you'll be able to press the Left or Right arrow keys to move the cube. Great! Now, then, Get the source blend / code here for Part 2 of the Blender Game Engine with Python Tutorial.

If you notice, there's no use of the my variable - this would be used for moving the Player on the Y-Axis as well. However, I'm not going to show you how to do that - it will be an exercise.

EXERCISE: Implement Y-axis movement by using the my movement variable to make the Player move using all keyboard arrow keys.

That's all for now - if you have questions, don't hesitate to contact me either via E-Mail, on the comments section here or on any of the other posts on my blog, or right on the BlenderArtists forum - I and the others there will be glad to help you out.

32 comments:

  1. getting a syntax error when trying to do the tutorial. it says
    python module can't be imported - controller "Python#CONTR#1":
    line 14
    if kbleft > 0:
    SyntaxError: invalid syntax

    it says that the ; is causing the error

    been following your tutorial and can't seem to find any typos. Tried copying the whole script, but still says the same...

    ReplyDelete
  2. strange your tutorial has a & and then gt for the <> signs.... got it to work by changing them to the correct. all is well

    ReplyDelete
  3. Wow, that's weird. I guess I somehow mangled the script in transfer - I'll have to fix it. Thanks.

    ReplyDelete
  4. There is a typo in the script.

    it currently reads

    elif kbright < 0:

    but it should say

    elif kbright > 0:

    ReplyDelete
  5. Where are the rest of the details?

    ReplyDelete
  6. Wow, this is turning out to be a lot easier than I expected. Thanks for posting these!

    I have a small suggestion for the way you've implemented the controls. Instead of using if/elif statements, it's a lot more natural to just use if statements for all the arrows. The way it is now, the left arrow overrides the right arrow if they're both pressed, while it would make more sense for them to cancel each other out.

    When you add movement in the y direction, using elif statements means that the left and right arrows always override the up and down arrows (assuming they're listed first in the code), but if you use if statements for all four arrow keys, you can move the cube diagonally, which is very handy in 3D games.

    ReplyDelete
  7. Thanks, man. Also, thanks for the suggestions. In the later tutorials, I handle left / right and up / down keys separately, but each pair is exclusive (you can only press either left or right, and either up or down - they don't all cancel each other out). To me, it made sense for left to not allow for right to be pressed, for example, but perhaps you're right.

    ReplyDelete
  8. Okay, I've done a few more tutorials and I see that you did another if/elif for the vertical arrows, which allows diagonal movement. When I added them myself I just put on two more elifs, which was not such a good idea.

    I suppose it's a matter of personal taste, but I still prefer using an if statement for each direction, and letting opposite arrows cancel each other out instead of giving one priority over the other. With the if/elif approach, left doesn't allow right to be pressed, but right still yields to left. With the if/if approach, neither key has priority over the other. To me, it feels more intuitive, but others may disagree.

    People working through the tutorial should probably just try both methods and go with what they like best.

    ReplyDelete
  9. That's a good point. I'll add that to the tutorial.

    ReplyDelete
  10. As already mentioned, there is still a typo in the script:
    elif kbright < 0:

    but it should say
    elif kbright > 0:

    ReplyDelete
  11. @Anonymous - Thanks for telling me - I fixed the typo.

    ReplyDelete
  12. JPcoolMan (ChippyMunk)June 26, 2011 at 5:31 PM

    Hey SolarLune! I optimized the code. Now left doesn't overide right.
    from bge import logic
    from bge import events

    def Player():
    cont = logic.getCurrentController()
    obj = cont.owner

    motion = cont.actuators['Motion']

    key = logic.keyboard.events
    kbleft = key[events.LEFTARROWKEY]
    kbright = key[events.RIGHTARROWKEY]

    mx = 0.0
    my = 0.0

    if kbleft > 0 and not kbright >0:
    mx = -0.1
    elif kbright > 0 and not kbleft >0:
    mx = 0.1

    motion.dLoc = [mx, my, 0.0]
    cont.activate(motion)

    ReplyDelete
    Replies
    1. more simple:

      if kbleft > 0:
      mx -= 0.1
      if kbright > 0:
      mx += 0.1

      Delete
  13. @JP - Hey, how's it going? Good to see people working with the BGE.

    ReplyDelete
  14. JPcoolMan (ChippyMunk)June 27, 2011 at 6:35 PM

    I've been making models with blender for a long time. Just now getting into python in the BGE.

    ReplyDelete
  15. Hay, you should know, the current version of blender 2.58? doesn't keep the little console window for some reason.

    ReplyDelete
  16. hello,
    i have a error message of type:

    TypeError: vector indices must be integers, not tuple

    for the code: motion.dLoc = [mx, my, 0.0]

    =\

    ReplyDelete
  17. can u help me? =\

    ReplyDelete
  18. Could you possibly print out what mx and my are set to? They should be numbers, but it sounds like they're a list.

    ReplyDelete
  19. Hello, i have an error in the console when i trid that code. It says :
    motion = cont.actuators['Motion']
    KeyError:'requested item "Motion" does not exist'

    ReplyDelete
  20. Nevermind, i found the solution.

    ReplyDelete
  21. I'm having the problem Huun had except I haven't found the solution. Help please? (Thanks for these tutorials btw, there very good)

    ReplyDelete
  22. @EYF - That's odd... Are you having the problem in the source code, or is it on trying to do it yourself? Ensure that the motion actuator is indeed connected to the object and is named "Motion".

    ReplyDelete
  23. Don't know if the API changed but I couldn't get this to work. So I modified it using examples from the API. This is for 2.6.1

    from bge import logic
    from bge import events
    def Player(cont):
    ob = cont.owner
    motion = cont.actuators['Motion']
    keyboard = logic.keyboard
    JUST_ACTIVATED = logic.KX_INPUT_ACTIVE
    mx = 0.0
    my = 0.0
    if keyboard.events[events.LEFTARROWKEY] == JUST_ACTIVATED:
    mx = -.1
    elif keyboard.events[events.RIGHTARROWKEY] == JUST_ACTIVATED:
    mx = .1
    motion.dLoc = [mx, my, 0.0]
    cont.activate(motion)

    ReplyDelete
  24. would it be at all possible for you to update this tutorial for the current blender API?

    The main problem is just importing the modules, it is no longer bge it is bpy and there is no module named logic :\

    yea i'm screwed

    ReplyDelete
    Replies
    1. Actually, this is the current BGE API. BPY isn't made for the BGE - it's for Blender itself. Make sure that you're importing 'bge.logic' and that you're not running the script normally (i.e. with the Run Script button), but with a Python controller logic brick.

      Delete
  25. Here's a real noob question, but how do you find out how blender has named all the keys?

    ReplyDelete
    Replies
    1. You can check the BGE API and see the event constant values here: http://www.blender.org/documentation/blender_python_api_2_63_17/bge.events.html

      Delete
    2. You could also use the Python command print(dir(events)) to print the directory of the >events< module to the console and see what constants are defined within.

      Delete
  26. Hey loving the tut help so far but I'm not getting any response here.
    Here's a screen shot of what ive got:
    http://i284.photobucket.com/albums/ll34/ozaffer/PythBGEtut2prob_zpsadcc2599.jpg

    Trying to use the "if" method the quick dig movement sounded nice.
    Also obviously using the tut on my own model, trying to apply the movement to the collision model like my Logic Bricks.. Speaking of which I'm assume are not the problem because they are using a,s,w,d instead of left,down,up, and right.

    Ill check out anom's "just activated" suggestion while waiting for a response.

    Thanks again, Zak

    ReplyDelete
  27. If this is not working it is because you have to add an 'actuator' under the 'Logic Editor' using the SCA (Sensors, Controllers, Actuators 'logic bricks'. That is why I prefer the 'KX' commands which use less logic bricks hence I think cleaner.

    ReplyDelete
  28. There is a bug with Blender 2.68 that causes errors using this method. My final solution was to down load Blender 2.70 and it worked out great, line for line no work arounds needed. :) lol I spent 2 weeks on trying to figure out what I was doing wrong. from a newbe to all other newbes.

    ReplyDelete