背景
UI要素のサイズ(RectTransform.sizeDelta)を別UI要素に追従させ、見た目上で一致させたい
- 一致させる対象が直接の親の場合
- Stretch指定でOK
- 一致させる対象が子かつ子の数が1つの場合
- 下記公式ドキュメントの実装で可能
- https://docs.unity3d.com/ja/2018.4/Manual/HOWTO-UIFitContentSize.html
- Horizontal/Vertical Layout Groupを使用する必要があり、自動で整列されるため子1つのみの制約あり
- 構成が複雑でわかりづらい
上記の様なヒエラルキーや要素数に依存しないコンポーネントが欲しかったので自作しました
また、Inspector上でのサイズ変更を不可にしました
UISizeFitter
using UnityEngine;
[ExecuteInEditMode, RequireComponent(typeof(RectTransform))]
public class UISizeFitter : MonoBehaviour
{
[SerializeField] RectTransform _target;
RectTransform _selfRect;
Vector3 _cachedTargetLossyScale;
Vector2 _cachedTargetSizeDelta;
Vector3 _cachedSelfLossyScale;
bool _isDirty = true;
void UpdateDirty()
{
if (_cachedTargetSizeDelta == _target.sizeDelta &&
_cachedTargetLossyScale == _target.lossyScale &&
_cachedSelfLossyScale == _selfRect.lossyScale)
{
return;
}
_cachedTargetSizeDelta = _target.sizeDelta;
_cachedTargetLossyScale = _target.lossyScale;
_cachedSelfLossyScale = _selfRect.lossyScale;
_isDirty = true;
}
void AdjustSize()
{
if (!_isDirty)
{
return;
}
if (_selfRect.lossyScale.x != 0 && _selfRect.lossyScale.y != 0)
{
_selfRect.sizeDelta = _cachedTargetSizeDelta * _cachedTargetLossyScale / _selfRect.lossyScale;
}
_isDirty = false;
}
// Layout Groupみたいに「Some values driven by 〜」とInspector上の編集を禁止する
void LockRectTransformProperty()
{
DrivenRectTransformTracker dt = new DrivenRectTransformTracker();
dt.Clear();
dt.Add(this, _selfRect, DrivenTransformProperties.SizeDelta | DrivenTransformProperties.Scale);
}
void FitSize()
{
if (_target == null)
{
return;
}
UpdateDirty();
AdjustSize();
}
void Awake()
{
_selfRect = GetComponent<RectTransform>();
_selfRect.transform.localScale = Vector2.one;
LockRectTransformProperty();
}
void Start()
{
FitSize();
}
void Update()
{
FitSize();
}
}
補足
常に追従させたかったのでUpdateで実行していますが、負荷的に問題になったり、動的に変更しないのであればStartの一度のみ実行するのが良いと思います
また、 lossyScale
がUnity側でキャッシュしているのか毎回計算しているのかが不明だったため、とりあえずComponent側でキャッシュしていますがUnity側で毎回計算している様だと比較時の負荷が若干気になります
_cached〜でキャッシュしているのは比較の方がコピーより高速だろうという考えの下です