Данная статья рассказывает о новой системе пользовательского интерфейса, которая появилась в новых версиях Unity, начиная с Unity 4.6.
Вступление
Раньше весь интерфейс строился на основе кода. В нужных скриптах было необходимо обрабатывать событие OnGUI, отображать и обрабатывать события всех элементов которые нам необходимы. В результате код получался громоздким. Конечно существовали и альтернативы, например nGUI, однако данный компонент предоставляется на платной основе. С выходом Unity 4.6 все изменилось. В Unity теперь добавилась отличная система пользовательского интерфейса, с нужными для этого инструментами. Конечно старая система осталась полностью работоспособной, но не переходить на новую систему я считаю не правильно. Лично меня очень сильно поразили возможности для создания интерфейса. Новая UI System получилась одновременно и гибкой, и простой, и при этом весьма функциональной.
Начало. Canvas.
Я думаю объяснять как создавать сцену нет смысла, поэтому пропустим все лишние действия и перейдем непосредственно к самому интерфейсу. Новая система построена на модулях ввода (Input Module), полотне (Canvas) и самих объектах интерфейса. Для создания интерфейса нам надо знать только про полотно и сами элементы.
Как вы уже догадались нам потребуется полотно, на котором мы будем размещать наши элементы. Для этого идем в меню "GameObject -> UI -> Canvas"
После того, как оно появилось на сцене, выберите его и нажмите F для того, чтобы отобразить его перед камерой, так же удобство можно переключиться в 2D режим Unity. В инспекторе для редактирования доступны следующие очень важные свойства, которые определяют поведение нашего полотна:
Render Mode
В зависимости от этого параметра становятся доступны другие остальные поля. Поэтому сперва рассмотрим значения данного поля.
Screen Space - Overlay
В данном режиме полотно растягивается по размерам экрана и отображается без связи со сценой или камерой (UI будет показан даже в том случае, когда на сцене нету камеры). В случае изменения размера окна, полотно будет растянуто под размеры экрана и размещенные элементы будут перегруппированы. Полотно будет рисоваться поверх всех остальных графических элементов.
Pixel perfect - использовать ли antialiasing
Screen Space - Camera
В данном режиме полотно рисуется на плоскости перпендикулярной взгляду камеры, на некотором расстоянии от точки взгляда. Размер полотна не меняется с изменением расстояния, оно всегда растягивается, чтобы заполнять разрез пирамиды видимости у камеры (camera frustrum view). Интерфейс будет заслоняться любыми 3D элементами, которые находятся перед плоскостью интерфейса.
Pixel perfect - использовать ли antialiasing.
Render camera - камера с помощью которой будет отображен интерфейс.
Plane distance - дистанция плоскости нашего интерфейса от камеры.
World Space
В данном режиме полотно располагается в мировых координатах и является плоским 3D объектом на который действую законы обычного процесса визуализации.
Event camera - камера с помощью которой будут происходить обработки событий ввода пользователя.
Все немного сложно, да? По скриншотам, представленных ниже, Вы с легкостью сможете разобраться, что к чему. Красный прямоугольник - это часть нашего полотна. Во всех случаях объекты и камера были не подвижны. Менялись лишь настройки нашего полотна.
С полотном мы разобрались, можно приступать к созданию простого интерфейса.
Начало. Элементы.
Для начало нам надо принять удобную позу. Поэтому заварите себе кружечку горячего чая, откиньтесь на спинку вашего мягкого кресла (или не менее мягкую деревянную спинку стула :) ) и расслабьтесь. Расслабились? Отлично, теперь возвращаемся к Unity. Если у вас полотно используется в режиме мировых координат (World Space), то выберите его и нажмите F и дальше просто подгоните камеру как вам удобно. В другом случае лучше сделать так. Установить режим использования камеры, указать любую камеру, и поставить дистанцию равную 1.0 и нажимаем F, так же не забываем переключиться в 2D режим для удобства работы с интерфейсом. Подгоняем вид как нам удобно, желательно чтобы было видно всю область полотна, и переходим к созданию элементов.
В нашем распоряжении есть классический набор элементов, без которых не возможно существование ни одного нормального интерфейса:
- Panel - требуется для группировки элементов на себе.
- Button - обычная типичная кнопка.
- Text - просто текст.
- Image - изображение, используются спрайты.
- RawImage - изображение, используется текстура, для которой можно задать UV координаты.
- Slider - ползунок для изменения какого-либо значения.
- ScrollBar - ползунок, но с шаговыми значениями.
- Toggle - флажок.
- InputField - поле для ввода
Все эти элементы можно создать из меню "GameObject -> UI -> ...".
После того как вы создадите элемент, надо будет переключиться на новый инструмент Rect tool
С помощью него можно легко настраивать расположение и размеры элементов. Давайте посмотрим на возможности данного инструмента
- Вы можете менять положение элементов, перетаскивая сами элементы (Pos X, Pos Y, Pos Z).
- Вы можете менять размер элементов, перетаскивая их границы или угловые точки (Width, Height).
- Вы можете вращать элемент, перетаскивая область вне элемента рядом с угловой точкой (Rotation).
- Вы можете менять центр вращения, чтобы правильно настроить вращение элемента (Pivot).
- Вы можете настроить точки крепления к родительскому объекту (Anchors).
В принципе если с первыми 3-мя все понятно, то про остальные я думаю стоит рассказать подробнее.
Начнем с точки вращения. Чтобы его изменить нужно перетаскивать синий кружочек (изначально он находится в центре). Сейчас покажу на скриншотах как это работает. Во всех случаях позиция самого элемента не менялась, лишь якорь и угол вращения.
Теперь давайте ка разберемся с точками крепления. Для их изменения придумали такой вот крестик (скриншот справа). Его можно перемещать либо весь, либо только один из его углов. Так же присутствуют так называемые пресеты для данного параметра. При выборе одного из них, позиция якоря будет меняться. Так же можно зажать клавишу Alt во время выбора пресета и тогда будет меняться положение самого элемента. А если еще и зажать Shift - то так же будет изменена позиция точки вращения.
А теперь давайте поговорим о смысле якоря. В принципе если вам абсолютно не важно как ваш интерфейс выглядит, то (стоп, что за бред?) .... К сути: настройки якоря помогают сделать ваш интерфейс адаптивным, чтобы он мог подстраиваться и растягиваться под разные размеры экрана. Вот например если вы оставите у всех элементов точку крепления в центре, то при изменении размеров родительского элемента (будь то панель, или полотно) элементы так и останутся в центре. Если поставить якорь в левом верхнем углу, то и элементы всегда будут там. Но если правильно настроить якорь, то можно добиться классных эффектов. В общем это трудно описать на словах и показать на скриншотах, поэтому советую вам самим поэкспериментировать с данным свойством. Но все же я хочу рассмотреть один не больший пример. Запомните, если вы сможете понять, что к чему, то вам будет не сложно построить интерфейс любой сложности. Давайте рассмотрим такой вот пример:
Как видите расположение кнопок относительное. Но чтобы этого добиться надо немного поразмыслить, как вся эта система работает. Для этого я предлагаю рассмотреть скриншоты слева. На них вы можете видеть 3 линейки разных цветов.
- Красная - обозначает итоговое расстояние от границы родителя до границы элемента.
- Синяя - обозначает расстояние от границы родителя до границы якоря.
- Зеленая - обозначает расстояние от границы якоря до границы элемента.
Принцип их работы прост. Красная высчитывается UI системой. А вот остальные мы задаем сами. Запоминайте:
- расстояние от границы родителя до границы якоря - всегда в процентном соотношение с размерами родителя.
- расстояние от границы якоря до границы элемента - всегда фиксировано.
А теперь расскажу как это нужно использовать. Если вам нужно, чтобы элемент растягивался, то разместите якори данного элементы по границам элемента, или около того (тут можно долго экспериментировать). Если вы хотите, чтобы отступ элемента тоже был в процентном соотношении, то просто поместите ваш элемент в пустую панель и поставьте этой панели якорь так, чтобы он растягивался. А самому элементу поставьте якорь в центр панели. И тогда вы добьетесь такого же поведения, как и на скриншотах выше.
После того как вы научились размещать элементы на полотне, я думаю можно поближе познакомится с особенностью элементов. Начнем с самого простого - картинки!
Image
- Source Image - спрайт, который будет использовать для картинки.
- Color - окрас спрайта, например можно добавить красных тонов картинке, указав здесь красный цвет.
- Material - можно задать материал для картинки.
- Image Type - способ отображения изображения
- Simple - отображение изображения как есть
- Sliced - спрайт имеющий 9 частей будет отображен в виде повторяющихся частей, что позволяет картинке выглядеть качество при разных размерах элемента
- Tiled - картинка будет повторяться, чтобы заполнить всю область
- Filled - один из самых интересных режимов. Как он работает, смотрим на скриншотах
Text
Я думаю с параметрами этого элемента вы сами сможете спокойно разобраться.
Button
Кнопка состоит из картинки и текста, а так же специального скрипта кнопки (о нем ниже). Текст расположен как отдельный элемент внутри кнопки.
- Interactable - можно ли на нашу кнопку нажать.
- Transition - поведение кнопки для каждого из состояния (про состояния см. ниже):
- ColorTint - добавление цвета фону кнопки в зависимости от состояния.
- Animation - анимации для каждого из поведения. Нажмите кнопку "Auto Generate Animation" чтобы создать нужные компоненты и анимации для каждого из состояния кнопки. Потом вы сможете их легко редактировать в редакторе анимации.
- SpriteSwap - замена спрайта кнопки в зависимости от состояния.
- None - ничего не делать.
- OnClick() - какие события будут вызываться при нажатии на эту кнопку (см. ниже)
Всего есть 4 состояния:
- Normal - обычное состояние.
- Highlighted - на кнопку наведена мышка или кнопка была выбрана.
- Pressed - кнопка была нажата.
- Disabled - кнопка заблокирована и на нее нельзя нажать и выбрать.
Добавлять события очень легко. Сперва надо нажать "+" в правом нижнем углу. В списке появится новый пункт, первым делом надо перетащить туда объект, а затем выбрать нужный метод, который будет вызван при клике на кнопку.
Slider
Часть параметров такие же как и кнопки, поэтому я думаю их можно больше не описывать.
- Direction - направление слайдера.
- Min Value - минимальное значение, которое будет принимать слайдер.
- Max Value - максимальное значение, которое будет принимать слайдер.
- Whole Numbers - если установлено, то слайдер будет принимать только целые значения.
- OnValueChanged() - какие события будут вызываться при нажатии на эту кнопку. В качестве события должен быть метод, который принимает один параметр типа float**
Input Field
Данный элемент служит для ввода текста.
- Starting Value - начальное значение поля.
- InputType
- Standart - просто текст
- Password - текст заменяется звездочками
- Character limit - максимальная длина текста (0 - без ограничения)
- Multi Line - разрешить использование много-строчного текста
Scrollbar
- Size - размер бегунка.
- Number of steps - кол-во состояний (0 - не ограничено).
- Value - значение. Всегда в диапазоне от 0 до 1.
Так же имеет событие OnValueChanged, как и у Slider'а.
Пример работы из кода
Тут я хочу вам показать, как можно реализовать кнопку способности, как в Warcraft 3. Для этого нам понадобятся знания полученные выше, немного кривых рук ну и конечно же много экспериментов (вы же хотите научиться делать игры, не так ли? Так что больше экспериментируйте и не бойтесь что-нибудь сломать тут вам всегда смогут помочь).
Приступим:
Сперва надо набросать примерный дизайн:
Затем надо написать скрипт:
using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; using UnityEngine.Events; using System.Collections; public class SpellButton : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler { /// <summary> /// Описание способности. /// </summary> [Multiline] public string SpellDescription; /// <summary> /// Время перезарядки способности. /// </summary> public float CooldownTime; private float timeElapsed; /// <summary> /// Показывает сколько времени осталось до перезарядки. /// </summary> public float TimeElapsed { get { return timeElapsed; } } /// <summary> /// Кнопка способности. /// </summary> public Button Button; /// <summary> /// Картинка с процессом кулдауна. /// </summary> public Image CooldownImage; /// <summary> /// Текст в подсказке. /// </summary> public Text DescriptionText; /// <summary> /// Панель для подсказки. /// </summary> public GameObject HintPanel; /// <summary> /// Событие которые вызывается в случае успешного каста. /// </summary> public UnityEvent OnSpellCasted; public void SpellCasted() { timeElapsed = Mathf.Max(CooldownTime, 0.1f); CooldownImage.enabled = true; OnSpellCasted.Invoke(); } public void ResetCooldown() { timeElapsed = 0.0f; timeUpdated(); } void Start() { CooldownImage.enabled = false; DescriptionText.text = SpellDescription; HintPanel.SetActive(false); } private void timeUpdated() { if (timeElapsed <= 0.0f) { timeElapsed = 0.0f; CooldownImage.enabled = false; } else { CooldownImage.fillAmount = timeElapsed / CooldownTime; } } void Update() { if (timeElapsed > 0.0f) { timeElapsed -= Time.deltaTime; } timeUpdated(); } public void OnPointerEnter(PointerEventData eventData) { HintPanel.SetActive(true); } public void OnPointerExit(PointerEventData eventData) { HintPanel.SetActive(false); } }
В заключении
В общем то на этом наверное стоит закончить. Конечно же это только основы, на самом деле функционал данной системе очень большой и охватить её в рамках одной статьи конечно же сложно. Но я думаю вы сами сможете разобраться с остальным, главное не бойтесь экспериментировать, это залог вашего успешного обучения.
Всем спасибо за внимание.
Смотрите также:
Комментарии
Очень полезно. Долго искал инфу о событиях OnPointerEnter и т.д.
Я так понял, чтобы метод появился в OnClick, он должен быть публичным?
lentinant, абсолютно верно. И это касается не только метода, но и полей. Кст надо бы еще проверить, как будет себя вести с атрибутом HideInInspector
У меня почему то не сработало событие OnPointerEnter, хотелось бы увидеть статью по событиям еще. Этой жирный плюс.
У меня почему то не сработало событие OnPointerEnter, хотелось бы увидеть статью по событиям еще. Этой жирный плюс.
Если ты создаешь элемент интерфейса через редактор, события надо добавлять с помощью компонента EventTrigger, создать в нем событие OnPointerEnter, и назначить ему методы, которые будут выполняться при этом событии (аналогично кнопке, в которой надо назначать методы для OnClick).
А в статье, что из скрипта добавляли? Из редактора я знаю как =) думал тут расширю свое понимание. А то скачал вчера Юнити, и думаю, что это за UI такой. Просто из редактора немного ограничен выбор методов. Ну тогда вроде все ясно понятно, спасибо.
У меня почему то не сработало событие OnPointerEnter, хотелось бы увидеть статью по событиям еще. Этой жирный плюс.
Я собирался написать эту статью, но пока что у меня нет времени этим заниматься(
Неплохо, полезная статья, мне как раз пригодилась :)
CollectableItemData.cs
[CreateMenuItem(fileName = "newItem", menuName = "Data/Items/Collectable", order = 51]