Search Results

Monday, February 28, 2011

Python in the Blender Game Engine - Part 5.5

Here's part 5.5 (part 2 of part 5) for making Maze Crawl the game using Python in the BGE. Hey, so we left off looking at the source code for the Player.
So, here's the source code for the Player's function.

---- Bullet Physics Player Function ----

from bge import logic
from bge import events

def Sign(value):
    if value > 0:
        return 1
    elif value < 0:
        return -1
    else:
        return 0
       
def AxisCheck(mx, my, size = 1.0):

    cont = logic.getCurrentController()
    obj = cont.owner

    pos = obj.position                                    # Get the Player's current position
    topos = pos.copy()                                    # Make a copy of the Player's current position
    topos.x += (Sign(mx) * size) + mx                    # Offset the copy by the movement value for the X-axis
    topos.y += (Sign(my) * size) + my                    # And offset the copy by the movement value for the Y-axis
   
    return obj.rayCast(topos, pos, 0, 'wall', 1, 1)        # We may have just collided with something on the line specified
   
def Player():
    cont = logic.getCurrentController()
    obj = cont.owner
   
    motion = cont.actuators['Motion']
   
    key = logic.keyboard.events
    kbleft = key[events.LEFTARROWKEY]
    kbright = key[events.RIGHTARROWKEY]
    kbup = key[events.UPARROWKEY]
    kbdown = key[events.DOWNARROWKEY]

    def Init():
        if not 'init' in obj:
            obj['init'] = 1
   
    def Update():
   
        movespd = 10.0
        mx = 0.0
        my = 0.0
       
        if kbleft > 0:
            mx = -movespd
        elif kbright > 0:
            mx = movespd
       
        if kbup > 0:
            my = movespd
        elif kbdown > 0:
            my = -movespd
          
        motion.linV = [mx, my, 0.0]
        cont.activate(motion)
           
    Init()
    Update()
   

As you can see, we don't use the AxisCheck functions anymore (they're commented out in the source code file) because the Bullet physics engine will handle collision. Bullet also handles the acceleration and movement functions - Bullet will make the Player gradually move up to top speed and control his movement angles. This is the upside of the Bullet engine - we don't have to worry about these things ourselves.

The downside is that we can't handle things like gravity locally - the physics engine will do it for us. We could get around it by using a motion actuator setting force equal to gravity (9.8 default) upwards to negate gravity's effect and then make any changes from there, but it's still a bit easier to just do this ourself. Let's take a look next at the custom Player function without Bullet physics enabled.

---- Custom Physics Player Function ----

from bge import logic
from bge import events

def Sign(value):
    if value > 0:
        return 1
    elif value < 0:
        return -1
    else:
        return 0
   
def Clamp(value, min, max):
    if value > max:
        return max
    elif value < min:
        return min
    else:
        return value
   
def AxisCheck(mx, my, size = 1.0):

    cont = logic.getCurrentController()
    obj = cont.owner

    pos = obj.position                                    # Get the Player's current position
    topos = pos.copy()                                    # Make a copy of the Player's current position
    topos.x += (Sign(mx) * size) + mx                    # Offset the copy by the movement value for the X-axis
    topos.y += (Sign(my) * size) + my                    # And offset the copy by the movement value for the Y-axis
   
    return obj.rayCast(topos, pos, 0, 'wall', 1, 1)        # We may have just collided with something on the line specified
   
def Player():
    cont = logic.getCurrentController()
    obj = cont.owner
   
    motion = cont.actuators['Motion']
   
    key = logic.keyboard.events
    kbleft = key[events.LEFTARROWKEY]
    kbright = key[events.RIGHTARROWKEY]
    kbup = key[events.UPARROWKEY]
    kbdown = key[events.DOWNARROWKEY]

    def Init():
        if not 'init' in obj:
            obj['init'] = 1
           
            obj['mx'] = 0.0            # Movement values
            obj['my'] = 0.0
            obj['accel'] = 0.05        # Acceleration rate
            obj['maxspd'] = 0.2        # Maximum speed
            obj['friction'] = 0.7    # Friction
   
    def Update():
   
        if kbleft > 0:
            obj['mx'] -= obj['accel']
        elif kbright > 0:
            obj['mx'] += obj['accel']
        else:
            obj['mx'] *= obj['friction']
       
        if kbup > 0:
            obj['my'] += obj['accel']
        elif kbdown > 0:
            obj['my'] -= obj['accel']
        else:
            obj['my'] *= obj['friction']
           
        obj['mx'] = Clamp(obj['mx'], -obj['maxspd'], obj['maxspd'])
        obj['my'] = Clamp(obj['my'], -obj['maxspd'], obj['maxspd'])
       
        if AxisCheck(obj['mx'], 0, .5)[0] != None:    # We just collided with something on the X-axis
            obj['mx'] = 0    # So stop movement on the X-axis
           
        if AxisCheck(0, obj['my'], .5)[0] != None:    # We just collided with something on the Y-axis
            obj['my'] = 0  # So stop movement on the Y-axis
       
        motion.dLoc = [obj['mx'], obj['my'], 0.0]
        cont.activate(motion)
           
    Init()
    Update()


In this source, there's a bit of difference. We now use an object variable (mx and my) to store the movement variable so that the Player can move gradually by adding acceleration to those variables. When we don't press any keys, the speed slows down by basically multiplying the speed by a friction amount. We created a new function, Clamp, which will do just that - clamp the input variable to the specified minimum and maximum values. We clamp the movement speed values to ensure that it doesn't go over the new variable maxspd.  Then, we move the object by the new movement values.

So, that's the basic difference between the Bullet Physics Player function and the Custom Physics Player Function. You'll have to decide which to use - I'd personally lean towards the Python physics method unless you want to have advanced physics-enabled objects like ropes or chains.

Anyway, if you have input, please don't hesitate to tell me here or on the BlenderArtists forum (on the post related to this blog or by personal messaging me). Anyway, here's the source code for Maze Crawl Part 5 - Physics (Bullet) and (Custom Python). As always, have fun!

4 comments:

  1. So here's a case where your if/elif control scheme has a clear advantage over my if/if approach. When you add the friction variable, you can just use an if/elif/else statement to code it up. My code is less elegant (since you end up with six if statements in a row!!!):

    if kbleft > 0:
    obj['mx'] -= obj['accel']
    if kbright > 0:
    obj['mx'] += obj['accel']
    if (kbleft, kbright) == (0, 0):
    obj['mx'] *= obj['friction']

    In the last if statement, it's important that the tuple on each side of the '==' is in parentheses.

    For keyboard controls, I personally still like this approach, but for a joystick or gamepad, pressing two buttons at once is impossible, so I would definitely go with your code.

    ReplyDelete
  2. Ah, I see. That's a good point. With 'elif', friction's easier to implement.

    ReplyDelete
  3. Something else I noticed from playing around with this -- if you set friction to 0.99, you've got a nice little simulated hockey rink!

    ReplyDelete
  4. Just wanted to let you know that the freezing stuff when moving the character disapeared since we use Servo Control now.... i think it had to do with the script loop or something.

    ReplyDelete