Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

3D camera relatively using gyroscope a la N.O.V.A.2

Discussion in 'iOS and tvOS' started by yetstay, Mar 28, 2012.

  1. yetstay

    yetstay

    Joined:
    Mar 28, 2012
    Posts:
    5
    Hello awesome people on Unity forum! :) I am trying to make a 3D camera that uses iphone 4's gyro. I've researched quite a bit on google and unity forum and unity answers and found this http://forum.unity3d.com/threads/98...-camera-on-iPhone-4/page2?highlight=gyroscope and this http://forum.unity3d.com/threads/75288-Gyroscope-Quaternion-to-Unity-Camera-(HELP-PLEASE!) and read whole bunch of other links and I have the basic idea about how to use gyro for the game's main camera. Basically, offset the raw data(attitude) gotten from gyro and apply a fixed quaternion rotation to make it meaningful (although I don't have a slightest idea how phoberman came up with the numbers in his script) then apply that rotation to camera. I've gotten the script to work in my prototype for now.

    My problem is this. I am trying to make a camera that uses gyro, but relative to the current position(or rotation) of the phone and also incorporate a finger control , a la N.O.V.A. 2. The camera in that game starts looking "forward (vector3(0, 0, 1) in the game world, if you will)" regardless of the position you are holding the phone, then moves according to the gyro using that "forward" as a reference. Then you can still look around by touching anywhere on the screen with one finger and moving it around.

    The first step would be making the gyro relative, not absolute (as in making the camera point where the gyro is pointing) as what my current prototype is doing. Right now if I start my game holding the phone downward, it will start looking down, not "forward." I have been thinking about it and thought if I applied a quaternion rotation from the gyro's attitude to "forward", the game would start and the camera would look "forward." It did, but the movement update was not really working as I expected. Then I tried getting two directional vectors from where the camera is looking(governed by gyro) and the initial forward, got a quaternion rotation from them using fromtorotation function, and tried using it but it didn't work either.

    What I had in my mind was to get some sort of fixed rotation or a fixed angle between the initial gyro lookat and the initial "forward" vector then to apply it constantly whenever the camera's moving, but it failed. I realized that that way of thinking was wrong as initially I thought of moving camera like this: lock your thumb(gyro's lookat) and index finger(initial"forward") and move them together. Now I realize it's more like two index fingers, independent from each other but still related. However this is further confusing me.

    Now I think I should be doing the following:

    Start() {
    1. get the gyro's lookat.
    2. get the initial forward and name it "forward" or something
    }

    Update() {
    3. get the gyro's new position and compare it to 1 to get a new rotation. then assign old lookat = new lookat.
    4. somehow convert it to a relative rotation on the game world => <<< lost here >>>
    5. applyl 4 to "forward" and update the camera using camera.lookat(forward) or something like that

    }

    More on 4. I am using LFR as in unity. So If i am looking down to start the game, my initial lookat is down(0, -1, 0), but my "forward" is (0, 0, 1). If I move my phone to the right, the gyro will think it's moving from down -> right(1, 0, 0) but my "forward" should also look at right(1, 0, 0). Now if I initially started the game holding my phone left(-1, 0, 0) then moved 90 degrees to right(at this point lookat would be looking at (0, 0, 1)), "forward" should be looking at right(1, 0, 0) after that rotation. Does this make sense?

    Am I wrong in thinking that? Can anyone please shed some light on this issue? Quaternion is driving me crazy :( And I haven't even begun to think about applying the finger control...
     
  2. yetstay

    yetstay

    Joined:
    Mar 28, 2012
    Posts:
    5
    So I've been fiddling with some stuff, and made a bit of progress, if not much. I made two game objects and applied gyro's rotation to the first one and saved the initial lookat Vector on the second one as the forward vector of that object, then found a rotation between those two objects then applied it on the camera. It's still buggy for some reason, and far from being completed but if anyone's interested here's a package file : http://www.bigupload.com/en/file/36474/iphonegyrocamtest.unitypackage.html If you don't have an iphone to build this on, you can still use the editor to see what I was trying to do.

    This is working for the most of the part as far as detecting the starting position of gyro and applying camera accordingly so it will start looking at "front." However I find it becoming irregular after building a couple of times as the script doesn't detect the forward vector properly? However this is a start. If anyone's got any idea as to what's causing that, please let me know. I also need to work on saving the camera's lookat so when the game's paused and gyro's moved, the camera's still looking at the same position. Finger control can come after that...
     
  3. yetstay

    yetstay

    Joined:
    Mar 28, 2012
    Posts:
    5
    So I did some more work and updated code, but there is a slight error and I asked a question here. If anyone can shed a light on it I would appreciate it. In the meantime, I am posting updated code if anyone's interested in looking into it. It's still very messy and work-in-progress so I will change it around more, and finger control's not incorporated so there's still long way to go, but at least when you first build it on iPhone 4s via Xcode on Unity 3.5, it will face "front" regardless of where the gyro's facing, then gyro works from that point on. When the game is pushed into background by home button, it will pause the game and save the current camera position then when the game resumes, it will stay paused. When resumed from the gui button, the camera's moving again depending on the gyro's rotation. My problem is that when the game's not run from xcode and run from the phone(not connected to xcode), the camera's looking "up" to start with, rather than "front"...



    // iPhone gyroscope-controlled camera demo v0.3 8/8/11

    // Perry Hoberman <hoberman@bway.net>

    // Directions: Attach this script to main camera.

    // Note: Unity Remote does not currently support gyroscope.



    // Modified by Sean Lee <seanlee0819@gmail.com> 2012/04/12



    private var gyroBool : boolean;

    private var gyro : Gyroscope;

    private var rotFix : Quaternion;



    private var camParent : GameObject;



    // quaternion test

    public var gyroBody: GameObject;



    public var CamBody: GameObject;



    private var camBodyParent : GameObject;



    private var bodyForward1: Vector3;



    private var camForward: Vector3;



    private var inverseGyroRot: Quaternion;



    private var gyroRot: Quaternion;

    private var initialCamParentRot: Quaternion;

    private var initialCamRot: Quaternion;



    private var gamepaused;





    function Start() {



    gamepaused = false;



    var 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; //Input.isGyroAvailable;



    if (gyroBool) {



    gyro = Input.gyro;

    gyro.enabled = true;



    if (Screen.orientation == ScreenOrientation.LandscapeLeft) {

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



    } else if (Screen.orientation == ScreenOrientation.Portrait) {

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

    }



    if (Screen.orientation == ScreenOrientation.LandscapeLeft) {

    rotFix = Quaternion(0,0,0.7071,0.7071);

    } else if (Screen.orientation == ScreenOrientation.Portrait) {

    rotFix = Quaternion(0,0,1,0);

    }

    //Screen.sleepTimeout = 0;



    gyroRot = gyro.attitude * rotFix;



    gyroBody.transform.localRotation = gyroRot; // where gyro's facing

    //bodyForward1 = gyroBody.transform.forward;





    //var tempCamForward = Vector3(0, 0, 1);// initial lookat of camera

    //CamBody.transform.forward = tempCamForward;



    //if (Screen.orientation == ScreenOrientation.LandscapeLeft) {

    // CamBody.transform.eulerAngles = Vector3(90, 90, 0);

    //} else if (Screen.orientation == ScreenOrientation.Portrait) {

    // CamBody.transform.eulerAngles = Vector3(90, 180, 0);

    //}



    //initialCamParentRot = CamBody.transform.rotation;



    initialCamParentRot = camParent.transform.rotation * Quaternion.EulerRotation(90, 0, 0);



    initialCamRot = transform.localRotation; //camParent.transform.FindChild(transform.name).transform.localRotation;



    Debug.Log("initial Cam Rot = "+initialCamRot.eulerAngles);

    //camForward = CamBody.transform.forward;



    //inverseGyroRot = Quaternion.FromToRotation(bodyForward1, camForward);

    inverseGyroRot = Quaternion.Inverse(gyroBody.transform.rotation);



    //Debug.Log("bodyForward1 = "+bodyForward1);

    //Debug.Log("camForward = " + camForward);

    Debug.Log("inverseGyroRot euler angle= "+inverseGyroRot.eulerAngles);

    //Debug.Log("gyro.attitude= "+gyro.attitude);

    }



    else {



    var originalCamBodyParent = CamBody.transform.parent; // check if this transform has a parent

    camBodyParent = new GameObject ("camBodyParent"); // make a new parent

    camBodyParent.transform.position = CamBody.transform.position; // move the new parent to this transform position

    CamBody.transform.parent = camBodyParent.transform; // make this transform a child of the new parent

    camBodyParent.transform.parent = originalCamBodyParent; // make the new parent a child of the original parent



    print("NO GYRO");



    initialCamParentRot = camBodyParent.transform.rotation;

    initialCamRot = CamBody.transform.localRotation;



    inverseGyroRot = Quaternion.Inverse(gyroBody.transform.rotation);

    Debug.Log("inverseGyroRot in euler angles= " + inverseGyroRot.eulerAngles);



    }

    }



    function Update () {



    // the following is for pausing in simulator

    if(Input.GetKeyDown("p")){

    if(!gamepaused){

    gamepaused = true;

    Debug.Log("game paused!!");



    initialCamParentRot = camParent.transform.rotation;

    initialCamRot = CamBody.transform.localRotation;



    }

    else{

    gamepaused = false;

    Debug.Log("game resumed");



    inverseGyroRot = Quaternion.Inverse(gyroBody.transform.rotation);

    }

    }



    // handle the camera's transform

    if (gyroBool) {



    if(!gamepaused){



    gyroRot = gyro.attitude* rotFix;



    transform.localRotation = initialCamRot * initialCamParentRot * (inverseGyroRot * gyroRot) ;



    //gyroBody.transform.rotation = gyroRot; // where gyro's facing

    //bodyForward1 = gyroBody.transform.forward;

    //print("bodyForward1 = " + bodyForward1);



    }

    }

    else

    {

    if(!gamepaused){



    var tempRot3 = gyroBody.transform.rotation;

    CamBody.transform.localRotation = initialCamRot * initialCamParentRot * ( inverseGyroRot * tempRot3 );

    }

    }

    }





    function OnApplicationPause(gameInterrupted: boolean){



    //var tempGyroOffset:Quaternion;



    if(gameInterrupted){



    Debug.Log("game paused using the home button");





    initialCamRot = transform.localRotation * initialCamParentRot; //camParent.transform.FindChild(transform.name).transform.localRotation

    //tempGyroOffset = gyro.attitude * rotFix;



    Debug.Log("initial Cam Rot = "+initialCamRot.eulerAngles);



    //Time.timeScale = 0.0f; // pause the game so the gyro rotation won't matter



    this.gamepaused = true;



    }

    else{



    Debug.Log("game resumed from home screen!!!");



    gyroRot = gyro.attitude * rotFix;



    gyroBody.transform.localRotation = gyroRot; // where gyro's facing

    //bodyForward1 = gyroBody.transform.forward;



    //inverseGyroRot = Quaternion.FromToRotation(bodyForward1, savedCamForward);



    inverseGyroRot = Quaternion.Inverse(gyroBody.transform.rotation);



    //Time.timeScale = 1.0f; // resume the game



    //this.gamepaused = false;





    }



    Debug.Log("game paused bool = "+ this.gamepaused);

    Debug.Log("game interrupted bool = "+ gameInterrupted);





    }



    function OnGUI(){



    if(GUI.Button(Rect(10, 10, 150, 150), "pause"))

    pauseCam();



    }





    function pauseCam(){



    if(!gamepaused){



    Debug.Log("game paused using the GUI button");

    this.gamepaused = true;

    Debug.Log("game paused bool = "+ this.gamepaused);



    initialCamRot = transform.localRotation * initialCamParentRot; //camParent.transform.FindChild(transform.name).transform.localRotation



    Debug.Log("initial Cam Rot = "+initialCamRot.eulerAngles);



    //Time.timeScale = 0.0f; // pause the game so the gyro rotation won't matter



    }

    else{



    Debug.Log("game resumed!!!");

    this.gamepaused = false;



    Debug.Log("game paused bool = "+ this.gamepaused);







    gyroRot = gyro.attitude * rotFix;



    gyroBody.transform.localRotation = gyroRot; // where gyro's facing

    //bodyForward1 = gyroBody.transform.forward;



    //inverseGyroRot = Quaternion.FromToRotation(bodyForward1, savedCamForward);



    inverseGyroRot = Quaternion.Inverse(gyroBody.transform.rotation);



    //Time.timeScale = 1.0f; // resume the game





    }



    }
     
  4. techmage

    techmage

    Joined:
    Oct 31, 2009
    Posts:
    2,133
    There is a package in the asset store that has really nice code to map the gyro exactly to the camera. I extended the code to lock the y-axis and also smooth the gyro rotation. I think it was only like $25... worth getting, it breaks all the math needed to do it down to only like 3 clean lines of code. I would post it but, I think thatd be wrong.