Best method for jumping when touching the ground? (Python)

Hi, I am learning Python after a very long break from Blender, I am having a problem, how would I make my player jump when spacebar is pressed and the player is touching the ground? I can do this easily using logic bricks via rays or collision, but not so easily with Python, I would like to learn how to do this with Python.

Here is my .blend: http://www.pasteall.org/blend/34039 (0.5mb)
Here is the code being used:

import bge

def main():
    cont = bge.logic.getCurrentController()
    player = cont.owner
    keyboard = bge.logic.keyboard


    
    if bge.logic.KX_SENSOR_ACTIVE == keyboard.events[bge.events.WKEY]:
        player.applyMovement((0,.1,0), True)
    if bge.logic.KX_SENSOR_ACTIVE == keyboard.events[bge.events.SKEY]:
        player.applyMovement((0,-.1,0), True)
    if bge.logic.KX_SENSOR_ACTIVE == keyboard.events[bge.events.AKEY]:
        player.applyRotation((0,.0,.05), True)
    if bge.logic.KX_SENSOR_ACTIVE == keyboard.events[bge.events.DKEY]:
        player.applyRotation((0,0,-.05), True)
    
    if bge.logic.KX_SENSOR_JUST_ACTIVATED == keyboard.events[bge.events.SPACEKEY]:
        player.applyForce((0,0,400), True)
        
        #What I want:
        
        #if bge.logic.KX_SENSOR_JUST_ACTIVATED == keyboard.events[bge.events.SPACEKEY]
        # and touchingGround === True:
        # player.applyForce((0,0,400), True)
 
main()

So I have a player that can jump, but I want a player that can only jump when touching the ground.

Thanks for any help, also keep in mind I am very new to Python so please elaborate little with your response! Thank you.

EDIT: SOLVED

a = have a sensor pinned to the actor on the bottom

collide (map) ------and----------apply force
spacebar----------/ ---------Jump=1
if jump=0--------/

if jump min 1-max 29------------add 1 to jump

if jump =30--------and-------Jump=0
if collision (map)ā€”/

this is very nice because you can run your jump animation with the property

you can use a ray, but the problem with that is what if you are hanging over a ledge

Hi, thanks for replying but I still am unsure what python code to use. BluePrintRandom your reply is pretty confusing to be honest.

I am overwhelmed by the API and it is pretty confusing due to no examples, I would just like to know how to use the ā€˜andā€™ code in Python so I can do: spacebar and collision = jump

What I am asking for is how (in Python) do I use ā€˜andā€™.
Also what method should I use for collision (ie if collisionGround: do action).

Thanks anyone for their help.

collision sensor in object----------python

Collision=cont.sensors[ā€˜CollisionINSensorObjectā€™]

if Collision.positive and (your condition):
(tab) do stuff

Attachments

SensorJump.blend (409 KB)

1 Like

You shouldnā€™t use collision because that will allow wall jumping, or even ceiling jumps where you can stick to the roof when you jump.

use a ray, cast directly downward.
Iā€™m at work now but Iā€™ll post the code when I get home tonight.

you did not see my example, I am detecting with a sensor on the bottom :smiley:

rays donā€™t work well if you are hanging just over halfway over a ledge,


Yes, thatā€™s a good idea. I think I may use that for my on ground check.

However, once you start to get a more complex character you might want to do other checks, like if they are touching a wall, if they are on the edge of the wall, which edge, if they are on a slope, if they have a roof close overhead that would stop them from jumping etcā€¦

In that case it can be good to use a list of ray casts, rather than a lot of different objects and collision sensors.

In that case you can use:
http://bgepython.tutorialsforblender3d.com/GameObject/rayCast

or:
http://www.blender.org/api/blender_python_api_2_72_1/bge.types.KX_GameObject.html?highlight=raycast#bge.types.KX_GameObject.rayCast for more detailed info.

When getting the target location, you donā€™t need a proxy object, you can do it like this:

start = own.worldPosition.copy()
end = start.copy()

### now move the end point z axis 2 BU down
end.z -= 2.0

### cast the ray
ray = own.rayCast(end, start, 2.0, "ground", 0, 1, 0)

### if the ray hits:
if ray[0]:
    on_ground = True

@@Smoking_mirror, Iā€™m probably straying off topic from here on but how do you get the ā€œedgeā€ of cube or any surface with raycast?

Whenever Iā€™m working on ledge grabbing, Iā€™m constantly wondering if I could ever come up with some way to detect edges and grab it but short story is of course I failed and resort to use face normal(which isnā€™t that bad)ā€¦ Itā€™d be nice if you can explain how itā€™s done, thanx

Store a grab point Jack table,

local = Ray.hitPosition-Ray.hitObject.worldPosition
local = Ray.hitObject.worldOrientation.inverted()local
local
=-1

Is what I use

for grabpoints in grabpointlist:
Diff=local-grabpoint
If Diff.magnitude<.5:
Do stuff

There are several layers how to implement that requirements into Python code.

Logic brick level
The highest you already know: Logic bricks only + no Python at all
Everything can be configured at the GUI

Mixed level
The lower is a mix:
Logic bricks for detecting the conditions
Python to perform the operation


def jumping()
    if not allSensorsArePositive():
        return
    
    performJumping()

and the operation itself:


def performJumping():
    player = getPlayer() # e.g. the owner of the controller
    player.applyForce((0,0,400), True)   

I recommend this sort of processing if you need to perform operations that are hard (or not possible) with logic bricks.
It is a pretty efficient solution as the code runs only when necessary (triggered by sensors). There can be small triggers that do not result in performing the operation. But it is still better then running at every frame.
It is pretty easy to exchange the sensors with something else dependent on the situation.

