[SOLVED]Moving camera diagonally and smoothly

I’m using a DirectObject class for moving camera by keyboard:

class Controller(DirectObject.DirectObject):
    def __init__(self, camera):
        self.camera = camera
        # Arrow keys
        self.accept("arrow_up", self.move, ["foreward"])
        self.accept("arrow_up-repeat", self.move, ["foreward"])
        self.accept("arrow_down", self.move, ["backward"])
        self.accept("arrow_down-repeat", self.move, ["backward"])
        self.accept("arrow_left", self.move, ["left"])
        self.accept("arrow_left-repeat", self.move, ["left"])
        self.accept("arrow_right", self.move, ["right"])
        self.accept("arrow_right-repeat", self.move, ["right"])

    def move(self, direction):
        print("moving", direction, self.camera.getPos())
        if direction == "foreward":
            self.camera.setPos(self.camera, 0, 0.5, 0)
        elif direction == "backward":
            self.camera.setPos(self.camera, 0, -0.5, 0)
        elif direction == "left":
            self.camera.setPos(self.camera, -0.5, 0, 0)
        elif direction == "right":
            self.camera.setPos(self.camera, 0.5, 0, 0)

This code work. but i want to know how:
1- Can i have a smooth movement not stepped like above?
2- Can i have diagonal move?
like when i keep down up and right arrow keys at the same time. in above approach only one key can process at a time.


What you’re doing is changing the position only when someone fires an event. This is the wrong approach. Instead, you should create a task (a method that runs every frame). In this method, you should check whether each button is currently pressed, and if so, add something to the position.

I did a quick test and it seems i can get two key at the same time:

def key_task(self, task):
    is_down = self.mouseWatcherNode.is_button_down
    if is_down(KeyboardButton.up()):
        print("up key is down.")
    if is_down(KeyboardButton.right()):
        print("right key is down.")

    return Task.cont

But this means i have to use Polling interface, right?

Yes, that’s the best way to do it. Just do something like this in your task:

SPEED = 1 #constant
velocity = LVector3(0)

if is_down(KeyboardButton.up()):
    velocity.y += SPEED
if is_down(KeyboardButton.down()):
    velocity.y -= SPEED
if is_down(KeyboardButton.left()):
    velocity.x -= SPEED
if is_down(KeyboardButton.right()):
    velocity.x += SPEED

dt = globalClock.get_dt()
camera.set_pos(camera, velocity * dt)

(Assuming you’re sticking to the snake-case style methods, otherwise replace set_pos and set_dt with setPos and setDt, of course.)

Yeah i like snakes!!

Two minor additions to the above:

  1. You might want to include dt in the updating of velocity that you do within the “if”-statements–something like this (using the first statement as an example):
if is_down(KeyboardButton.up()):
    velocity.y += ACCELERATION*dt
  1. Depending on your intentions, it might be a good idea to enforce a maximum speed; without doing so, the player could potentially end up moving unplayably fast, and perhaps even encounter physics on some machines issues due to the player “skipping past” obstacles.

In the code above, I would think that this should be inserted between the code that updates the velocity value (in the “if”-statements) and that which updates the player’s position, so that it operates on the most recent velocity value, but takes effect before the position is updated. It might look something like this:

speed = velocity.length()

#  MAX_SPEED would presumably be a float value defined elsewhere,
# and adjusted to your liking.
if speed > MAX_SPEED:
    velocity *= MAX_SPEED

The code I gave shows how to move with constant speed when the arrows are pressed. Your suggestion is incorrect unless you store the velocity and use the arrow keys to accelerate, which is of course valid as a different type of movement system, eg. for controlling a spacecraft, in which case it does indeed make sense to limit the speed. In my example the speed is constant, so setting a maximum speed is pointless.

You’re right about normalizing the direction to avoid diagonal movement being at 1.414x speed, though, so the code would look like:

def movetask(task):
    SPEED = 1 #constant
    direction = LVector3(0)

    if is_down(KeyboardButton.up()):
        direction.y += 1
    if is_down(KeyboardButton.down()):
        direction.y -= 1
    if is_down(KeyboardButton.left()):
        direction.x -= 1
    if is_down(KeyboardButton.right()):
        direction.x += 1

    if velocity.normalize():
        dt = globalClock.get_dt()
        camera.set_pos(camera, velocity * SPEED * dt)

    return Task.cont

Or, with fewer lines of code:

def movetask(task):
    SPEED = 1 #constant
    direction = LVector3(
        is_down(KeyboardButton.right()) - is_down(KeyboardButton.left()),
        is_down(KeyboardButton.up()) - is_down(KeyboardButton.down()),

    if velocity.normalize():
        dt = globalClock.get_dt()
        camera.set_pos(camera, velocity * SPEED * dt)

    return Task.cont

Ah, right–I missed that the velocity was zeroed just before the keys were polled; I was reading the addition of the “SPEED” value as an acceleration. My mistake!


I am a little confused about the last code posted. Is the direction vector, rather than the velocity vector, the one that is suppose to be in the camera.set_pos? Otherwise it looks like the direction vector is not used, and I’m not sure when/how the velocity vector is set. Or am I missing something about how LVector3 works?