【Unity】UI Toolkitにおけるローカル空間・ワールド空間の概念と変換方法

UnityのUI Toolkitにおけるローカル空間とワールド空間の概念およびその変換方法についてまとめました。

Unity2022.2.17

ローカル空間とワールド空間

UI Toolkitにはローカル空間とワールド空間という概念があります。

ローカル空間は、親のVisual Elementを基準とした空間のことです。
これに対してワールド空間は、全てのVisual ElementのルートにあるPanel(EditorWindowの場合にはウィンドウ)を基準とした空間のことです。

ローカル・ワールド空間変換

VisualElementには拡張メソッドとして、Vector2Rect型をローカル・ワールド空間にそれぞれ相互変換するメソッドが用意されています。

VisualElement element;
element.WorldToLocal(pos); // ワールド空間のVector2やRectをローカル空間に変換する
element.LocalToWorld(pos); // ローカル空間のVector2やRectをワールド空間に変換する

またこれとは別に、あるVisualElementのローカル空間のVector2Rectを別のVisualElementのローカル空間に移すメソッドも用意されています。
内部的には上記のメソッドを呼んでいるだけですが、こちらの方が使用頻度は高そうです。

VisualElement element1;
VisualElement element2;
element1.ChangeCoordinatesTo(element2, pos);

挙動を試してみる

最後に簡単に挙動を試してみます。
まず下図のようなレイアウトを作ります。
赤、緑、青のボタンを適当な場所にPosition: Absoluteで配置し、白い矩形も配置しておきます。
それぞれサイズは (100, 100)です。

レイアウト

次にこれを描画するEditorWindowを作成します。
今回はそれぞれのボタンを押下した時に、そのボタンのローカル空間における (100, 100) の位置に白い矩形(Target)を移動させるようにしました。

using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

public sealed class Example : EditorWindow
{
    [SerializeField] private VisualTreeAsset visualTree;

    private void CreateGUI()
    {
        visualTree.CloneTree(rootVisualElement);
        var redButton = rootVisualElement.Q<Button>("RedButton");
        var greenButton = rootVisualElement.Q<Button>("GreenButton");
        var blueButton = rootVisualElement.Q<Button>("BlueButton");
        var target = rootVisualElement.Q<VisualElement>("Target");

        void OnButtonClicked(VisualElement buttonElement)
        {
            // 各ボタンをクリックした時に、そのローカル空間の (100, 100) の位置にTargetを移動させる
            var newPos = buttonElement.ChangeCoordinatesTo(rootVisualElement, Vector2.one * 100);
            target.style.left = newPos.x;
            target.style.top = newPos.y;
        }

        redButton.clicked += () => OnButtonClicked(redButton);
        greenButton.clicked += () => OnButtonClicked(greenButton);
        blueButton.clicked += () => OnButtonClicked(blueButton);

        // ちなみにこのCreateGUIの時点だとworldTransformが正常に入ってきておらず正しく空間を変換できない
        // delayCall後にやるとうまくいく
        //EditorApplication.delayCall += () => { ... };
    }

    [MenuItem("Window/Example")]
    public static void ShowWindow()
    {
        GetWindow<Example>();
    }
}

ちなみにコメントとして書いていますが、CreateGUIが呼ばれた時点では座標変換がうまく行きませんでした(不具合なのか仕様なのかは不明)。

Window > Exampleからウィンドウを開き、各ボタンを押下すると下図のように動作することを確認できます。

動作

参考

docs.unity3d.com