Version: Unity 6.0 (6000.0)
言語 : 日本語
複雑なリストビューの作成
スクロールビュー内でコンテンツを折り返す

リストビューランタイム UI の作成

バージョン: 2022.3+

この例では、リストビューのランタイム UI を作成する方法を示します。この例では、UXML ファイルと USS ファイルを直接使用して、UI の構造体とスタイルを作成します。UI Toolkit を初めて使用する場合に、UI Builder を使用して UI を作成するには、UI Builder を使用した UI の例の作成を参照してください。

例の概要

この例では、簡単なキャラクター選択画面を作成します。左側のリストからキャラクターの名前をクリックすると、右側にキャラクターの詳細が表示されます。

ランタイム UI の最終ビュー
ランタイム UI の最終ビュー

この例で作成する完成したファイルは、こちらの GitHub リポジトリにあります。

必要な要件

このガイドは、Unity エディター、UI Toolkit、および C# スクリプトに精通している開発者を対象としています。始める前に、以下の点を理解しておいてください。

メイン UI Document の作成

メインビューの UI Document と USS ファイルを作成し、ビジュアル要素のスタイルを設定します。UI Document に 2 つのビジュアル要素をコンテナとして追加します。1 つはキャラクター名のリストが含まれる要素、もう 1 つは選択したキャラクターの詳細情報が含まれる要素です。

メインビューの UI レイアウト設定
メインビューの UI レイアウト設定
  1. Unity で任意のテンプレートを使用してプロジェクトを作成します。

  2. Project ウィンドウに UI という名前のフォルダーを作成し、すべての UI Document ファイルとスタイルシートファイルを保存します。

  3. UI フォルダーに、以下の内容で、MainView.uxml という名前の UI Document を作成します。

    <ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
        <Style src="MainView.uss" />
        <ui:VisualElement name="background">
            <ui:VisualElement name="main-container">
                <ui:ListView focusable="true" name="character-list" />
                <ui:VisualElement name="right-container">
                    <ui:VisualElement name="details-container">
                        <ui:VisualElement name="details">
                            <ui:VisualElement name="character-portrait" />
                        </ui:VisualElement>
                        <ui:Label text="Label" name="character-name" />
                        <ui:Label text="Label" display-tooltip-when-elided="true" name="character-class" />
                    </ui:VisualElement>
                </ui:VisualElement>
            </ui:VisualElement>
        </ui:VisualElement>
    </ui:UXML>
    
  4. UI フォルダーに、以下の内容で MainView.uss という名前の USS スタイルシートを作成します。

#background {
    flex-grow: 1;
    align-items: center;
    justify-content: center;
    background-color: rgb(115, 37, 38);
}

#main-container {
    flex-direction: row;
    height: 350px;
}

#character-list {
    width: 230px;
    border-color: rgb(49, 26, 17);
    border-width: 4px;
    background-color: rgb(110, 57, 37);
    border-radius: 15px;
    margin-right: 6px;
}

#character-name {
    -unity-font-style: bold;
    font-size: 18px;
}

#character-class {
    margin-top: 2px;
    margin-bottom: 8px;
    padding-top: 0;
    padding-bottom: 0;
}

#right-container {
    justify-content: space-between; 
    align-items: flex-end;
}

#details-container {
    align-items: center; 
    background-color: rgb(170, 89, 57); 
    border-width: 4px; 
    border-color: rgb(49, 26, 17);
    border-radius: 15px;
    width: 252px; 
    justify-content: center; 
    padding: 8px;
    height: 163px;
}

#details {
    border-color: rgb(49, 26, 17); 
    border-width: 2px; 
    height: 120px; 
    width: 120px; 
    border-radius: 13px; 
    padding: 4px;
    background-color: rgb(255, 133, 84);
}

#character-portrait {
    flex-grow: 1; 
    -unity-background-scale-mode: scale-to-fit;
}

.unity-collection-view__item {
    justify-content: center;
}

リストエントリーの UI Document の作成

リスト内の個々のエントリーの UI Document とスタイルシートを作成します。キャラクターリストのエントリーは、色のついた背景フレームとキャラクター名で構成されています。

キャラクターの名前を表示するリストエントリー
キャラクターの名前を表示するリストエントリー
  1. UI フォルダーに、以下の内容で、ListEntry.uxml という名前の UI Document を作成します。

    <ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    <Style src="ListEntry.uss" />
    <ui:VisualElement name="list-entry">
        <ui:Label text="Label" display-tooltip-when-elided="true" name="character-name" />
    </ui:VisualElement>
    </ui:UXML>
    
  2. UI フォルダーに、以下の内容で ListEntry.uss という名前のスタイルシートファイルを作成します。

