Switch between touchscreen and Google VR to control the Unity camera


I’m trying to use both the touchscreen and the device-sensors to control the Unity camera in my mobile Google VR app.

Currently, i’m using the following script, on the camera, to switch between Google VR and touch navigation :

if (Input.touchCount == 1)
                //Touch began
                if (Input.GetTouch(0).phase == TouchPhase.Began)
                    //Disable device tracking
                    gvrHead.trackRotation = false;

                    //save position
                    firstPoint = Input.GetTouch(0).position;

                    xAngleTemp = xAngle;
                    yAngleTemp = yAngle;
                //Move finger by screen
                if (Input.GetTouch(0).phase == TouchPhase.Moved)
                    secondPoint = Input.GetTouch(0).position;
                    //Mainly, about rotate camera. For example, for Screen.width rotate on 180°
                    xAngle = xAngleTemp + (secondPoint.x - firstPoint.x) * 180.0f / Screen.width;
                    yAngle = yAngleTemp - (secondPoint.y - firstPoint.y) * 90.0f / Screen.height;

                    //Apply rotation
                    transform.rotation = Quaternion.Euler(-yAngle, -xAngle, 0.0f);
                //Touch ended
                if (Input.GetTouch(0).phase == TouchPhase.Ended)
                    //Enable device tracking
                    gvrHead.trackRotation = true;

It’s working fine until on TouchPhase.Ended, when I re-enable the Google VR trackRotation, the camera is going back to the position before I disabled it.

I’m looking for something more additive : when the Google VR control is enabled back, controller position match the camera rotation (or something like this), and not the opposite (So there is no perceptible transition between touch-look and sensor-look).

I hope my intentions are clear enough, thank you for your help.

Hey @bloodbc,

So my solution is also far from perfect, but I think it’s good enough for my current use case. Maybe it will help others who are in a similar situation.

The name of the thing we’re trying to achieve is “Magic Window”, a monoscopic view, magically oriented by the gyroscope, so you can look around, but with the (common) addition of touch controls to provide additional input to the camera yaw, so the user can choose their forward facing direction without having to physically turn their body.

This was a feature of the GoogleVR SDK, but the plugin team are currently in the process of moving the sdk to run natively within Unity, rather than installing it as a plugin. Annoyingly, during this period the “Magic Window” feature has been removed from the SDK. See this issue on github .

For the time being, I had a look at using your code as a base and expanding on it to add the feature back in. You asked for a more additive approach, and I agree, doing this is probably the best way of achieving this issue, however from what I could see, the Pose3D class which is used for capturing the phone’s orientation / position is pretty locked down, and I didn’t want to hack into it.

What I did find out, is the GvrHead has a public “target” property, which essentially acts like a parent to the head. For example, if a body moves position or rotates, you’d expect the head to transform with it.

So I’m using the target to set the orientation when using touch movements and resetting the head position when the touch end event happens.

It’s easier to explain with some code:

private GvrHead head;

private float xAngle = 0.0f;
private float yAngle = 0.0f;

private float xAngleTemp = 0.0f;
private float yAngleTemp = 0.0f;

private Vector2 firstPoint;
private Vector2 secondPoint;

private GameObject rotationTarget;

void Start() {
  rotationTarget = GameObject.FindGameObjectWithTag ("rotationTarget");
  // The head is added to the camera on the fly by the plugin, wait for the next frame to make sure it's been added before trying to grab it.
  StartCoroutine (initiateHead ());

IEnumerator initiateHead() {
  yield return 0;
  head = gameObject.GetComponent(typeof(GvrHead)) as GvrHead;
  // Set the head target with an empty game object transform.
  head.target = rotationTarget.transform;

void Update () {
  if (Input.touchCount == 1) {
    if (Input.GetTouch (0).phase == TouchPhase.Began) {
      // Stop the head from using the gyro.
      head.trackRotation = false;

      firstPoint = Input.GetTouch (0).position;

      // Take the current head's local euler angles, so touch moves continues from the head's current rotation. 
      xAngleTemp = -head.transform.localEulerAngles.y;
      yAngleTemp = -head.transform.localEulerAngles.x;

    } else if (Input.GetTouch (0).phase == TouchPhase.Moved) {
      secondPoint = Input.GetTouch (0).position;

      xAngle = xAngleTemp + (secondPoint.x - firstPoint.x) * 180.0f / Screen.width;
      yAngle = yAngleTemp - (secondPoint.y - firstPoint.y) * 90.0f / Screen.height;

      transform.rotation = Quaternion.Euler (-yAngle, -xAngle, 0.0f);
      // Set the rotation target rotation. Should be the same as the head rotation.
      rotationTarget.transform.rotation = Quaternion.Euler (-yAngle, -xAngle, 0.0f);

    } else if (Input.GetTouch (0).phase == TouchPhase.Ended) {
      // After the touch has ended, recenter the head, so it should be pointing the same way as the rotation target.
      // Start the gyro tracking again.
      head.trackRotation = true;

Some extra validation should be put in there to only execute this when outside of VR mode, plus there should be something extra in place for dealing with devices in portrait.

The only other issue I’m having this current implementation, is there is a slight jump between moving with touch, and letting go. I’m not entirely sure why it happens, it shouldn’t as the camera is re-centered and the target is set to the same orientation as the head.

It’s also worth noting the GvrHead is listed as deprecated in their docs. So This is definitely not a long term solution. I’d imagine they will remove this when they fully integrate the SDK within Unity natively. Hopefully by this time, they would’ve re-integrated magic window.

So close, but not quite there :slight_smile: