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

Camera Polarity Switch

Discussion in 'Scripting' started by jcooper0211, Jul 28, 2015.

  1. jcooper0211

    jcooper0211

    Joined:
    May 21, 2015
    Posts:
    7
    Hey everyone. I'm working on creating a switch that'll change the camera's rotation and position.

    If you look at the image I've attached, you'll see that I have a CAD style view in my game.

    upload_2015-7-28_14-24-31.png

    I have five cameras: main, top, front, side, and isometric. Right now, I'm trying to get a button that will switch the polarity of the top camera so I can examine the air plane model from underneath. The top camera moves on the x and z axes, and is locked on the y-axis for movement only along the x and z axes. I want the camera to position itself in it's opposite direction, which would be on the negative y-axis and negative rotation along the x-axis.

    I've started on a script that kind of works:

    if(Input.GetKeyDown(KeyCode.V)|| Input.GetButtonDown(buttonName: "Polarity Switch"))
    {
    if(cameras[currentCameraIndex].camera.name == "Top_Bot_Cam")
    {
    cameras[currentCameraIndex].camera.transform.position = new Vector3(
    Mathf.Clamp (transform.position.x,topInfo.minX, topInfo.maxX),
    Mathf.Clamp (-transform.position.y, -transform.position.y, -transform.position.y),
    Mathf.Clamp(transform.position.z, topInfo.minY, topInfo.maxY));
    cameras[currentCameraIndex].camera.transform.rotation = Quaternion.Euler(
    -(cameras[currentCameraIndex].camera.gameObject.transform.rotation.x),
    cameras[currentCameraIndex].camera.gameObject.transform.rotation.y,
    cameras[currentCameraIndex].camera.gameObject.transform.rotation.z);

    }

    }



    When I press the "Y" button on my XBox controller, the the code runs, but I get an error in the debugger:

    1. NullReferenceException: Object reference not set to an instance of an object
    CameraController.Update () (at Assets/CameraController.cs:126) -- this is the line with red text


    Any help will be much appreciated. Thank you!
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,514
    That code is written with a lot of confusing multiple indirection. I suggest perhaps you unpack it into one statement per line in order to discover where the null is coming from precisely.

    Another way to do what you need is to make X number of empty game objects oriented precisely the way you want the cameras to be oriented, and then when you switch views, copy over the transform components (position and rotation) to the active camera, which will cause it to snap to that new view.

    That way you can a) edit the individual views easily in the editor (be sure to put colored Unity editor tags on your game objects so they are grabbable), and b) you can confidently list available views in a UI somewhere, such as if you were to name each of them "camview..." and search them up at program start to make a master list.
     
  3. jcooper0211

    jcooper0211

    Joined:
    May 21, 2015
    Posts:
    7
    Thank you for your response Kurt. I didn't post the rest of my code because it's kind of lengthy for a forum discussion haha. I will surely take what you've said into consideration. Currently I have an array of cameras attached to an empty game object. In this way, depending on the camera array's current index, I can access the camera's data and components. I hope this helps clear some of the confusion. Now that I've stated this, do you know how I could go about this using my array of cameras?
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,514
    It seems what you are doing should be a viable approach. However, you are definitely doing a few gratuitous indirection steps.

    For instance, assuming cameras[] are an array of GameObjects, it is not necessary to do .camera.gameObject.transform... you can just cut to the chase and use cameras[n].transform. to access what you want. Furthermore, if you mostly use the cameras[] array as cameras, then make them be cameras, and then you don't need to use the .camera shortcut property (which by the way is going away in Unity5), you just access it direclty as-is.

    Also, when I mentioned above the idea of "unpacking" the code, I meant basically doing things such as changing a compound statement into series of single-assignment temporary variable assignments so that you can discover precisely at which step the null pointer is biting you, as well as properly set debug breakpoints if you should so desire. If you have a big statement of yabba.dabba.doo.foo = 1234; and you get a null exception, you cannot at a glance determine if it was yabba, dabba, or doo who was null.

    A better way is:

    var d1 = yabba.dabba;
    var d2 = d1.foo;
    d2.doo = 1234;

    That way you can see precisely where your error is, even without attaching a debugger.
     
  5. jcooper0211

    jcooper0211

    Joined:
    May 21, 2015
    Posts:
    7
    Okay I see what you're saying. I'll work on this approach today. Thank you so much.
     
  6. jcooper0211

    jcooper0211

    Joined:
    May 21, 2015
    Posts:
    7
    Hey Kurt. I'm geting closer. What I did was set temporary variables to handle the values of the y-position and x-rotation. The good thing is, when I press the button, the values switch to their negative values correctly, but they do not change their physical location. Can you look at my code to see how I could make the camera snap to the new location? "1" is the index of my TopViewCamera.


    Code (CSharp):
    1. public Camera[] cameras;
    2.     private int currentCameraIndex;
    3.     public float yPosition = 0f;
    4.     public float xRotation = 0f;
    5.  
    6. void Start ()
    7.     {
    8.         yPosition = cameras[1].transform.position.y;
    9.         xRotation = cameras [1].transform.rotation.x;
    10.     }
    11.  
    12. void Update ()
    13.     {
    14.         if(Input.GetKeyDown(KeyCode.V)|| Input.GetButtonDown(buttonName: "Polarity Switch"))
    15.         {
    16.             if(cameras[currentCameraIndex].camera.name == "Top_Bot_Cam")
    17.             {
    18.                 yPosition = yPosition * -1.0f;
    19.                 xRotation = xRotation * -1.0f;
    20.             }
    21.  
    22.     }
     
  7. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,514
    In the codelet above, lines 8 and 9 copy out the current position.y and rotation.x and store it in yPosition and xRotation which are instance variables in your class.

    If the lines at 18 and 19 are executed, those local copies of the values are changed (multiplied by -1.0f) and stored again in the instance variables.

    This does nothing to the camera itself. You need to get those variables back into the camera's transform. You cannot set individual fields of a position, but rather you need to assign the entire position a new value, like so:

    Code (csharp):
    1. foo.position = new Vector3( foo.position.x, yPosition, foo.position.z);
    As for rotation, that's quite a bit trickier, because you need to know what the other rotations of the object are before you go setting the object's new Euler angles. Assuming the other rotations are zero, you can do this:

    Code (csharp):
    1. foo.position = Quaternion.Euler( xRotation, 0, 0);
    But ONLY if the Y rotation and Z rotation are supposed to be zero. You can get the Euler angles out of the original quaternion, but there are edge cases were you get something called gimbal lock, and things don't work quite as expected.
     
  8. jcooper0211

    jcooper0211

    Joined:
    May 21, 2015
    Posts:
    7
    Hey Kurt. Just looked at your reply. I figured it out about 2 hours ago. I used the approach similar to the one that you stated above. Here's the code:

    Code (CSharp):
    1.  
    2.  
    3.     public Camera[] cameras;
    4.     private int currentCameraIndex;  
    5.     private float yPosition = 0f;
    6.     private float xRotation = 0f;
    7.     private Vector3 topVector = new Vector3();
    8.     private Quaternion topAngles = new Quaternion();
    9.  
    10. void Start ()
    11.     {
    12.         yPosition = cameras[1].transform.position.y;
    13.         xRotation = cameras [1].transform.rotation.x;
    14.         topVector = cameras [1].transform.position;
    15.         topAngles = cameras [1].transform.rotation;
    16.     }
    17.  
    18. if(Input.GetKeyDown(KeyCode.V)|| Input.GetButtonDown(buttonName: "Polarity Switch"))
    19.         {
    20.             if(cameras[currentCameraIndex].camera.name == "Top_Bot_Cam")
    21.             {
    22.                 yPosition = yPosition * -1.0f;
    23.                 xRotation = xRotation * -1.0f;
    24.                 topVector.y = Mathf.Clamp (yPosition, yPosition, yPosition);
    25.                 cameras[currentCameraIndex].transform.position = topVector;
    26.                 topAngles.x = xRotation;
    27.                 cameras[currentCameraIndex].transform.rotation = topAngles;
    28.        }
     
  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,514
    Very nice! That's a good pattern, storing the entire rotation, because then you can manipulate other directions at will too. Glad it worked out.
     
  10. jcooper0211

    jcooper0211

    Joined:
    May 21, 2015
    Posts:
    7
    Yes thank you so much for your help! Let's be sure to stay in touch. Your hints really helped to point me in the right thought process.