Search Results

Monday, February 7, 2011

Using Python in the Blender Game Engine - Part 4

Hey, there. It's been a little while since the last tutorial post, but here's Part 4 in the series of game development in the Blender Game Engine using Python to make the maze-type game Maze Crawl.

There's only a little bit of changes to the code this time around in the tutorial series. Let's see what the differences are.

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 = 0.2
        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
          
        if AxisCheck(mx, 0, 1.0)[0] != None:    # We just collided with something on the X-axis
            mx = 0    # So stop movement on the X-axis

        if AxisCheck(0, my, 1.0)[0] != None:    # We just collided with something on the Y-axis
            my = 0  # So stop movement on the Y-axis
      
        motion.dLoc = [mx, my, 0.0]
        cont.activate(motion)
          
    Init()
    Update()
    

Okay, so maybe it's not just a few changes... Anyway, as you can see, there is a major difference in code between the previous part in the series and this one, but the actual amount of tasks finished by the code is the same. What we have done in the source code here is define a new function called AxisCheck that checks for a collision on the given X and Y axes and a distance value (wrongly called size in the example, LOL). This check is initiated by calling the new AxisCheck function with the X, Y, and distance arguments, as can be seen above.

You can also see that the new AxisCheck function uses another new function, the Sign function. The Sign function returns the sign of the inputted number - if the number is greater than 0, it returns 1. If the number is less than 0, it returns -1, and if the number is 0, then 0 is returned. This simply converts the motion amount into a uniform value that we use in collision checking.

That's the only difference in code for this part - there's another difference actually in the source .blend file. Before this part in the tutorial series, the Player cube was a Sensor-type collision object. Sensor-type objects are basically ghost-type Static objects, except that they can test for collisions against Static-type objects. However, Sensors can't detect collision against other Static sensors, and Static-type objects can't detect collisions for Sensors (the collision event is only generated one way). So, how do we navigate this?

Well, it's actually quite simple. We set the Player cube to be Dynamic. Since we control its movement with Python, we won't need the Bullet physics engine to compute collisions on the cube itself, so we make it Ghost-type. Also, we'll control gravity (or lack thereof in this maze-type game), so we also turn the global gravity to 0. Why do all of this?

As you can see, the Dynamic Ghost is highly versatile.
Because, as is seen in the diagram, with the Player set to a Dynamic Ghost-type object, we can check for collisions against scenery (static-type) or other game objects (other Dynamic Ghost-types). This freedom of collision-checking is something that will surely help us control our game dynamics.

Here is the source code for Part 4 of the Maze Crawl Source Code, made in the Blender Game Engine. Have fun!

Note: I'm trying out a new download host (MediaFire), so if the download goes down, E-Mail me at solarlune@gmail.com and I'll sort things out.

11 comments:

  1. Thanks for these tutorials! They are easy to follow and quite helpful. I'm currently mentoring a high school about Game Design. I don't really know what I'm doing, but I'm trying to stay one step ahead of him and these tutorials have been great for that.

    One question about the code above; I can't figure out why you need these lines of code in the Player function;

    pos = obj.position # Get the Player's current position
    topos = pos.copy() # Make a copy of the Player's current position
    topos.y += Sign(my) + my # And offset the copy by the movement value for the Y-axis

    I feel like those could be deleted now that you have the AxisCheck function. I went ahead and deleted them and collision detection seems to work fine without them, but maybe I'm breaking something further down the road. Any thoughts?

    ReplyDelete
  2. Oh, sorry. You're right - that part of code is unnecessary. I'll fix it. Thanks.

    ReplyDelete
  3. when i download your's it works, but when i change the python controller from module to script, and then load in the same scripit, it and use that in the script box it dosn't work. why does it do that

    ReplyDelete
  4. how about using pasteall.org for the .blend files! :D

    ReplyDelete
  5. @Oirod - Can't do that. There's more to the source code files than just the blend files.
    @Anonymous - It won't work straight out because it needs to run a function, not just run through the code. You'll have to delete the function definition if you want to execute the code straight out of the script file.

    ReplyDelete
  6. Hello!

    First thank you so much for this beginner's guide. I know some coding but havent been able to find something to start properly with Blender, and this tutorial is perfect.

    I followed it until this part, and i can notice on blender 2.61 a regular and small freeze in its movement, almost every second, if you keep moving the cube.
    First i tried to change framerate to see any change, and.... none.
    I also changed logic steps, but it makes things worse, and i cant figure out why it behaves like that.

    Would you have an idea?

    ReplyDelete
  7. @Sythu - It seems like it stutters like this to me, as well, sometimes. Try setting VSync to be enabled in your graphics card settings - it seems to help some. If that is not satisfactory, perhaps you should post a thread on the BlenderArtists forum - they can be quite expedient about getting questions answered.

    ReplyDelete
  8. @SolarLune : Sync to Vblank was already enabled... and it felt a bit better disabling it...
    I'll give try in blenderartist, thanks for the tip.

    ReplyDelete
  9. Hi,
    this part still blows my mind. Are you just trying to get a minute directional value (e.g. +/- x or y) to help point the ray? Thanks.

    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 of the x-axis
    topos.y += (Sign(my) * size) + my #same for the y axis
    return obj.rayCast(topos, pos, 0, 'wall', 1, 1) #collided with something on the specified axis

    ReplyDelete
  10. Hi,
    this part still blows my mind. Are you just trying to get a minute directional value (e.g. +/- x or y) to help point the ray? Thanks.

    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 of the x-axis
    topos.y += (Sign(my) * size) + my #same for the y axis
    return obj.rayCast(topos, pos, 0, 'wall', 1, 1) #collided with something on the specified axis

    ReplyDelete