Unity3D: Create your first runtime UI

ygtu

May 16, 2023 • 27 min read

Recommended: Add NSDT Scene Editor to your 3D toolchain
3D Toolset: NSDT Jianshi Digital Twin

Create your first runtime UI

This page walks you through the steps of setting up a simple character selection screen using the UI toolkit. It covers the creation of UI elements and templates, scene setup, and how to connect script logic to the UI. This guide will not cover styling via USS and will only use the default styles and themes.

You can find the final source code for this guide here at the bottom of this page.

Topics: UI Builder, List Views, Labels, Panel Settings, UIDocument, Selection Handling

This guide will guide you through the following steps:

  • Create the main UI view
  • Set the scene
  • Create sample data to display
  • Create a controller for the main view
  • Create a list entry UI template
  • Create a controller for list items
  • react to user choices

Create the main UI view

The final UI screen consists of two separate UI templates (UXML). The main view template contains a list with character names, a smaller panel that displays details about the selected character, and a button. In this section, you set up this template using UI Builder.

Note : If you are familiar with UI Builder and wish to skip this step, you can copy the UXML code for the main view from the bottom of this page and paste it directly into a new file. Save it as  Assets/UI/MainView.uxml .

UI layout settings for main view

Open the "UI Builder" window > "UI Builder" window via the menu "UI Toolkit > UI Builder". Create a new UXML document using the File menu in the upper left corner of the viewport.

UI Builder File Menu

When developing a game UI, always make sure to select in the upper right corner of the UI Builder viewport. The default font size and color differ between the editor and runtime themes, which affects the layout.Unity Default Runtime Theme

Create new elements by dragging from the library

Select the new UXML file in the hierarchy and enable the Match Game View checkbox. You may need to set the Unity Editor to landscape resolution if it is not already set.

Enable tournament game view

Now it's time to create the UI elements! Create new visual elements by dragging them from the library into the hierarchy .

Create new elements by dragging from the library

The new element needs to cover the entire screen, so the property needs to be set to 1.  Select an element from the hierarchy and find the accordion labeled Flex in the Inspector panel on the right  . Change the "Growth " value from 0 to 1.flex-grow

Set Flex properties

To center all of this visual element's children in the middle of the screen, change the visual element's Alignment property. You need to set both "Align items " and "Content alignment " to center .

middle child

Finally, you can choose a background color under "Background > Color ". This step is optional. This example is used as color.#732526

root element background color

Next, create a new visual element under the existing visual element. This will become the parent container for the left and right parts of the UI.

Add child visual elements

Set the properties of this new element to (default is column). You also need to set the fixed height to 350 pixels.flex-directionrow

Center container properties

This is what the current UI looks like. Please note that your screen may look different depending on the game view 's resolution and aspect ratio.

Background container with empty elements inside

To create a list of character names, select ListView control from the library  and add it as a child under the visual element you just created. Select the element and give it a name in the inspector. This is required so that you can later access this list via the controller script.CharacterList

Background container with empty elements inside

Set the list to a fixed width of 230 pixels. Also give it a 6 px wide margin on the right side to keep it at a distance from the next element you want to create.

Character list size and margin collapse

You can also specify a background color for the list and set a rounded border. This guide is for background and border colors, with a border width of 4px and a radius of 15px. This step is optional.#6E3925#311A11

Style character list

Add a new visual element under the same parent as . This will contain the character details panel and buttons. Under the Alignment accordion, change the settings for Item Alignment and Content Alignment to .CharacterListflex-endspace-between

Justify content attributes

Add new visual elements to this new container. This will become the character details panel. When the user selects a character from the list on the left, it will display the character's portrait, name, and class.

Set a fixed width of 276 pixels for the element, and toggle Align Item and Content Align to Center . Also add an 8-pixel wide padding to the element so that the child maintains a minimum distance from the container border.

Properties of the character details container

You can style the panel by setting the background color to a 4px wide border and a 15px radius border color. This step is optional.#AA5939#311A11

Your UI layout should now look like the image below.

Empty character details panel

Next, you add individual UI controls to the character details. The first is the portrait. It consists of two elements - the frame in the background and the image in the foreground.

Start by adding the new visual element to the character details container of the background frame. Assign it a fixed size of 120x120 pixels and a padding of 4 pixels so that the containing image does not directly touch the border.

