[sharing] gyroscope-controlled camera on iPhone 4

This is wonderful! Thank you!
I have to figure out how to add it to a character controller next!

I think I’m getting something wrong somewhere though, I’m getting flickering weirdness in the background. It’s like it’s repeating the screen image in layers over each other. Something to do with the refresh/frame rate?
Any ideas what’s causing this?

Thanks.

754113--27534--$weirdness2.jpg

I would like to use this code for an AR app, but in this case would be useful to reset the Quaternion (or the eulers) to the magnetic compass heading (I know it is not accurate but better than have the cam oriented to East when it should be North…)
Any idea how to do this?

Hmmm - just tried building for iPad2 and iPhone 4S and the image is just static on the “Front Z+” part. Camera doesnt rotate at all.

The Update() is called, bool is true and I can see the gyro.attitude values changing. Just doesnt change the camera.

Any ideas?

/Thomas

Worked on a very similar feature at the beginning of this year :

http://itunes.apple.com/us/app/id428087225?mt=8

Maybe this could give you some ideas…
I heard they was supposed to release the SDK, but I really dunno if they did… O_o…

wow… great… giant… thanks very much phoberman for your script…

Cheers for the script!

Just tried this on android now 3.5 supports it and works great.

just needed to change
gyroBool = Input.isGyroAvailable;
with
gyroBool = SystemInfo.supportsGyroscope;

Hi

The code works great, but it lacks support for ScreenOrientation.LandscapeRight and ScreenOrientation.PortraitUpsideDown.

I can’t get my head around how this works and would really appreciate if someone could tell me how to implement support for the missing screen orientations?? What should be in the ?? fileds below for the Vector3 and the Quaternion?

Any help will be much appreciated.

Regards
Dan

if (Screen.orientation == ScreenOrientation.LandscapeLeft) 
{
	camParent.transform.eulerAngles = new Vector3(90,90,0);
	rotFix = new Quaternion(0f,0f,0.7071f,0.7071f);
} 
else if (Screen.orientation == ScreenOrientation.LandscapeRight) 
{
	camParent.transform.eulerAngles = new Vector3(??,??,??);
	rotFix = new Quaternion(??,??,??,??);
}
else if (Screen.orientation == ScreenOrientation.Portrait) 
{
	camParent.transform.eulerAngles = new Vector3(90,180,0);
	rotFix = new Quaternion(0f,0f,1f,0f);
} 
if (Screen.orientation == ScreenOrientation.PortraitUpsideDown) 
{
	camParent.transform.eulerAngles = new Vector3(??,??,??);
	rotFix = new Quaternion(??,??,??,??);
}

I’m in the same boat as Funder75. Any way to add support for LanscapeRight?

Yes, I actually did find the solution for LandscapeRight. Unfortunatly I’m stuck on the PortraitUpsideDown!

else if (Screen.orientation == ScreenOrientation.LandscapeRight) 

{

    camParent.transform.eulerAngles = new Vector3(90,90,0);

    rotFix = new Quaternion(0f, 0f, -0.7071f, 0.7071f);

}

With a lot of trial’n’error, some luck and a brief moment of clarity, I’ve succeeded in making all orientations work.
I guess the code needs some cleaning up, but here it is…

using UnityEngine;
using System.Collections;

public class GyroCam : MonoBehaviour 
{
	
	public CamManager target;
	private bool gyroBool;
	private Gyroscope gyro;
	private Quaternion rotFix;	
	
	private GameObject camParent;
	public ScreenOrientation currentOrientation;
	
	private bool initialized = false;
	private Vector3 inititialRotation = new Vector3(90,90,0);
	

	
	private void init()
	{
		if (!initialized)
		{
			Transform originalParent = transform.parent; // check if this transform has a parent
			camParent = new GameObject ("camParent"); // make a new parent
			camParent.transform.position = transform.position; // move the new parent to this transform position
			transform.parent = camParent.transform; // make this transform a child of the new parent
			camParent.transform.parent = originalParent; // make the new parent a child of the original parent
			
			gyroBool = SystemInfo.supportsGyroscope;
			
			if (gyroBool) {
				
				gyro = Input.gyro;
				gyro.enabled = true;
				
			} 
			else 
			{
				print("NO GYRO");
			}
			
			initialized = true;
		}
	}
	