#list-entry {
    height: 41px;
    align-items: flex-start;
    justify-content: center;
    padding-left: 10px;
    background-color: rgb(170, 89, 57);
    border-color: rgb(49, 26, 17);
    border-width: 2px;
    border-radius: 15px;
}

#character-name {
    -unity-font-style: bold;
    font-size: 18px;
    color: rgb(49, 26, 17);
}

表示するサンプルデータの作成

UI のキャラクターリストを埋めるサンプルデータを作成します。キャラクターリストに、キャラクター名、クラス、ポートレート画像を保持するクラスを作成します。

  1. Asset フォルダーに、C# スクリプトを格納する Scripts という名前のフォルダーを作成します。

  2. Scripts フォルダーに、以下の内容の C# スクリプトを作成し CharacterData.cs と命名します。

    using UnityEngine;
        
    public enum ECharacterClass
    {
        Knight, Ranger, Wizard
    }
        
    [CreateAssetMenu]
    public class CharacterData : ScriptableObject
    {
        public string CharacterName;
        public ECharacterClass Class;
        public Sprite PortraitImage;
    }
    
  3. Assets フォルダーに、Resources という名前のフォルダーを作成します。

  4. Resources フォルダーに、すべてのサンプルキャラクターデータを格納する Characters という名前のフォルダーを作成します。

  5. Characters フォルダー内で右クリックし、Create > Character Data を選択して ScriptableObject のインスタンスを作成します。

  6. CharacterData インスタンスを追加作成し、プレースホルダーデータを入力します。

シーンの設定

サンプルシーン内に UIDocument ゲームオブジェクトを作成し、UI Document をソースアセットとして加えます。

  1. サンプルシーンで GameObject > UI Toolkit > UI Document の順に選択します。
  2. Hierarchy ウィンドウで UIDocument ゲームオブジェクトを選択します。
  3. MainView.uxml を Project ウィンドウから Inspector ウィンドウの UI Document コンポーネントの Source Asset フィールドにドラッグします。これで、ソースアセットが UXML ファイルに参照されます。

リストエントリーとメインビューのコントローラーの作成

以下のクラスで 2 つの C# スクリプトを作成します。

  • リストエントリーの UI にキャラクターインスタンスのデータを表示するための CharacterListEntryController クラス。キャラクター名のラベルにアクセスし、指定したキャラクターインスタンスの名前を表示するように設定する必要があります。
  • メインビューのキャラクターリストの CharacterListController クラスと、それをインスタンス化してビジュアルツリーに割り当てる MonoBehaviour スクリプト。

