3
\$\begingroup\$

A helpful fellow showed me the script for a free look camera mode that rotates with the mouse. The script is included at the bottom of the page.

I want to have a system that right clicks once to enter this camera mode and right clicks again to exit it and slerps back to the original position. I tried to do it, but it seems like when I right click once and then right click again, it works ok because it's the first time I do it. But when I right click again, it snaps back to the rotation that it last left off from in free look (the starting rotation of the slerp).

Does this have to do with the way the yaw and pitch variables are stored? I assume that after exiting free look, yaw and pitch still hold the values from the last rotation. Consequently, I tried resetting yaw and pitch, but it still has the same old problem.

On the other hand, after playing around a bit, sometimes it resets to a particular rotation no matter what. So does that mean I need to do a bit of testing and say,

if (rotation = x) 
{ 
    pitch = y;
} // figure out what y should be through trial and error testing)

or something like that?


using UnityEngine;
using System.Collections;

public class FirstPersonCam : MonoBehaviour {

    public float speedH = 2.0f;
    public float speedV = 2.0f;

    private float yaw = 0.0f;
    private float pitch = 0.0f;

    void Update () {
        yaw += speedH * Input.GetAxis("Mouse X");
        pitch -= speedV * Input.GetAxis("Mouse Y");

        transform.eulerAngles = new Vector3(pitch, yaw, 0.0f);
    }
}
\$\endgroup\$
  • \$\begingroup\$ How do you enter and exit the free look camera mode? \$\endgroup\$ – Vadim Tatarnikov Jul 31 '15 at 20:49
  • \$\begingroup\$ I want to have a system that right clicks once to enter this camera mode and right clicks again to exit it and slerp back to the original position. \$\endgroup\$ – reincarnationofstackexchange Jul 31 '15 at 21:14
1
\$\begingroup\$

Looks to me like you should set the yaw and pitch to the current rotation's yaw and pitch whenever you enter the freemode.

Should be something simple like

if( enterFreemode )
{
     pitch = transform.eulerAngles.x;
     yaw = transform.eulerAngles.y;
}

That should ensure that whenever you enter the free camera, you have the same rotation as the original camera.

\$\endgroup\$
  • \$\begingroup\$ That was a simple solution. Thank you so much. What I didn't get before is how the Vector3 transforms the rotation. So I guess Unity makes a new unit vector to represent direction each time free look is entered? \$\endgroup\$ – reincarnationofstackexchange Aug 1 '15 at 17:11
1
\$\begingroup\$

I'm assuming you are using two different cameras (as you probably should).

If so, then the script below should work for what you want.

Attach this script to the player (or any gameobject that is always active in your scene). Then be sure to assign the cameras to the script from the inspector.

When you are switching cameras, particularly from the other cam to the first-person cam, the FirstPersonCam script is already active so it is going to react to mouse movements immediately. It has to be deactivated, then slerp, then activated when the interpolation is completed. Also, when the script is deactivated, the angles need to be reset so it transitions to a clean state looking straight ahead.

SwitchCamera.cs

using UnityEngine;
using System.Collections.Generic;

public class SwitchCamera : MonoBehaviour {
    public float slerpDuration = 1.0f;
    private float slerpTime = 0.0f;

    public List<Camera> cameras;

    private bool interpolating = false;

    private Vector3 oldPosition;
    private Quaternion oldRotation;
    private Vector3 newPosition;
    private Quaternion newRotation;

    void OnEnable() {
        if ((cameras == null) || (cameras.Count < 2)) {
            Debug.LogError("At least two cameras must be added to the 'cameras' list.");
        }
    }

    void Update() {
        if (!interpolating && Input.GetMouseButtonUp(1)) {
            Camera oldCamera = Camera.main;
            Camera newCamera;

            int next = cameras.IndexOf(oldCamera);
            if (++next >= cameras.Count) { next = 0; }
            newCamera = cameras[next];

            oldCamera.enabled = false;
            newCamera.enabled = true;

            EnableFirstPersonCam(false);

            oldPosition = oldCamera.transform.position;
            oldRotation = oldCamera.transform.rotation;

            newPosition = newCamera.transform.position;
            newRotation = newCamera.transform.rotation;

            newCamera.transform.position = oldCamera.transform.position;
            newCamera.transform.rotation = oldCamera.transform.rotation;

            interpolating = true;
            slerpTime = 0.0f;

        } else if (interpolating) {
            if (slerpTime < slerpDuration) {
                slerpTime += Time.deltaTime;

                Camera.main.transform.position = Vector3.Slerp(oldPosition, newPosition, slerpTime);
                Camera.main.transform.rotation = Quaternion.Slerp(oldRotation, newRotation, slerpTime);

            } else {
                interpolating = false;
                EnableFirstPersonCam(true);
            }
        }
    }

    private void EnableFirstPersonCam(bool enabled) {
        FirstPersonCam fpc = Camera.main.GetComponent<FirstPersonCam>();
        if (fpc != null) { fpc.enabled = enabled; }
    }
}

I also had to add an OnDisable event to the FirstPersonCam script. (I changed the code a bit, but other than the OnDisable() and the addition of support for Roll), it is the same. Roll is not actually used, since the roll speed is 0.0f;

FirstPersonCam.cs

using UnityEngine;
using System.Collections;

public class FirstPersonCam : MonoBehaviour {

    public Vector3 defaultAngles = new Vector3(0.0f, 0.0f, 0.0f); // pitch, yaw, roll in degrees (euler)
    public Vector3 rotationSpeed = new Vector3(-2.0f, +2.0f, +0.0f); // pitch, yaw, roll (degrees per update)

    private Vector3 currentAngles;

    void OnDisable() {
        currentAngles = defaultAngles;
        transform.eulerAngles = currentAngles;
    }

    // Update is called once per frame
    void Update () {
        currentAngles.x += rotationSpeed.x * Input.GetAxis("Mouse Y"); // pitch
        currentAngles.y += rotationSpeed.y * Input.GetAxis("Mouse X"); // yaw
        currentAngles.z += rotationSpeed.z * Input.GetAxis("Mouse ScrollWheel"); // roll

        transform.eulerAngles = currentAngles;
    }

}

PS. The SwitchCamera script actually cycles between all cameras in the list, so if you have more than 2, it will transition to the next camera in the list.

\$\endgroup\$
  • \$\begingroup\$ I'm actually using just 1 camera for now. It works fine, so I guess I'll leave it at that, although your solution is potentially clearer and easier to work with. Thanks anyway. I will keep what you said in mind. Cheers! \$\endgroup\$ – reincarnationofstackexchange Aug 1 '15 at 17:14

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.