2
\$\begingroup\$

In my game I implement movement in the Player class like so:

// called from Player's update() method, which is called from the gameloop.
void move(){
    velocity.x += acceleration.x;
    velocity.y += acceleration.y;
    position.x += velocity.x;
    position.y += velocity.y;
}

Keyboard input affects the acceleration directly, for example:

// in Player, called from main gameloop.
void update(){
    if(playerInput.rightPressed()) (acceleration.x += 2);
    // .. etc
    move();
}

I think so far this is standard (if not, please say so).

Now my question:

During movement invoked by player input, I want the acceleration to be constant. So when the player presses the right arrow key, the acceleration.x will always be 2 - and not 2, and then 4, and then 6, etc.

I could easily acheive this be having if(playerInput.rightPressed())(acceleration.x = 2), but then other forces acting on the entity will be ignored.

What is the standard way to implement this?

\$\endgroup\$
3
\$\begingroup\$

Generalising on concept3d's answer, you could make it possible to add and revoke arbitrary forces.

This is all LiveScript-flavoured pseudocode:

class Player
    -> # constructor
        this.forces = HashMap()
        this.mass = 1
    move : ->
        this.acceleration = sum(this.forces.values) / this.mass
        this.velocity += this.acceleration
        this.position += this.velocity
    add-force : (force) ->
        new-id = generate-some-random-string()
        this.forces.set(new-id, f)
        return new-id
    revoke-force : (force-id) ->
        if force-id == null { return; }
        this.forces.delete(force-id)

Same in English: Store each force separately, each by some generated ID. Return that ID as the force is added. Allow forces to be removed based on ID. Resolve forces to a total acceleration on game step.

Having instantiated a player, you could then add gravity like this:

gravity-force-id = player.add-force({ x : 0, y : 1 })

And similarly, a movement-input-force:

input-force-id = null

if player-input.right-pressed()
    input-force-id = player.add-force({ x : 2, y : 0 })
else
    player.revoke-force(input-force-id)

…or any other force at all.

\$\endgroup\$
  • \$\begingroup\$ I like this approach :) A question: I understand that when a key is pressed, I add the appropriate force to the list (actually only add it once, but add-force could take care of this internally). And when the key isn't pressed, I need to remove the force. But after removing the acceleration force, the velocity will still be high. I need to lower it back to 0. So when a key is released, I can revoke the force, and add a new decceleration force. The question: how can I make the decceleration force disappear when there's no need to deccelerate anymore (aka we're at 0 velocity)? \$\endgroup\$ – Aviv Cohn Jun 19 '14 at 13:18
  • \$\begingroup\$ @Prog In your game step, you could apply a force opposite to the player's movement direction if the button isn't pressed and their velocity is still too high. (You can revoke it once the velocity is low enough or a button gets pressed, just as above.) \$\endgroup\$ – Anko Jun 19 '14 at 15:32
  • \$\begingroup\$ Thanks, I did that and it works fine. Another question: could it be a good idea to empty the forces map in the end of each update step? This way I don't need to revoke forces. Each update step, if a force is needed it is applied, and if it isn't needed anymore, no need to revoke it. It's removed automatically. E.g with steering behaviors, where as long as the agent wants to Seek, a new appropriate force is applied each update step while the last is deleted, and when the agent doesn't want to Seek anymore, it doesn't need to do anything, only to not Seek. What is your opinion? \$\endgroup\$ – Aviv Cohn Jun 20 '14 at 3:36
  • \$\begingroup\$ @Prog That way, if you have a lot of forces acting on many objects (hundreds of each?), I'd expect low performance since you'd need to keep adding each one again every frame. However, if you usually have only few forces acting at a time, the extra processing is negligible and it's a good simplification. It's always better to conserve programmer time over machine time and that definitely makes this easier to think about. (I agree; implement that at first and only make it more complex if you have performance issues.) \$\endgroup\$ – Anko Jun 20 '14 at 8:25
1
\$\begingroup\$

but then other forces acting on the entity will be ignored.

You can take other forces into consideration. Use different acceleration variable for each force and sum them. For example:

if(playerInput.rightPressed())(inputAcceleration.x = 2)

vec3 playerTotalAcceleration;

void move(){
    //Update acceleration
    playerTotalAcceleration  = vec3(0,0,0)
    foreach acceleration
       playerTotalAcceleration += acceleration[i] + inputAccelaration

    velocity.x += playerTotalAcceleration.x;
    velocity.y += playerTotalAcceleration.y;
    position.x += velocity.x;
    position.y += velocity.y;
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.