このページでは、UI Toolkit を使用して簡単なキャラクター選択画面を設定する手順を説明します。UI エレメントとテンプレートの作成、シーンの設定、スクリプトロジックを UI に適用する方法について説明します。このガイドでは、USS によるスタイリングはカバーせず、デフォルトのスタイルとテーマのみを使用します。
このガイドの最終的なソースコードは、このページの 下の方 にあります。
説明項目 UI Builder、ListView、Label、PanelSettings、UIDocument、選択処理
このガイドでは、以下の手順で説明しています。
最終的な UI 画面は、2 つの個別の UI テンプレート (UXML) で構成されています。メインビューテンプレートには、キャラクター名の入ったリスト、選択したキャラクターの詳細を表示するための小さなパネル、ボタンが含まれています。このセクションでは、UI Builder を使用してこのテンプレートを設定します。
ノート |
---|
If you are familiar with UI Builder and want to skip this step, you can copy the UXML code for the main view from the bottom of this page and paste it into a new file directly. Save it as Assets/UI/MainView.uxml. |
メニュー Window > UI Toolkit > UI Builder から、UI Builder ウィンドウを開きます。ビューポートの左上にあるファイルメニューを使用して、新しい UXML ドキュメントを作成します。
ゲームの UI を開発するときは、UI Builder のビューポートの右上にある Unity Default Runtime Theme
を常に選択します。エディターとランタイムのテーマではデフォルトのフォントサイズと色が異なるため、レイアウトに影響が出ます。
Hierarchy で新しい UXML ファイルを選択し、Match Game View のチェックボックスを有効にします。Unity エディターを横向きの解像度に設定していない場合は、設定が必要なことがあります。
いよいよ UI 要素を作成します。 Library から Hierarchy にドラッグして、新しい VisualElement を作成します。
新しい要素は、画面全体を覆う必要があるため、flex-grow
プロパティを 1 に設定する必要があります。Hierarchy から要素を選択し、右側の Inspector で Flex と書かれた折りたたみ部分を探します。Grow の値を 0 から 1 に変更します。
この VisualElement のすべての子要素を画面の中央に配置するには、VisualElement の Align プロパティを変更します。 Align Items と Justify Content の両方を、center (中央) に設定する必要があります。
最後に、Background > Color で背景色を選択します。このステップは必須ではありません。この例では、#732526
の色を使用しています。
次に、既存の VisualElement の下に新しい VisualElement を作成します。これは、UI の左側と右側のセクションの親コンテナになります。
この新しい要素の flex-direction
プロパティを row
(行) に設定します (デフォルトでは列になっています)。また、高さを 350 ピクセルに固定する必要があります。
現在の UI はこのように表示されるはずです。ゲームビュー の解像度やアスペクト比によって、画面表示が異なる場合があることに注意してください。
キャラクター名のリストを作成するには、ListView コントロールを Library から選択し、先ほど作成した VisualElement の下に子要素として加えます。この要素を選択し、Inspector で CharacterList
という名前を割り当てます。これは、後でコントローラースクリプトからこのリストにアクセスするために必要です。
リストの幅を 230 ピクセルに固定します。また、これから作成する次の要素までの距離を確保するために、右側に 6 ピクセルの幅のマージンを与えます。
また、リストの背景色を割り当て、丸みを帯びた境界線を設定することができます。このガイドでは、背景に #6E3925
を、境界線の色に #311A11
を使用し、境界線の幅を 4px、半径を 15px に設定しています。このステップは必須ではありません。
CharacterList
と同じ親に、新しい VisualElement を追加します。これには、キャラクター詳細パネルとボタンが含まれます。 Align 折りたたみの下で、Align Items の設定を flex-end
に、Justify Content の設定を space-between
に変更します。
この新しいコンテナに、新しい VisualElement を加えます。これがキャラクター詳細パネルになります。ユーザーが左のリストからキャラクターを選択すると、そのキャラクターの画像、名前、クラスが表示されます。
要素に固定幅 276 ピクセルを設定し、Align Items と Justify Content を center (中央) に切り替えます。また、要素に 8 ピクセル幅のパディングを加えます。これにより、子要素はコンテナの境界から最小限の距離を保つことができます。
背景色を #AA5939
に、境界色を #311A11
に、境界線幅 4 ピクセル、半径 15 ピクセルでパネルのスタイルを設定します。このステップは必須ではありません。
これで、UI レイアウトは以下の画像のようになります。
次に、キャラクターの詳細に、個々の UI コントロールを加えます。まず、キャラクターの画像です。これは、背景のフレームと前景の画像の 2 つの要素で構成されています。
まず、背景フレーム用のキャラクター詳細コンテナに、新しい VisualElement を加えます。固定サイズ 120x120 ピクセル、パディング 4 ピクセルを割り当て、含まれる画像が境界に直接触れないようにします。
幅 2 ピクセル、半径 15 ピクセルのボーダーで、色は #311A11
、背景色は #FF8554
で要素のスタイルを設定します。代わりに、独自の色やスタイルを自由に適用することができます。
実際の画像は、先ほど作成したフレームの子として、新しい VisualElement を加えます。名前は CharacterPortrait
とし、後でコントローラースクリプトの中でアクセスできるようにします。
Flex > Grow を 1 に設定します。そうすれば、画像は利用可能なスペースをすべて利用します。また、「 Background > Scale Mode 」のスケーリングモードを「 scale-to-fit
」に変更することを確認します。これにより、正しいアスペクト比を維持したまま、要素のサイズに合わせて画像を拡大または縮小することができます。
次に、選択したキャラクターの名前とクラスを表示するために後で使用する 2 つのラベルコントロールをキャラクター詳細コンテナに加えます。これらを CharacterName
と CharacterClass
と名付けます。
クラスよりもキャラクターの名前を目立たせるには、そのラベルのフォントサイズを 18 に変更し、スタイルを B (太字) に設定します。
UI 画面は下図のように表示されます。
最後に、右側の UI コンテナに Button コントロールを追加します。後にこのボタンにコントローラースクリプトでアクセスし、キャラクターが選択されたときや選択解除されたときに有効または無効にします。ボタンの名前は SelectCharButton
とし、固定幅を 150 ピクセルにします。また、ボタンのラベルテキストを Select Character
に設定します。
ボタンのスタイルは、背景色を #FF8554
に、境界色を #311A11
に、境界線幅 2 ピクセルに設定します。このステップは必須ではありません。
完成したメインビューは、以下の画像のように表示されます。
UXML テンプレートを Assets/UI/MainView.uxml という名前で保存します。 また、このテンプレートの最終的な UXML コード は、ページの下の方に記載されています。
このセクションでは、前セクションで作成した UI テンプレートをランタイムにゲームにロードして表示する方法について学びます。
まず始めに、PanelSettings アセットを作成する必要があります。このアセットで、スケーリングモードやレンダリング順序など、画面の設定を定義します。また、UI Toolkit Debugger に表示される UI の名前もこのアセットで決定されます。
Project ビューで右クリックし、新しい Panel Settings Asset
を作成します。Create > UI Toolkit > Panel Settings Asset を選択します。新しく作成したファイルに GameUI_Panel
という名前を付けます。
このガイドでは、すべての設定をデフォルト値のままにしておくことができます。
前セクションのメインビュー UI テンプレートを表示するには、シーン内に新しいゲームオブジェクトを作成する必要があります。それに、UIDocument コンポーネントをアタッチします。
Unity が再生モードに入ると、UIDocument
は割り当てられた VisualTreeAsset
を自動的にロードします。VisualTreeAsset
は、UXML テンプレートです。MainView.uxml
と、新しい GameUI_Panel
パネル設定の両方をコンポーネントに割り当てます。
ノート |
---|
If you do not assign a PanelSettings asset to your UI Document component, it will automatically search the project and use the first Panel Settings Asset it finds automatically. Keep this in mind when renaming or moving assets. |
Unity エディターで再生モードに入り、ゲームビューに表示される UI を確認できるようになりました。
ノート |
---|
If you have multiple UI Documents in your scene, you can assign the same panel settings asset to all. This will cause all UI to be rendered on the same panel, optimizing performance. |
このセクションでは、UI のキャラクターリストにデータを入力するためのサンプルデータを作成します。
キャラクターリストには、キャラクター名、クラス、ポートレート画像を保持するシンプルなクラスが必要です。 新しい ScriptableObject スクリプト Assets/Scripts/CharacterData.cs を作成し、次のコードをファイルに貼り付けてください。
using UnityEngine;
public enum ECharacterClass
{
Knight, Ranger, Wizard
}
[CreateAssetMenu]
public class CharacterData : ScriptableObject
{
public string m_CharacterName;
public ECharacterClass m_Class;
public Sprite m_PortraitImage;
}
[CreateAssetMenu]
属性は、Create メニューに自動的にエントリーを加えます。
Project ビューでフォルダーを右クリックし、新しい ScriptableObject のインスタンスを作成します。
ここで、いくつかの CharacterData
インスタンスを作成し、ランダムなデータで埋める必要があります。これらのインスタンスをすべて Resources/Characters というフォルダーに配置します。後で、このフォルダーからすべてのキャラクターデータを自動的に解析してロードするスクリプトを作成します。
このセクションでは、リスト内の個々のエントリー用の UI テンプレートを作成します。ランタイムに、コントローラースクリプトは各キャラクターに対してこの UI のインスタンスを作成し、リストに追加します。 キャラクターリストのエントリーの UI は、色のついた背景フレームとキャラクター名で構成されています。
ノート |
---|
If you want to skip this step, you can copy the UXML code for the list entry from the bottom of this page and paste it into a new file directly. Save it as Assets/UI/ListEntry.uxml. |
メニュー Window > UI Toolkit > UI Builder から、UI Builder ウィンドウを開きます。 File > New を選択して、新しい UXML テンプレートを作成します。
背景に VisualElement を追加し、高さを 41 px に固定します。エントリー内のテキストは左寄せにして要素の中央に配置する必要があるので、Align 折りたたみ部分を開き、Align Items から Left (左)、 Justify Content を center (中央) に設定します。 また、左パディングを 10 px に設定し、ラベルがフレームの左境界線から最小限の距離を保つようにします。
背景色を #AA5939
、境界線幅 2 ピクセル、半径 15 ピクセル、境界色を #311A11
で、パネルのスタイルを設定します。このステップは必須ではありません。
既存の VisualElement の子としてラベルを追加し、その名前を CharacterName
とします。これは、後でコントローラースクリプトでアクセスできるようにするためです。Font Style を B (太字) に、フォントサイズを 18 に設定します。
UXML テンプレートを Assets/UI/ListEntry.uxml という名前で保存します。 また、このテンプレートの最終的な UXML コード は、ページの下の方に記載されています。
このセクションでは、リストエントリーのコントローラースクリプトを作成します。このスクリプトの目的は、リストエントリーの UI にキャラクターインスタンスのデータを表示することです。キャラクター名のラベルにアクセスし、指定したキャラクターインスタンスの名前を表示するように設定する必要があります。
新規スクリプト Assets/Scripts/UI/CharacterListEntryController.cs を作成し、以下のコードを貼り付けます。
using UnityEngine.UIElements;
public class CharacterListEntryController
{
private Label m_NameLabel;
public void SetVisualElement(VisualElement rootEleme)
{
m_NameLabel = visualElement.Q<Label>("CharacterName");
}
public void SetCharacterData(CharacterData characterData)
{
m_NameLabel.text = characterData.m_CharacterName;
}
}
このクラスには 2 つの関数があり、どちらも Set
関数です。
SetvisualElement(VisualElement visualElement)
This function will receive a visual element that is an instance of the ListEntry
UI template you created in the previous section. The main view controller will create this instance. The purpose of this function is to retrieve a reference to the character name label inside the UI element.
SetCharacterData(CharacterData characterData)
この関数は、このリスト要素が表示する名前を持つキャラクターを取得します。ListView
の要素リストはプールされて再利用されるため、どのキャラクターのデータを表示するかを変更するために Set
関数が必要です。
CharacterListEntry
クラスは、MonoBehaviour
ではないことに注意してください。UI Toolkit のビジュアル要素はゲームオブジェクトではないので、コンポーネントをアタッチすることはできません。代わりに、このクラスは、次のセクションで、userData
プロパティにアタッチされます。
ここでは、メインビューのキャラクターリスト用のコントローラースクリプトと、それをインスタンス化してビジュアルツリーに割り当てる MonoBehaviour スクリプトを作成します。
まず、Assets/Scripts/UI/CharacterListController.cs の下に新しいスクリプトを作成し、次のコードを貼り付けます。
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class CharacterListController
{
public void InitializeCharacterList(VisualElement root, VisualTreeAsset listElementTemplate)
{
}
}
InitializeCharacterList()
メソッドを後で入力しますが、今は空のメソッドを追加しておくことが重要です。次のセクションでそれを呼び出すことができるようにするためです。
コントローラースクリプトをメインビューにアタッチ
CharacterListEntryController
と同様に、 CharacterListController
は MonoBehaviour
ではないので、別の方法でビジュアルツリーにアタッチする必要があります。
UIDocument
と同じゲームオブジェクトにアタッチできる MonoBehaviour スクリプトを作成する必要があります。これにより、CharacterListController
をインスタンス化し、ビジュアルツリーにアタッチします。
新規スクリプト Assets/Scripts/UI/MainView.cs を作成し、以下のコードを貼り付けます。
using UnityEngine;
using UnityEngine.UIElements;
public class MainView : MonoBehaviour
{
[SerializeField] private 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);
}
}
Unity エディターで、UIDocument
があるのと同じゲームオブジェクトに、このスクリプトをアタッチします。ListEntry.uxml
を List Entry Template プロパティに割り当てます。
MainView UXML のインスタンス化は、同じゲームオブジェクトの UIDocument
コンポーネントで自動的に行われるため、スクリプトコンポーネントで行う必要はありません。MainView
スクリプトは UIDocument コンポーネントにアクセスし、すでにインスタンス化されているビジュアルツリーの参照を取得します。次に、CharacterListController
のインスタンスを作成し、ビジュアルツリーのルート要素と、個々のリスト要素に使用される UXML テンプレートを渡します。
ノート |
---|
When the UI is reloaded, companion MonoBehaviour components on the same GameOBject containing the UIDocument component will be disabled prior to the reload, and then re-enabled after the reload. Therefore it’s a good practice to place code that interacts with the UI in the OnEnable and OnDisable methods of these MonoBehaviours. |
すべてのキャラクターデータインスタンスを列挙
コントローラースクリプトに最初に加える機能は、先に作成したキャラクターデータのインスタンスをすべて列挙する関数です。これらは、リストを埋めるために使用されます。
以下のコードを CharacterListController
クラスにコピーしてください。
private List<CharacterData> m_AllCharacters;
private void EnumerateAllCharacters()
{
m_AllCharacters = new List<CharacterData>();
m_AllCharacters.AddRange(Resources.LoadAll<CharacterData>("Characters"));
}
ノート |
---|
This code assumes that you created the character instances in the Resources/Characters folder. You might need to adjust the folder name accordingly if you placed the characters in a different folder. |
ここで、初期化中に EnumerateAllCharacter
メソッドを呼び出す必要があります。 InitializeCharacterList
メソッドの先頭にその呼び出しを加えてください。
public void InitializeCharacterList(VisualElement root, VisualTreeAsset listElementTemplate)
{
// Enumerate all characters
EnumerateAllCharacters();
}
UI 要素への参照を取得
このセクションでは、InitializeCharacterList
メソッドのコンテンツを記入します。最初に行う必要があるのは、情報を表示するためにアクセスが必要なすべての UI コントロールへの参照を個々に取得することです。名前、USS クラス、型、またはこれらの組み合わせによって個々の UI コントロールを取得するには API の UQuery ファミリーを使用します。
CharacterListController
クラス内のコードを以下のコードで拡張します。
// UXML template for list entries
private VisualTreeAsset m_ListEntryTemplate;
// UI element references
private ListView m_CharacterList;
private Label m_CharClassLabel;
private Label m_CharNameLabel;
private VisualElement m_CharPortrait;
private Button m_SelectCharButton;
public void InitializeCharacterList(VisualElement root, VisualTreeAsset listElementTemplate)
{
// Enumerate all characters
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>("CharacterList");
// Store references to the selected character info elements
m_CharClassLabel = root.Q<Label>("CharacterClass");
m_CharNameLabel = root.Q<Label>("CharacterName");
m_CharPortrait = root.Q<VisualElement>("CharacterPortrait");
// Store a reference to the select button
m_SelectCharButton = root.Q<Button>("SelectCharButton");
}
リストにエントリーを入力
次に、先ほど列挙してロードしたキャラクターを画面のリストに入力する必要があります。そのためには、CharacterListController
クラスの中に FillCharacterList
という新しいメソッドを作成する必要があります。
ListView に要素を入力するには、4 つのステップが必要です。
makeItem
関数を作成bindItem
関数を作成makeItem コールバック関数の目的は、UI を表す小さなビジュアルツリーを 1 つのリストアイテムで作成し、このツリーのルートの VisualElement を返すことです。
In this case, the makeItem
callback needs to instantiate the UXML template you created for the list entries. IT also needs to create an instance of the CharacterListEntryController
controller script, which takes care of filling the UI with the data from the CharacterData
.
クラス内に FillCharacterList
メソッドを作成し、以下のコードを貼り付けてください。
private 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;
};
}
makeItem
コールバックの一部として、コントローラースクリプトを、インスタンス化したビジュアル要素の userData
プロパティ内に格納します。これにより、後でスクリプトにアクセスし、リスト要素に異なるキャラクターを割り当てることができます。
// コントローラスクリプトをビジュアル要素に割り当てる
newListEntry.userData = newListEntryLogic;
メモリとパフォーマンスの最適化として、ListView
は、リスト内のエントリーごとに 1 つの要素をインスタンス化するのではなく、リスト要素を再利用します。これは、可視領域を満たすのに十分なビジュアル要素のみを作成し、リストがスクロールされるとそれらをプールして再利用します。
このため、データのインスタンス (この場合は CharacterData
) を個々のリスト要素に紐づけする bindItem コールバックを用意する必要があります。
FillCharacterList
メソッドの下部に以下のコードを追加し、拡張します。
private void FillCharacterList()
{
...
// Set up bind function for a specific list entry
m_CharacterList.bindItem = (item, index) =>
{
(item.userData as CharacterListEntryController).SetCharacterData(m_AllCharacters[index]);
};
}
bindItem
コールバックは、リストエントリーのビジュアルツリーのためのルートビジュアル要素への参照と、データへのインデックスを受け取ります。ビジュアル要素の userData
プロパティに CharacterListEntryController
への参照を保存したので、コードはこれにアクセスして直接CharacterData
を設定できます。
最後に、要素のアイテムの高さを設定し、リストのデータソースへの参照を提供する必要があります。これは、リストに含まれる要素の数を伝えます。
FillCharacterList
メソッドの下部に以下のコードを追加し、拡張します。
private void FillCharacterList()
{
...
// Set a fixed item height
m_CharacterList.fixedItemHeight = 45;
// For Unity versions earlier than 2021.2 use this:
//m_CharacterList.itemHeight = 45;
// Set the actual item's source list/array
m_CharacterList.itemsSource = m_AllCharacters;
}
初期化の最後に FillCharacterList
メソッドを呼び出す必要があります。
以下のように、 InitializeCharacterList
メソッドの一番下に呼び出しを追加します。
public void InitializeCharacterList(VisualElement root, VisualTreeAsset listElementTemplate)
{
...
FillCharacterList();
}
この時点で 再生モード にすると、キャラクターリストが作成したキャラクターの名前でいっぱいになります。
CharacterListController
スクリプトの最終的な コード は、このガイドの下の方にあります。
ユーザーがキャラクターを選択すると、画面右側のキャラクター詳細欄に、キャラクターの詳細、つまり画像、フルネーム、クラスが表示される必要があります。また、キャラクターを選択すると、選択ボタンが有効になる必要があります。キャラクターが選択されていないときは、再びボタンが無効になるようにします。
なお、リスト内のキャラクターをクリックして選択することはすでに可能です。選択とハイライトの機能は、ListView コントロールの一部です。必要なのは、ユーザーがリストの選択を変更するときに反応するコールバック関数だけです。ListView
コントロールは、この目的のために onSelectionChange
イベントを含んでいます。
InitializeCharacterList
メソッドの最後に以下のコードを加えます。
public void InitializeCharacterList(VisualElement root, VisualTreeAsset listElementTemplate)
{
...
// Register to get a callback when an item is selected
m_CharacterList.onSelectionChange += OnCharacterSelected;
}
ここで、上のコードで設定したコールバック関数 OnCharacterSelected
を実装する必要があります。この関数は、リスト内で選択されたすべてのアイテムのリストを受け取ります。ただし、このリストでは 1 つのアイテムしか選択できないため、リストの selectedItem プロパティを使用して、選択されたアイテムに直接アクセスできます。
以下のコードをクラス内にコピーしてください。
private void OnCharacterSelected(IEnumerable<object> selectedItems)
{
// Get the currently selected item directly from the ListView
var selectedCharacter = m_CharacterList.selectedItem as CharacterData;
}
selectedItem
プロパティが null を返す場合があります。これは、何も選択されていない場合、またはユーザーが ESC
キーを押してすべての選択を解除した場合に起こります。このケースは、最初に処理する必要があります。
OnCharacterSelected
メソッドを以下のように拡張します。
private 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;
// Disable the select button
m_SelectCharButton.SetEnabled(false);
return;
}
}
選択が有効である場合、UI にキャラクターの詳細を表示する必要があります。クラスの InitializeCharacterList
メソッドで取得した参照を通じて、ラベルとポートレート画像のビジュアル要素にアクセスできます。
以下のコードを OnCharacterSelected
メソッドにコピーします。
private void OnCharacterSelected(IEnumerable<object> selectedItems)
{
...
// Fill in character details
m_CharClassLabel.text = selectedCharacter.m_Class.ToString();
m_CharNameLabel.text = selectedCharacter.m_CharacterName;
m_CharPortrait.style.backgroundImage = new StyleBackground(selectedCharacter.m_PortraitImage);
// Enable the select button
m_SelectCharButton.SetEnabled(true);
}
これで 再生モード に入り、キャラクター選択リストの動作を確認することができます。Escape
キーを押すと、キャラクターの選択を解除できます。
以下に、このガイドで作成したすべてのファイルの完全なソースコードを掲載します。
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
<ui:VisualElement style="flex-grow: 1; align-items: center; justify-content: center; background-color: rgb(115, 37, 38);">
<ui:VisualElement style="flex-direction: row; height: 350px;">
<ui:ListView focusable="true" name="CharacterList" style="width: 230px; border-left-color: rgb(49, 26, 17); border-right-color: rgb(49, 26, 17); border-top-color: rgb(49, 26, 17); border-bottom-color: rgb(49, 26, 17); border-left-width: 4px; border-right-width: 4px; border-top-width: 4px; border-bottom-width: 4px; background-color: rgb(110, 57, 37); border-top-left-radius: 15px; border-bottom-left-radius: 15px; border-top-right-radius: 15px; border-bottom-right-radius: 15px; margin-right: 6px;" />
<ui:VisualElement style="justify-content: space-between; align-items: flex-end;">
<ui:VisualElement style="align-items: center; background-color: rgb(170, 89, 57); border-left-width: 4px; border-right-width: 4px; border-top-width: 4px; border-bottom-width: 4px; border-left-color: rgb(49, 26, 17); border-right-color: rgb(49, 26, 17); border-top-color: rgb(49, 26, 17); border-bottom-color: rgb(49, 26, 17); border-top-left-radius: 15px; border-bottom-left-radius: 15px; border-top-right-radius: 15px; border-bottom-right-radius: 15px; width: 276px; justify-content: center; padding-left: 8px; padding-right: 8px; padding-top: 8px; padding-bottom: 8px;">
<ui:VisualElement style="border-left-color: rgb(49, 26, 17); border-right-color: rgb(49, 26, 17); border-top-color: rgb(49, 26, 17); border-bottom-color: rgb(49, 26, 17); border-left-width: 2px; border-right-width: 2px; border-top-width: 2px; border-bottom-width: 2px; height: 120px; width: 120px; border-top-left-radius: 13px; border-bottom-left-radius: 13px; border-top-right-radius: 13px; border-bottom-right-radius: 13px; padding-left: 4px; padding-right: 4px; padding-top: 4px; padding-bottom: 4px; background-color: rgb(255, 133, 84);">
<ui:VisualElement name="CharacterPortrait" style="flex-grow: 1; -unity-background-scale-mode: scale-to-fit;" />
</ui:VisualElement>
<ui:Label text="Label" name="CharacterName" style="-unity-font-style: bold; font-size: 18px;" />
<ui:Label text="Label" display-tooltip-when-elided="true" name="CharacterClass" style="margin-top: 2px; margin-bottom: 8px; padding-top: 0; padding-bottom: 0;" />
</ui:VisualElement>
<ui:Button text="Select Character" display-tooltip-when-elided="true" name="SelectCharButton" style="width: 150px; border-left-color: rgb(49, 26, 17); border-right-color: rgb(49, 26, 17); border-top-color: rgb(49, 26, 17); border-bottom-color: rgb(49, 26, 17); background-color: rgb(255, 133, 84); border-left-width: 2px; border-right-width: 2px; border-top-width: 2px; border-bottom-width: 2px;" />
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
</ui:UXML>
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
<ui:VisualElement style="height: 41px; align-items: flex-start; justify-content: center; padding-left: 10px; background-color: rgba(170, 89, 57, 255); border-left-color: rgba(49, 26, 17, 255); border-right-color: rgba(49, 26, 17, 255); border-top-color: rgba(49, 26, 17, 255); border-bottom-color: rgba(49, 26, 17, 255); border-left-width: 2px; border-right-width: 2px; border-top-width: 2px; border-bottom-width: 2px; border-top-left-radius: 15px; border-bottom-left-radius: 15px; border-top-right-radius: 15px; border-bottom-right-radius: 15px;">
<ui:Label text="Label" display-tooltip-when-elided="true" name="CharacterName" style="-unity-font-style: bold; font-size: 18px;" />
</ui:VisualElement>
</ui:UXML>
using UnityEngine;
public enum ECharacterClass
{
Knight, Ranger, Wizard
}
[CreateAssetMenu]
public class CharacterData : ScriptableObject
{
public string m_CharacterName;
public ECharacterClass m_Class;
public Sprite m_PortraitImage;
}
using UnityEngine.UIElements;
public class CharacterListEntryController
{
private Label m_NameLabel;
public void SetVisualElement(VisualElement visualElement)
{
m_NameLabel = visualElement.Q<Label>("CharacterName");
}
public void SetCharacterData(CharacterData characterData)
{
m_NameLabel.text = characterData.m_CharacterName;
}
}
using UnityEngine;
using UnityEngine.UIElements;
public class MainView : MonoBehaviour
{
[SerializeField] private 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);
}
}
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class CharacterListController
{
private List<CharacterData> m_AllCharacters;
// UXML template for list entries
private VisualTreeAsset m_ListEntryTemplate;
// UI element references
private ListView m_CharacterList;
private Label m_CharClassLabel;
private Label m_CharNameLabel;
private VisualElement m_CharPortrait;
private Button m_SelectCharButton;
private void EnumerateAllCharacters()
{
m_AllCharacters = new List<CharacterData>();
m_AllCharacters.AddRange(Resources.LoadAll<CharacterData>("Characters"));
}
public void InitializeCharacterList(VisualElement root, VisualTreeAsset listElementTemplate)
{
// Enumerate all characters
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>("CharacterList");
// Store references to the selected character info elements
m_CharClassLabel = root.Q<Label>("CharacterClass");
m_CharNameLabel = root.Q<Label>("CharacterName");
m_CharPortrait = root.Q<VisualElement>("CharacterPortrait");
// Store a reference to the select button
m_SelectCharButton = root.Q<Button>("SelectCharButton");
FillCharacterList();
// Register to get a callback when an item is selected
m_CharacterList.onSelectionChange += OnCharacterSelected;
}
private 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;
// Disable the select button
m_SelectCharButton.SetEnabled(false);
return;
}
// Fill in character details
m_CharClassLabel.text = selectedCharacter.m_Class.ToString();
m_CharNameLabel.text = selectedCharacter.m_CharacterName;
m_CharPortrait.style.backgroundImage = new StyleBackground(selectedCharacter.m_PortraitImage);
// Enable the select button
m_SelectCharButton.SetEnabled(true);
}
private 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
m_CharacterList.fixedItemHeight = 45;
// For Unity versions earlier than 2021.2 use this:
//m_CharacterList.itemHeight = 45;
// Set the actual item's source list/array
m_CharacterList.itemsSource = m_AllCharacters;
}
}