Search Unity

Make sure 4 points of bounds are always visible no matter the aspect ratio

Discussion in 'Scripting' started by Hertzole, Sep 2, 2021.

  1. Hertzole

    Hertzole

    Joined:
    Jul 27, 2013
    Posts:
    422
    Hi

    I'm having a really hard time with some math to make sure my perspective camera can always see my fixed levels.

    I have a 16x9 level and the camera sees it like this, as expected... as long as the camera aspect ratio is 16x9.


    But if I change the aspect ratio to something like 4:3 it will cut off the edges.

    (aspect ratios not completely accurate)

    I want to make sure the bounds are always in view. So if you were in 4x3 you would see something like this

    where the top and bottom are way larger.
    Likewise, if you were on 16x10 the sides would be larger.


    I can not change the field of view, so I have to calculate the distance and more the camera on the Z-axis, that much I know. Also due to some other technical reasons as to how our levels are built, I can not use any UI components either.

    I've been looking around at multiple solutions but none have really taken the aspect ratio of my perspective camera into account.

    Any help is appreciated!
     
    Last edited: Sep 2, 2021
  2. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    I think maybe the aspect ratio fitter might work for this.
    https://docs.unity3d.com/Packages/com.unity.ugui@1.0/manual/script-AspectRatioFitter.html

    If this is a UI image. Then I think if the width is set to stretch so when the screen width is smaller, the Ratio fitter can adjust the height also. I haven't used this fitter directly, but I have seen it in a few work projects.

    The other option is you can set the canvas the UI component is under to match width and then when the UI image is adjusted, grab it's new width, old width, old height, and calculate the new height yourself.
     
  3. Hertzole

    Hertzole

    Joined:
    Jul 27, 2013
    Posts:
    422
    Unfortunately, I'm not using UI. I need to move the actual camera. Sorry for not specifying that!
     
  4. Lekret

    Lekret

    Joined:
    Sep 10, 2020
    Posts:
    353
    You can try Camera.WorldToViewportPoint(). If I'm not mistaken, if result is X and Y are above -1 and below 1, then object is visible, or at least its center. It should work no matter what aspect ration you have. I guess it can be helpful.
     
  5. Hertzole

    Hertzole

    Joined:
    Jul 27, 2013
    Posts:
    422
    I've seen some solutions that mention this but not much else. I don't know how I would use this to calculate the distance the camera needs to be at, unfortunately.
     
  6. PizzaPie

    PizzaPie

    Joined:
    Oct 11, 2015
    Posts:
    106
    With the restriction of the camera be perpendicular and centered to target quad you can use something like this.


    Code (CSharp):
    1. private void PositionCameraToCoverFullWidth(Camera cam, Transform targetObjectTransform)
    2.     {
    3.         var fovYRad = cam.fieldOfView * Mathf.Deg2Rad;
    4.         //calculate field of view in x (horizontal) axis
    5.         var fovXRad = Mathf.Atan(cam.aspect * Mathf.Tan(fovYRad/2f))*2f;
    6.  
    7.         //get the width of the target quad
    8.         var width = targetObjectTransform.GetComponent<MeshFilter>().mesh.bounds.size.x * targetObjectTransform.localScale.x;
    9.      
    10.    
    11.  
    12.         //calculate distance of the camera so the width of the target quad match the camera width at that point in the world
    13.         var targetDistance = (width/2f) / Mathf.Tan(fovXRad/2f );
    14.  
    15.         //move camera to wanted position
    16.         cam.transform.position = targetObjectTransform.transform.position - targetObjectTransform.transform.forward.normalized * targetDistance;
    17.  
    18.         //make sure that camera looks at the wanted target
    19.         cam.transform.LookAt(targetObjectTransform.transform);
    20.     }
    The principal is fairly simple which is that tangent of the half of fov angle in horizontal axis is equal to the half the width of the plane divided by the distance of the plane (from the camera). (tan (fovX/2f) = (obj.width/2f) / (distance))
     
    Schneider21 and Hertzole like this.
  7. Hertzole

    Hertzole

    Joined:
    Jul 27, 2013
    Posts:
    422
    This worked perfectly! The only thing it doesn't do is take into account the vertical bounds, but I don't think that will be important for my use case! Thank you!
     
  8. PizzaPie

    PizzaPie

    Joined:
    Oct 11, 2015
    Posts:
    106
    To do that your image should have the same aspect as the camera or you ll have to scale your quad.
    If you want to just fit it based on vertical length you need to change the

    Code (CSharp):
    1.  
    2. var targetDistance = (width/2f) / Mathf.Tan(fovXRad/2f );
    3.  
    4. //to
    5.  
    6. var targetDistance = (height/2f) / Mathf.Tan(fovYRad/2f );

    if you want to make sure that the whole thing is inside the frame you can calculate both distances based on above and assign the bigger value.


    Code (CSharp):
    1. var distX = (width/2f) / Mathf.Tan(fovXRad/2f );
    2. var distY = (height/2f) / Mathf.Tan(fovYRad/2f );
    3.  
    4. var targetDistance = distX > distY ? distX : distY;
    Where height can be acquired same way as width from y dimention of bounds.
     
    Hertzole and Schneider21 like this.
  9. Hertzole

    Hertzole

    Joined:
    Jul 27, 2013
    Posts:
    422
    This is perfect, thank you so much!