- Home /
Simple way to fit rotated UI element to parent
Hi there.
I'm working on a simple 2D interface for my game but there's one thing I haven't been able to sort without resorting to scripting. I'm looking to rotate an Image on a UI canvas and have it stretch to the size of the canvas. If I switch it to stretch on both axes, set the offsets to 0 and then Z rotation to 90, the aspect ratio is wrong.
In situations where the image or UI element does not depend on the parent, I can see how this makes sense, but to simply stretch to fit it's a pain.
To show what I mean:
I want my map to rotate based on the screen aspect ratio, but still stretch to the screen. The only way I can think to do this is to write a script that checks the size of the parent and uses that to override the size of the image. This would be a pain to get working reliably and so that it updates properly in the editor, etc.
Any ideas?
It seems this is also the same for the autolayout components. Add a HorizontalLayoutGroup to the parent of the image and it will stretch before it rotates, so the end result looks like above and does not correctly constrain the image.
I've tried writing a script to do this by getting the sizeDelta of the parent and setting the the sizeDelta of the image to (parentDelta.y, parentDelta.x), but it seems to refuse to get the correct values. For some reason it's always a few pixels out so the image is close but not quite right.
Still need help here guys :)
Ok, completely stumped now. In scripts I don't seem to be able to find a consistent way to get the dimensions of the parent (I've tried adding a Debug.Log that prints out various properties of the parent RectTransform, but I just can't work out which values directly relate to its size. I know that the canvas size is 640x360, and with a parent panel that's stretched to fit, I cannot find a single property that contains those 2 numbers. $$anonymous$$y $$anonymous$$d has just gone numb :P).
I'm giving up and moving on for a while to clear my head, but I'm hoping I don't have to resort to 2 images - a portrait and landscape. That's a huge waste of memory.
Use layout elements, specifically "Layout Element" and "Content size fitter".
Layout groups technically don't do much without the "Layout element" in place, and can overflow without "Content size fitter" in place.
I've tried that, but all the layout elements seem to process scale before rotation, so the end result is stretched along the wrong axis. Thanks for the suggestion though, but I don't think I can get them to work unless I'm missing something.
You're rotating the wrong thing. You're not meant to rotate the content, but rather the panel which contains the content, the one with the content size fitter. $$anonymous$$ake an extra panel for it if you must :)
Answer by YondernautsGames · Mar 19, 2015 at 03:46 PM
Ok, this is the best solution I've come up with:
using UnityEngine;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
#endif
[ExecuteInEditMode]
[RequireComponent(typeof(RectTransform))]
public class RotationFitter : MonoBehaviour, ILayoutSelfController {
private RectTransform canvasTransform;
void Awake() {
canvasTransform = GetComponentInParent<Canvas>().gameObject.transform as RectTransform;
}
public void SetLayoutHorizontal() {
RectTransform rt = transform as RectTransform;
rt.sizeDelta = new Vector2(canvasTransform.sizeDelta.y, canvasTransform.sizeDelta.x);
}
public void SetLayoutVertical() {
RectTransform rt = transform as RectTransform;
rt.sizeDelta = new Vector2(canvasTransform.sizeDelta.y, canvasTransform.sizeDelta.x);
}
#if UNITY_EDITOR
void Update () {
if (canvasTransform == null)
canvasTransform = GetComponentInParent<Canvas>().gameObject.transform as RectTransform;
RectTransform rt = transform as RectTransform;
rt.sizeDelta = new Vector2(canvasTransform.sizeDelta.y, canvasTransform.sizeDelta.x);
}
#endif
}
The [ExecuteInEditMode] and the editor only Update() function are required to get it to respond automatically in the editor. I've also created it as a LayoutController, but I'm not sure whether this is the best way, or whether I even need to fill both layout functions. Good enough though. Unless someone comes up with better, I'ma shippin it :P
Just to clarify its usage, this needs to be attached to the Image UI Object (or whatever it is that needs scaling), and will scale it to match the canvas size.
you are setting sizeDelta, that doesn't solve the issue from the source, other components that use sizeDelta will fail, like Aspect Ratio Fitter, this should be a bug report since there is no efficient way to solve the issue.
Answer by Pavulon · Apr 07, 2017 at 02:59 PM
My solution for this problem was this script which I was attaching for every element which needs to be rotated by +/-90 degrees:
using UnityEngine;
[RequireComponent(typeof(RectTransform))]
public class RotatedNodeFillParent : MonoBehaviour {
private RectTransform rectTransform;
void Awake() {
rectTransform = GetComponent<RectTransform>();
OnRectTransformDimensionsChange();
}
private void OnRectTransformDimensionsChange() {
if (!rectTransform) {
// OnRectTransformDimensionsChange might be called before Awake - let's ignore this call as we will update during Awake anyway.
return;
}
if (!transform.parent) {
return;
}
RectTransform parentTransform = transform.parent.GetComponent<RectTransform>();
if (!parentTransform) {
return;
}
float aspectRatio = parentTransform.rect.size.x / parentTransform.rect.size.y;
float halfAspectRatio = aspectRatio / 2.0f;
float halfAspectRatioInvert = (1.0f / aspectRatio) / 2.0f;
rectTransform.anchorMin = new Vector2(0.5f - halfAspectRatioInvert, 0.5f - halfAspectRatio);
rectTransform.anchorMax = new Vector2(0.5f + halfAspectRatioInvert, 0.5f + halfAspectRatio);
rectTransform.anchoredPosition = Vector3.zero;
rectTransform.offsetMin = Vector2.zero;
rectTransform.offsetMax = Vector2.zero;
}
}