【Unity】RectTransformのサイズをスクリプトから変更する

こじゃらこじゃら

次のようなUIのサイズをスクリプトから指定する方法はないの?

このはこのは

RectTransform.SetSizeWithCurrentAnchorsメソッドを使うと良いわ。

Unity UI(uGUI)などのRectTransform幅(width)高さ(height)をスクリプトから指定したい場合、RectTransform.SetSizeWithCurrentAnchorsメソッドを使うと実現できます。

サイズを指定するコード
// RectTransformコンポーネントの取得
RectTransform rectTransform = GetComponent<RectTransform>();

・・・(中略)・・・

// UIの幅(水平方向のサイズ)を300に変更する
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 300);

// UIの高さ(垂直方向のサイズ)を200に変更する
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 200);

RectTransform.sizeDeltaプロパティの変更でも可能ですが、サイズをそのまま代入するだけでは正しい挙動にならないケースがあり、親要素とアンカー領域のサイズを考慮した計算値を指定する必要があります。

sizeDeltaで直接指定する場合

不十分な例

RectTransform rectTransform = GetComponent<RectTransform>();
Vector2 size = new Vector2(300, 200);

・・・(中略)・・・

// アンカーが引き延ばされた場合に代入値通りのサイズにならない
rectTransform.sizeDelta = size;

正しい例

RectTransform rectTransform = GetComponent<RectTransform>();
Vector2 size = new Vector2(300, 200);

・・・(中略)・・・

// 親要素のサイズ取得
RectTransform parent = rectTransform.parent as RectTransform;
Vector2 parentSize = parent != null ? parent.rect.size : Vector2.zero;

// 自身のアンカーサイズ計算
Vector2 anchorSize = parentSize * (rectTransform.anchorMax - rectTransform.anchorMin);

// 入力サイズからアンカーサイズを引いた結果が
// sizeDeltaに指定すべき値
rectTransform.sizeDelta = size - anchorSize;

SetSizeWithCurrentAnchorsメソッドは、正しい例で示したような計算を内部的に行ってくれるので、基本的にこちらを使えば問題ありません。

sizeDeltaプロパティは、アンカー間との距離と比較したRectTransformのサイズとなっており、単純なサイズではありません。誤解されやすい点なので注意する必要があります。

本記事では、SetSizeWithCurrentAnchorsメソッドを用いたUIサイズの指定方法を解説していきます。また、メソッド内部の計算や使い方の工夫例についても紹介します。

動作環境
  • Unity 2022.2.13f1
  • Unity UI 1.0.0

スポンサーリンク

前提条件

次のようにパネル(親要素)の下にUI(子要素)が配置されているものとします。

本記事では、上記画像(子要素)に対してサイズ変更を適用していくことを例にとって解説していきます。

SetSizeWithCurrentAnchorsメソッドの使い方

指定されたUI要素のサイズを指定するメソッドです。

次のような形式で定義されています。

public void SetSizeWithCurrentAnchors(RectTransform.Axis axis, float size);

第1引数axisには、サイズ変更対象の軸を指定します。Axis.Horizontalで水平方向Axis.Verticalで垂直方向が指定できます。

第2引数sizeには、変更後のUIサイズを指定します。水平方向ならx軸方向の幅垂直方向ならy軸方向の高さとなります。

サンプルスクリプト

以下、自身のRectTransformのサイズを指定するサンプルスクリプトです。

SetSizeExample.cs
using UnityEngine;

public class SetSizeExample : MonoBehaviour
{
    // 自身のUIに指定するサイズ
    [SerializeField] private Vector2 _size = new Vector2(100, 100);

    private RectTransform _rectTransform;

    // 初期化
    private void Awake()
    {
        // 予めRectTransformを取得して保持しておく
        _rectTransform = GetComponent<RectTransform>();
    }

    // 毎フレーム処理
    private void Update()
    {
        // nullチェック
        if (_rectTransform == null) return;

        // 自身のUIのサイズを指定する
        _rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _size.x);
        _rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _size.y);
    }
}

上記スクリプトをSetSizeExample.csという名前で保存し、サイズを変更したいUI(RectTransformが適用されているオブジェクト)に追加すると機能するようになります。

インスペクターより指定したいサイズを反映できるようにしています。

実行結果

インスペクターより指定したサイズがUIに反映されていることを確認できました。

途中でアンカーを変更していますが、ストレッチされた状態でも正しく動作するようになっています。

Tips

アンカー領域シーンビュー上の三角マークとして表示されます。

ストレッチされていない時centerが指定されている)は4つの三角マークが一点に集中していますが、ストレッチされている時stretchが指定されている)は三角マークの領域が拡大されます。

アンカーがcenterの場合
アンカーがstretchの場合

スクリプトの説明

以下部分で水平方向と垂直方向それぞれでサイズを反映させています。

// 自身のUIのサイズを指定する
_rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _size.x);
_rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _size.y);

水平と垂直同時に指定したい場合は、メソッドを2回呼び出す必要があります。

親要素が変更された場合への対応

親要素のサイズが変更されると、その子要素のサイズが変化することがあります。

これは、子要素のアンカー領域が一点でない場合stretchに設定されている場合)に起こります。

このような場合でも子要素のサイズを維持したい場合、親要素のサイズを変更した時に子要素のサイズを設定し直す必要があります。

