Unity3D :创建您的第一个运行时 UI

news2024/11/24 3:03:43

ygtu

May 16, 2023 • 27 min read

推荐:将 NSDT场景编辑器 加入你的3D工具链
3D工具集: NSDT简石数字孪生

创建您的第一个运行时 UI

本页将指导您完成使用 UI 工具包设置简单字符选择屏幕的步骤。它涵盖了 UI 元素和模板的创建、场景设置以及如何将脚本逻辑连接到 UI。本指南不会介绍通过 USS 设置样式,并且仅使用默认样式和主题。

您可以在本页底部此处找到本指南的最终源代码。

主题:UI 生成器、列表视图、标签、面板设置、UIDocument、选择处理

本指南将指导您完成以下步骤:

  • 创建主 UI 视图
  • 设置场景
  • 创建要显示的示例数据
  • 为主视图创建控制器
  • 创建列表条目 UI 模板
  • 为列表条目创建控制器
  • 对用户选择做出反应

创建主 UI 视图

最终的 UI 屏幕由两个单独的 UI 模板 (UXML) 组成。主视图模板包含包含字符名称的列表、用于显示所选字符详细信息的较小面板以及一个按钮。在本节中,您将使用 UI 生成器设置此模板。

注意:如果您熟悉 UI 生成器并希望跳过此步骤,则可以从此页面底部复制主视图的 UXML 代码,并将其直接粘贴到新文件中。将其另存为 Assets/UI/MainView.uxml

主视图的 UI 布局设置

通过菜单“UI Toolkit> UI Builder”打开“UI 生成器”窗口>“UI 生成器”窗口。使用视口左上角的文件菜单创建新的 UXML 文档。

UI 生成器文件菜单

开发游戏 UI 时,请始终确保选择 UI 生成器视口右上角的 。编辑器和运行时主题之间的默认字体大小和颜色不同,这会影响布局。Unity Default Runtime Theme

通过从库中拖动来创建新元素

层次结构中选择新的 UXML 文件,然后启用匹配游戏视图复选框。您可能需要将 Unity 编辑器设置为横向分辨率(如果尚未设置)。

启用比赛游戏视图

现在是时候创建 UI 元素了! 通过将新的可视元素从拖到层次结构中来创建新的可视元素。

通过从库中拖动来创建新元素

新元素需要覆盖整个屏幕,因此需要将属性设置为 1。从层次结构中选择元素,然后在右侧的“检查器”面板中找到标记为 Flex 的折叠页。将“增长”的值从 0 更改为 1。flex-grow

设置 Flex 属性

若要将此可视元素的所有子项居中置于屏幕中间,请更改可视元素的“对齐”属性。您需要将“对齐项目”和“内容对齐”都设置为居中

居中儿童

最后,您可以在“背景>颜色”下选择一种背景颜色。此步骤是可选的。此示例用作颜色。#732526

根元素背景色

接下来,在现有可视元素下创建一个新的可视元素。这将成为 UI 左侧和右侧部分的父容器。

添加子可视元素

将此新元素的属性设置为 (默认为列)。您还需要将固定高度设置为 350 像素。flex-directionrow

中心容器属性

这就是当前 UI 的外观。请注意,您的屏幕可能看起来会有所不同,具体取决于游戏视图的分辨率和纵横比。

内部有空元素的背景容器

若要为字符名称创建列表,请从库中选择一个 ListView 控件,并将其作为子项添加到刚创建的可视元素下。选择元素并在检查器中为其指定名称。这是必需的,以便您稍后可以通过控制器脚本访问此列表。CharacterList

内部有空元素的背景容器

将列表设置为固定宽度为 230 像素。还要在右侧给它一个 6 px 宽的边距,以便与要创建的下一个元素保持一定距离。

字符列表的大小和边距折叠

您还可以为列表指定背景颜色并设置圆角边框。本指南用于背景和边框颜色,边框宽为 4px,半径为 15px。此步骤是可选的。#6E3925#311A11

样式字符列表

在与 相同的父级下添加新的可视元素。这将包含角色详细信息面板和按钮。在“对齐”折叠式下,将“项目对齐”“内容对齐”的设置更改为 。CharacterListflex-endspace-between

证明内容属性的合理性

将新的可视元素添加到此新容器。这将成为角色详细信息面板。当用户从左侧列表中选择一个角色时,它将显示该角色的肖像、名称和类。

为元素设置 276 像素的固定宽度,并将“对齐项目”和“内容对齐”切换为居中。还要为元素添加一个 8 像素宽的填充,以便子项与容器边界保持最小距离。

字符详细信息容器的属性

您可以通过将背景颜色设置为 4px 宽边框和 15px 半径的边框颜色来设置面板样式。此步骤是可选的。#AA5939#311A11

您的 UI 布局现在应类似于下图。

空字符详细信息面板

接下来,您将各个 Ui 控件添加到字符详细信息中。首先是人物肖像。它由两个元素组成 - 背景中的帧和前景中的图像。

首先将新的可视元素添加到背景帧的字符详细信息容器中。为其分配 120x120 像素的固定大小和 4 像素的填充,以便包含的图像不会直接接触边框。

您可以使用 2px 宽、半径为 15px 的边框(颜色为 和背景色)来设置元素样式。随意应用您自己的颜色和样式。#311A11#FF8554

人物肖像的背景框架

对于实际图像,将新的可视元素作为子元素添加到刚创建的框架中。为其命名,以便以后可以在控制器脚本中访问它。CharacterPortrait

“Flex > Grow”设置为 1,以便图像利用所有可用空间。此外,请确保将“背景>缩放模式”下的缩放模式更改为 ,以便可以放大或缩小图像以匹配元素大小,同时保持正确的纵横比。scale-to-fit

纵向图像的可视元素

接下来,将两个标签控件添加到字符详细信息容器,稍后将使用这些控件显示所选字符的名称和类。将它们命名为 和 。CharacterNameCharacterClass

为名称和类添加标签

若要使字符的名称比类更突出,请将标签的字体大小更改为 18,并将样式设置为体。

更改字体设置

您的 UI 屏幕现在应类似于下图。

完成的角色详细信息面板

最后,将按钮控件添加到右侧 UI 容器。稍后,您将在控制器脚本中访问此按钮,并在选择或取消选择字符时启用或禁用它。命名按钮并为其指定 150px 的固定宽度。还应将按钮的标签文本设置为 。SelectCharButtonSelect Character

用于字符选择的“添加”按钮

要设置按钮样式,请将背景色设置为 ,并将 2px 边框设置为颜色为 。此步骤是可选的。#FF8554#311A11

完成的主视图应类似于下图。

最终主视图布局

将 UXML 模板另存为 Assets/UI/MainView.uxml。 您还可以在此处的页面底部找到此模板的最终 UXML 代码。

设置场景

在本节中,你将了解如何在运行时在游戏中加载和显示你在上一节中创建的 UI 模板。

要开始,您需要创建一个面板设置资产。此资产将定义屏幕的设置,例如缩放模式和渲染顺序。它还将确定 UI 在 UI 工具包调试器中显示的名称。

创建新的面板设置资源

通过在项目视图中单击鼠标右键来创建新的。选择创建> UI 工具包>面板设置资产。将新创建的文件命名为 。 对于本指南,您可以将所有设置保留为其默认值。Panel Settings AssetGameUI_Panel

无需更改默认面板设置

要显示上一节中的主视图 UI 模板,您需要在场景中创建新的游戏对象。将 UIDocument 组件附加到它。

在 Unity 中进入播放模式时,将自动加载分配的内容。A 是一个 UXML 模板。将面板设置和新面板设置都分配给组件。UIDocumentVisualTreeAssetVisualTreeAssetMainView.uxmlGameUI_Panel

UI 文档组件

注意:如果未将资源分配给 UI 文档组件,它将自动搜索项目并使用自动找到的第一个面板设置资源。重命名或移动资产时请记住这一点。PanelSettings

现在,您可以在 Unity 编辑器中进入运行模式,并在游戏视图中查看您的 UI。

运行时显示的 UI

: 如果场景中有多个 UI 文档,则可以将同一面板设置资源分配给所有文档。这将导致所有 UI 呈现在同一面板上,从而优化性能。

创建要显示的示例数据

在本节中,你将创建一些示例数据,这些数据将用于用数据填充 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]

新建“创建”菜单项

