【Unity】【UI Toolkit】VisualElementの並び順を動的に変更する

UnityのUI ToolkitでVisualElementの並び順を動的に変更する方法についてまとめました。

Unity 2022.2.17

UI Toolkitにおける並び順

UI Toolkitでは、ツリーにおける各VisualElementの並び順が重要な意味を持ちます。

一つ目がレイアウトへの影響です。
UI ToolkitはFlexboxの概念に基づいたレイアウトシステムを採用しているため、子要素をその並び順に応じてレイアウトします。

また、並び順は描画順にも影響を与えます。
UI Toolkitでは、深さ優先探索により描画順が決定されます。
要するに、UI BuilderのHierarchyにおいてより上にあるものから優先的に描画されます。

上にあるものから優先的に描画

本記事ではこのように重要な意味を持つ並び順をランタイムで制御する方法についてまとめます。

並び順を動的に変更するメソッド

並び順を動的に変更するメソッドとして、VisualElementには以下の拡張メソッドが定義されています。

VisualElement visualElement;
visualElement.SendToBack(); // 最背面に移動 = Hierarchyにおける親の一番上の子要素に移動される
visualElement.BringToFront(); // 最前面に移動 = Hierarchyにおける親の一番下の子要素の移動される
visualElement.PlaceBehind(sibling); // siblingの背面に移動(parentが同じである必要がある)
visualElement.PlaceInFront(sibling); // siblingの前面に移動(parentが同じである必要がある)

これらを使うことで各VisualElementの並び順を動的に変更することができます。

メソッドの挙動を確認する

最後に上述のメソッドを使って実際に並び順を変更してみます。

まずUI Builderで以下のようなレイアウトを組みます。

レイアウト

赤、緑、青のVisualElementを配置し、赤いVisualElementの子要素として、前面に移動するためのボタンと背面に移動するためのボタンを配置しました。

次にスクリプトからこれをコントロールします。

using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

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

    private void CreateGUI()
    {
        visualTree.CloneTree(rootVisualElement);
        var red = rootVisualElement.Q<VisualElement>("Red");
        var moveForwardButton = rootVisualElement.Q<Button>("MoveForwardButton");
        var moveBackwardButton = rootVisualElement.Q<Button>("MoveBackwardButton");
        
        moveForwardButton.clickable.clicked += () =>
        {
            // 前面に移動する
            var redIndex = red.parent.IndexOf(red);
            if (redIndex == red.parent.childCount - 1)
            {
                return;
            }
            
            var next = red.parent.ElementAt(redIndex + 1);
            red.PlaceInFront(next);
        };
        
        moveBackwardButton.clickable.clicked += () =>
        {
            // 背面に移動する
            var redIndex = red.parent.IndexOf(red);
            if (redIndex == 0)
            {
                return;
            }
            
            var previous = red.parent.ElementAt(redIndex - 1);
            red.PlaceBehind(previous);
        };
    }

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

赤いVisualElementのボタンがクリックされた時に赤い要素の並び順を変更しています。

Window > Exampleからウィンドウを開いて挙動を確認します。

挙動を確認

正常に動作していることを確認できました。

参考

docs.unity3d.com