	private void updateScreenOrientation()
	{
		if (Screen.orientation == ScreenOrientation.LandscapeLeft) 
		{
			camParent.transform.eulerAngles = inititialRotation;
			rotFix = new Quaternion(0f,0f,0.7071f,0.7071f);
		} 
		else if (Screen.orientation == ScreenOrientation.LandscapeRight) 
		{
			camParent.transform.eulerAngles = inititialRotation;
			rotFix = new Quaternion(0f,0f,-0.7071f,0.7071f);
		}
		else if (Screen.orientation == ScreenOrientation.Portrait) 
		{
			camParent.transform.eulerAngles = inititialRotation;
			rotFix = new Quaternion(0f,0f,1f,0f);
		} 
		if (Screen.orientation == ScreenOrientation.PortraitUpsideDown) 
		{
			camParent.transform.eulerAngles = inititialRotation;
			rotFix = new Quaternion(0f,0f,0f,1f);
		}
		
				
	}
	
	void Update () 
	{
		init();
        if (currentOrientation != Screen.orientation)
        {
            currentOrientation = Screen.orientation;
            updateScreenOrientation();
        }		
		
		if (gyroBool)
		{
			Quaternion camRot = gyro.attitude * rotFix;
			transform.localRotation = camRot;
			target.SetRotation(transform.rotation);
		}
		else
		{
			target.SetRotation(Quaternion.identity);
		}

	}
}

hi all -

Thanks for all the kind words. I forgot to check this thread for a while but I’m glad the script has proven to be so popular and useful.

In the meantime I’ve been working with Android as well as iOS, and I had to do a little work to get the code working on both.

Here’s my current script, works on both my phones (iPhone4 and Galaxy Nexus) - still haven’t tackled anything except LandscapeLeft but now maybe I’ll get around to it -

private var gyroBool : boolean;
private var gyro : Gyroscope;
private var quatMult : Quaternion;
private var quatMap : Quaternion;

function Start() {
	// find the current parent of the camera's transform
	var currentParent = transform.parent;
	// instantiate a new transform
	var camParent = new GameObject ("camParent");
	// match the transform to the camera position
	camParent.transform.position = transform.position;
	// make the new transform the parent of the camera transform
	transform.parent = camParent.transform;
	// make the original parent the grandparent of the camera transform
	camParent.transform.parent = currentParent;
	// check whether device supports gyroscope
	#if UNITY_3_4
	gyroBool = Input.isGyroAvailable;
	#endif
	#if UNITY_3_5
	gyroBool = SystemInfo.supportsGyroscope;
	#endif
	
	if (gyroBool) {
		gyro = Input.gyro;
		gyro.enabled = true;
		#if UNITY_IPHONE
			camParent.transform.eulerAngles = Vector3(90,90,0);
			quatMult = Quaternion(0,0,0.7071,0.7071);
		#endif
		#if UNITY_ANDROID
			camParent.transform.eulerAngles = Vector3(-90,0,0);
			quatMult = Quaternion(0, 0, 0.7071, -0.7071);
		#endif
		Screen.sleepTimeout = 0;
	} else {
		#if UNITY_EDITOR
			print("NO GYRO");
		#endif
	}
}

function Update () {
	if (gyroBool) {
		#if UNITY_IPHONE
			quatMap = gyro.attitude;
		#endif
		#if UNITY_ANDROID
			quatMap = Quaternion(gyro.attitude.w,gyro.attitude.x,gyro.attitude.y,gyro.attitude.z);
		#endif
		transform.localRotation = quatMap * quatMult;
	}
}

Here’s an update that covers all orientations - for both iOS Android