You can style the element with a 2px wide border with a 15px radius (color and background color). Feel free to apply your own colors and styles.#311A11#FF8554

Background frame for people portrait

For the actual image, add the new visual element as a child element to the frame you just created. Give it a name so you can access it later in the controller script.CharacterPortrait

Set Flex > Grow to 1 so that the image uses all available space. Also, make sure to change the scaling mode under "Background > Scale Mode " so that you can scale the image up or down to match the element size while maintaining the correct aspect ratio.scale-to-fit

Visual elements of portrait images

Next, add two label controls to the character details container, which you will later use to display the name and class of the selected character. Name them and.CharacterNameCharacterClass

Add labels to names and classes

To make the character's name stand out more than the class, change the label's font size to 18 and set the style to bold .

Change font settings

Your UI screen should now look like the image below.

Completed role details panel

Finally, add a button control to the right UI container. Later in your controller script you will access this button and enable or disable it when a character is selected or deselected. Name the button and give it a fixed width of 150px. The button's label text should also be set to .SelectCharButtonSelect Character

Add button for character selection

To style the button, set the background color to , and the 2px border to color . This step is optional.#FF8554#311A11

The finished main view should look like the image below.

Final main view layout

Save the UXML template as  Assets/UI/MainView.uxml . You can also find the final UXML code for this template at the bottom of the page here.

Set the scene

In this section, you'll learn how to load and display the UI template you created in the previous section in your game at runtime.

To get started, you need to create a panel settings asset. This asset will define the screen's settings, such as scaling mode and rendering order. It also determines the name of the UI as it appears in the UI Toolkit debugger.

Create a new panel settings resource

Create a new one by right-clicking in the project view. Select Create > UI Kit > Panel Setup Asset . Name the newly created file. For this guide, you can leave all settings at their default values.Panel Settings AssetGameUI_Panel

No need to change default panel settings

To display the main view UI template from the previous section, you need to create a new game object in the scene. Attach the UIDocument component to it.

When entering play mode in Unity, assigned content is automatically loaded. A is a UXML template. Assign both panel settings and new panel settings to components.UIDocumentVisualTreeAssetVisualTreeAssetMainView.uxmlGameUI_Panel

UI Documentation Component

Note : If a resource is not assigned to the UI Document component, it will automatically search the project and set the resource using the first panel found automatically. Keep this in mind when renaming or moving assets.PanelSettings

You can now enter run mode in the Unity Editor and view your UI in the game view.

UI displayed at runtime

Note : If you have multiple UI documents in your scene, you can assign the same panel settings resource to all of them. This will cause all UI to be rendered on the same panel, optimizing performance.

Create sample data to display

In this section, you create some sample data that will be used to populate a character list in the UI with data.

For the character list, you need a simple class that contains the character name, class, and portrait image. Create a new ScriptableObject script  Assets/Scripts/CharacterData.cs and paste the following code into the file:

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;
}

This property will automatically add an entry to the Create menu. Right-click a folder in the Project view to create a new instance of the Scriptable Object.[CreateAssetMenu]

Create a new "Create" menu item

Now you need to create some instances and populate them with random data. Put them all in the resources /characters folder. Later you'll write a script that automatically parses and loads all character data in this folder.CharacterData

Create some sample characters

Create a list entry UI template

In this section, you create UI templates for each entry in the list. At run time, the controller script will create an instance of this UI for each character and add it to the list. The UI for character list entries consists of a colored background frame and the character name.

List entry showing character name

Note : If you want to skip this step, you can copy the list entry 's UXML code from the bottom of this page and paste it directly into a new file. Save it as  Assets/UI/ListEntry.uxml .

Open the "UI Builder" window > "UI Builder" window via the menu "UI Toolkit > UI Builder". Create a new UXML template by selecting New File > Create a new UXML template.

Create a new UXML template in UI Builder

Add a visual element to the background and set the fixed height to 41px. Since text within an entry should be left-aligned and placed in the middle of the element, open the Alignment fold and set Item Left Alignment and Content Alignment to Center . Also set a left margin of 10px to minimize the distance between the label and the left border of the frame.

For styling, you can use a background color and add a 2px wide border with a radius of 15px and a color of . This step is optional, you can apply your own color and style.#AA5939#311A11

background visual elements

