Unity 6.3
0 онлайн 101 гостей 3 в системе
Вход
UI системы Шаг 263 из 317

Перемещение элементов во время выполнения

В этом примере показано, как динамически устанавливать позицию визуального элемента во время выполнения. Рекомендуемая наилучшая практика для перемещения элементов во время выполнения заключается в использовании style.translate и установке подсказки использования DynamicTransform на передвигаемом элементе. Этот подход оптимален для производительности, так как позволяет избежать загрязнения макета и ограничивает обновления стадией преобразования. Дополнительные сведения см. в Оптимизация производительности передвижающихся элементов во время выполнения.

Примерный обзор

Этот пример создает префаб в сцене. префаб представляет неигрового персонажа (NPC), который перемещается случайным образом в пределах определенного диапазона. NPC имеет тег имени, который следует за ним и масштабируется в зависимости от расстояния от камеры.

Перемещение элементов во время выполнения
Перемещение элементов во время выполнения

Вы можете найти завершенные файлы, созданные этим примером, в хранилище GitHub.

Предварительные условия

Это руководство предназначено для разработчиков, знакомых со скриптами Unity Editor, UI Toolkit и C#. Перед началом работы ознакомьтесь со следующим:

Создать контейнер UI

Создайте UXML в вашем проекте и определите VisualElement внутри него. VisualElment действует как родительский контейнер для любых дочерних элементов, которые вы планируете перемещать динамически во время выполнения.

  1. Создайте проект Unity с любым шаблоном.
  2. В окне Project создайте папку UI для хранения файлов UXML и USS.
  3. В папке UI создайте файл UXML с именем NameTagContainer.uxml.
  4. Дважды щелкните файл NameTagContainer.uxml, чтобы открыть его в UI Builder.
  5. Добавьте VisualElement в панель Hierarchy и дайте ей имя BaseContainer.
  6. Сохраните изменения.

Завершенный файл 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.

  1. В папке 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);
     }
    
  2. В папке UI создайте файл UXML с именем NameTag.uxml.

  3. Дважды щелкните файл NameTag.uxml, чтобы открыть его в UI Builder.

  4. Добавить метку в панель Hierarchy и назвать ее NameTag.

  5. В панели Inspector выполните следующие действия:

    • Настройка Текст до NPC. Это значение представляет собой имя NPC.
    • Установите Picking Mode на Ignore. Это предотвращает отклик элемента Label на события мыши, что более оптимально для производительности.
  6. В панели StyleSheets выберите + > Добавить существующие USS.

  7. Следуйте инструкциям для добавления файла NameTag.uss.

  8. Проверьте элемент Label в Viewport. Стиль translate: -50% -50% центрирует точку перемещения элемента Label, заставляя его появляться в верхнем левом углу Viewport. Это ожидаемое поведение. Тег name будет расположен относительно NPC GameObject в сцене.

    NameTag в окне

  9. Сохраните изменения.

Завершенный файл 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 на движущийся элемент для оптимизации производительности.

Примечание: Рекомендуемая передовая практика заключается в применении подсказки использования к преобразованному элементу, который в данном случае является контейнером шаблона теги имени, а не дочерней меткой.

  1. В окне Project создайте папку Scripts для хранения скриптов C#.
  2. В папке 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, который сортирует теги имен в зависимости от их масштаба.

  1. В SampleScene добавьте Плоскость GameObject с именем Floor.

  2. Добавить материал на пол.

  3. В окне Hierarchy выберите UI Toolkit > UIDocument и назовите его UI.

  4. В окне Inspector Документа UI GameObject выполните следующие действия:

    • Установите Источник Актива на NameTagContainer.
    • Добавить компонент SortElements.
  5. В поле компонента 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 или другие компоненты.

  1. В SampleScene создайте пустой GameObject с именем NPC.

  2. В течение Inspector окно NPC GameObject, добавить RandomMovement компонент. Этот компонент управляет случайным движением NPC.

  3. В поле компонента Random Movement выполните следующие действия:

  4. Установите скорость от до 2. Это значение определяет скорость, с которой движется NPC.

  5. Установите Диапазон до 10. Это значение определяет диапазон, в котором движется NPC.

Примечание: компонент Random Movement содержит поле Цель Позиция, представляющее следующую целевую позицию NPC, которая случайным образом задаётся в скрипте. Это поле предназначено главным образом для отладки, и задавать его вручную не требуется.

Настройка дочерей NPC префаб

NPC префаб содержит три дочерних GameObjects: капсулу GameObject и две GameObjects.

Капсула GameObject представляет NPC в сцене. Один GameObject представляет метку имени, которая следует за NPC. Второй GameObject представляет позицию метки имени относительно NPC. Этот GameObject действует как точка отсчета или якорь для метки имени, определяя, где размещается метка имени относительно NPC.

  1. Щелкните правой кнопкой мыши NPC GameObject и выберите Create Empty, чтобы добавить дочернюю GameObject и назвать ее UI_NameTag.

  2. Добавьте компонент MovingNameTag к компоненту UI_NameTag GameObject. Этот компонент управляет позицией и масштабом тега имени.

  3. В поле компонента Moving Name Tag выполните следующие действия:

    • Установите шаблон Name Tag Template на NameTag.
    • Установите Базовый Контейнер Документа на UI (UI Document). Это значение представляет собой контейнер для элементов UI, которые перемещаются во время выполнения.
    • Установите UI Transform до UI_Transform. Это значение представляет позицию тега имен.
    • Установите Scale Multiplier на 50. Это значение масштабирует имя тега в зависимости от расстояния от камеры.
    • Установите Дистанционное удаление диапазона до 20. Это значение удаляет имя тега, когда он слишком далеко или за камерой.
  4. Щелкните правой кнопкой мыши NPC GameObject и выберите 3D Объект > Капсула, чтобы добавить дочернюю капсулу GameObject. Затем установите ее положение Y на 1, чтобы она не врезалась в плоскость пола.

  5. Добавьте желаемый материал в капсулу GameObject, например, синий материал поверхности, если у вас есть такой.

  6. Щелкните правой кнопкой мыши NPC GameObject и выберите Create Empty, чтобы добавить еще один пустой дочерний файл GameObject и назвать его UI_Transform.

  7. В течение Inspector окно UI_Transform GameObject, выберите значок стрелки в правом верхнем углу и выберите для него значок. Этот значок позволяет легко идентифицировать GameObject в 2000 году Scene для отладки.

    Установить значок для UI_Transform

  8. В течение Inspector окно UI_Transform GameObject, установить положение на (0, 3, 0). Это значение представляет собой положение тега имен относительно NPC GameObject.

Проверка примера

Дублируйте NPC префаб для создания нескольких NPCs в сцене. Тег с именем следует за NPCs и масштабируется в зависимости от расстояния от камеры.

  1. В окне Hierarchy щелкните правой кнопкой мыши NPC GameObject и выберите Duplicate.
  2. Повторите предыдущие шаги несколько раз, чтобы создать несколько NPCs в сцене.
  3. Переместите NPCs в разные позиции в сцене.
  4. Вход в режим воспроизведения. Теги имен следуют за NPCs и масштабируются в зависимости от расстояния от Camera.
  5. Приостановить игру.
  6. Выберите Окно > UI Toolkit > Отладчик, чтобы открыть окно UI Toolkit Отладчик.
  7. В окне UI Toolkit Отладчик из списка Выберите панель выберите PanelSettings.
  8. Выберите PanelRootElement > TemplateContainer > VisualElement > TemplateContainer для просмотра свойств стиля тега имени.
  9. Возобновление игры. Свойства перевода тега имен динамически обновляются по мере того, как NPC перемещается.

Дополнительные ресурсы