前述のサンプルスクリプトはUpdateイベントで毎フレーム更新しているため、子要素のサイズは維持されます。 [1]

メソッドの内部挙動

RectTransform.SetSizeWithCurrentAnchorsメソッドは、内部的には子要素のアンカーサイズに基づいて計算した値をRectTransform.sizeDeltaプロパティに指定しています。

次のような計算式になっています。

sizeDeltaの計算式

sizeDelta = 指定サイズ – 子要素のアンカーサイズ

子要素のアンカーサイズ = 親要素のサイズ × (anchorMax – anchorMin)

指定サイズの単位はピクセルですが、anchorMinanchorMax親サイズを1とした単位です。

そのため、ピクセル単位のアンカーサイズを求めるために、親要素のサイズを掛ける必要があります。

参考:UnityCsReference/RectTransform.bindings.cs at master · Unity-Technologies/UnityCsReference

親要素のサイズ次のようなコードで計算できます。

// 親要素のサイズ取得
RectTransform parent = rectTransform.parent as RectTransform;
Vector2 parentSize = parent != null ? parent.rect.size : Vector2.zero;

RectTransform.rectプロパティに親要素の領域情報が格納されているため、これのsizeプロパティから矩形のサイズが得られます。

子要素のアンカーサイズは次のようなコードで計算されます。

// 自身のアンカー領域サイズ計算
Vector2 anchorSize = parentSize * (rectTransform.anchorMax - rectTransform.anchorMin);

アンカーにcenterが指定されている場合はanchorMaxとanchorMinプロパティの値が一致するため、アンカーサイズは0になります。

stretchが指定されている場合、anchorMinとanchorMaxの差は1となります。これらは、親要素のサイズを基準に正規化された値であるためです。

そのため、アンカーサイズは前者に親要素サイズを掛けることで求めます。

そして、最終的に次のように計算式で計算した結果をsizeDeltaプロパティにセットしています。

// 親とアンカーと入力サイズから
// sizeDeltaに指定すべき値を計算して反映
rectTransform.sizeDelta = size - anchorSize;

幅と高さを一緒に指定できる拡張メソッドを自作する

ここまでx軸とy軸それぞれでSetSizeWithCurrentAnchorsメソッドを呼び出してサイズ指定する方法を紹介しました。

メソッドを2回呼び出すのが不便な場合は、拡張メソッドを自作すれば楽できるでしょう。

以下、SetSizeというRectTransformの拡張メソッドを実装した例です。

RectTransformExtensions.cs
using UnityEngine;

public static class RectTransformExtensions
{
    /// <summary>
    /// RectTransformにサイズを指定する
    /// </summary>
    public static void SetSize(this RectTransform rectTransform, Vector2 size)
    {
        // 各軸それぞれサイズ指定
        rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, size.x);
        rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, size.y);
    }
}

内部的にSetSizeWithCurrentAnchorsメソッドを2回呼び出す処理を行うようにすれば良いです。

ただし、この場合はRectTransform.parentやRectTransform.sizeDeltaプロパティに2度余分にアクセスしてしまうため、パフォーマンスを求める場合は次のようなコードにすると良いでしょう。

RectTransformExtensions.cs
using UnityEngine;

public static class RectTransformExtensions
{
    /// <summary>
    /// RectTransformにサイズを指定する
    /// </summary>
    public static void SetSize(this RectTransform rectTransform, Vector2 size)
    {
        // 親要素のサイズ取得
        var parent = rectTransform.parent as RectTransform;
        var parentSize = parent != null ? parent.rect.size : Vector2.zero;

        // 自身のアンカーサイズを計算
        var anchorSize = parentSize * (rectTransform.anchorMax - rectTransform.anchorMin);

        // 入力サイズからアンカーサイズを引いた結果が
        // sizeDeltaに指定すべき値
        rectTransform.sizeDelta = size - anchorSize;
    }
}

上記をRectTransformExtensions.csという名前でUnityプロジェクトに保存すると拡張メソッドが使えるようになります。

使う側は以下のようになります。

UseExtensionExample.cs
using UnityEngine;

public class UseExtensionExample : MonoBehaviour
{
    // 自身のUIに指定するサイズ
    [SerializeField] private Vector2 _size = new Vector2(100, 100);

    private RectTransform _rectTransform;

    // 初期化
    private void Awake()
    {
        // 予めRectTransformを取得して保持しておく
        _rectTransform = GetComponent<RectTransform>();
    }

    // 毎フレーム処理
    private void Update()
    {
        // nullチェック
        if (_rectTransform == null) return;

        // xy軸のサイズを一気に指定する
        _rectTransform.SetSize(_size);
    }
}

以下のように指定処理が非常にシンプルになりました。

// xy軸のサイズを一気に指定する
_rectTransform.SetSize(_size);

さいごに

RectTransformのサイズをスクリプトから変更したい場合、RectTransform.SetSizeWithCurrentAnchorsメソッドを使えば実現できます。

ただし、幅と高さを一度に指定できず、各軸ずつメソッドを2回呼ぶ形で指定する必要があります。

一度に指定するメソッドはUnity2022.2現在ではUnityから提供されていないため、拡張メソッドを自作して対応すると良いでしょう。

関連記事

参考サイト

スポンサーリンク