Add the label as a child to an existing visual element and name it so you can access it later in the controller script. Set Font Style to Bold and Font Size to 18.CharacterName

Add tags to character names

Save the UXML template as  Assets/UI/ListEntry.uxml . You can also find the final UXML code for this template at the bottom of the page here.

Create a controller for list items

In this section, you create the controller script for the list entries. The purpose of this script is to display the character instance's data in the UI of the list entry. It takes access to the label of the character name and sets it to display the name of the given character instance.

Create a new script  Assets/Scripts/UI/CharacterListEntryController.cs and paste the following code into it:

using UnityEngine.UIElements;

public class CharacterListEntryController
{
    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;
    }
}

There are two functions in this class, both of them are functions.Set

SetVisualElement(VisualElement visualElement)This function receives a visual element that is an instance of the 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 a character name tag within a UI element.ListEntry

SetCharacterData(CharacterData characterData)This function receives the characters whose name this list element should display. Since the list of elements in is pooled and reused, there must be a function that changes the character's data to be displayed.ListViewSet

Note that this class is not. Since the visual elements in the UI Kit are not game objects, components cannot be attached to them. Instead, this class will be attached to the properties in the next section.CharacterListEntryMonoBehaviouruserData

Create a controller for the main view

In this section, you create a controller script for the character list in the main view, and a MonoBehavior script that instantiates and assigns it to the visual tree.

First,   create a new script.cs under Assets/Scripts/UI/CharacterListController and paste the following code into it.

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

public class CharacterListController
{
    public void InitializeCharacterList(VisualElement root, VisualTreeAsset listElementTemplate)
    {
    }
}

This method will be populated later, but it is important to add the empty method now so that it can be called in the next section.InitializeCharacterList()

Attach controller script to main view

Like, the is not, needs to be attached to the visual tree in a different way. You need to create a MonoBehavior script that can be attached to the same game object. It will be instantiated and attached to the visual tree.CharacterListEntryControllerCharacterListControllerMonoBehaviourUIDocumentCharacterListController

Create a new script  Assets/Scripts/UI/MainView.cs and paste the following code into it:

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);
    }
}

Go into the Unity Editor and attach the script to the same game object. Will be assigned to the " List Entry Template " property.UIDocumentListEntry.uxml

Add main view script and assign reference

Script components do not need to instantiate MainView UXML as this is done automatically in the component of the same game object. The script accesses the UIDocument component to obtain a reference to the instantiated visual tree. It then creates instances of the root element of the visual tree and the UXML template for each list element.UIDocumentMainViewCharacterListController

Note : When the UI is reloaded, the companion MonoBehavior component on the same game object that contains the component will be disabled before the reload, and then re-enabled after the reload. Therefore, it is best to place the code that interacts with the UI in the and methods of these MonoBehaviors.UIDocumentOnEnableOnDisable

Enumerate all character data instances

The first functionality that should be added to the controller script is a function that enumerates all previously created character data instances. These will be used to fill out the list.

Copy the code below into the class.CharacterListController

List<CharacterData> m_AllCharacters;

void EnumerateAllCharacters()
{
    m_AllCharacters = new List<CharacterData>();
    m_AllCharacters.AddRange(Resources.LoadAll<CharacterData>("Characters"));
}

NOTE : This code assumes youcreated the character instance in the Resources/ Characters folder. If you place the characters in a different folder, you may need to adjust the folder name accordingly.

Now you need to call this method during initialization. Add a call to it at the top of the method:EnumerateAllCharacterInitializeCharacterList

public void InitializeCharacterList(VisualElement root, VisualTreeAsset listElementTemplate)
{
    EnumerateAllCharacters();
}

Get a reference to a UI element

In this section, you will fill in the contents of the method. The first thing this method needs to do is obtain references to all the individual UI controls it needs to access to display the information. Use the UQuery family of APIs to retrieve individual UI controls by name, USS class, type, or a combination of these controls.InitializeCharacterList

Extend the code in the class with the following code:CharacterListController

// UXML template for list entries
VisualTreeAsset m_ListEntryTemplate;

// UI element references
ListView m_CharacterList;
Label m_CharClassLabel;
Label m_CharNameLabel;
VisualElement m_CharPortrait;
Button m_SelectCharButton;

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>("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");
}

Populate the list with entries

Next, you need to populate the on-screen list with the characters you enumerated and loaded earlier. To do this, you need to create a new method in the class.FillCharacterListCharacterListController

Populating a list view with elements requires 4 steps:

  1. Create functionmakeItem
  2. Create functionbindItem
  3. Set item height
  4. Set project source

The purpose of the makeItem callback function is to create a small visual tree that represents the UI of a single list item and return the root visual element of this tree.

In this case, the callback needs to instantiate the UXML template you created for the list entries. It also requires creating an instance of the controller script that is responsible for using the .makeItemCharacterListEntryControllerCharacterData

Create a method inside the class and paste the code below.FillCharacterList

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;
    };
}

As part of the callback, store the controller script in a property of the instantiated visual element. This allows you to access the script later and assign different characters to list elements.makeItemuserData

// Assign the controller script to the visual element
newListEntry.userData = newListEntryLogic;

As a memory and performance optimization, list elements are reused rather than instantiating an element for each entry in the list. It only creates enough visual elements to fill the visible area, then pools and reuses them as the list is scrolled.ListView

For this reason, you need to provide a bindItem callback, which binds one of your instances (in this case) to an individual list element.CharacterData

Extend the method by adding the following code at the bottom.FillCharacterList

// Set up bind function for a specific list entry
m_CharacterList.bindItem = (item, index) =>
{
    (item.userData as CharacterListEntryController).SetCharacterData(m_AllCharacters[index]);
};

The callback receives a reference to the root visual element of the visual tree of list entries, as well as the index of the data. Since you store a reference to the . in the visual element's attribute, the code can access it and set the . directly.bindItemCharacterListEntryControlleruserDataCharacterData

Finally, you need to set the element's item height and give the list a reference to the data source. This tells the list how many elements it contains.

Extend the method by adding the following code at the bottom.FillCharacterList

// Set a fixed item height
m_CharacterList.fixedItemHeight = 45;

// Set the actual item's source list/array
m_CharacterList.itemsSource = m_AllCharacters;

Finally, you need to call this method at the end of initialization. Add the call to the bottom of the method as follows:FillCharacterListInitializeCharacterList

FillCharacterList();

If you enter play mode now , the character list will be filled with the names of the characters you created.

Character list is no longer empty

You can find the final code for the script at the bottom of this guide, hereCharacterListController

react to user choices

When a user selects a character, the character's details - namely portrait, full name and category - need to be displayed in the character details section on the right side of the screen. Additionally, when selecting characters, the select button needs to be enabled. If no characters are selected, the button should be disabled again.

Note that you can already click and select characters in the list. Functionality for selection and highlighting is part of the ListView control. You just need to add a callback function to react when the user changes the selection in the list. The control contains events for this purpose:ListViewonSelectionChange

Add the following code to the bottom of the method:InitializeCharacterList

// Register to get a callback when an item is selected
m_CharacterList.onSelectionChange += OnCharacterSelected;

Now, you need to implement the callback function you set up in the code above. This function will receive a list of all selected items in the list. However, since lists only allow selection of a single item, the selected item can be accessed directly through the list's selectedItem property.OnCharacterSelected

Copy the following code into your class:

void OnCharacterSelected(IEnumerable<object> selectedItems)
{
    // Get the currently selected item directly from the ListView
    var selectedCharacter = m_CharacterList.selectedItem as CharacterData;
}

This property may return null. This occurs if nothing is selected, or if the user presses the key to deselect everything. This case needs to be dealt with first.selectedItemESC

extension method as follows:OnCharacterSelected

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;
    }
}

If the selection is valid, the role's details need to be displayed in the UI. The label and vertical image visual elements can be accessed through references retrieved in the class's methods.InitializeCharacterList

Copy the following code into the method:OnCharacterSelected

// 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);

You can now enter play mode and view your character selection list. Press the key to deselect a character.Escape

final runtime UI

final script

Below you can find the full source code for all the files created in this guide.

MainView.uxml

<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>

ListEntry.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>

Character data.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;
}

Character list entry controller.cs

using UnityEngine.UIElements;

public class CharacterListEntryController
{
    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;
    }
}

Main view.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);
    }
}

Character list controller.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;
    Button m_SelectCharButton;

    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>("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;
    }

    List<CharacterData> m_AllCharacters;

    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
        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;

            // 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);
    }
}

Guess you like

Origin blog.csdn.net/jianshi2023/article/details/130718546