0
\$\begingroup\$

I have a camera that follows the position and direction of the player. These are updated using spin and velocity. The velocity is updated like so:

auto velocity = get_velocity();
velocity += get_acceleration().x * get_right();
velocity += get_acceleration().y * get_up();
velocity -= get_acceleration().z * get_forward();

set_velocity(velocity);

... where acceleration is relative to the object, not the world. This is the first time I have used OpenGL and I am not sure if this is the best solution, but it does what I want. I was wondering if there is a better way to do this using OpenGL Mathematics (GLM).

Additionally, this method works like a rocket, which is good for what I am trying to do, but if I want something more like a human, I have to change it to:

auto velocity = get_velocity();
auto const y = velocity.y + get_acceleration().y;
velocity += get_acceleration().x * get_right();
velocity -= get_acceleration().z * get_forward();
velocity.y = y;

set_velocity(velocity);

... to prevent the player from being able to fly by looking up. Is there a way of simplifying this case, too?

Acceleration is defined as:

switch (dir) {
    case left:  return glm::vec3(-1,  0,  0);
    case right: return glm::vec3( 1,  0,  0);
    case front: return glm::vec3( 0,  0, -1);
    case back:  return glm::vec3( 0,  0,  1);
    case up:    return glm::vec3( 0,  1,  0);
    case down:  return glm::vec3( 0, -1,  0);
    default: assert(false);
}
\$\endgroup\$
0
\$\begingroup\$

3D velocity and acceleration can be represented as 3D vectors. And you should use that instead of handling each case separately. As we know, position vector changes by velocity vector times some delta time. So does the Velocity by acceleration. AKA dv = a*dt; dPos = dv * dt. What you want to do would be

// assume [wasd] is your controller, and [wasd]Pressed() return 1 if key is pressed and 0 otherwise
Vec3 forward = Camera.forward()
Vec3 right = Camera.right();

acceleration = (forward - forward.y) * (wPressed()-sPressed()) + Camera.right() * (dPressed - aPressed()); // here we build direction of acceleration
velocity += normalize(acceleration)*accel *dt;
position += velocity * dt;

You have to take into account that constructing velocity/accelration from separate axes will give faster movement/acceleration while moving diagonally, so you always want to normalize the vector and multiply by magnitude. This solution also takes into consideration situations when multiple keys are pressed at once (left+right would result in no sideways movement).

This solution assumes that gravity and ground is down (0,-1,0). Otherwise you have to simply subtract from the forward vector the vector that is projected onto the normal to the ground.

But this sounds like you lack the physics engine that manages gravity and collisions... ?

@EDIT by question's author

Anyways, this form can be rewritten to match your second example as follows:

auto const f = get_facing();
auto const a = get_acceleration();
auto const abs_a = a.x * get_right()
                 + a.y * get_up()
                 - a.z * glm::vec3(f.x, 0.0f, f.z);
add_velocity(abs_a);

^ That's not true. First of all - you messed up y and z components. Second - what do get_acceleration/facing/right/up return? Naming of those functions is obscure and I don't understand what they return. You have to name your functions so that they names describe best their functionality, and don't be afraid of longer names like getCameraRight() or getAccelerationFromInput().

Also don't be afraid of commenting your code. If you have forward-forwad.y you, of course CAN replace it by glm::vec3(forward.x, 0, forward.y), but... it's longer... and harder to modify in the future... Just put a comment in code that //zero UP acceleration to disable flying

If your UP vector is not (0,1,0) then you have to reinvent the logic. forward - proj(forward, UP) would be enough for my solution. I insist that you try my solution, because it is extendable if you want to, for example, have different speeds for walking forward, backward or strafing. You would just do

acceleration = normalize((forward - forward.y)) * (wPressed()*wSpeed()-sPressed()*sSpeed()) + normalize(Camera.right()) * (dPressed*dSpeed() - aPressed()*aSpeed()); // here we build direction of acceleration
velocity += acceleration *dt;
position += velocity * dt;

Don't forget about normalizing vectors to have uniform behavior when looking at horizon and looking up.

\$\endgroup\$
3
  • \$\begingroup\$ Thanks for pointing out diagonally movement, completely forgot. You are correct about the lack of gravity and collision. The camera is supposed to noclip and everything is floating in the void. The main take away from this answer is forward - forward.y. I'll implement it later tonight and let you know how it goes. \$\endgroup\$ – user1032677 Mar 6 '20 at 21:56
  • \$\begingroup\$ forward - forward.y can also be achieved by fixing the y-axis to zero, which may be more clear: glm::vec3(f.x, 0.0f, f.z) \$\endgroup\$ – user1032677 Mar 8 '20 at 3:09
  • \$\begingroup\$ In future, I'd recommend posting your own answer, @user1032677, rather than modifying another user's answer. It's totally OK to answer your own question, and you can give credit to the users who helped you arrive at the solution that worked best for you. \$\endgroup\$ – DMGregory Mar 9 '20 at 1:31

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.