Better 8-directional movement

Hey, I am trying to get this 8-directional movement to work, but I have it wrong and would like to know a way of doing it without having to check each and every key combination (up + left, up + right, up + down, etc.).
Here’s what I have so far:


own = cont.owner


    key = logic.keyboard.events
    wkey = key[events.WKEY]
    skey = key[events.SKEY]
    akey = key[events.AKEY]
    dkey = key[events.DKEY]


    own["speed"] = 4.0


    own["x"] = (-akey + dkey) / 2
    own["y"] = (-skey + wkey) / 2


    if ((akey + wkey) or (dkey + wkey) or (akey + skey) or (dkey + skey))/2 == 2:
        own["x"] /= 2**0.5
        own["y"] /= 2**0.5


    print(own["x"], own["y"])


    own.setLinearVelocity([own["x"] * own["speed"], own["y"] * own["speed"], 0], False)

^this doesn’t work as intended.

My idea is essentially to use the int values returned by logic.keyboard.events to determine the direction of the player; then I want to check for diagonal movement and change the values of both the x and y to 2**0.5 (pythag theorem) – but in doing so without the annoying line “(akey + wkey) or (dkey + wkey) or (akey + skey) or (dkey + skey)” (it’s not fundamentally essential for it to work, I’d just like to know a simpler way).

Thanks :slight_smile:

Use a vector to add the keys to and then normalize it (which sets the magnitude to 1) or set the magnitude to the object’s movement speed. Vectors automatically handle diagonals correctly when you normalize the magnitude (or set the magnitude to a number manually).

Interesting. Haven’t ever used Vector normalization. I don’t quite understand how you mean to use it. When I enter the values, it returns None.


    own["x"] = (-akey + dkey) / 2
    own["y"] = (-skey + wkey) / 2


    vec = Vector([own["x"], own["y"], 0]).normalize()

Is this how you do it?

Vector normalization is simply dividing by its length, such that it has a length of 1.0
The normalize() attribute will normalize a vector, returning None, whilst normalized() returns a new normalized vector. It’s more efficient to normalize the existing vector seeing as you won’t need the original for any other purpose. (so assign vec, then normalize with another statement).

You do not need to divide the inputs by two either, nor store them. Just subtract the inputs for each axis, then create a 3D vector for the X and Y components (z=0.0), then normalise this and multiply it by the movement speed. This will be a “local” space movement vector. If you want to have acceleration, add this to the velocity, rather than setting the velocity, but you’ll also need to handle damping and limiting velocity with this path.

Multiply the movement vector by the object’s worldOrientation in order to get a world space velocity vector

Vector manipulation is easy with the mathutils module.



updown = up_key - down_key
rightleft = right_key - left_key

vector = Vector((rightleft, updown))
vector.normalize()

object.worldLinearVelocity.x = vector.x
object.worldLinearVelocity.y = vector.y


where object is a KX_GameObject (your player presumably) and the up_key, down_key, right_key, left_key are booleans, 1 or 0, pressed or not pressed buttons respectively.

I dunno, I just use nested if’s to check for left/right keypresses while up/down are being pressed.

Thanks, this works like a charm :slight_smile: Final code:


own = cont.owner
key = logic.keyboard.events
wkey = key[events.WKEY]
skey = key[events.SKEY]
akey = key[events.AKEY]
dkey = key[events.DKEY]

own["speed"] = 8.0

own.setLinearVelocity(Vector([-akey + dkey, -skey + wkey, 0]).normalized() * own["speed"], False)