ノート: CharacterListEntryController クラスは MonoBehaviour ではありません。UI Toolkit のビジュアル要素はゲームオブジェクトではないため、コンポーネントをアタッチすることはできません。代わりに、クラスを CharacterListController クラスの userData プロパティにアタッチします。

  1. Scripts フォルダーに、以下の内容の C# スクリプトを作成し CharacterListEntryController.cs と命名します。

    using UnityEngine.UIElements;
        
    public class CharacterListEntryController
    {
        Label m_NameLabel;
        
        // This function retrieves a reference to the 
        // character name label inside the UI element.
        public void SetVisualElement(VisualElement visualElement)
        {
            m_NameLabel = visualElement.Q<Label>("character-name");
        }
        
        // This function receives the character whose name this list 
        // element is supposed to display. Since the elements list 
        // in a `ListView` are pooled and reused, it's necessary to 
        // have a `Set` function to change which character's data to display.
        public void SetCharacterData(CharacterData characterData)
        {
            m_NameLabel.text = characterData.CharacterName;
        }
    }
    
  2. Scripts フォルダーに、以下の内容の C# スクリプトを作成し CharacterListController.cs と命名します。

    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UIElements;
        
    public class CharacterListController
    {
        // UXML template for list entries
        VisualTreeAsset m_ListEntryTemplate;
        
        // UI element references
        ListView m_CharacterList;
        Label m_CharClassLabel;
        Label m_CharNameLabel;
        VisualElement m_CharPortrait;
        
        List<CharacterData> m_AllCharacters;
        
        public void InitializeCharacterList(VisualElement root, VisualTreeAsset listElementTemplate)
        {
            EnumerateAllCharacters();
        
            // Store a reference to the template for the list entries
            m_ListEntryTemplate = listElementTemplate;
        
            // Store a reference to the character list element
            m_CharacterList = root.Q<ListView>("character-list");
        
            // Store references to the selected character info elements
            m_CharClassLabel = root.Q<Label>("character-class");
            m_CharNameLabel = root.Q<Label>("character-name");
            m_CharPortrait = root.Q<VisualElement>("character-portrait");
        
            FillCharacterList();
        
            // Register to get a callback when an item is selected
            m_CharacterList.selectionChanged += OnCharacterSelected;
        }
        
        void EnumerateAllCharacters()
        {
            m_AllCharacters = new List<CharacterData>();
            m_AllCharacters.AddRange(Resources.LoadAll<CharacterData>("Characters"));
        }
        
        void FillCharacterList()
        {
            // Set up a make item function for a list entry
            m_CharacterList.makeItem = () =>
            {
                // Instantiate the UXML template for the entry
                var newListEntry = m_ListEntryTemplate.Instantiate();
        
                // Instantiate a controller for the data
                var newListEntryLogic = new CharacterListEntryController();
        
                // Assign the controller script to the visual element
                newListEntry.userData = newListEntryLogic;
        
                // Initialize the controller script
                newListEntryLogic.SetVisualElement(newListEntry);
        
                // Return the root of the instantiated visual tree
                return newListEntry;
            };
        
            // Set up bind function for a specific list entry
            m_CharacterList.bindItem = (item, index) =>
            {
                (item.userData as CharacterListEntryController)?.SetCharacterData(m_AllCharacters[index]);
            };
        
            // Set a fixed item height matching the height of the item provided in makeItem. 
            // For dynamic height, see the virtualizationMethod property.
            m_CharacterList.fixedItemHeight = 45;
        
            // Set the actual item's source list/array
            m_CharacterList.itemsSource = m_AllCharacters;
        }
        
        void OnCharacterSelected(IEnumerable<object> selectedItems)
        {
            // Get the currently selected item directly from the ListView
            var selectedCharacter = m_CharacterList.selectedItem as CharacterData;
        
            // Handle none-selection (Escape to deselect everything)
            if (selectedCharacter == null)
            {
                // Clear
                m_CharClassLabel.text = "";
                m_CharNameLabel.text = "";
                m_CharPortrait.style.backgroundImage = null;
        
                return;
            }
        
            // Fill in character details
            m_CharClassLabel.text = selectedCharacter.Class.ToString();
            m_CharNameLabel.text = selectedCharacter.CharacterName;
            m_CharPortrait.style.backgroundImage = new StyleBackground(selectedCharacter.PortraitImage);
        }
    }
    

コントローラースクリプトをメインビューにアタッチ

CharacterListControllerMonoBehaviour ではないため、ゲームオブジェクトに直接アタッチすることはできません。これを解決するには、MonoBehaviour スクリプトを作成し、UIDocument と同じゲームオブジェクトにアタッチします。このスクリプトでは、MainView.uxml はすでに UIDocument コンポーネントによってインスタンス化されているため、インスタンス化する必要はありません。代わりに UIDocument コンポーネントにアクセスし、すでにインスタンス化されているビジュアルツリーのリファレンスを取得します。次に、CharacterListController のインスタンスを作成し、ビジュアルツリーのルート要素と、個々のリスト要素に使用される UXML テンプレートを渡します。

ノート: UI がリロードされると、UIDocument コンポーネントを含む同じゲームオブジェクト上の関連する MonoBehaviour コンポーネントはすべてリロード前に無効になり、リロード後に再び有効になります。したがって、UI 関連のコードは、この MonoBehaviourOnEnable メソッドと OnDisable メソッド内に配置する必要があります。詳細については、ゲームビューでの UI のレンダリングを参照してください。

  1. Scripts フォルダーに、以下の内容の C# スクリプトを作成し MainView.cs と命名します。

    using UnityEngine;
    using UnityEngine.UIElements;
        
    public class MainView : MonoBehaviour
    {
        [SerializeField]
        VisualTreeAsset m_ListEntryTemplate;
        
        void OnEnable()
        {
            // The UXML is already instantiated by the UIDocument component
            var uiDocument = GetComponent<UIDocument>();
        
            // Initialize the character list controller
            var characterListController = new CharacterListController();
            characterListController.InitializeCharacterList(uiDocument.rootVisualElement, m_ListEntryTemplate);
        }
    }
    
  2. SampleScene で、UIDocument を選択します。

  3. Inspector ウィンドウで MainView.csAdd Component にドラッグします。

  4. ListEntry.uxmlListEntry Template フィールドにドラッグします。

  5. 再生モードに切り替えると、ゲームビューに UI が表示されます。

追加リソース

複雑なリストビューの作成
スクロールビュー内でコンテンツを折り返す