0

I have developed a VR game using Unity and Google VR SDK for Android. I want the game to be playable without a VR headset too. How should I implement switching from VR to Normal mode and vice versa? I want to maintain 360 rotation while in Normal Mode using the phone gyroscope. I have looked through many scripts online, but I can't find anything that would make this possible.

I have found that switching modes can be done using XRSettings.enabled = true/false (depending on the mode), but how to maintain 360 rotation while in Normal (Non VR mode)

Here is the script I wrote:

public class GyroToggleManager : MonoBehaviour {

private int flag = 0;
private Quaternion offset;

IEnumerator SwitchToVR() {
    string desiredDevice = "cardboard";
    XRSettings.LoadDeviceByName(desiredDevice);
    yield return null;
    XRSettings.enabled = true;
    transform.localRotation = Quaternion.identity;
}

IEnumerator SwitchTo2D() {
    Input.gyro.enabled = true;

    // couldn't figure out how to find this.
    offset = ;

    XRSettings.LoadDeviceByName("");
    yield return null;
    transform.localRotation = Quaternion.identity;
}

// Use this for initialization
void Start () {
    if(XRSettings.enabled == false){
        Input.gyro.enabled = true;
    }
}

// Update is called once per frame
void Update () {
    if (XRSettings.enabled) {
        return;
    }

    //Also tried different combinations here nothing worked.
    transform.localRotation = Input.gyro.attitude ;
}

public void StartVR(){
    if(XRSettings.enabled == false){
        StartCoroutine (SwitchToVR ());
    }
}

public void StartN(){
    if(XRSettings.enabled == true){
        StartCoroutine(SwitchTo2D());
    }
}

}

Updated Script:

public class GyroToggleManager : MonoBehaviour {

Quaternion offset;

IEnumerator SwitchToVR() {
    string desiredDevice = "cardboard";
    XRSettings.LoadDeviceByName(desiredDevice);
    yield return null;
    XRSettings.enabled = true;
    transform.rotation = Quaternion.identity;
}

IEnumerator SwitchTo2D()
{
    Input.gyro.enabled = true;

    //Get offset.. Subtract Camera rotation from Gyro rotation
    offset = transform.rotation * Quaternion.Inverse(GyroToUnity(Input.gyro.attitude));

    XRSettings.LoadDeviceByName("");
    yield return null;
    XRSettings.enabled = false;
}

private static Quaternion GyroToUnity(Quaternion q)
{
    return new Quaternion(q.x, q.y, -q.z, -q.w);
}

// Use this for initialization
void Start () {
        if(XRSettings.enabled == false){
    Input.gyro.enabled = true;
     }
}

void Update()
{
    if (XRSettings.enabled)
    {
        return;
    }

    //Add the gyro value with the offset then apply to the camera 
    transform.rotation = offset * GyroToUnity(Input.gyro.attitude);
}


public void StartVR(){
    if(XRSettings.enabled == false){
        StartCoroutine (SwitchToVR ());
    }
}

public void StartN(){
    if(XRSettings.enabled == true){
        StartCoroutine(SwitchTo2D());
    }
}

}

  • I changed the title because it makes your question a duplicate and not really your issue. For your problem, get the rotation **before** toggling the mode. After toggling the mode, set the camera rotation to the rotation you got before. – Programmer Apr 30 '18 at 08:31
  • @Programmer Thanks for the title change. But, Even if I get the rotation before toggling to normal mode, 360 movement stops as soon as XRSettings.enabled is set to false. How to maintain this 360 rotation? – Himanshu Aggarwal Apr 30 '18 at 08:38
  • I may have misunderstood you. When toggle from VR to normal mode, you want the camera to keep the-same rotation? Face the-same way it was before you toggle it? If yes then see my first comment – Programmer Apr 30 '18 at 08:44
  • @Programmer Yes, I want the camera to keep the same rotation but along with that, I want to use the mobile gyroscope to maintain head tracking while in normal mode. – Himanshu Aggarwal Apr 30 '18 at 08:50
  • I missed the gyrocscope part. Before toggling, get the camera position and subtract the value with current gyroscope value. This value is the offset. Every frame,(`Update` function) get the gyroscope value and apply it to the camera with that offset too. Give this a try. If you run into issues, post your attempt code including the toggling code and I provide a fix if possible. – Programmer Apr 30 '18 at 08:57
  • @Programmer I am a beginner in Unity Game Development so I couldn't figure out how to find the offset and also update is not working correctly. How to share the script It's exceeding the comment size? – Himanshu Aggarwal Apr 30 '18 at 10:24
  • That's fine. Edit your question and add the code there. It's hard to read code from the comment section – Programmer Apr 30 '18 at 10:26
  • @Programmer Ok. – Himanshu Aggarwal Apr 30 '18 at 10:30
  • @Programmer Also the above script is not starting with the same rotation when the mode is changed. – Himanshu Aggarwal Apr 30 '18 at 10:56
  • Is this script attached to the camera? – Programmer Apr 30 '18 at 10:57
  • @Programmer Yes – Himanshu Aggarwal Apr 30 '18 at 11:01
  • Sorry for the long answer. It had to be longer to make sure you understand this. Don't just skip to the code part. Try to read it. Let me know if you have any question – Programmer Apr 30 '18 at 11:14