#pragma strict
// Gyroscope-controlled camera for iPhone  Android revised 2.26.12
// Perry Hoberman <hoberman@bway.net>
//
// Usage: 
// Attach this script to main camera.
// Note: Unity Remote does not currently support gyroscope. 
//
// This script uses three techniques to get the correct orientation out of the gyroscope attitude:
// 1. creates a parent transform (camParent) and rotates it with eulerAngles
// 2. for Android (Samsung Galaxy Nexus) only: remaps gyro.Attitude quaternion values from xyzw to wxyz (quatMap)
// 3. multiplies attitude quaternion by quaternion quatMult

// Also creates a grandparent (camGrandparent) which can be rotated with localEulerAngles.y
// This node allows an arbitrary heading to be added to the gyroscope reading
// so that the virtual camera can be facing any direction in the scene, no matter what the phone's heading

static var gyroBool : boolean;
private var gyro : Gyroscope;
private var quatMult : Quaternion;
private var quatMap : Quaternion;

function Awake() {
	// find the current parent of the camera's transform
	var currentParent = transform.parent;
	// instantiate a new transform
	var camParent = new GameObject ("camParent");
	// match the transform to the camera position
	camParent.transform.position = transform.position;
	// make the new transform the parent of the camera transform
	transform.parent = camParent.transform;
	// make the original parent the grandparent of the camera transform
	//camParent.transform.parent = currentParent;
	// instantiate a new transform
	var camGrandparent = new GameObject ("camGrandParent");
	// match the transform to the camera position
	camGrandparent.transform.position = transform.position;
	// make the new transform the parent of the camera transform
	camParent.transform.parent = camGrandparent.transform;
	// make the original parent the grandparent of the camera transform
	camGrandparent.transform.parent = currentParent;
		
	// check whether device supports gyroscope
	#if UNITY_3_4
	gyroBool = Input.isGyroAvailable;
	#endif
	#if UNITY_3_5
	gyroBool = SystemInfo.supportsGyroscope;
	#endif
	
	if (gyroBool) {
		gyro = Input.gyro;
		gyro.enabled = true;
		#if UNITY_IPHONE
			camParent.transform.eulerAngles = Vector3(90,90,0);
			if (Screen.orientation == ScreenOrientation.LandscapeLeft) {
				quatMult = Quaternion(0f,0,0.7071,0.7071);
			} else if (Screen.orientation == ScreenOrientation.LandscapeRight) {
 				quatMult = Quaternion(0,0,-0.7071,0.7071);
        	} else if (Screen.orientation == ScreenOrientation.Portrait) {
            	quatMult = Quaternion(0,0,1,0);
			} else if (Screen.orientation == ScreenOrientation.PortraitUpsideDown) {
            	quatMult = new Quaternion(0,0,0,1);
			}
		#endif
		#if UNITY_ANDROID
			camParent.transform.eulerAngles = Vector3(-90,0,0);
			if (Screen.orientation == ScreenOrientation.LandscapeLeft) {
				quatMult = Quaternion(0f,0,0.7071,-0.7071);
			} else if (Screen.orientation == ScreenOrientation.LandscapeRight) {
  				quatMult = Quaternion(0,0,-0.7071,-0.7071);
        	} else if (Screen.orientation == ScreenOrientation.Portrait) {
            	quatMult = Quaternion(0,0,0,1);
			} else if (Screen.orientation == ScreenOrientation.PortraitUpsideDown) {
            	quatMult = new Quaternion(0,0,1,0);
			}
		#endif
		Screen.sleepTimeout = SleepTimeout.NeverSleep;
	} else {
		#if UNITY_EDITOR
			//print("NO GYRO");
		#endif
	}
}

function Update () {
	if (gyroBool) {
		#if UNITY_IPHONE
			quatMap = gyro.attitude;
		#endif
		#if UNITY_ANDROID
			quatMap = Quaternion(gyro.attitude.w,gyro.attitude.x,gyro.attitude.y,gyro.attitude.z);
		#endif
		transform.localRotation = quatMap * quatMult;
	}
}

