5
\$\begingroup\$

I'm currently using the Google Cardboard SDK for Unity to build a fighter pilot simulator. What I'm currently trying to do is recenter the view(CardboardHead) smoothly over time when the user looks away from the screen. This is because I want to put HUD elements out of the main central view that can be quickly seen with a "head jerk". I don't want to let the camera be free from constraints because I feel piloting is much better when the view is fixed and doesn't wobble about with every slight movement of the player's head. The Head already has a horizontal constraint that does not allow any later movement, but now I want to put a semi-constraint on the vertical axis so that the player can quickly look up or down to see something like i.e. a fuel economy indicator and then have the view quickly "follow his gaze" and recenter itself.

I noticed that the Cardboard plugin already does this type of constraint on the Head roll input when playing "in editor" and works as follows: When you press and hold Ctrl and move the mouse left/right the view rolls left/right. Upon release of Ctrl the view smoothly rolls back to 0. This is the code in the Cardboard script:

bool rolled = false;
if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)) {
      rolled = true;
      mouseZ += Input.GetAxis("Mouse X") * 5;
      mouseZ = Mathf.Clamp(mouseZ, -80, 80);
    }
    if (!rolled && autoUntiltHead) {
      // People don't usually leave their heads tilted to one side for long.
      mouseZ = Mathf.Lerp(mouseZ, 0, Time.deltaTime / (Time.deltaTime + 0.1f));
    }
    var rot = Quaternion.Euler(mouseY, mouseX, mouseZ);
    var neck = (rot * neckOffset - neckOffset.y * Vector3.up) * NeckModelScale;
    headView = Matrix4x4.TRS(neck, rot, Vector3.one);
  }

How could I implement this for vertical rotation of the Head?

In the Update method of the script that controls the Head it simply does transform.localRotation = Cardboard.SDK.HeadRotation;

This is what I've tried but it doesn't really do anything except some jerky movements:

transform.Rotate(new Vector3(Mathf.Lerp(rot.eulerAngles.x, 0, Time.deltaTime / (Time.deltaTime + 0.1f)),0,0));

(I put this in the same Update function for the Head after the above line)

Any ideas??

\$\endgroup\$
  • \$\begingroup\$ Does the last transform.Rotate line you give actually cause the cardboard camera to be rotated? I was under the impression that only the user head motions are allowed to rotate the camera in the cardboard SDK. \$\endgroup\$ – Gazihan Alankus Jul 26 '16 at 8:27
1
\$\begingroup\$

Look at using Unity's Quaternion.RotateTowards and smooth the rotation per frame with something like the following:

    void Update()
    {
        float speed = 1f; // can adjust this to move faster or slower
        float step = speed * Time.smoothDeltaTime;
        Quaternion targetRotation = Quaternion.identity;
        transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, step);
    }
\$\endgroup\$
  • \$\begingroup\$ Don't use Euler Angles for animation. Use Quaternion.RotateTowards instead. \$\endgroup\$ – Ed Marty Nov 27 '18 at 17:29
  • \$\begingroup\$ Author was using eulerAngles in his example, so matched -- feels a little heavy handed on the downvote here :/ \$\endgroup\$ – MackelRow Nov 28 '18 at 18:11
  • 1
    \$\begingroup\$ RotateTowards is appropriate when you're rotating a vector representing a direction or offset in Cartesian space. It is not correct when your vector represents an Euler angle triplet. Even if your input and output are ultimately in Euler angles, it's almost never a good idea to try to blend or interpolate in this coordinate system. The results can be far from intuitive or natural-looking. You'll typically want to convert to another format like a quaternion, matrix, or basis vectors to do your computations, then convert the result back to an angle triplet if you need it for output. \$\endgroup\$ – DMGregory Nov 28 '18 at 18:12
  • \$\begingroup\$ Thanks @DMGregory, nice explanation. Updated example to use Quaternion.RotateTowards instead \$\endgroup\$ – MackelRow Nov 28 '18 at 18:19
  • \$\begingroup\$ You'll also likely want to use Time.deltaTime here, not Time.smoothDeltaTime. You want the amount of rotation to be proportional to the actual time elapsed since the last update, not an averaged-out duration. \$\endgroup\$ – DMGregory Nov 28 '18 at 18:44

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.