1 Answers1

1

Below is a simple camera follow script that follows a player ball while maintaining the offset distance between the camera and the player. It uses an offset value to do that by subtracting the camera's position from the player's position and then re-applying that offset to the camera's position with current player position in the Update or LateUpdate function.

public Transform playerTransform;
public Transform mainCameraTransform = null;
private Vector3 cameraOffset = Vector3.zero;

void Start()
{

    mainCameraTransform = Camera.main.transform;

    //Get camera-player Transform Offset that will be used to move the camera 
    cameraOffset = mainCameraTransform.position - playerTransform.position;
}

void LateUpdate()
{
    //Move the camera to the position of the playerTransform with the offset that was saved in the beginning
    mainCameraTransform.position = playerTransform.position + cameraOffset;
}

The example and code above is not exactly your solution but it's the easiest way to understand what you need to do.

In your case you need to subtract the camera's rotation from the gyro sensor or Input.gyro.attitude. The minor changes is that you can't really use - or + for that since both are Quaternion not Vector3 as in the example above.

  • To subtract a Quaternion from another Quaternion like I did in the Start function with Vector3, multiply the inverse of the other Quaternion. The inverse is obtained with Quaternion.Inverse.

  • To add two Quaternions like I did in the LateUpdate function above with Vector3, simply multiply both Quaternion together.

Here is the relevant changes in your code:

Quaternion offset;

IEnumerator SwitchTo2D()
{
    Input.gyro.enabled = true;

    //Get offset.. Subtract Camera rotation from Gyro rotation
    offset = transform.rotation * Quaternion.Inverse(GyroToUnity(Input.gyro.attitude));

    XRSettings.LoadDeviceByName("");
    yield return null;
}

// Update is called once per frame
void Update()
{
    if (XRSettings.enabled)
    {
        return;
    }

    //Add the gyro value with the offset then apply to the camera 
    transform.rotation = offset * GyroToUnity(Input.gyro.attitude);
}

private static Quaternion GyroToUnity(Quaternion q)
{
    return new Quaternion(q.x, q.y, -q.z, -q.w);
}

The GyroToUnity function is used to convert the gyroscope coordinate into Unity's coordinate before applying it to the camera. The gyroscope sensor is using the right-handed coordinate while Unity's camera and other objects are using the left-handed coordinate. See this for more information.

Programmer
  • 121,791
  • 22
  • 236
  • 328
  • I understood the offset concept :) But when I attached the updated script to the Main Camera on changing the mode to normal the 360 rotation freezes. – Himanshu Aggarwal Apr 30 '18 at 11:31
  • You are supposed to set `XRSettings.enabled` to `false` in normal mode so that the code in the `Update` function can execute. You never did that so `if (XRSettings.enabled) { return; }` is true and returning from that function. Do that at the end of the `SwitchTo2D` function. – Programmer Apr 30 '18 at 11:36
  • Can you add EDIT to your question then add the new script under it? Don't remove the old script. Just add the new one under it. I want to see what you have now. Also mention if you are running this on the Editor or the device. – Programmer Apr 30 '18 at 12:22
  • Edited the Question. I am running it on an Android device. – Himanshu Aggarwal Apr 30 '18 at 12:37
  • I don't have my device to test with me right now and the code looks fine. Put `Debug.Log(XRSettings.enabled)` inside the `Update` function and see if it changing to false at-all. [This](https://stackoverflow.com/questions/44690357/how-to-create-and-read-log-on-android-devices/44690501#44690501) is how to see a `Debug.Log` log on android. – Programmer Apr 30 '18 at 12:43
  • Yes, XRSettings.enabled changes to false when the mode is changed. I think there is some problem with this line of code: transform.rotation = offset * GyroToUnity(Input.gyro.attitude); because when I remove the offset and use it like: transform.rotation = GyroToUnity(Input.gyro.attitude); it gives 360 rotation but the rotations are not correct in that case. – Himanshu Aggarwal Apr 30 '18 at 12:49
  • I really don't think that the problem is from the offset code. I've been using this code in all my projects. Try changing the order of the multiplication. Change `transform.rotation = offset * GyroToUnity(Input.gyro.attitude);` to `transform.rotation = GyroToUnity(Input.gyro.attitude) * offset;`. If that doesn't work then you'll have to wait unless I get access to my device to test this. – Programmer Apr 30 '18 at 13:00
  • Using the transform.rotation = GyroToUnity(Input.gyro.attitude) * offset; head tracking is now working but there is still one more problem: the rotation of camera is not matching with the rotation of the device.On tilting the device along some axis but camera rotates about another axis. – Himanshu Aggarwal Apr 30 '18 at 13:29
  • What happens when you create a new project without any plugin. Just use the code in my answer and check if it works with the gyro. I now suspect that a plugin is modifying the camera. Not sure. – Programmer Apr 30 '18 at 13:33
  • It's still not working for me. Rotation of device and camera not matching. Can you share any Github link for any project that you have made using this gyroscope in unity for android? – Himanshu Aggarwal Apr 30 '18 at 14:01
  • It's a large complete project. I will get back to you when I get my Android device back. – Programmer Apr 30 '18 at 14:05
  • Ok, I'll be waiting. I'll try myself too if I could find a solution. – Himanshu Aggarwal Apr 30 '18 at 15:39