Man Thanks Perry for the share !!! and updated Code…
i did update it myself and thought to post it here but you are faster and your Code look’s more polished :slight_smile:
anyway many thanks for sharing… in this times it is not often that this happen
i really miss the good old time here in the forum where help sharing was normal…

Wow really nice share.

Does this also work on the Galaxy Tab? Because i don’t have the right input…
My screen is always rotated 90 degrees. Is this because it is build for phone?

Here’s another update - I added basic touch input so that a horizontal swipe rotates the heading.

Black Spider, I would just experiment with the setting for camParent.transform.eulerAngles - try different rotations in 90 degree increments.

By the way, this script is part of my very first package for the Asset Store - which just got approved!
It’s called FOV2GO and it’s a comprehensive stereo 3D toolkit.
Did I mention that it’s FREE?
Check it out: Unity Asset Store - The Best Assets for Game Making

pH

#pragma strict
// Gyroscope-controlled camera for iPhone  Android revised 2.26.12
// Stereoskopix FOV2GO Copyright (c) 2011 Perry Hoberman
// Perry Hoberman <hoberman@bway.net>
//
// Usage: 
// Attach this script to main camera.
// Note: Unity Remote does not currently support gyroscope. 
// Use Landscape Left for correct orientation
//
// This script uses three techniques to get the correct orientation out of the gyroscope attitude:
// 1. creates a parent transform (camParent) and rotates it with eulerAngles
// 2. for Android (Samsung Galaxy Nexus) only: remaps gyro.Attitude quaternion values from xyzw to wxyz (quatMap)
// 3. multiplies attitude quaternion by quaternion quatMult

// Also creates a grandparent (camGrandparent) which can be rotated to change heading
// This node allows an arbitrary heading to be added to the gyroscope reading
// so that the virtual camera can be facing any direction in the scene, no matter which way the phone is actually facing
// Option for touch input - horizontal swipe controls heading

static var gyroBool : boolean;
private var gyro : Gyroscope;
private var quatMult : Quaternion;
private var quatMap : Quaternion;
// camera grandparent node to rotate heading
private var camGrandparent : GameObject;
private var heading : float = 0;

// mouse/touch input
public var touchRotatesHeading : boolean = true;
private var screenSize : Vector2;
private var mouseStartPoint: Vector2;
private var headingAtTouchStart : float = 0;
@script AddComponentMenu ("stereoskopix/s3d Gyro Cam")

function Awake() {
	// find the current parent of the camera's transform
	var currentParent = transform.parent;
	// instantiate a new transform
	var camParent = new GameObject ("camParent");
	// match the transform to the camera position
	camParent.transform.position = transform.position;
	// make the new transform the parent of the camera transform
	transform.parent = camParent.transform;
	// instantiate a new transform
	camGrandparent = new GameObject ("camGrandParent");
	// match the transform to the camera position
	camGrandparent.transform.position = transform.position;
	// make the new transform the grandparent of the camera transform
	camParent.transform.parent = camGrandparent.transform;
	// make the original parent the great grandparent of the camera transform
	camGrandparent.transform.parent = currentParent;
		
	// check whether device supports gyroscope
	#if UNITY_3_4
	gyroBool = Input.isGyroAvailable;
	#endif
	#if UNITY_3_5
	gyroBool = SystemInfo.supportsGyroscope;
	#endif
	
	if (gyroBool) {
		gyro = Input.gyro;
		gyro.enabled = true;
		#if UNITY_IPHONE
			camParent.transform.eulerAngles = Vector3(90,90,0);
			if (Screen.orientation == ScreenOrientation.LandscapeLeft) {
				quatMult = Quaternion(0,0,0.7071,0.7071);
			} else if (Screen.orientation == ScreenOrientation.LandscapeRight) {
 				quatMult = Quaternion(0,0,-0.7071,0.7071);
        	} else if (Screen.orientation == ScreenOrientation.Portrait) {
            	quatMult = Quaternion(0,0,1,0);
			} else if (Screen.orientation == ScreenOrientation.PortraitUpsideDown) {
            	quatMult = Quaternion(0,0,0,1);
			}
		#endif
		#if UNITY_ANDROID
			camParent.transform.eulerAngles = Vector3(-90,0,0);
			if (Screen.orientation == ScreenOrientation.LandscapeLeft) {
				quatMult = Quaternion(0,0,0.7071,-0.7071);
			} else if (Screen.orientation == ScreenOrientation.LandscapeRight) {
  				quatMult = Quaternion(0,0,-0.7071,-0.7071);
        	} else if (Screen.orientation == ScreenOrientation.Portrait) {
            	quatMult = Quaternion(0,0,0,1);
			} else if (Screen.orientation == ScreenOrientation.PortraitUpsideDown) {
            	quatMult = Quaternion(0,0,1,0);
			}
		#endif
		Screen.sleepTimeout = SleepTimeout.NeverSleep;
	} else {
		#if UNITY_EDITOR
			//print("NO GYRO");
		#endif
	}
}

