UI element large as sliced sprite internal rect
Hi. I want to have a RectTransform exactly large as the internal rect of a sliced image. The problem seems almost simple, you have to put the anchors as 0 and 1 and put some value for padding, but when the canvas has a canvas scaler, it can change the size of the borders, so the solution seems to be the a script. I started with the following one:
using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof(RectTransform))]
public class FillSize : MonoBehaviour
{
private void Start()
{
var borders = GetComponentInParent<Image>().sprite.border;
var rectTransform = GetComponent<RectTransform>();
rectTransform.anchorMin = Vector2.zero;
rectTransform.anchorMax = Vector2.one;
rectTransform.offsetMin = new Vector2(borders.x, borders.y);
rectTransform.offsetMax = -new Vector2(borders.z, borders.w);
}
}
It almost works, but the offset should be scaled by a factor specifying how much pixels are large in the RectTransform. It is possible, obviously, to do the math with the values of the canvas scaler (even if I didn't totally understand the reference pixels per unit yet), but it's very complex because the scaler and the hierarchy can have a lot of different settings, and I should consider all of them. So, is there a different path?
Answer by Totoro83y · Apr 05, 2018 at 06:35 PM
I think I solved the problem, the factor to scale the offset seems to be the one obtained by canvas.referencePixelsPerUnit / sprite.pixelsPerUnit, so the full code is something like this:
using UnityEngine;
using UnityEngine.UI;
[ExecuteInEditMode]
[DisallowMultipleComponent]
[RequireComponent(typeof(RectTransform))]
///Use it under a Sliced or Tiled UI.Image to have the RectTransform on the
///GameObject containg this script filling the space inside the former Image
///inner rect
public class FillInSlicedImage : MonoBehaviour
{
private void Start()
{
Fill();
}
///Call it when the borders could be changed (main cases are when the
///sprite changed, when the referencePixelsPerUnit in the canvas changed
///or when something in the hierarchy that can change borders changed)
public void Fill()
{
var image = transform.parent.GetComponent<Image>();
var canvas = GetComponentInParent<Canvas>();
var rectTransform = GetComponent<RectTransform>();
var sprite = image.overrideSprite ?? image.sprite;
var border = sprite.border;
#if UNITY_EDITOR
if (image.type != Image.Type.Sliced && image.type != Image.Type.Tiled)
throw new System.Exception("Parent Image not Sliced or Tiled");
#endif
rectTransform.anchorMin = Vector2.zero;
rectTransform.anchorMax = Vector2.one;
border *= canvas.referencePixelsPerUnit / sprite.pixelsPerUnit;
rectTransform.offsetMin = new Vector2(border.x, border.y);
rectTransform.offsetMax = -new Vector2(border.z, border.w);
}
#if UNITY_EDITOR
private void LateUpdate()
{
if (UnityEditor.EditorApplication.isPlaying)
return;
try
{
Fill();
}
catch
{
}
}
[UnityEditor.Callbacks.PostProcessScene]
private static void OnPostProcessScene()
{
try
{
foreach (var o in FindObjectsOfType<FillInSlicedImage>())
o.Fill();
}
catch
{
Debug.LogError("FillSize must have an Image (the first one if " +
"many) of type Sliced or Tiled in the parent and " +
"a Canvas in a grandparent");
}
}
#endif
}