Converting Canvas Space to Screen Space
Hey all,
I am very new to Unity and my math skills aren't fantastic so I apologise in advance for the newbie question ;)
Scenario:
I have a camera in my scene and I need to adjust it's viewport as the screen resizes in order to place it correctly within the surrounding UI. I decided to add a container to my UI canvas so that this can be repositioned and sized automatically by Unity. Then my plan was to simply make the cameras viewport position and size match it.
In order to achieve this I decided to make the below component to handle the repositioning and resizing.
I am working in 2D and therefore using an orthographic camera, the UI is in Screen Overlay mode with a pixel perfect canvas. There is also a canvas scaler set to scale to screen size with a reference resolution of 1920x1080.
Code:
Note that I have removed contractual (null) checks from Awake to make the example more concise:
public class ScaleCameraViewportToCanvasTarget : MonoBehaviour
{
// Unity visible properties
public GameObject ScaleToFit;
//
private Camera cameraToScale;
private RectTransform targetTransform;
private Canvas canvas;
public void Awake()
{
this.targetTransform = ScaleToFit.GetComponent<RectTransform>();
this.canvas = ScaleToFit.GetComponentInParent<Canvas>();
this.cameraToScale = GetComponent<Camera>();
}
public void LateUpdate()
{
// Determine target dimensions taking in to consideration canvas scaling
var width = targetTransform.rect.width * canvas.scaleFactor;
var height = targetTransform.rect.height * canvas.scaleFactor;
// Get centre point of our target in Canvas Space
var centreX = targetTransform.anchoredPosition.x;
var centreY = targetTransform.anchoredPosition.y;
// Adjust for anchor from centre to upper left
var left = centreX - (width * 0.5f);
var top = centreY - (height * 0.5f);
// Convert from Canvas Space to Screen Space
left = left + (Screen.width * 0.5f);
top = top + (Screen.height * 0.5f);
// Adjust camera viewport
var targetRect = new Rect(left, top, width, height);
this.cameraToScale.pixelRect = targetRect;
}
}
Problem:
This does kinda work but it isn't accurate. When canvas scaling is off everything works as expected but when it is enabled the width and height are correct but the left, top positioning isn't correct.
For example:
X is -0.03813588 when it should be 0.0
Y is 0.04876541when it should be 0.03876541 (±)
Any ideas? I have spent a lot of time on this and I can't help but think I am doing something fundamentally wrong.
Many thanks in advance,
Mark
is it -0.03813588, or is it -0.03813588e-xx
The second one means "as good as zero", and happens because of rounding errors (which are invevitable whenever you're working with floating point numbers). It's not actually visible on the screen ever.
@Baste - The former (no exponent). It is worth me clarifying that I see a noticeable visual difference between 0.0 and -0.03813588, hence the problem.
Answer by SuperMoog · Aug 24, 2015 at 08:03 AM
Hi all,
I have worked out the issue myself. I wasn't adjusting the position by the scale factor. Applying the factor to the anchored position prior to manipulation fixes the issue:
var centreX = targetTransform.anchoredPosition.x * canvas.scaleFactor;
var centreY = targetTransform.anchoredPosition.y * canvas.scaleFactor;
Many Thanks,
Mark
Answer by Malkyne · Dec 13, 2015 at 08:13 PM
For future reference, this is the code I use to do the same thing.
Whenever your RectTransform resizes, it's going to trigger OnRectTransformDimensionsChange. You can use this to do your camera viewport adjustments. So, you can put this code on your RectTransform:
[RequireComponent(typeof(RectTransform))]
public class CameraViewSizer : MonoBehaviour
{
[SerializeField]
private Camera _camera;
protected void OnRectTransformDimensionsChange()
{
_camera.pixelRect = Util.RectTransformToScreenSpace((RectTransform)this.transform);
}
}
This is the RectTransformToScreenSpace function used above:
public static class Util
{
public static Rect RectTransformToScreenSpace(RectTransform transform)
{
Vector2 size = Vector2.Scale(transform.rect.size, transform.lossyScale);
return new Rect((Vector2)transform.position - (size * 0.5f), size);
}
}
Your answer
Follow this Question
Related Questions
Moved GameObject under Canvas (Parent), transform.position vector coordinates mismatch 0 Answers
How to detect if a scrollrect is close to reach its bottom? 0 Answers
Cell size to Canvas size 1 Answer
UI Canvas Anchor points are stuck in the bottom left corner and disabled when in overlay mode. 1 Answer
How to tell if a RectTransform is within the visible area of a ScrollRect 1 Answer