1
\$\begingroup\$

This is the code I am currently using, and it works great, except for the strafe always causes the camera to move along the X axis which is not relative to the direction in which the camera is actually facing. As you can see currently only the x location is updated: [delta * -1, 0, 0]

How should I take into account the direction in which the camera is facing (I have the camera's target x,y,z) when creating a first person strafe (left/right) movement?

    case 'a':
        var eyeOriginal = g_eye;
        var targetOriginal = g_target;
        var viewEye = g_math.subVector(g_eye, g_target);
        var viewTarget = g_math.subVector(g_target, g_eye);
        viewEye = g_math.addVector([delta * -1, 0, 0], viewEye);
        viewTarget = g_math.addVector([delta * -1, 0, 0], viewTarget);
        g_eye = g_math.addVector(viewEye, targetOriginal);
        g_target = g_math.addVector(viewTarget, eyeOriginal);
        break;
    case 'd':
        var eyeOriginal = g_eye;
        var targetOriginal = g_target;
        var viewEye = g_math.subVector(g_eye, g_target);
        var viewTarget = g_math.subVector(g_target, g_eye);
        viewEye = g_math.addVector([delta, 0, 0], viewEye);
        viewTarget = g_math.addVector([delta, 0, 0], viewTarget);
        g_eye = g_math.addVector(viewEye, targetOriginal);
        g_target = g_math.addVector(viewTarget, eyeOriginal);
        break;
\$\endgroup\$
5
  • \$\begingroup\$ Which language and framework is this? \$\endgroup\$ Commented Jan 7, 2011 at 17:51
  • \$\begingroup\$ If your camera is in unit vectors and the Z axis is the one that goes into and out of the screen, isnt the X axis the unit direction for strafing? \$\endgroup\$
    – James
    Commented Jan 7, 2011 at 22:58
  • \$\begingroup\$ @Jonathan: The language is JavaScript and the framework is Google O3D WebGL \$\endgroup\$
    – Chris
    Commented Jan 7, 2011 at 23:57
  • \$\begingroup\$ @James: I allow the user to spin the camera around, so the Z and X axis are not always relative to the direction in which the camera is facing, therefore I must align the strafe X with the camera's direction which is what Jonathan has attempted to assist me with \$\endgroup\$
    – Chris
    Commented Jan 8, 2011 at 0:02
  • \$\begingroup\$ Cross product between the up axis and the camera's forward vector will give you one of your strafe directions. \$\endgroup\$
    – Phrogz
    Commented Jan 8, 2011 at 15:17

2 Answers 2

6
\$\begingroup\$

Code quality first and foremost

That's a lot of duplicated code you've got going on there. If you've got duplicated code, you need to do it differently - duplicated code is dangerous (you'll change one side but forget to change the other and get logic errors).

  • If you've got duplicated code down both ends of a conditional, take it out of the conditional.
  • If you still need to only execute it conditionally (e.g. when the key is a or d) wrap it all in its own conditional.
  • Consider helper methods.

Now the answer

What you need is to get the Camera's rotation matrix: the exact direction it's pointing.

If the classes you're working with can return that from the camera itself, great! If it can't, you can calculate it based on the camera position and target - see here for one way to do that.

Once you have the camera's rotation matrix, you can just set up the movement vector then rotate it to point in the camera's direction using its rotation matrix.

The advantage of this approach is you can instantiate one general movement matrix, throw in all the vertical, horizontal and forward movement for your character in that update, do all the calculations you'd like on all of it then you do one rotation to face the camera and make that movement happen all at once.

I have no idea which language or framework you're using so I'm going to be using some C# and XNA here.

var keys = Keyboard.GetState();

if (MovementKeysPressed())
{
    var eyeOriginal = g_eye;
    var targetOriginal = g_target;
    var viewEye = g_math.subVector(g_eye, g_target);
    var viewTarget = g_math.subVector(g_target, g_eye);

    // The important camera rotation stuff happens here

    // Determine camera rotation. I am assuming that:
    // g_eye is the camera's location,
    // g_target is where it's looking,
    // and they are both Vector3s
    var up = [0, g_up[1]*-1, 0];
    var cameraRotationMatrix4 = g_math.matrix4.lookAt(g_eye, g_target, up);

    // The rotation component of a 4D transformation matrix is always
    // the upper-left 3x3
    var cameraRotation = g_math.getUpper3x3(cameraRotationMatrix4);

    // Right now we're only doing *direction* of movement - working with 1s only.
    // Speed comes after rotation.
    var xMovement = 0;
    if (keys.A.Down) xMovement -= 1;
    if (keys.D.Down) xMovement += 1;
    var movementVector = [xMovement, 0, 0];
    // Note that holding both A and D is possible and gets you nowhere.
    
    // Sets the vector's length to 1. You'll understand why this is important
    // after we introduce speed.
    var movementVector = g_math.normalize(movementVector);
    
    // rotate movement to face camera. 
    var realMovementVector = g_math.rowMajor.mulVectorMatrix(movementVector, cameraRotation);

    // Now for speed.
    // We earlier normalized movementVector to give it a magnitude (length) of 1.
    // After we rotated it, it still only had magnitude of 1.
    // Any movement would always create only 1 unit of movement in any direction.
    // Let's say you want your character to move faster than that though!
    // What if you want your character to move at 4.2 units per update?
    // By multiplying it by a scalar (aka integer), we multiplied its magnitude,
    // which is now (1 x 4.2) = 4.2. It will now move 4.2 units either left or right
    // of the camera (unless the player has both 'a' and 'd' held down).
    var speed = 4.2;
    var speedThisStep = delta * speed;
    realMovementVector = g_math.mulVectorScalar(realMovementVector, speedThisStep);
    
    viewEye = g_math.addVector(realMovementVector, viewEye);
    viewTarget = g_math.addVector(realMovementVector, viewTarget);

    // End important camera stuff

    g_eye = g_math.addVector(viewEye, targetOriginal);
    g_target = g_math.addVector(viewTarget, eyeOriginal);
}

MovementKeysPressed = function() {
    // current state of the keyboard, stores for each key
    // whether it's pressed or not
    var keys = Keyboard.GetState();
    return (keys.A.State == KeyState.Down || keys.D.State == KeyState.Down);
}

Changes:

  • Complete refactor
  • Did camera-rotatey things in the code.
  • Changed the first argument in the last two occurrences of viewEye and viewTarget
\$\endgroup\$
16
  • \$\begingroup\$ Hi Jonathan, I greatly appreciate your response, thank you. I fully take on board the requirement for a re-factor, the language I am using is JavaScript which I am new to using so intensively, so I am being careful not to overwrite any variables whilst attempting to implement a solution. The camera's rotation matrix would appear to be the final piece of the puzzle, now that I have an explanation as to how the rotation matrix would be applied to the movement vector. I have taken a look at the example you provided, which fits the bill (by taking the camera position and target), \$\endgroup\$
    – Chris
    Commented Jan 8, 2011 at 0:42
  • \$\begingroup\$ unfortunately I am still having trouble wrapping my head around how I take these values to produce the rotation matrix. The example mentions using a quaternion which is new to me, are you able to help me understand how I create the quaternion from these values? The framework I am using does have a quaternion helper, maybe it will make more sense to you? (code.google.com/p/o3d/source/browse/trunk/samples_webgl/o3djs/…) it does appear to only provide functions for manipulating quaternions, but does have quaternionToRotation \$\endgroup\$
    – Chris
    Commented Jan 8, 2011 at 0:42
  • \$\begingroup\$ @Chris: After taking a look through o3d I've found a way to compute the camera's rotation matrix (I believe) using o3djs.math.matrix4.lookAt = function(eye, target, up). I've also changed almost everything that was in the pseudocode block, including determining the xMovement. Hopefully it's now all legal JavaScript! \$\endgroup\$ Commented Jan 8, 2011 at 3:46
  • \$\begingroup\$ @Chris: Quaternions turned out not to be necessary, but it would be good if you were to learn about them. They are very useful in 3D for rotations since they're far more efficient than rotation matrices. \$\endgroup\$ Commented Jan 8, 2011 at 3:46
  • \$\begingroup\$ @Chris: Do note I've left delta in the xMovement multiplication. If you are using the delta value to control how many units you move per second, you may want to take that out of the movement vector and move it to the speed variable (e.g. var speed = delta * 4.2). The movementVector is only being used for direction, only the speed variable introduces actual distance to it. Any speed information would be lost when the matrix is normalized just before rotating it. \$\endgroup\$ Commented Jan 8, 2011 at 3:55
1
\$\begingroup\$

If you have the forward vector and the vector for straight "up", you can take the cross product to get a vector pointing sideways for strafing.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .