Перемещение элементов во время выполнения
В этом примере показано, как динамически устанавливать позицию визуального элемента во время выполнения. Рекомендуемая наилучшая практика для перемещения элементов во время выполнения заключается в использовании style.translate и установке подсказки использования DynamicTransform на передвигаемом элементе. Этот подход оптимален для производительности, так как позволяет избежать загрязнения макета и ограничивает обновления стадией преобразования. Дополнительные сведения см. в Оптимизация производительности передвижающихся элементов во время выполнения.
Примерный обзор
Этот пример создает префаб в сцене. префаб представляет неигрового персонажа (NPC), который перемещается случайным образом в пределах определенного диапазона. NPC имеет тег имени, который следует за ним и масштабируется в зависимости от расстояния от камеры.
Вы можете найти завершенные файлы, созданные этим примером, в хранилище GitHub.
Предварительные условия
Это руководство предназначено для разработчиков, знакомых со скриптами Unity Editor, UI Toolkit и C#. Перед началом работы ознакомьтесь со следующим:
Создать контейнер UI
Создайте UXML в вашем проекте и определите VisualElement внутри него. VisualElment действует как родительский контейнер для любых дочерних элементов, которые вы планируете перемещать динамически во время выполнения.
- Создайте проект Unity с любым шаблоном.
- В окне Project создайте папку
UIдля хранения файлов UXML и USS. - В папке
UIсоздайте файл UXML с именемNameTagContainer.uxml. - Дважды щелкните файл
NameTagContainer.uxml, чтобы открыть его в UI Builder. - Добавьте VisualElement в панель Hierarchy и дайте ей имя
BaseContainer. - Сохраните изменения.
Завершенный файл NameTagContainer.uxml выглядит следующим образом:
<engine:UXML xmlns:engine="UnityEngine.UIElements" >
<engine:VisualElement name="BaseContainer" style="flex-grow: 1;" />
</engine:UXML>
Создать шаблон тега UXML
Создайте файл USS для стилизации тега и файл UXML для определения шаблона тега. Шаблон тега является элементом Label, который отображает имя NPC.. Тег следует за NPC в Scene.
-
В папке
UIсоздайте файл USS с именемNameTag.ussсо следующим содержимым:#NameTag { /* This sets the position of the Label element to be absolute, rather than relative to the parent container. */ position: absolute; /* This ensures that the pivot point of the Label element is centered, rather than in the top left corner.*/ translate: -50% -50%; -unity-font-style: bold; color: rgb(181, 210, 248); -unity-text-outline-width: 1px; -unity-text-outline-color: rgb(11, 60, 123); } В папке
UIсоздайте файл UXML с именемNameTag.uxml.Дважды щелкните файл
NameTag.uxml, чтобы открыть его в UI Builder.Добавить метку в панель Hierarchy и назвать ее
NameTag.-
В панели Inspector выполните следующие действия:
- Настройка Текст до
NPC. Это значение представляет собой имя NPC. - Установите Picking Mode на
Ignore. Это предотвращает отклик элемента Label на события мыши, что более оптимально для производительности.
- Настройка Текст до
В панели StyleSheets выберите + > Добавить существующие USS.
Следуйте инструкциям для добавления файла
NameTag.uss.-
Проверьте элемент Label в Viewport. Стиль
translate: -50% -50%центрирует точку перемещения элемента Label, заставляя его появляться в верхнем левом углу Viewport. Это ожидаемое поведение. Тег name будет расположен относительно NPC GameObject в сцене.
Сохраните изменения.
Завершенный файл NameTag.uxml может выглядеть следующим образом:
<engine:UXML xmlns:engine="UnityEngine.UIElements">
<Style src="NameTag.uss" />
<engine:Label text="NPC" name="NameTag" picking-mode="Ignore" />
</engine:UXML>
Создание скрипта случайного перемещения
Создать RandomMovement класс, отвечающий за случайное перемещение префаба NPC. NPC перемещается в заданном диапазоне и меняет направление, достигнув границы.
Установите подсказку DynamicTransform на движущийся элемент для оптимизации производительности.
Примечание: Рекомендуемая передовая практика заключается в применении подсказки использования к преобразованному элементу, который в данном случае является контейнером шаблона теги имени, а не дочерней меткой.
- В окне Project создайте папку
Scriptsдля хранения скриптов C#. - В папке
Scriptsсоздайте скрипт C# с именемRandomMovement.csсо следующим содержанием:
using UnityEngine;
using Random = UnityEngine.Random;
public class RandomMovement : MonoBehaviour
{
public float moveSpeed;
public float movementRange;
public Vector3 targetPosition;
private float m_PositionY;
// Initialize the starting position and target position of the GameObject.
void Start()
{
m_PositionY = transform.position.y;
targetPosition = new Vector3(0, m_PositionY, 0);
}
// Updates the position of the GameObject at fixed intervals.
// Move the GameObject towards the target position, and sets a new random target position when the current target is reached.
void FixedUpdate()
{
if (transform.position != targetPosition)
{
transform.position = Vector3.MoveTowards(transform.position, targetPosition, moveSpeed * Time.deltaTime);
}
else
{
targetPosition = new Vector3(Random.Range(-movementRange, movementRange), m_PositionY, Random.Range(-movementRange, movementRange));
}
}
}
Создание скрипта перемещаемого тега с именем
Создать MovingNameTag класс, управляющий положением и масштабом таблички с именем. Табличка следует за GameObject NPC в сцене, масштабируется в зависимости от расстояния до камеры и отсекается, когда оказывается слишком далеко или позади камеры.
- В папке
Scriptsсоздайте скрипт C# с именемMovingNameTag.csсо следующим содержанием:
using System;
using UnityEngine;
using UnityEngine.UIElements;
public class MovingNameTag : MonoBehaviour
{
[SerializeField]
VisualTreeAsset m_NameTagTemplate;
[SerializeField]
UIDocument m_BaseContainerDocument;
[SerializeField]
Transform m_UITransform;
[SerializeField]
float m_ScaleMultiplier;
[SerializeField]
float m_DistanceCullingRange;
VisualElement m_Root;
VisualElement m_BaseContainer;
VisualElement m_NpcNameTag;
Camera m_MainCamera;
void Awake()
{
m_MainCamera = Camera.main;
m_BaseContainer = m_BaseContainerDocument.rootVisualElement.Q<VisualElement>("BaseContainer");
m_NpcNameTag = m_NameTagTemplate.Instantiate();
// Set DynamicTransform hint on the moving element to optimize performance.
m_NpcNameTag.usageHints = UsageHints.DynamicTransform;
m_BaseContainer.Add(m_NpcNameTag);
m_NpcNameTag.style.position = new StyleEnum<Position>(Position.Absolute);
}
void Update()
{
SetNameTagPositionAndScale();
}
void SetNameTagPositionAndScale()
{
var cameraSpaceLocation = GetCameraSpaceLocation(m_UITransform);
// Use style.translate to set the position of the name tag.
m_NpcNameTag.style.translate = new Translate(cameraSpaceLocation.x, cameraSpaceLocation.y);
// Get distance of NPC from camera.
var distance = Vector3.Distance(m_UITransform.position, m_MainCamera.transform.position);
// Calculate 1/distance so the name tag get smaller as the distance gets bigger.
var scale = 1 / distance * m_ScaleMultiplier;
m_NpcNameTag.style.scale = new Scale(new Vector2(scale, scale));
// Display name tag based on whether it's in front of the camera and within culling range.
if (cameraSpaceLocation.z < 0 || distance > m_DistanceCullingRange)
{
m_NpcNameTag.style.display = DisplayStyle.None;
}
else
{
m_NpcNameTag.style.display = DisplayStyle.Flex;
}
}
Vector3 GetCameraSpaceLocation(Transform objectTransform)
{
// Get the size of the parent visual element of the name tag.
var containerSize = m_BaseContainer.layout.size;
var screenPoint = m_MainCamera.WorldToViewportPoint(objectTransform.position);
var output = new Vector3(screenPoint.x * containerSize.x, (1 - screenPoint.y) * containerSize.y, screenPoint.z);
return output;
}
}
Создание скрипта сортировки элементов
Создать SortElements класс, сортирующий таблички с именами по их масштабу, который зависит от расстояния до объектов, за которыми они следуют.
- В папке
Scriptsсоздайте скрипт C# с именемSortElements.csсо следующим содержанием:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class SortElements : MonoBehaviour
{
[SerializeField]
UIDocument m_MovingElements;
VisualElement m_BaseContainer;
MovingNameTag[] m_MovingNameTags;
void Start()
{
m_MovingNameTags = FindObjectsByType<MovingNameTag>(FindObjectsSortMode.None);
m_BaseContainer = m_MovingElements.rootVisualElement.Q<VisualElement>("BaseContainer");
}
void Update()
{
m_BaseContainer.Sort(CompareOrder);
}
static int CompareOrder(VisualElement x, VisualElement y)
{
// Compare the scale of the visual elements in the base container, which is
// determined by the distance of the object it follows in the MovingNameTag component
return x.style.scale.value.value.x.CompareTo(y.style.scale.value.value.x);
}
}
Настройка основ сцены
Настройте сцену с полом и UI Документом GameObject. Пол представляет собой Плоскость GameObject, которая представляет пол сцены.
Документ UI Документ GameObject — это UI, который содержит визуальные элементы, которые перемещаются во время выполнения. UI содержит компонент SortElements, который сортирует теги имен в зависимости от их масштаба.
В SampleScene добавьте Плоскость GameObject с именем
Floor.Добавить материал на пол.
В окне Hierarchy выберите UI Toolkit > UIDocument и назовите его
UI.-
В окне Inspector Документа UI GameObject выполните следующие действия:
- Установите Источник Актива на
NameTagContainer. - Добавить компонент
SortElements.
- Установите Источник Актива на
В поле компонента Sort Elements установите Moving Elements на
UI. Это сделает компонентSortElementsссылкой на Компонент Документа UI, который в свою очередь ссылается на исходный ресурсNameTagContainer.
Настройка NPC префаб
Создайте NPC префаб, который перемещается случайным образом в пределах указанного диапазона. Он содержит три дочерних GameObjects:
- Капсула GameObject, представляющая NPC
- Документ UI Документ GameObject, называемый
UI_NameTag, который представляет метку имени, следующую за NPC - Пустой GameObject, называемый
UI_Transform, который представляет позицию тега name
UI_Transform GameObject выступает в качестве точки отсчета для тега имени. Компонент MovingNameTag перемещает тег имени в зависимости от положения UI_Transform GameObject. Эта настройка позволяет лучше организовать и гибкость. Если вам нужно изменить положение тега имени, вы можете просто изменить положение UI_Transform GameObject без влияния на NPC GameObject или другие компоненты.
В SampleScene создайте пустой GameObject с именем
NPC.В течение Inspector окно
NPCGameObject, добавитьRandomMovementкомпонент. Этот компонент управляет случайным движением NPC.В поле компонента Random Movement выполните следующие действия:
Установите скорость от до
2. Это значение определяет скорость, с которой движется NPC.Установите Диапазон до
10. Это значение определяет диапазон, в котором движется NPC.
Примечание: компонент Random Movement содержит поле Цель Позиция, представляющее следующую целевую позицию NPC, которая случайным образом задаётся в скрипте. Это поле предназначено главным образом для отладки, и задавать его вручную не требуется.
Настройка дочерей NPC префаб
NPC префаб содержит три дочерних GameObjects: капсулу GameObject и две GameObjects.
Капсула GameObject представляет NPC в сцене. Один GameObject представляет метку имени, которая следует за NPC. Второй GameObject представляет позицию метки имени относительно NPC. Этот GameObject действует как точка отсчета или якорь для метки имени, определяя, где размещается метка имени относительно NPC.
Щелкните правой кнопкой мыши
NPCGameObject и выберите Create Empty, чтобы добавить дочернюю GameObject и назвать ееUI_NameTag.Добавьте компонент
MovingNameTagк компонентуUI_NameTagGameObject. Этот компонент управляет позицией и масштабом тега имени.-
В поле компонента Moving Name Tag выполните следующие действия:
- Установите шаблон Name Tag Template на
NameTag. - Установите Базовый Контейнер Документа на
UI (UI Document). Это значение представляет собой контейнер для элементов UI, которые перемещаются во время выполнения. - Установите UI Transform до
UI_Transform. Это значение представляет позицию тега имен. - Установите Scale Multiplier на
50. Это значение масштабирует имя тега в зависимости от расстояния от камеры. - Установите Дистанционное удаление диапазона до
20. Это значение удаляет имя тега, когда он слишком далеко или за камерой.
- Установите шаблон Name Tag Template на
Щелкните правой кнопкой мыши
NPCGameObject и выберите 3D Объект > Капсула, чтобы добавить дочернюю капсулу GameObject. Затем установите ее положение Y на1, чтобы она не врезалась в плоскость пола.Добавьте желаемый материал в капсулу GameObject, например, синий материал поверхности, если у вас есть такой.
Щелкните правой кнопкой мыши
NPCGameObject и выберите Create Empty, чтобы добавить еще один пустой дочерний файл GameObject и назвать егоUI_Transform.-
В течение Inspector окно
UI_TransformGameObject, выберите значок стрелки в правом верхнем углу и выберите для него значок. Этот значок позволяет легко идентифицировать GameObject в 2000 году Scene для отладки.
В течение Inspector окно
UI_TransformGameObject, установить положение на(0, 3, 0). Это значение представляет собой положение тега имен относительно NPC GameObject.
Проверка примера
Дублируйте NPC префаб для создания нескольких NPCs в сцене. Тег с именем следует за NPCs и масштабируется в зависимости от расстояния от камеры.
- В окне Hierarchy щелкните правой кнопкой мыши
NPCGameObject и выберите Duplicate. - Повторите предыдущие шаги несколько раз, чтобы создать несколько NPCs в сцене.
- Переместите NPCs в разные позиции в сцене.
- Вход в режим воспроизведения. Теги имен следуют за NPCs и масштабируются в зависимости от расстояния от Camera.
- Приостановить игру.
- Выберите Окно > UI Toolkit > Отладчик, чтобы открыть окно UI Toolkit Отладчик.
- В окне UI Toolkit Отладчик из списка Выберите панель выберите PanelSettings.
- Выберите PanelRootElement > TemplateContainer > VisualElement > TemplateContainer для просмотра свойств стиля тега имени.
- Возобновление игры. Свойства перевода тега имен динамически обновляются по мере того, как NPC перемещается.