Pure Python Level
Means you perform both: detecting the events and performing the operation in Python.
I strongly advice to separate these two tasks.


def jumping():
   if not shouldJump():
      return

   performJumping()

obviously you need some sort of implementation:


def shouldJump():
   return isJumpKeyPressed() and isTouchingGround()

def isJumpKeyPressed():
   ... your keyboard event checking

def isTouchingGround():
   ... fire a ray to ground ... [you could even check a collision sensor ;)]

The operation is described at the snippet above. No difference on that.

I recommend this method on (see Mixed Level) and:

  • There is no sensor to detect your events
  • There is a dynamic nature that canā€™t be fulfilled by a static number of sensors
  • The checks and operations are somehow mixed (usually but not always an indicator for bad design)

This method requires to run the controller (nearly) all the time. So you better perform cheap detection first (e.g. keyboard) and heavy detection later (e.g. ray). This way you can skip the heavy part if the cheap part shows there is nothing to do anyway.

This is the lowest level you can work on (game level). If you want to go deeper (engine level) you need to modify the BGE code ;).

The code in Mixed Level and Pure Python Level is not that different. In both situations you delegate tasks to the BGE. The difference is where to place certain configuration (e.g. how to identify ground, what key is the ā€œjumping keyā€).

Other operations
Btw.
I would not mix jumping with walking. Usually you do not walk in air.

If you think about strafe, double jump, direction change - this is a common game concept (which does not fit into real life). Typically this kind of motion is different while jumping means you might change direction faster when walking than jumping. Therefore it makes sense to deal with it differently.

For side scrolling games I used two or three parallel (or divergent) rays. Hereā€™s an example, but be aware it only really works for 2D games, 3d is a different situation.


Case A: All rays are positive. You are on the ground. In this state, you can jump.
Case B: you are near the edge. This is a useful state for dealing with AI. If the AI character enters this situation they are in danger of walking off the edge. They should turn around and walk the other way.
Case C: You have jumped or fallen but have been caught on the edge. This sometimes happens, and in this case you need to add a little force to push the player over the edge, so they donā€™t get stuck. Alternatively, you could say that this state is the same as on ground and allow walking and jumping as normal (so the player can walk up on to the edge or down off the edge as they like).
Case D: Your character is falling. In this state you shouldnā€™t be able to turn, walk or jump (unless you are Mario).

In case C, you can allow the player to pull themselves up, or vault up on to the platform.

If you want to grab the edge of a wall while falling youā€™d need extra rays cast from the front of the player, but you only need to cast those rays when not on ground, saving some processing time.

BPRā€™s method with the small collision box would be fine for most of those situations. But Case B & C would require at least 2 collision boxes.

For a 3d game you need more collision boxes or rays. I think boxes would be better for 3d, but the benefit of rays over collision box sensors are that they are easier to turn off when you donā€™t need to check them. (although Iā€™ve heard that collision sensors just get data from the physics engine which already exists, so thereā€™s not much overhead).

Thanks everyone for the help and examples. especially Monster who went above and beyond, that was very clear, thanks guys!

[SOLVED]

my code is similar to this , (traction is just one addiction to give some some control also in air)
(lack the part annoyng for limits the speed )






import bge
import imput
from mathutils import Vector


VEC_Z = Vector((0,0,1))


def main():
    own = bge.logic.getCurrentController().owner
    
    w, s, a, d, space, leftshift = imput.keyboard.keys("w", "s", "a", "d", "space", "leftshift")
    on_ground = bool(own.rayCast(own.worldPosition -VEC_Z*1.1, own)[0])
    
    
    
    if on_ground:
        #applyFrictions()
        traction = 1.0
    else:
        traction = 0.05
        
        
    z = 0
    if a:
        z = 0.1
    if d:
        z = -0.1
    if z:
        own.applyRotation((0,0,z*traction),1)
    
    x, y, z = 0,0,0
    if w:
        y = 2
        if leftshift:
            y = 4
    if s:
        y = -2
    if space == 1:
        z = 5
    if x or y or z:
        force = own.mass * Vector((x,y,z))*traction
        own.applyForce(force,1)






main()





@@Smoking_mirror, I never wouldā€™ve thought of using multiple rays to find edge like that, thanx

Hey MarcoIT, is input a standard python module (I couldnā€™t find it in the API), or is it one of your modules?

is a my module that i had in the folder of blender.
here is the code (or at least the part matter, otherwise is a bit long)


import bge


bge_logic_keyboard = bge.logic.keyboard
bge_logic_mouse = bge.logic.mouse


ACTIVES_STATUS = {1, 2}


def _create_map(dictionary, postfix):
    numbers = {k for k in dictionary}
    strings = {}
    for att in dir(bge.events):
        numb = getattr(bge.events, att)
        if numb in numbers:
            # WKEY, W, wkey, w
            if att.endswith(postfix) and len(att) &gt; len(postfix):
                strings[att] = numb
                strings[att[:-len(postfix)]] = numb
                strings[att.lower()] = numb
                strings[att.lower()[:-len(postfix)]] = numb
    return strings


KEYBOARD_MAP = _create_map(bge.logic.keyboard.events, "KEY")
MOUSE_MAP = _create_map(bge.logic.mouse.events, "MOUSE")




class Keyboard:
    def keys(self, *strings):
        d = bge_logic_keyboard.events
        return [d[KEYBOARD_MAP[s]] for s in strings]

keyboard = Keyboard()