function Start() {
	screenSize.x = Screen.width;
	screenSize.y = Screen.height;
}

function Update () {
	if (gyroBool) {
		#if UNITY_IPHONE
			quatMap = gyro.attitude;
		#endif
		#if UNITY_ANDROID
			quatMap = Quaternion(gyro.attitude.w,gyro.attitude.x,gyro.attitude.y,gyro.attitude.z);
		#endif
		transform.localRotation = quatMap * quatMult;
	}
	#if (UNITY_IPHONE || UNITY_ANDROID)  !UNITY_EDITOR
		if (touchRotatesHeading) {
			GetTouchMouseInput();
		}
		camGrandparent.transform.localEulerAngles.y = heading;
	#endif
}

function GetTouchMouseInput() {
    if(Input.GetMouseButtonDown(0)) {
        mouseStartPoint = Input.mousePosition;
        headingAtTouchStart = heading;
    } else if (Input.GetMouseButton(0)) {
		var delta : Vector2;
    	var mousePos = Input.mousePosition;
    	delta.x = (mousePos.x - mouseStartPoint.x)/screenSize.x;
    	delta.y = (mousePos.y - mouseStartPoint.y)/screenSize.y;
		heading = (headingAtTouchStart+delta.x*100);
		heading = heading%360;
	}
}
1 Like

Great script! But how did you calculate which values to use for the rotation fix and why is it only necessary to calibrate in the Awake method and not Update?

I noticed that it would always add 90 degrees to the Z rotation of the child camera, so I tried doing only this and still getting the same results as before:

transform.parent.transform.eulerAngles = new Vector3 (90, 90, 0);
Quaternion currentGyro = Input.gyro.attitude;
transform.localRotation = currentGyro * Quaternion.Euler(0, 0, 90);

Thanks for the great script! Works like a charm on iOS, but I’m having some trouble on Android. Objects seem to drift with the camera, especially when moving side to side. I’m running in landscape left. Seems to be much better on the Galaxy Nexus, but other devices (Nexus S, Galaxy SII, etc) have terrible drift. Any ideas what might be causing this?

Is Unity maybe faking the gyro input with accelerometer inputs?

I would guess that the drift has more to do with the phone itself - drivers, OS, or gyroscope hardware - than with Unity.

I was getting terrible results (probably similar to yours) on the LG Thrill 4G running Android 2.3 - just upgraded to Gingerbread - now the gyro works perfectly.

First, phoberman, thanks so much for sharing…really a fun tool.

I have a quick question though. Is there anyway to reset the camera back to a pre-defined location and then lock its location?

I’m exploring something that I work with a couple of different cameras. And when I flip on a gyro camera (with your script), the location of the camera is always weird (as though the iPad’s gyroscope has been keeping track of the camera all along and then passing that along to the gyro camera when its activated). I really want to be able to have that gyro camera stay in one place, but be able to look around from that fixed point…

So thankful for any guidance or suggestions you could provide.

nm8shun/ I am trying (relatively) the same thing myself and started a thread here, and here but no one seems to be interested so I will ask phoberman the same thing here…:slight_smile: