Привязка данных
Содержание
По сути пользовательский интерфейс — это связь игрока с данными, которые движут вашим приложением. Именно через него игрок видит, трогает и меняет внутреннее состояние и логику игры.
Игроки не будут видеть необработанную статистику; вместо этого, они будут видеть шкалу здоровья. Вместо того, чтобы читать списки предметов напрямую, они используют перетаскивание и сбрасывание инвентаря. Это взаимодействие между UI и его данными повлияет на то, как вы структурируете свой проект.
UI, который отражает ваши данные игры
Вот окно статистики персонажа в UI Toolkit образец – Дракон взрывателей. Этот интерфейс демонстрирует ключевые характеристики из игры в духе RPG.
Представление — это сам UI, то есть его часть, с которой взаимодействуют игроки. Контейнеры с вкладками упорядочивают способности персонажа и упрощают навигацию.
За кулисами, данные живут в модели, например, ScriptableObject, хранящей статистику каждого персонажа.
Это разделение интересов между представлением и моделью — ключевой принцип архитектуры интерфейса. Отделение визуального интерфейса от нижележащих данных делает код гибче, пригоднее для повторного использования и проще в сопровождении.
Традиционно, это включает в себя прямые обновления или системы, управляемые событиями, где наблюдатели обновляют UI при изменении данных. Хотя эти операции синхронизации эффективны, они могут ввести повторяющийся, типичный код.
По мере роста вашего проекта, управление этими системами может стать сложным. Добавление новых элементов или зависимостей часто требует дополнительной логики обновления или обработчиков событий. Это может перегружать ваши скрипты, затрудняя их чтение и обслуживание.
Введите привязку данных времени выполнения
Runtime data binding в Unity 6 предлагает упрощенное решение этой проблемы. Он связывает данные приложения непосредственно с элементами UI, обеспечивая, что изменения в одном автоматически отражаются в другом.
Эта архитектура Model-view-viewmodel (MVVM) добавляет слой логики представления между представлением и моделью. Модель представления действует как посредник, выводя данные из модели, форматированные для представления.
Узнайте больше о MVVM вместе с более шаблонами дизайна в Unity e-book Уровень ваш код с шаблонами дизайна и SOLID.
Например, шкала здоровья может автоматически отображать здоровье игрока, или метка счета может обновляться в реальном времени без необходимости дополнительной логики скрипта или ручной обработки событий. С меньшим количеством логики синхронизации для управления, ваш проект может масштабироваться более эффективно.
Давайте рассмотрим примеры привязки данных UI Toolkit во время выполнения, чтобы посмотреть, как вы можете использовать это в вашем проекте.
Концепции связывания данных
Unity 6 вводит систему привязки данных во время выполнения, которая обеспечивает структурированный способ соединения элементов UI с данными приложения. Чтобы привязать свойство визуального элемента к источнику данных, создайте экземпляр DataBinding.
Вот несколько важных концепций:
Источник данных: Это объект, который содержит данные для привязок UI.
Путь к источнику данных: Это свойство или поле в источнике данных является тем, к которому подключается элемент UI.
Режим привязки: Этот режим контролирует поток данных между источником и UI и может быть либо односторонним, либо двусторонним.
Эти части работают вместе, чтобы создать привязки данных. Давайте рассмотрим их более подробно.
Подготовка источника данных
A источник данных объект, содержащий данные для UI любые связки. C# объект может служить источником данных, включая ScriptableObjects, MonoBehaviours, или обычная C# Использование структур в качестве источников данных может повысить производительность благодаря легкому распределению памяти и уменьшению сбора мусора. Привязка данных может быть настроена как через код, так и через Inspector.
Этот демонстрационный проект использует ScriptableObjects в качестве источника данных для их удобной способности сериализировать данные в Unity Inspector.
Используйте атрибут CreateProperty
Чтобы открыть свойства для связывания, UI Toolkit опирается на сумки имущества в результате деятельности Unity Свойства модуль. Они определяют, какие свойства в источнике данных доступны для UI связки.
Чтобы сделать свойства привязными, используйте атрибуты CreateProperty. Это явно обозначает свойства для системы привязки. Вот общая схема настройки:
[SerializeField, DontCreateProperty]
int m_Value;
[CreateProperty]
public int Value
{
get => m_Value;
set => m_Value = value;
}
В данном примере m_Value помечен атрибутом SerializeField для серийного кодирования, но исключен из привязки атрибутом DontCreateProperty.
Свойство Value, с другой стороны, помечено CreateProperty, что делает его доступным для системы привязки. Это четкое разделение помогает управлять потоком данных между моделью и UI.
Привязки данных времени выполнения используют пакеты свойств и для эффективного обхода и манипулирования данными типа. По умолчанию Unity генерирует пакеты свойств с помощью отражения при первом доступе к типу, что добавляет небольшую накладную на время выполнения.
Чтобы избежать этого, используйте атрибут CreateProperty при определении свойств. Это генерирует код привязки во время компиляции, устраняя необходимость отражения во время выполнения и снижая нагрузку на производительность.
Источники данных и пути
Как только ваш источник данных готов, его можно связать с UI. A путь к источнику данных указывает свойство или поле в этом источнике данных, которое требуется подключить к UI элемент. Например, если ваш источник данных имеет свойство «здравоохранение», путь будет указывать непосредственно на свойство, использующее его в UXML или с помощью привязки в C#Давайте посмотрим, как это выглядит на практике.
В UI Builder: Выберите элемент Hierarchy, перейдите к Inspector и используйте опцию Добавить связь из меню опций (ru).
Затем, назначить ваш Источник данных, как PlayerDataSO ScriptableObject, и указать Путь к источнику данных, например CurrentHealth.
В UXML: Когда вы настраиваете привязку данных в UI Builder, она генерирует соответствующий UXML. Путь источника данных можно также добавить или изменить вручную в текстовом редакторе. Это блок кода, который создает привязку:
<Bindings>
<ui:DataBinding property="text" data-source-path="Health"/>
</Bindings>
Использование C#: Создайте экземпляр или ссылку на объект источника данных в вашем скрипте, например, ScriptableObject. Назначьте его свойству dataSource корневого элемента. Используйте dataSourcePath для указания точного свойства для привязки.
Вот фрагмент, который показывает, как установить свойства dataSource и dataSourcePath в скрипте. Мы обсуждаем это более подробно в разделе ниже о настройке привязки данных в C#.
var label = new Label();
var parentData = ScriptableObject.CreateInstance<PlayerDataSO>();
playerData.Health = 100;
label.SetBinding("text", new DataBinding()
{
dataSource = playerData,
dataSourcePath = new PropertyPath(nameof(PlayerDataSO.Health)),
});
Примечание: Конфликт может возникнуть, если вы определяете привязки данных для одного и того же элемента UI. Во избежание путаницы:
Используйте привязки UI Builder/UXML для статических конфигураций данных или конфигураций по умолчанию, которые не нужно менять во время выполнения.
Используйте привязки C# для динамических обновлений или случаев, когда источник данных должен изменяться во время игры.
Также можно настроить часть привязки в UI Builder/UXML и завершить привязку во время выполнения. Дополнительный контекст см. в рабочем процессе Неразрешенные привязки данных.
Наследование источников данных
Визуальные элементы автоматически наследуют источник данных родительского элемента, если только не назначен новый источник данных. Например, если корневой элемент имеет источник данных, все дочерние элементы используют его по умолчанию. На диаграмме показано это поведение:
Когда родительский элемент имеет источник данных, его дочерние элементы автоматически наследуют его. В UI Builder поле Источник данных для дочернего элемента предварительно заполнено родительским источником данных, но может быть переопределено по мере необходимости.
Такая же логика наследования применяется при работе с C#, как показано в следующем примере:
var root = new VisualElement();
var parentData = ScriptableObject.CreateInstance<PlayerDataSO>();
parentData.Health = 100;
// Assign a data source to the root element
root.dataSource = parentData;
var child = new VisualElement();
var childData = ScriptableObject.CreateInstance<PlayerDataSO>();
childData.Health = 50;
// Override the inherited data source for the child
child.dataSource = childData;
root.Add(child);
Здесь дочерняя структура переопределяет родительскую структуру, предоставляя ей независимый источник данных.
Моды связывания
Режимы привязки управляют потоком данных между источником данных и UI.
Эти параметры присутствуют в UI Builder и C# API:
TwoWay (По умолчанию): Изменения распространяются как из источника данных в UI, так и из UI в источник данных. Используйте это для интерактивных элементов, таких как ползунки или текстовые поля, где пользователь может изменять данные.
ToTarget: Данные поступают только из источника данных в UI. Используйте это для элементов только для чтения UI.
ToSource: Данные поступают только от UI к источнику данных. Это полезно для входов, где вам не нужно отображать текущее значение первоначально.
ToTargetOnce: Данные поступают из источника данных в UI только один раз и не отслеживают дальнейшие изменения в источнике данных.
Пример: Данные, связывающие шкалу здоровья
Давайте посмотрим на практический пример, чтобы увидеть, как создать некоторые базовые привязки данных в UI Toolkit. Вот пример из демо сцены — простой бар здоровья, который динамически обновляется в зависимости от здоровья игрока.
Демонстрационная сцена
Следующие примеры можно найти в демо-уроке Привязка данных, включённом в примерный проект QuizU.
Чтобы получить доступ к нему во время выполнения, перейдите в Главное меню и выберите Демос > Привязка данных, или загрузить DataBindingDemo сцена непосредственно после отключения загрузчика (Тест > Не загружайте Bootstrap Scene в игре).
Демонстрационная сцена включает в себя две шкалы здоровья, одна с связями, созданными в UXML с UI Builder и другая с связями, созданными в C#.
Подготовка источника данных
Образец проекта содержит информацию и статистику игрока, которые хранятся в PlayerDataSO ScriptableObject. Соответствующие свойства в PlayerDataSO помечены атрибутами CreateProperty, что делает их доступными для привязки.
Каждый столбец здоровья представляет только подмножество данных в PlayerDataSO, включая имя игрока и значения здоровья. Фрагмент класса показывает некоторые его свойства и связанные поля:
using System;
using Unity.Properties;
using UnityEngine;
using UnityEngine.UIElements;
[CreateAssetMenu(fileName = "PlayerDataSO", menuName = "Demos/Player_Data")]
public class PlayerDataSO : ScriptableObject
{
[CreateProperty] public string PlayerName => m_PlayerName;
[CreateProperty] public int CurrentHealth =>
Mathf.Clamp(m_CurrentHealth, 0, m_MaximumHealth);
[CreateProperty] public int MaximumHealth => m_MaximumHealth;
[SerializeField] string m_PlayerName;
[SerializeField] int m_MaximumHealth = 100;
[SerializeField] [Range(0, k_MaxHealthRange)]
int m_CurrentHealth = 100;
const int k_MaxHealthRange = 200;
}
UI использует конкретные пути данных, такие как PlayerName, CurrentHealthи MaximumHealth, для визуального отображения этой информации на экране.
Привязка данных в UI Builder/UXML
UI Builder предлагает визуальный, интерактивный способ привязки UI элементов к данным. Он идеально подходит для UI художников, которые предпочитают рабочий процесс, ориентированный на дизайн, и разработчиков, которые пользуются обратной связью в режиме реального времени во время настройки. Он также служит полезным инструментом обучения для тех, кто не знаком с привязкой данных.
В демонстрационной сцене привязки данных шкалы здоровья Игрока Один полностью настроены в UI Builder. Это включает:
Выбор корневого элемента: Выберите корневой элемент в иерархии, который содержит строку здоровья. В этом примере верхним контейнером является элемент demo_container-uxml.
Назначение источника данных: В разделе Привязка данных Inspector установите источник данных на ассет ScriptableObject. Это назначает источник данных и распространяет его на все дочерние элементы.
Определение путей источников данных: Укажите пути источников данных для связывания отдельных элементов UI с их соответствующими свойствами в ScriptableObject (e.g.,
PlayerDataSO.PlayerName).
Как только источник данных установлен на корне, он должен появиться как источник данных по умолчанию для дочерних элементов. Просто заполните правильный путь к источнику данных. В этой таблице показаны привязки данных, соединяющие свойства элемента UI со свойствами ScriptableObject:
| UI элемент | UI Свойство элемента | Обязательное имущество | Примечания: |
|---|---|---|---|
health-bar__player-name |
text |
PlayerName |
Отображает имя игрока |
health-bar__current-health |
text |
CurrentHealth |
Показывает текущее значение здоровья |
health-bar__max-health |
text |
MaximumHealth |
Отображает максимальное здоровье |
health-bar__progress |
style.width |
Progress |
Динамически регулирует ширину полосы |
Когда связывание данных завершено, шкала здоровья обновляется в реальном времени, показывая метки и шкалу прогресса для здоровья игрока.
Смена источников данных проста – просто назначьте новый ассет ScriptableObject, и UI автоматически отразит новые значения, сохраняя те же привязки.
Когда вы устанавливаете привязки данных в UI Builder, они добавляются непосредственно в файл UXML, создавая блок <Bindings> для каждого связанного элемента.
Вот фрагмент полученного результата UXML при связывании health-bar__player-name элементов text свойства PlayerName свойство (некоторые атрибуты опущены для удобства чтения):
<ui:Label text="Placeholder" name="health-bar__player-name" class="health-bar__player-name">
<Bindings>
<ui:DataBinding property="text" data-source-path="PlayerName" binding-mode="ToTarget" />
</Bindings>
</ui:Label>
Опытные пользователи могут также создавать эти привязки непосредственно в UXML.. Делая это в коде, можно получить точный контроль и быстрее редактировать при работе с большим количеством привязок. Ручно написанный UXML также предлагает более ясные различия для управления версиями, что облегчает разрешение конфликтов слияний или отслеживание изменений.
Настройка привязки данных в C#
UI Builder отлично подходит для прототипирования на статических данных (например, на заранее подготовленных ассетах ScriptableObject), но данные времени выполнения обычно требуют динамической обработки в C#. В этом примере кода показано, как в демонстрационной сцене работает полоса здоровья второго игрока:
using UnityEngine;
using UnityEngine.UIElements;
using Unity.Properties;
public class HealthBar : MonoBehaviour
{
[SerializeField] PlayerDataSO m_HealthData;
public void Initialize(VisualElement root)
{
var m_PlayerName = root.Q<Label>("health-bar__player-name");
root.dataSource = m_HealthData;
m_PlayerName.SetBinding("text", new DataBinding()
{
dataSourcePath = new PropertyPath(nameof(PlayerDataSO.PlayerName)),
bindingMode = BindingMode.ToTarget
});
}
}
Скрипт HealthBar обрабатывает это в методе Initialize, который вызывается из главного контроллера в скрипте OnEnable.
Сначала мы запрашиваем элемент
health-bar__player-name. Затем мы назначаем данные ScriptableObject в качестве источника.Затем метод
SetBindingпривязывает свойствоtextк новому экземпляру DataBinding и устанавливает параметрыdataSourcePathиbindingMode.
Все четыре привязки в таблице выше настроены одинаково. Используйте слайдер ScriptableObject или пользовательский редактор свойств, чтобы настроить CurrentHealth. Демо включает в себя управление тестом игры (+, -, Select) для увеличения, уменьшения или выбора ScriptableObject. Сигнал здоровья обновляется динамически по мере изменений.
Неразрешенный рабочий процесс привязки данных
Unity 6 также поддерживает гибридный рабочий процесс привязки данных, который объединяет визуальную настройку UI Builder с гибкостью скриптов.
Вместо жестко кодированных источников данных в UXML можно указать Тип источника данных и оставить фактический источник данных неразрешенным. UI Builder обозначает эти неполные привязки полым значком. Это означает, что пути и типы заданы, но источник данных еще не назначен.
Во время выполнения, вы можете назначить источник данных только с одной строкой кода. Например:
myElement.dataSource = myNewDataSource;
Здесь, назначение myNewDataSource до myElement разрешает привязки замещающих символов, определенные в UXML, что позволяет UI автоматически обновляться. Это устраняет повторяющиеся SetBinding вызывает и держит UXML гибкий.
Например, образец Драконы предопределяет пути данных в UXML, устанавливая фактические источники данных во время выполнения.
Нажатие следующей и последней кнопок в UI устанавливает выбранный символ в качестве источника данных. Изменение источника данных не требует изменения UXML.
Неразрешенные привязки показывают правильную статистику персонажа после установки нового источника данных.
Обратите внимание: Если файл UXML устанавливает конкретный источник данных (e.g., data-source="PlayerDataSO.asset"), привязка становится фиксированной и не может быть изменена во время выполнения. Чтобы разрешить изменения во время выполнения, оставьте атрибут data-source пустым или используйте вместо него data-source-type.
См. Привязка списка к ListView для примера этого гибридного рабочего процесса привязки данных.
Преобразователи типов
Преобразователи типов в Unity 6 позволяют преобразовать необработанные данные в более удобные для пользователя форматы для отображения в вашем UI. Они действуют как посредники между вашим источником данных и UI, преобразуя данные в более интуитивный формат для пользователя.
Например, преобразователи типов могут преобразовывать радианы в градусы или необработанные значения здоровья в цвета для шкалы здоровья. Это позволяет UI представлять информацию в формате, который ясен и легко понятен. Преобразователи типов делают это, не требуя много ручной логики преобразования.
Unity 6 поддерживает две категории типа преобразователей:
Глобальные преобразователи: Применяйте их к любым привязкам, требующим преобразования конкретного типа. Например, глобальные преобразователи могут превратить любую процентную величину состояния с плавающей запятой в цветовое значение или преобразовать объекты
Colorв типыStyleColor, обеспечивая последовательное поведение во всех ваших UI.Преобразователи для каждой привязки: Примените эти преобразователи к конкретным привязкам данных для более детального управления.
Пример: Преобразование значения в цвет
Ствол здоровья, который меняет цвет в зависимости от здоровья игрока, иллюстрирует использование связывания данных с конвертером типов. Отображая текущее здоровье игрока в цветовом градиенте (e.g. зеленый для высокого здоровья, желтый для низкого здоровья и красный для критического здоровья), игроки могут быстро оценить свой статус во время игры.
Вы можете увидеть это в действии в сцене DataBindingDemo в проекте QuizU.
HealthDataConverter настройка
В сцене DataBindingDemo, HealthBarWithConverter класс использует часть функциональности статического HealthDataConverter чтобы зарегистрировать несколько DataConverters:
Процент здоровья управляет цветовым градиентом для шкалы здоровья, переходя от зеленого (полное здоровье) к красному (критическое здоровье).
Метка может представлять числовое значение в виде строки процентов (e.g., “75%”).
Другая метка может отображать ту же процентную величину здоровья на такой метке состояния, как «Полное», «Среднее» или «Критическое».
Вот фрагмент HealthDataConverter class:
public static class HealthDataConverter
{
static readonly Color s_FullColor = new Color(0.2f, 1f, 0.2f);
static readonly Color s_MidColor = Color.yellow;
static readonly Color s_LowColor = new Color(1f, 0.3f, 0f);
static readonly Color s_CriticalColor = Color.red;
public static void Register()
{
RegisterHealthColorConverter();
// ...
}
static void RegisterHealthColorConverter()
{
var colorConverter = new ConverterGroup("HealthColor");
colorConverter.AddConverter((ref float healthPercentage) =>
{
if (healthPercentage > 0.5f)
{
return new StyleColor(Color.Lerp(s_MidColor, s_FullColor,
(healthPercentage - 0.5f) * 2f));
}
else if (healthPercentage > 0.25f)
{
return new StyleColor(Color.Lerp(s_LowColor, s_MidColor,
(healthPercentage - 0.25f) * 4f));
}
else
{
return new StyleColor(Color.Lerp(s_CriticalColor, s_LowColor,
healthPercentage * 4f));
}
});
ConverterGroups.RegisterConverterGroup(colorConverter);
}
// ...
}
Вышеприведенная логика создает HealthColor ConverterGroup, который преобразует процент здоровья float (от 0 до 1) в соответствующее значение StyleColor между красным (низкое здоровье) и зеленым (полное здоровье).
HealthDataConverter класс содержит также конвертеры для двух меток. Они могут представлять HealthPercentage свойства PlayerDataSO Хотя вы можете объединить несколько конвертеров в один ConverterGroup, в этом демо они разделены на отдельные ConverterGroups для удобства чтения.
HealthBarWithConverter
Примечание: HealthDataConverter класс содержит саму функциональность. HealthBarWithConverter просто:
public class HealthBarWithConverter : HealthBar
{
#if UNITY_EDITOR
[UnityEditor.InitializeOnLoadMethod]
#else
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
#endif
public static void RegisterConverters()
{
HealthDataConverter.Register();
}
}
Примечание:
UnityEditor.InitializeOnLoadMethodгарантирует, что ConverterGroup зарегистрирован и доступен для UI Builder, позволяя вам видеть и применять его в Редакторе.RuntimeInitializeOnLoadMethodобеспечивает доступность ConverterGroup во время выполнения, когда игра запускается.
Директива препроцессора #if UNITY_EDITOR обеспечивает выполнение соответствующих методов в зависимости от того, выполняется ли код в Редакторе или во время игры.
Применение DataConverter в UI Builder
После регистрации, этот DataConverter может быть применен к любой привязке, которая нуждается в этом преобразовании. Чтобы использовать его непосредственно в UI Builder:
Откройте файл UXML и выберите элемент строки прогресса. В проекте QuizU вы можете открыть файлы RuntimeDataBinding.uxml, чтобы увидеть, как он настроен.
Установить источник данных на
PlayerDataSOScriptableObject.Привязать свойство стиля
backgroundColorстроки прогресса к пути данных HealthPercentage.-
Используйте
HealthColorConverterGroup, чтобы преобразовать процент здоровья в цветной фон для строки прогресса.
Настройка привязки данных для шкалы здоровья. Перетаскивание значения
CurrentHealthизPlayerDataSOScriptableObject теперь обновляет цвет строки здоровья. Градиент плавно переходит от зеленого (полное здоровье) к желтому (среднее), оранжевому (низкое) и красному (критическое).
Этот глобальный DataConverter теперь доступен в любом месте приложения, где необходимо преобразовать значение с плавающей запятой в этот цветовой градиент.
Передовая практика
При работе с конвертерами типов, имейте в виду следующие советы:
Минимизировать выделения: Сохранять делегированные преобразования легкими, особенно для частых операций, чтобы избежать ненужной нагрузки на производительность.
Держите всё простым: Написать простые, сфокусированные преобразователи для быстрых преобразований. Избегайте встраивания сложной или ресурсоемкой логики.
Интеграция преобразования в источник данных: Обработка основных преобразований в самом источнике данных (e.g., предварительное форматирование процентов работоспособности в свойстве ScriptableObject). Зарезервируйте DataConverters для преобразований, специфических для привязок UI.
Пример: Привязка списка к ListView
В зависимости от вашей игры UI, вашему приложению может понадобиться отображать динамические списки данных, такие как список предметов в инвентаре персонажа, журнал заданий, отслеживающий цели, рейтинг игроков в таблице лидеров и т.д.
A ListView предлагает аккуратный прокручиваемый интерфейс, в котором удобно управлять этими сведениями и показывать их. Unity 6 упрощает процесс благодаря привязке данных во время выполнения: обновлять интерфейс вручную или писать для этого отдельные скрипты больше не нужно.
В предыдущих версиях Unity, настройка ListView требовала написания пользовательского кода для заполнения списка и обработки обновлений при изменении данных. С Unity 6, ListView может связываться непосредственно с источником данных, автоматически отслеживая и отражая изменения в UI.
В демонстрационной сцене есть простой ListView, привязанный к списку ScriptableObject типа PlayerDataSO. Это позволяет получить интерфейс, похожий на лобби многопользовательской игры или таблицу рекордов.
С помощью привязки данных во время выполнения вы можете связать ListView непосредственно с источником данных, например, ScriptableObject. ListView автоматически отслеживает изменения в данных, упрощая настройку и обслуживание.
Привязка данных ListView к списку включает в себя установку нескольких неразрешенных привязок, а затем завершение привязки данных во время выполнения.
Настройка списка и шаблонов
Для настройки списка и шаблонов используйте следующий трехступенчатый рабочий процесс:
Определить источник данных: Вашему ListView нужен список данных. В этом примере TeamSO ScriptableObject содержит список объектов PlayerDataSO. Каждый элемент в этом списке соответствует строке в ListView.
-
Создайте шаблон элемента UXML: В UI Builder создайте шаблон UXML (шаблон VisualTreeAsset), который определяет, как выглядит один элемент списка. Например, шаблон
team-list-itemв демо включает имя игрока и некоторые свойства Texture2D. Вместо прямой ссылки на источник данных установите Тип источника данных и Путь источника данных в UI Builder. Это оставляет привязку неразрешенной, готовой к завершению позже во время выполнения.
Создание визуального дерева в UI Builder. Добавьте ListView в основной пользовательский интерфейс: В другом файле UXML добавьте элемент ListView, который будет отображать весь список игроков. Назначьте свой шаблон элемента как шаблон ListView Шаблон элемента. В данный момент ListView знает, как должна выглядеть каждая строка, но пока не знает, какой конкретный источник данных использовать.
В демо сцене ListView используются только некоторые базовые настройки (показанные выше). Для более продвинутых функций, ознакомьтесь с официальной документацией ListView.
Завершение привязки во время выполнения
Во время выполнения простой скрипт TeamList завершает привязку, предоставляя фактический источник данных. Эти строки завершают ранее неразрешенные привязки:
// Set the data source
m_ListView.dataSource = m_TeamData;
// Bind the "itemsSource" to the Players list
m_ListView.SetBinding("itemsSource", new DataBinding
{
dataSourcePath = new PropertyPath("Players")
});
Здесь m_TeamData (экземпляр TeamSO) назначается ListView. Вызов SetBinding один раз ассоциирует свойство Players с itemsSource. Это позволяет ListView заполнять строки UI.
Поскольку эти привязки остаются неразрешенными в UXML до времени выполнения, вам не нужно индивидуально подключать каждый элемент списка. UI Toolkit разрешает эти привязки самостоятельно и заполняет данные для каждого элемента списка.
Любые изменения в списке в источнике данных (e.g., добавление, удаление или перестановка игроков) немедленно появляются в UI без необходимости дальнейшего скрипта.
Помните, что этот гибридный подход к привязке данных может сократить количество повторяющегося стандартного кода. Настраивая пути данных в UXML, вы можете отложить назначение фактического источника данных до времени выполнения.
Если вы измените модель данных, нет необходимости переписывать всю логику привязки. Одно обновление при запуске может перепроводить UI в новый источник.
Для всеобъемлющего взгляда на связывание ListView к списку, смотрите здесь документации.
Оптимизация привязки данных
Экономные привязки помогают сохранять отзывчивость интерфейса. Избыточные или чрезмерные привязки перегружают систему, вызывая ненужные обновления и снижая производительность. Это особенно важно, если интерфейс сложен или требователен к ресурсам.
По умолчанию система привязки времени выполнения обновляет элементы UI каждый кадр. Для небольшого приложения это достаточно гибко, но может стать узким местом в производительности, если привязок больше.
В настоящем разделе рассматриваются методы повышения эффективности привязки данных для более крупных проектов.
Управление типами значений
Если ваш источник данных использует типы-значения (int, float, структуры), помните о затратах на упаковку. Поскольку свойство dataSource работает с object, частые преобразования типов-значений создают дополнительные накладные расходы.
Чтобы уменьшить это, минимизируйте ненужные привязки или избыточные обновления при работе со свойствами типа значения.
Минимизация накладных расходов
Начните с выявления привязок, которые обновляют одни и те же элементы много раз или отслеживают данные, которые редко меняются. Консолидируйте или удалите эти привязки, чтобы сократить ненужную работу. Используйте плоские, простые структуры данных вместо сложных иерархий, когда это возможно. Это может избежать узких мест производительности, вызванных частыми поисками данных.
Рассмотрите возможность предварительного вычисления или кэширования значений, требующих интенсивных вычислений. Привязка к этим предварительно вычисленным значениям снижает вычислительную нагрузку на систему привязки и позволяет избежать повторных перерасчетов.
Убедитесь, что привязки применяются только к элементам, требующим частых обновлений. Для элементов, не требующих постоянной синхронизации, удалите ненужные привязки и вместо этого назначайте значения напрямую или обновляйте их только при вызовах событий.
Использование триггеров обновления
Обновление привязок на основе триггеров обновления, которые определяют, как часто UI синхронизируется с источником данных. Это позволяет вам балансировать производительность с чувствительностью. Эти параметры определяют, как часто обновляются привязки:
Каждый кадр: Это обновляется непрерывно. Используйте это для элементов, которые требуют постоянного обновления, как в примере со стрелками здоровья.
При обнаружении изменений: Обновляется при изменении источника данных, или каждый кадр, если обнаружение невозможно. Например, используйте это для панелей статистики или списков инвентаризации, которые зависят от наблюдаемых данных.
При маркировке как грязная: В сценариях, где обновления не часто, явное маркирование привязок как грязных с помощью
MarkDirtyпозволяет избежать ненужного цикла обновления. Это обновление вызывает работу для элементов, таких как меню настроек, которые изменяются только в определенных контекстах.
Сопоставляя триггеры обновления с потребностями каждого элемента UI, вы можете сбалансировать скорость реагирования с эффективностью.
Контроль версий и отслеживание изменений
Чтобы уменьшить количество ненужных обновлений, можно интегрировать в источники данных функции управления версиями и отслеживания изменений.
Два интерфейса могут помочь сделать привязку данных более эффективной:
IDataSourceViewHashProvider: здесь общие изменения отслеживаются по хешу версии, и обновления запускаются только при изменении источника данных. Это удобно для статических или почти статических данных, которые обновляются редко.
INotifyBindablePropertyChanged: отслеживает изменения на уровне свойств, обеспечивая обновление затронутых привязок. Это обеспечивает гранулярный контроль.
Добавление этих интерфейсов к источнику данных. Их можно использовать независимо или вместе для обеспечения большего контроля над обновлениями. документации для использования и передовой практики.
💡 Совет: Больше UI Toolkit советы оптимизации
В этом Unite 2024 выступлении об оптимизации UI Toolkit, вы узнаете о таких темах, как реализация цепных draw-calls и последствия размеров буферов, лучшие практики динамического атласирования, и работа с ограничениями, такими как пользовательские шейдеры и 3D UI.