Rotate gyroscope-based camera over it's Y axis by swiping

Hello everyone,

Im trying to rotate the camera based on a user’s swipe, normally it would be quite simple(yRotation=xSwipe) but my camera is based on the gyroscope which causes the game-world to stay straight relative to the real world no matter what the device orientation is. When swiping, I only want to rotate the camera over it’s Y axis so the world still stays straight no matter how you swipe.

The gyroscope based camera already works but the swiping part does not. The problem is that the concept of “horizontal swipe” can be on both/either the X and/or Y axis on the screen.

The orientation Unity player setting is set to landscape so if you hold your device like the bottom phone of the image, a horizontal swipe would be on the screen-space X axis, if you hold your device like the top-left phone, a horizontal swipe would be on the screen-space Y axis, and if you hold your phone like the top-right phone, a horizontal swipe would be on both the screen-space X and Y axis, and if you hold your phone upside down, a horizontal swipe would even be on the negative-y axis.

I have tried, sketched and visualized a lot of situations but I can not come up with a way that works mathematically to allow this rotation by swiping, perhaps it’s my lack of knowledge of quaternions or vector arithmetic or simply the limits of my imagination.

The properties I can work with are the swipe vector(x & y distance in pixels within a frame), the camera rotation which could be used as both a Quaternion or Euler angle and the camera up-vector. However I do not know how to convert/superimpose this X and Y movement in a way that considers the “up” of the camera.

Can someone help me out? Anything helps!

I’ve partially solved it by placing a spherical mesh collider with normals aiming inwards around the camera and writing this code(still messy but I’ll optimize it when it works):

void Update()
	{
		RaycastHit hit1, hit2;
		Vector3 begin = Vector3.zero, end = Vector3.zero;
		float angle1, angle2;

		if(Input.GetMouseButtonDown(0))
		{
			//get initial
			initialRay = Camera.main.ScreenPointToRay(Input.mousePosition);
			Debug.Log("Started swiping");
		}
		if(Input.GetMouseButton(0))
		{
			//get current
			updateRay = Camera.main.ScreenPointToRay(Input.mousePosition);
			//rotate
			Collider collider = GetComponentInParent<Collider>();
			if(collider.Raycast(initialRay, out hit1, 2f))
			{
				begin = hit1.point;
			}
			if(collider.Raycast(updateRay, out hit2, 2f))
			{
				end = hit2.point;
			}
			angle1 = Mathf.Atan2(begin.z - transform.position.z, begin.x - transform.position.x) * Mathf.Rad2Deg;
			angle2 = Mathf.Atan2(end.z - transform.position.z, end.x - transform.position.x) * Mathf.Rad2Deg;
			transform.Rotate(transform.parent.up, angle2 - angle1, Space.World);
			//reset initial
			initialRay = updateRay;
		}
		if(Input.GetMouseButtonUp(0))
		{
			Debug.Log("Stopped swiping");
		}
	}

It works very good but there’s only one problem left, when you click somewhere and move the mouse or your finger to a different location on your screen and stop moving there while holding your mouse or finger on the screen, the rotation shakes all the time between two positions, I would expect it to do nothing because the initial ray and update ray would be exactly the same but for some reason it shakes. I’ll take a look at it and return when I find a fix.

Thanks

@Cambesa, your camera tilts, but screen’s coordinate system stays fixed relative to the phone.

We need to make the coordinate system follow the tilt, not the phone.

//re-specify the coordinate of the touch as being relative to the center of the screen, not the bottom left corner of the phone.
Vector2 centerPos =  Input.touch[0].position - new Vector2(Screen.width, Screen.height) *0.5f ;

Quaternion gyro_rot = Input.gyro.attitude;

//we are only interested in rotation around the phone's perpendicular axis, which "sticks-out"
//http://answers.unity3d.com/questions/434096/lock-rotation-of-gyroscope-controlled-camera-to-fo.html
Quaternion referenceRotation = Quaternion.identity;
 Quaternion deviceRotation = DeviceRotation.Get();
 Quaternion eliminationOfXY = Quaternion.Inverse(
     Quaternion.FromToRotation(referenceRotation * Vector3.forward, 
                               deviceRotation * Vector3.forward)
 );
 Quaternion rotationZ = eliminationOfXY * deviceRotation;



Vector2 rotatedTouchPos =  rotationZ * centerPos;

I think this should work. If not try:

Quaternion.Inverse(rotationZ) * centerPos;