现在,您需要创建一些实例并用随机数据填充它们。将它们全部放在资源/字符文件夹中。稍后您将编写一个脚本,该脚本会自动解析并加载此文件夹中的所有字符数据。CharacterData

创建一些示例字符

创建列表条目 UI 模板

在本节中,您将为列表中的各个条目创建 UI 模板。在运行时,控制器脚本将为每个字符创建此 UI 的实例,并将其添加到列表中。 字符列表条目的 UI 由彩色背景框架和字符名称组成。

显示字符名称的列表条目

注意:如果要跳过此步骤,可以从此页面底部复制列表条目的UXML代码,并将其直接粘贴到新文件中。将其另存为 Assets/UI/ListEntry.uxml

通过菜单“UI Toolkit> UI Builder”打开“UI 生成器”窗口>“UI 生成器”窗口。 通过选择“新建文件”>创建新的 UXML 模板。

在 UI 生成器中创建新的 UXML 模板

为背景添加一个视觉元素,并将固定高度设置为 41px。由于条目内的文本应左对齐并放置在元素的中间,因此请打开“对齐”折叠页并将“项目左对齐”和内容对齐”设置为居中。 还要设置 10px 的左边距,以使标签与框架左边框的距离最小。

对于样式,您可以使用背景色并添加 2px 宽的边框,半径为 15px,颜色为 .此步骤是可选的,您可以应用自己的颜色和样式。#AA5939#311A11

背景视觉元素

将标签作为子项添加到现有可视元素并命名 ,以便稍后可以在控制器脚本中访问它。将“字体样式”设置为体,将“字体大小”设置为 18。CharacterName

为角色名称添加标签

将 UXML 模板另存为 Assets/UI/ListEntry.uxml。 您还可以在此处的页面底部找到此模板的最终 UXML 代码。

为列表条目创建控制器

在本节中,您将为列表条目创建控制器脚本。该脚本的目的是在列表条目的 UI 中显示字符实例的数据。它需要访问字符名称的标签,并将其设置为显示给定字符实例的名称。

创建一个新脚本 Assets/Scripts/UI/CharacterListEntryController.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;
    }
}

此类中有两个函数,它们都是函数。Set

SetVisualElement(VisualElement visualElement)此函数将接收一个可视元素,该元素是您在上一节中创建的 UI 模板的实例。主视图控制器将创建此实例。此函数的目的是检索对 UI 元素内字符名称标签的引用。ListEntry

SetCharacterData(CharacterData characterData)此函数接收此列表元素应显示其名称的字符。由于 中的元素列表是池化和重用的,因此必须有一个函数来更改要显示的角色的数据。ListViewSet

请注意,该类不是 .由于 UI 工具包中的可视元素不是游戏对象,因此无法将组件附加到它们。相反,此类将附加到下一节中的属性。CharacterListEntryMonoBehaviouruserData

为主视图创建控制器

在本节中,您将为主视图中的字符列表创建一个控制器脚本,以及一个实例化并将其分配给可视化树的 MonoBehavior 脚本。

首先,在 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()

将控制器脚本附加到主视图

就像 一样,the 不是 ,需要以不同的方式附加到可视化树。 您需要创建一个 MonoBehavior 脚本,该脚本可以附加到与 相同的游戏对象。它将实例化并将其附加到可视化树。CharacterListEntryControllerCharacterListControllerMonoBehaviourUIDocumentCharacterListController

创建一个新脚本 Assets/Scripts/UI/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);
    }
}

进入 Unity 编辑器,将脚本附加到 所在的同一游戏对象。将 分配给“列表条目模板”属性。UIDocumentListEntry.uxml

添加主视图脚本并分配引用

脚本组件无需实例化 MainView UXML,因为这是在同一游戏对象的组件中自动完成的。该脚本访问 UIDocument 组件以获取已实例化的可视化树的引用。然后,它会创建可视化树的根元素和用于各个列表元素的 UXML 模板的 和 的实例。UIDocumentMainViewCharacterListController

注意:重新加载 UI 时,包含该组件的同一游戏对象上的配套 MonoBehavior 组件将在重新加载之前被禁用,然后在重新加载后重新启用。因此,最好将与 UI 交互的代码放置在这些 MonoBehavior 的 and 方法中。UIDocumentOnEnableOnDisable

枚举所有字符数据实例

应添加到控制器脚本的第一个功能是枚举之前创建的所有字符数据实例的函数。这些将用于填写列表。

将下面的代码复制到类中。CharacterListController

List<CharacterData> m_AllCharacters;

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

注意:此代码假定您在“资源/字符”文件夹中创建了字符实例。如果将字符放在其他文件夹中,则可能需要相应地调整文件夹名称。

现在,您需要在初始化期间调用该方法。将对其的调用添加到方法的顶部:EnumerateAllCharacterInitializeCharacterList

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

获取对 UI 元素的引用

在本节中,您将填写该方法的内容。此方法需要做的第一件事是获取对它访问以显示信息所需的所有单个 UI 控件的引用。使用 UQuery 系列 API 按名称、USS 类、类型或这些控件的组合检索各个 UI 控件。InitializeCharacterList

使用以下代码扩展类中的代码: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");
}

用条目填充列表

接下来,您需要用之前枚举和加载的字符填充屏幕上的列表。为此,您需要在类中创建一个新方法。FillCharacterListCharacterListController

用元素填充列表视图需要 4 个步骤:

  1. 创建函数makeItem
  2. 创建函数bindItem
  3. 设置项目高度
  4. 设置项目源

makeItem 回调函数的目的是创建一个小的可视化树,该树表示单个列表项的 UI,并返回此树的根可视元素。

在这种情况下,回调需要实例化您为列表条目创建的 UXML 模板。它还需要创建控制器脚本的实例,该实例负责使用来自 .makeItemCharacterListEntryControllerCharacterData

在类内创建一个方法并粘贴下面的代码。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;
    };
}

作为回调的一部分,将控制器脚本存储在实例化可视元素的属性中。这允许您稍后访问脚本并为列表元素分配不同的字符。makeItemuserData

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

作为内存和性能优化,重用列表元素,而不是为列表中的每个条目实例化一个元素。它只创建足够的视觉元素来填充可见区域,然后在滚动列表时汇集并重复使用它们。ListView

出于这个原因,你需要提供一个 bindItem 回调,它将你的一个实例(在本例中)绑定到一个单独的列表元素。CharacterData

通过在底部添加下面的代码来扩展该方法。FillCharacterList

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

回调接收对列表条目的可视化树的根可视元素的引用,以及数据的索引。由于您在可视元素的属性中存储了对 的引用,因此代码可以访问它并直接设置 .bindItemCharacterListEntryControlleruserDataCharacterData

最后,您需要设置元素的项高度,并为列表提供对数据源的引用。这告诉列表它包含多少元素。

通过在底部添加下面的代码来扩展该方法。FillCharacterList

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

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

最后,您需要在初始化结束时调用该方法。 将调用添加到方法底部,如下所示:FillCharacterListInitializeCharacterList

FillCharacterList();

如果您现在进入播放模式,角色列表将填满您创建的角色的名称。

字符列表不再为空

您可以在本指南底部找到脚本的最终代码,此处CharacterListController

对用户选择做出反应

当用户选择角色时,角色的详细信息 - 即肖像,全名和类别 - 需要显示在屏幕右侧的角色详细信息部分中。此外,选择字符时,需要启用选择按钮。如果未选择任何字符,该按钮应再次禁用。

请注意,您已经可以单击并选择列表中的字符。用于选择和突出显示的功能是 ListView 控件的一部分。您只需要添加一个回调函数,以便在用户更改列表中的选择时做出反应。该控件包含用于此目的的事件:ListViewonSelectionChange

将以下代码添加到方法的底部:InitializeCharacterList

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

现在,您需要实现在上面的代码中设置的回调函数。此函数将接收列表中所有选定项目的列表。但是,由于列表仅允许选择单个项,因此可以直接通过列表的 selectedItem 属性访问所选项。OnCharacterSelected

将以下代码复制到您的类中:

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

该属性可能返回 null。如果未选择任何内容,或者用户按下该键取消选择所有内容,则会出现这种情况。这个案子需要先处理。selectedItemESC

扩展方法,如下所示: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;
    }
}

如果选择有效,则需要在UI中显示角色的详细信息。可以通过在类的方法中检索到的引用来访问标签和纵向图像视觉元素。InitializeCharacterList

将下面的代码复制到方法中: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);

您现在可以进入播放模式并查看您的角色选择列表。按键取消选择字符。Escape

最终运行时 UI

最终脚本

您可以在下面找到本指南中创建的所有文件的完整源代码。

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>

字符数据.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;
}

字符列表入口控制器.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;
    }
}

主视图.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);
    }
}

字符列表控制器.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);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/535330.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

公网SSH远程连接Termux - 电脑使用安卓Termux【cpolar内网穿透】

文章目录 1.安装ssh2.安装cpolar内网穿透3.远程ssh连接配置4.公网远程连接5.固定远程连接地址 使用安卓机跑东西的时候&#xff0c;屏幕太小&#xff0c;有时候操作不习惯。不过我们可以开启ssh&#xff0c;使用电脑PC端SSH远程连接手机termux。 本次教程主要实现在安卓手机te…

如何提高PID控制器离散化精度(SCL+梯形图代码)

通常采样率(执行频率)1/Ts的典型取值为闭环带宽的5~10倍。实际上大部分工程采样率范围会使我们所期望的闭环性能下降,要忽略采样造成的影响,采样率至少是典型值的10倍。原则上,采样率1/Ts在允许的计算能力范围内越大越好。当采样率较低时,提高PID控制器离散化精度的一种方…

DSSR122 4899001-NK汽车电子电气架构进化的终点在哪里

​ DSSR122 4899001-NK汽车电子电气架构进化的终点在哪里 随着人们对汽车性能要求的不断提高&#xff0c;越来越多的汽车电子/电气&#xff08;E/E&#xff09;架构系统被集成到汽车中&#xff0c;成为智能交通、自动驾驶、智能城市等更广泛生态系统的组成部分。汽车行业自动化…

Spring Boot 集成 Redis

Spring Boot 集成 Redis 一 集成Jedis1.1 Jedis1.2 集成步骤1.2.1 建Module1.2.2 改POM1.2.3 写YML1.2.4 主启动1.2.5 业务类 二 集成lettuce2.1 lettuce2.2 lettuce Vs Jedis2.3 集成步骤2.3.1 改pom2.3.2 业务类 三 集成RedisTemplate3.1 单机集成3.1.1 POM3.1.2 YML配置3.1.…

terrapos处理pospac数据(二)

上篇&#xff08;参考文献2&#xff09;说到terrapos不能处理pospac中的GPS数据&#xff0c;博主猜测可能的原因是没有记录主天线、方向天线和差分天线的原始数据&#xff0c;但是&#xff0c;即使记录这三种原始数据&#xff0c;当从pospac中提取IMU和GPS数据的时候&#xff0…

macOS Ventura 13.4 RC3(22F66)发布

系统介绍 5 月 17 日消息&#xff0c;苹果今日向 Mac 电脑用户推送了 macOS 13.4 RC 3 更新&#xff08;内部版本号&#xff1a;22F66&#xff09;&#xff0c;本次更新距离上次发布隔了 5 天。 macOS Ventura 带来了台前调度、连续互通相机、FaceTime 通话接力等功能。其中&…

我和C++的故事---第一次见面.

&#x1f4dd;个人主页&#xff1a;认真写博客的夏目浅石. &#x1f3e0;学习社区&#xff1a;夏目友人帐. 文章目录 前言一、第一个C程序二、C 关键字(C98)三、命名空间1、命名空间的定义2、命名空间的使用3、命名空间的三种展开方式 四、C输入&&输出&&换行1、…

doxygen: 在Windows上源码编译

文章目录 1. 目的2. 思路3. 安装 Chocolatey4. 用 choco 安装 bison 和 flex安装 gs:安装 libiconv 5. 编译报错 1. 目的 在 windows 上源码编译 doxygen&#xff0c; 改代码加功能。 2. 思路 doxygen 依赖 flex 和 bison&#xff0c; 手动编译 flex 和 bison 很麻烦可以用…

孤独患者的树洞?AI情感陪伴是如何实现的?

一、人工智能大模型 自ChatGPT发布以来&#xff0c;越来越多的中国企业和研究机构扎堆冲入AI大模型赛道&#xff0c;试图在自然语言处理、机器翻译、问答等方面取得重大突破。在未来&#xff0c;这些模型有机会应用于金融、医疗、电商等各领域。 人工智能大模型的应用不仅可以…

矿山电子封条 yolov8网络模型

矿山电子封条通过yolov8网络模型利用AI图像智能视频识别等技术&#xff0c;矿山电子封条yolov8网络模型智能分析异常情况&#xff0c;包括不限于人数变化情况、出入井人员以及相关现场设备开停状态进行自动全天候远程监控。YOLOv8 主要参考了最近提出的诸如 YOLOX、YOLOv6、YOL…

为什么Windows/Linux 的端口数量最大限制为65535?

端口0&#xff1a;一般是用来表示所有端口&#xff0c;即1–65535。另外其他答主也提到1–1024是知名端口号&#xff0c;但那是很久以前数据&#xff0c;是该更新了。举几个大于1024的知名端口号&#xff1a;1614/1615&#xff1a;思科ISE用于AAA的端口号1812/1813&#xff1a;…

【腾讯云 Finops Crane 集训营】基于 Kubernetes 实现云资源分析与成本优化平台

基于 Kubernetes 实现云资源分析与成本优化平台 一、基本介绍1.主要功能2.整体架构 二、基于 Kubernetes 实现云资源分析与成本优化平台1.准备工作2.安装 Prometheus/Grafana 软件包3.安装 Crane 软件包4. 使用智能弹性 EffectiveHPA4.配置集群 三、功能验证1.成本展示2.资源推…

3分钟了解FactoryBean的作用和底层工作原理

大家好&#xff0c;我是冰点&#xff0c;本节我们通过对FactoryBean 接口源码解析&#xff0c;以及使用示例剖析&#xff0c;让大家3分钟了解FactoryBean的作用和底层工作原理。以及FactoryBean的使用场景&#xff0c;学以致用。 FactoryBean是Spring框架中的一个接口&#xf…

基于 resnet 对 CIFAR-10 图片进行分类和网络微调

本文基于李沐老师的 实战 Kaggle 比赛&#xff1a;图像分类 (CIFAR-10) 文章目录 数据格式数据加载标签转化划分数据集查看数据搭建网络主训练函数网络训练 数据格式 CIFAR-10 下载地址&#xff1a;https://www.kaggle.com/competitions/cifar-10/data 下拉到最下面有一个 Do…

JavaScript中的Event(事件)详解

Event 对象 Event 对象代表事件的状态&#xff0c;比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态。 事件通常与函数结合使用&#xff0c;函数不会在事件发生前被执行&#xff01; 事件句柄 (Event Handlers) HTML 4.0 的新特性之一是能够使 HTML 事…

Maven uber-jar(带依赖的打包插件)maven-shade-plugin

文章目录 最基础的 maven-shade-plugin 使用生成可执行的 Jar 包 和 常用的资源转换类包名重命名打包时排除依赖与其他常用打包插件比较 本文是对 maven-shade-plugin 常用配置的介绍&#xff0c;更详细的学习请参照 Apache Maven Shade Plugin 官方文档 通过使用 maven-shade…

BetaFlight飞控启动运行过程简介疑问跟踪

BetaFlight飞控启动&运行过程简介疑问跟踪 1. 源由2. 【已解存疑】问题一&#xff1a;6.1 Why desiredPeriodCycles is so important to Betaflight task?3. 【已解】问题二&#xff1a;6.2 What root cause has made gyro task to been overrun, so scheduler has to ski…

轮式机械臂小车实现语音控制

1. 功能说明 本文实例将实现语音控制R214e样机运动&#xff08;前进、后退、左转、右转、拿起、放下&#xff09;的功能。 2. 电子硬件 在这个示例中&#xff0c;我们采用了以下硬件&#xff0c;请大家参考&#xff1a; 主控板 Basra主控板&#xff08;兼容Arduino Uno&#x…

港科夜闻|推进湾区产学研融合发展,香港科大(广州)—广州市属国企校企合作专题交流会圆满举行...

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、推进湾区产学研融合发展&#xff0c;香港科大(广州)—广州市属国企校企合作专题交流会圆满举行。本次交流会由香港科大(广州)、广州市人民政府国有资产监督管理委员会主办&#xff0c;越秀集团协办&#xff0c;旨在充分发…

springboot+jsp网上图书商城销售系统java

开发环境 开发语言&#xff1a;Java 框架&#xff1a;springboot 技术&#xff1a;JSP JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven…