12
\$\begingroup\$

I'm a beginner programmer and I'm creating a project which allows me to walk around rooms via a first person camera. So far, I have the buildings drawn and on, but now I'm stuck and don't know how to make a first-person camera to allow me to walk around.

Could anyone point me towards a camera class I could use, or some useful code?

\$\endgroup\$
1

3 Answers 3

7
\$\begingroup\$

Here are some tips on camera rotation (mouselook). After naiively implementing a camera class from scratch, I found I had to do a few additional tweaks for good rotational behavior:

  1. Reset mouse coordinates to center-of-screen on each frame, so that mouse never gets caught on the screen borders

  2. Maintain the camera's "up" vector (disallow roll) and recompute the "sideways" vector

  3. Disallow looking up past the vertical +y axis, or down past the -y axis (too far up/down)

  4. Get the order of rotations correct (up/down first, then left/right)

  5. Renormalize the "up", "aim", and "sideways" vectors each frame

Hopefully you can use some of this code to your advantage:

    const int mouseDeltaX = mouseAxisX * (input.GetMouseX() - int(app.GetWidth()/2));
    const int mouseDeltaY = -mouseAxisY * (input.GetMouseY() - int(app.GetHeight()/2));  // mouse y-offsets are upside-down!

    // HACK:  reset the cursor pos.:
    app.SetCursorPosition(app.GetWidth()/2, app.GetHeight()/2);

    float lookRightRads = mouseDeltaX * CAMERA_ANGULAR_SPEED_DEG * DEG_TO_RAD;
    float lookUpRads    = mouseDeltaY * CAMERA_ANGULAR_SPEED_DEG * DEG_TO_RAD;

    // Limit the aim vector in such a way that the 'up' vector never drops below the horizon:
    static const float zenithMinDeclination = DEG_TO_RAD * MIN_UPWARDS_TILT_DEG;
    static const float zenithMaxDeclination = DEG_TO_RAD * (180.0f - MIN_UPWARDS_TILT_DEG);

    const float currentDeclination = std::acosf(camera.aim_.y_);  ///< declination from vertical y-axis
    const float requestedDeclination = currentDeclination - lookUpRads;

    // Clamp the up/down rotation to at most the min/max zenith:
    if(requestedDeclination < zenithMinDeclination)
        lookUpRads = currentDeclination - zenithMinDeclination;
    else if(requestedDeclination > zenithMaxDeclination)
        lookUpRads = currentDeclination - zenithMaxDeclination;

    // Rotate both the "aim" vector and the "up" vector ccw by 
    // lookUpRads radians within their common plane -- which should 
    // also contain the y-axis:  (i.e. no diagonal tilt allowed!)
    camera.aim_.rotateAboutAxis(camera.right_, lookUpRads);
    camera.up_.rotateAboutAxis(camera.right_, lookUpRads);
    ASSERT_ORTHONORMAL(camera.aim_, camera.up_, camera.right_);

    // Rotate both the "aim" and the "up" vector ccw about the vertical y-axis:
    // (again, this keeps the y-axis in their common plane, and disallows diagonal tilt)
    camera.aim_.rotateAboutAxis(Y_AXIS, -lookRightRads);
    camera.up_.rotateAboutAxis(Y_AXIS, -lookRightRads);
    camera.updateRightAxis();

Note that:

mouseAxisX and mouseAxisY are defined to be +/-1, depending on whether you want the x or y axis mouselook inverted. Usually games offer this option at least for the vertical axis.

MIN_UPWARDS_TILT_DEG is defined to be 1.0 degrees (so the viewer is allowed to look from -89 deg downwards to +89 degrees upwards, which looks quite convincingly like a full 180 degree vertical range -- the missing 2 degrees at the extremes are quite negligible).

camera.aim_, camera.right_, and camera.up_ are of course 3D vectors, and the rotateAboutAxis() method you can cobble together from wikipedia and any number of online sources. Y_AXIS is a fixed constant (0,1,0) vector.

ASSERT_ORTHONORMAL() is a debug-mode-only sanity check, which never gets compiled in optimized/release mode.

Apologies in advance for the C-style code... then again, here you are taking advice from a guy named Mediocritus! ;^)

\$\endgroup\$
1
  • \$\begingroup\$ What are you talking about? C-style code is the cornerstone of hacker spirit! 8-) \$\endgroup\$
    – blissfreak
    Commented Nov 3, 2013 at 4:37
5
\$\begingroup\$

There are several ways of going about this (see tutorials here, here and here for example, with plenty more available on the Internet via Google). The technology used in the resources you may find online may vary slightly (in terms of D3D, XNA, OpenGL, et cetera), but the underlying principles are going to be the same:

  • your camera object maintains its position and its direction, and optionally another pair of vectors that, alongside the direction, form an orthogonal basis for the camera's coordinate system.
  • your camera's public API exposes methods for yawing, pitching, and optionally rolling the camera around it's basis vectors -- the act of adjusting the camera orientation will update the basis vectors for subsequent use.

You can elect to store the vectors directly, or recompute the underlying view matrix every time, as you need and prefer. There's a lot of flexibility to the technique, so if you need help beyond those general steps you may want to post a new question with a more specific query.

\$\endgroup\$
2
\$\begingroup\$

For a specific reference to an implementation of camera system using Direct3D, I recommend this article: http://www.toymaker.info/Games/html/camera.html. The author also describes other methods and features that may be used to expand on the implementation.

\$\endgroup\$

You must log in to answer this question.

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