Как сделать переключатель unity

Обновлено: 06.07.2024

Редактор Unity знаменит не только тем, что в нем несложно научится делать игры используя уже имеющиеся инструменты, но и также возможностью дополнять сам редактор, улучшая его инструменты или создавая новые конкретно под проект. Чаще всего расширяют редактор именно с целью создания новых инструментов для удобства работы в нем.

Перед началом работы над расширением редактора, необходимо усвоить одно главное правило:

  • Работа в редакторе и работа игры это два разных процесса. Процесс, когда вы запускаете игру после сборки проекта, обычно называют Runtime . Во время этого процесса редактор и его инструменты недоступны. Все элементы, созданные в процессе расширения редактора, также будут недоступны в режиме Runtime , поэтому необходимо заранее разделять области, где вы будете работать с редактором, а где с объектами игры.

Все области редактора или окна, которые вы видите при запуске Unity , имеют свою определенную роль. В окне Hierarchy находятся все объекты сцены, в окне Project – все файлы проекта, а в окне Inspector – настройки и свойства объектов. Также можно добавлять новые окна если необходимо, к примеру – Scene , Game или Profiler , но в этой статье мы будем заниматься только расширением окна “ инспектора ” ( Inspector ).


Часто бывает так, что в крупных проектах могут встречаться объекты с очень большим набором самых разных параметров, данных и свойств. Управлять всеми этими свойствами бывает очень трудно, так как просто теряешься в них или забываешь, какой параметр за что отвечает. Для примера, создадим простой скрипт юнита Unity и наполним скрипт данными.

  1. publicclass Unit : MonoBehaviour
  2. public Sprite icon ;
  3. publicstring description = “Описание” ;
  4. publicfloat minHealth = 0 ;
  5. publicfloat maxHealth = 100 ;
  6. publicbool rangedUnit ;
  7. publicint rangeAttack = 100 ;
  8. public AttackType type ;
  9. publicfloat damage = 10 ;
  10. >

Здесь есть параметр иконки юнита в виде Sprite , описание юнита в виде текста string , минимальный и максимальный запас здоровья юнита, радиус атаки, урон и тип атаки юнита.

Тип атаки AttackType – это отдельное перечисление enum , которое будет указывать, какой тип урона наносит юнит. Так как юниту можно будет указать несколько типов атак, в редакторе представим перечисление в виде маски. Теперь сохраним этот скрипт и закинем какому нибудь объекту на сцене. Вот так выглядит компонент Unit со всеми настройками.


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


Насколько сразу стало все понятно, разные настройки юнита вынесены в разные области.

Редактор для компонента

Настройки каждого компонента в инспекторе отображаются с помощью стандартного редактора, к которому необходимо получить доступ и перенастроить его. У нас уже есть компонент юнита Unit . Чтобы изменить его редактор, необходимо создать специальный скрипт, управляющий его отображением в инспекторе.

Перед тем как создать скрипт редактора, нужно вспомнить главное правило разделения процессов редактора и Runtime’а , для этого достаточно использовать одну из специальных папок редактора – Editor . Эта специальная папка Editor , как и другие специальные папки по типу Resource или StreamingAssets выполняет отдельные функции в редакторе Unity . В папках Editor должны находится только те объекты, которые используются только в редакторе. Там могут находится скрипты для расширения, изображения, другие файлы. Задача этой папки в том, чтобы эти файлы не попали в конечную сборку игры и тем самым не засоряли ее объектами, которые никогда не используются в самой игре.

Поэтому создаем в проекте специальную папку Editor и новый скрипт в ней, для расширения компонента Unit . По негласному правилу среди разработчиков, принято называть скрипты расширения также, как и названия их компонентов, только добавляя в конце *Editor . В нашем случае назовем этот скрипт UnitEditor .

  1. using System.Collections ;
  2. using System.Collections.Generic ;
  3. publicclass UnitEditor : MonoBehaviour
  4. >

Сейчас этот скрипт наследуется от класса MonoBehaviour и представляет собой обычный компонент. Для того, чтобы сделать из него “ новый редактор ”, необходимо унаследовать его от класса “ расширения ” Editor . Находится этот класс в специальном пространстве имен UnityEditor . Добавить это пространство можно, дописав в самом верху скрипта два слова: using UnityEditor;

  1. using System.Collections ;
  2. using System.Collections.Generic ;
  3. using UnityEditor ;
  4. publicclass UnitEditor : Editor
  5. >

Готово, новое пространство подключено и скрипт унаследован от класса Editor . Далее необходимо указать, какой именно компонент необходимо расширить. Для этого используем стандартный атрибут CustomEditor .

  1. [ CustomEditor (typeof( Unit ))]
  2. publicclass UnitEditor : Editor
  3. >

В атрибуте CustomEditor указывается название компонента, к которому необходимо подключить новый редактор. В нашем случае просто указывается компонент юнита – Unit .

Расширение инспектора

Как и для всех остальных областей редактора Unity , панель инспектора ( Inspector ) имеет в классе Editor свой специальный метод для работы, называется он OnInspectorGUI . Именно этот метод отвечает за то? как будут выглядеть настройки компонента в окне инспектора.

  1. [ CustomEditor (typeof( Unit ))]
  2. publicclass UnitEditor : Editor
  3. publicoverridevoid OnInspectorGUI ()
  4. >
  5. >

То, что мы будем видеть в окне инспектора, будет сначала обрабатываться здесь – в методе OnInspectorGUI . Этот метод виртуальный, так как он изначально уже имеет свой стандартный редактор, и после того как мы его перегрузили, сохраняем скрипт, и видим в окне инспектора, что все настройки компонента Unit исчезли.


Для того, чтобы “ нарисовать ” новый редактор компонента Unit, используется специальный GUI . Тем, кто работал раньше с GUI , известно, что это набор статических методов для отображения процедурного графического интерфейса. К примеру, попробуем вывести какой то текст в окне инспектора. Для этого воспользуемся обычным статическим методом Label класса GUILayout .

  1. [ CustomEditor (typeof( Unit ))]
  2. publicclass UnitEditor : Editor
  3. publicoverridevoid OnInspectorGUI ()
  4. GUILayout . Label ( “Hello inspector” );
  5. >
  6. >

Сохраняем скрипт и видим в окне настройки компонента текст Hello inspector .


Теперь займемся созданием новых настроек для компонента Unit . Возвращаемся снова в метод OnInspectorGUI и для начала определим объект редактора как тип Unit .

  1. [ CustomEditor (typeof( Unit ))]
  2. publicclass UnitEditor : Editor
  3. publicoverridevoid OnInspectorGUI ()
  4. Unit unit = this . target as Unit ;
  5. >
  6. >

Для этого воспользуемся преобразованием переменной target в тип Unit , это необходимо сделать, чтобы четко дать понять редактору, с каким объектом мы работаем, ведь изначально редактор не может знать, какой компонент к нему обращается, хоть мы это и указали в атрибуте CustomEditor . Далее возьмем переменную description объекта Unit и “ нарисуем ” для нее новый тип настроек, так чтобы теперь вмешалось больше текста чем раньше.

  1. [ CustomEditor (typeof( Unit ))]
  2. publicclass UnitEditor : Editor
  3. publicoverridevoid OnInspectorGUI ()
  4. Unit unit = this . target as Unit ;
  5. unit . description = EditorGUILayout . TextArea ( this . unit . description , GUILayout . Height ( 50 ));
  6. >
  7. >

Сохраняем скрипт и смотрим результат в инспекторе. Для примера в левой части изображения показан старый вид настройки переменной description , а новый вид справа.


Статический метод TextArea позволяет отображать простую переменную текста как целую область, в которой намного более удобно этот текст редактировать. Все необходимые методы для работы с инспектором в редакторе находятся в классе EditorGUI и EditorGUILayout .

Теперь перейдем к настройке переменных количества жизней, для этого воспользуемся еще одним новым методом Slider класса EditorGUILayout .

  1. publicoverridevoid OnInspectorGUI ()
  2. Unit unit = this . target as Unit ;
  3. unit . description = EditorGUILayout . TextArea ( this . unit . description , GUILayout . Height ( 50 ));
  4. string text = “ Мин . здоровья : ” ;
  5. unit . minHealth = EditorGUILayout . Slider ( text, unit . minHealth , 0 , unit . maxHealth );
  6. text = “Мак. здоровья: “ ;
  7. unit . maxHealth = EditorGUILayout . Slider ( text, unit . maxHealth , unit . minHealth , 1000 );
  8. >

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

Сохраняем и смотрим результат.

Далее поработаем с переключаемыми параметрами редактора и настройкой дальности атаки. По условию, если юнит может проводить атаки дальнего боя, то ему необходимо настроить эту саму дальность атаки. Для этого в компоненте Unit есть две переменные: boolean переменная, rangedUnit для обозначения, что объект является юнитом дальнего боя, и числовая переменная int rangeAttack для настройки самой дальности атаки.

Чтобы отобразить в редакторе эти две настройки, воспользуемся “ групповым ” переключателем. Этот переключатель не будет давать возможности изменить дальность атаки юниту, пока он является юнитом ближнего боя и наоборот.

public override void OnInspectorGUI ()

  1. Unit unit = this . target as Unit ;
  2. unit . description = EditorGUILayout . TextArea ( this . unit . description , GUILayout . Height ( 50 ));
  3. string text = “ Мин . здоровья : ” ;
  4. unit . minHealth = EditorGUILayout . Slider ( text, unit . minHealth , 0 , unit . maxHealth );
  5. text = “Мак. здоровья: “ ;
  6. unit . maxHealth = EditorGUILayout . Slider ( text, unit . maxHealth , unit . minHealth , 1000 );
  7. text = “Включить дальнобойность: “ ;
  8. unit . rangedUnit = EditorGUILayout . BeginToggleGroup ( text, unit . rangedUnit );
  9. text = “Радиус атаки: “ ;
  10. unit . rangeAttack = EditorGUILayout . IntSlider ( text, unit . rangeAttack , 100 , 1000 );
  11. EditorGUILayout . EndToggleGroup ();
  12. >

Снова сохраняем скрипт и смотрим результат.

Метод BeginToggleGroup позволяет блокировать изменения любых переменных, находящихся в его области. Главное при работе с такими областями – не забывать их закрывать, в нашем случае после метода IntSlider , где настраивается дальность атаки юнита, метод EndToggleGroup закрывает область переключателя, тем самым не влияя на другие переменные в новом редакторе. Далее переходим к настройкам разного типа урона, для чего воспользуемся специальным редактором масок.

  1. publicoverridevoid OnInspectorGUI ()
  2. Unit unit = this . target as Unit ;
  3. unit . description = EditorGUILayout . TextArea ( this . unit . description , GUILayout . Height ( 50 ));
  4. string text = “ Мин . здоровья : ” ;
  5. unit . minHealth = EditorGUILayout . Slider ( text, unit . minHealth , 0 , unit . maxHealth );
  6. text = “Мак. здоровья: “ ;
  7. unit . maxHealth = EditorGUILayout . Slider ( text, unit . maxHealth , unit . minHealth , 1000 );
  8. text = “Включить дальнобойность: “ ;
  9. unit . rangedUnit = EditorGUILayout . BeginToggleGroup ( text, unit . rangedUnit );
  10. text = “Радиус атаки: “ ;
  11. unit . rangeAttack = EditorGUILayout . IntSlider ( text, unit . rangeAttack , 100 , 1000 );
  12. EditorGUILayout . EndToggleGroup ();
  13. text = “Тип атаки: “ ;
  14. unit . type = ( AttackType ) EditorGUILayout . EnumFlagsField ( text, unit . type );
  15. >

Можно сохранить и посмотреть результат.

Теперь юниту можно выбрать сразу несколько типов атак. Метод EnumFlagsField позволяет задавать перечислению enum сразу несколько значений, именно по такому же принципу настраиваются камеры и их параметр Culling mask .

Осталось настроить всего две переменных класса Unit , это количество урона и иконку. Так как переменная damage имеет числовой тип float , то и редактор тоже будет использовать метод FloatField для дробных числовых переменных.

  1. publicoverridevoid OnInspectorGUI ()
  2. Unit unit = this . target as Unit ;
  3. unit . description = EditorGUILayout . TextArea ( this . unit . description , GUILayout . Height ( 50 ));
  4. string text = “ Мин . здоровья : ” ;
  5. unit . minHealth = EditorGUILayout . Slider ( text, unit . minHealth , 0 , unit . maxHealth );
  6. text = “Мак. здоровья: “ ;
  7. unit . maxHealth = EditorGUILayout . Slider ( text, unit . maxHealth , unit . minHealth , 1000 );
  8. text = “Включить дальнобойность: “ ;
  9. unit . rangedUnit = EditorGUILayout . BeginToggleGroup ( text, unit . rangedUnit );
  10. text = “Радиус атаки: “ ;
  11. unit . rangeAttack = EditorGUILayout . IntSlider ( text, unit . rangeAttack , 100 , 1000 );
  12. EditorGUILayout . EndToggleGroup ();
  13. text = “Тип атаки: “ ;
  14. unit . type = ( AttackType ) EditorGUILayout . EnumFlagsField ( text, unit . type );
  15. text = “Кол-во урона: “ ;
  16. unit . damage = EditorGUILayout . FloatField ( text, unit . damage );
  17. >

Все поля, которые мы настраивали ранее, имеют простейший тип, это float , int , string и boolean , иконка юнита имеет тип Sprite, который в свою очередь является Unity объектом. Для любых unity объектов есть специальный редактор полей, называется он ObjectField . Попробуем добавить поле иконки перед полем редактирования описания в самом верху метода OnInspectorGUI .

  1. publicoverridevoid OnInspectorGUI ()
  2. Unit unit = this . target as Unit ;
  3. unit . icon = EditorGUILayout . ObjectField ( unit . icon , typeof( Sprite ) , false ) as Sprite ;
  4. unit . description = EditorGUILayout . TextArea ( this . unit . description , GUILayout . Height ( 50 ));
  5. string text = “ Мин . здоровья : ” ;
  6. unit . minHealth = EditorGUILayout . Slider ( text, unit . minHealth , 0 , unit . maxHealth );
  7. text = “Мак. здоровья: “ ;
  8. unit . maxHealth = EditorGUILayout . Slider ( text, unit . maxHealth , unit . minHealth , 1000 );
  9. text = “Включить дальнобойность: “ ;
  10. unit . rangedUnit = EditorGUILayout . BeginToggleGroup ( text, unit . rangedUnit );
  11. text = “Радиус атаки: “ ;
  12. unit . rangeAttack = EditorGUILayout . IntSlider ( text, unit . rangeAttack , 100 , 1000 );
  13. EditorGUILayout . EndToggleGroup ();
  14. text = “Тип атаки: “ ;
  15. unit . type = ( AttackType ) EditorGUILayout . EnumFlagsField ( text, unit . type );
  16. text = “Кол-во урона: “ ;
  17. unit . damage = EditorGUILayout . FloatField ( text, unit . damage );
  18. >

Первый параметр, который указывается в методе ObjectField – это сама переменная иконки. Далее необходимо указать тип объекта, в нашем случае это Sprite и последний boolean параметр “ запрещает ”, либо “ разрешает ” добавлять объекты со сцены.


Чтобы поле иконки выглядело лучше, можно указать минимальные размеры самого поля через GUILayout.Width , GUILayout.Height и добавить выравнивание по горизонтали вместе с полем описания.


  1. publicoverridevoid OnInspectorGUI ()
  2. Unit unit = this . target as Unit ;
  3. GUILayout . BeginHorizontal ();
  4. unit . icon = EditorGUILayout . ObjectField ( unit . icon , typeof( Sprite ) , false , GUILayout . Width ( 50 ) , GUILayout . Height ( 50 )) as Sprite ;
  5. unit . description = EditorGUILayout . TextArea ( this . unit . description , GUILayout . Height ( 50 ));
  6. GUILayout . EndHorizontal ();
  7. string text = “ Мин . здоровья : ” ;
  8. unit . minHealth = EditorGUILayout . Slider ( text, unit . minHealth , 0 , unit . maxHealth );
  9. text = “Мак. здоровья: “ ;
  10. unit . maxHealth = EditorGUILayout . Slider ( text, unit . maxHealth , unit . minHealth , 1000 );
  11. text = “Включить дальнобойность: “ ;
  12. unit . rangedUnit = EditorGUILayout . BeginToggleGroup ( text, unit . rangedUnit );
  13. text = “Радиус атаки: “ ;
  14. unit . rangeAttack = EditorGUILayout . IntSlider ( text, unit . rangeAttack , 100 , 1000 );
  15. EditorGUILayout . EndToggleGroup ();
  16. text = “Тип атаки: “ ;
  17. unit . type = ( AttackType ) EditorGUILayout . EnumFlagsField ( text, unit . type );
  18. text = “Кол-во урона: “ ;
  19. unit . damage = EditorGUILayout . FloatField ( text, unit . damage );
  20. >


Ну вот так на много лучше и удобней. Таким образом можно настраивать любые поля и свойства компонента, достаточно получить доступ к редактору объекта.

Заключение

Расширять редактор можно для любых unity объектов и компонентов, и даже не обязательно для MonoBehaviour скриптов, но и для уже существующих объектов в unity : Transform , Collider и другие. К примеру, для удобства работы с Transform’ом в 2D редакторе можно его просто улучшить также, как сделали этот с компонентом Unit .

Я искал, как это сделать, но я не могу найти его нигде. Я даже догадался, как включить / отключить элемент быстрого списка, чтобы добавить функцию, вызванную после щелчка элемента, но это все. Любые идеи?

Я хочу сделать быстрый список для своего приложения, которое будет состоять из флажков или переключателей. Я нашел информацию о том, как добавить элементы без связанного действия в quicklist ( tutorial ), но это все, что я нашел, нет информации о том, как добавлять другие типы элементов (флажки, переключатели, горизонтальные разделители или элемент со связанным действием), которые упоминаются там. Я пытаюсь получить что-то вроде этого .


Не так давно в Unity зарелизили новую систему управления, а значит самое время посмотреть на неё поближе.

Unity не только активно развивающийся, но и очень активно изменяющийся продукт. Замена рендер пайплайнов, UIElements, New Input System - всё это изменения последнего времени, которые призваны сделать движок ещё более быстрым, гибким и дружелюбным. История знает много примеров, когда компании предпринимали рефакторинг больших систем внутри своих продуктов, результатом таких изменений становились как новые успехи в их развитии, так и крах. Отбросив печальные размышления на эту тему, посмотрим, чем же новая система отличается от старой.

На смене поколений

Новая система управления это довольно заметное событие в мире Unity, комьюнити встретило её с очень большим энтузиазмом, обычно приговаривая, что текущая версия не очень удобна. Да и сама компания изготовитель разделяет это мнение и заявляет, что старая система была разработана до добавления большого количества поддерживаемых устройств и не очень была на них рассчитана. В результате интернет содержит довольно большое количество обзоров, которые обычно показывают настройку базового окна нового Action Asset, его сразу можно узнать по трём большим, горизонтально расположенным, блокам настроек. Мы же попробуем пойти в обратном направлении и сначала разберёмся в базовых элементах новой системы, а от них перейдём к более высокоуровневым.

Документация

Как ни странно, для такого нового пакета, у него довольно хорошая документация, которая всегда содержит в себе ответы на возникающие вопросы, так что сразу приведу на неё ссылку, в том числе и по причине наличия ограничений, которые нужно учитывать в случае её использования. Отмечу часть с вопросами и ответами, ознакомление с ней сильно упростит ваши первые шаги.

Установка

Package Manager

Так как пакет с новой системой управления вышел в релиз его легко найти в Package Manager поиском по названию Input System .

Warning

Во время установки вас уведомят о том, что у вас выключен native platforms backend и ничего не будет работать, пока вы не переключитесь на него, потеряв при этом доступ к текущему API.

Settings

Не очень понятно зачем они так пугают неокрепшие умы, так как в настройках Edit > Project settings… > Player > Other Settings > Active Input Handling* есть возможность включить сразу оба. Так что не очень важно как вы ответите на вопрос про отключение, вы можете сразу переключить backend в любое подходящее вам значение. =)

Встроенные примеры

Samples

После установки пакета появится доступ к приличному набору примеров по использованию новой системы, который содержит всё необходимое начиная от простых примеров и заканчивая добавлением управления на экран смартфона или перенастройки управления в рантайме.

Архитектура

Система настолько широка, что все её настройки рассмотреть довольно проблематично, да и не нужно, документация покрывает все необходимые для начала моменты.

Если же описать архитектуру в двух словах, то она состоит из 3х уровней:

  • Нативный бэкэнд - предоставляется текущей платформой и фактически не является частью новой системы ввода
  • Низкоуровневый слой - обменивается с бэкэндом информацией в виде эвентов и необработанных данных и сильно заточен на оптимизацию работы с памятью и производительность
  • Высокоуровневый слой - представляет низкоуровневые данные в удобном для настройки и использования виде

Отдельно стоит отметить, что новая система полностью построена на эвентах и всячески подталкивает к их использованию.

Input.GetMouseButtonDown(0)

Думаю, что не многие настраивают управление в начале разработки или при тестировании очередной механики, поэтому первым делом хотелось бы узнать, как отловить обычный клик мышки из кода. В новой системе вызов Input.GetMouseButtonDown(0) превратился в Mouse.current.leftButton.wasPressedThisFrame , как можно заметить количество символов немного увеличилось, с другой стороны, теперь можно не вспоминать, под каким индексом идёт левая кнопка, а если автодополнение в вашей IDE понимает чего вы хотите, то разница будет совсем незначительной. Такой же подход используется для доступа к клавиатуре, например, отпускание кнопки пробел на клавиатуре Keyboard.current.spaceKey.wasReleasedThisFrame .

InputAction

Использование прямого доступа к кнопкам не является основным методом работы с новой системой. Базовым понятием в ней являются объекты типа InputAction, по сути, это абстракция над источниками событий(кнопки, стики и т.п.), которая предоставляет данные об их состоянии(нажата ли кнопка или направление стика) и с помощью эвентов оповещает об их изменении. Лучше всего относиться к ним как к логическим элементам(прыжок или выстрел) и не привязываться к физическим свойствам, таким как нажатие кнопки.

Input action

Пример вида, добавленного в компонент InputAction:

Action Settings

Настройка InputAction предоставляет выбор таких параметров:

Action Type может быть трёх типов:

  • Value - стандартный тип, используется для любого типа ввода, постоянно отслеживающий состояние устройств
  • Button - отличается от Value тем, что может быть связан только с кнопками и используется для отслеживания изменения состояния
  • Pass-Through - в отличие от других типов не используют схему started-performed-canceled(основные типы эвентов) и фактически всегда находится в состоянии performed, его удобно использовать в случае необходимости обработки всех данных с нескольких источников событий

Control Type - описывает тип данных предоставляемых устройством, например, в случае со стиком можно получить данные в виде структуры Vector2.

Interactions - предоставляют возможность настройки специфических действий, допустим, долгого нажатия или мультиклика.

Processors - добавляют постобработку данных поступающих от контроллера перед выдачей их логике, например, нормализацию вектора направления стика на контроллере.

В зависимости от выбранных настроек предоставляется доступ к разным физическим устройствам, способным предоставить необходимые нам данные. В качестве примера кнопка мышки способна обрабатывать только клики, и, в случае выбора предоставления данных в виде вектора, не может быть использована в качестве отслеживаемого средства ввода.

Bindings

Также предоставляется доступ к нескольким композитным устройствам, например, направление движения можно получить в виде вектора, получаемого при помощи четырёх кнопок(WSAD или другой комбинации). Набор композитных устройств можно дополнить при необходимости своими, подробнее об этом можно прочитать в документации.

Использование InputAction не требует каких-то дополнительных сущностей и вполне подходит для использования в компонентах, только не стоит забывать, что при создании они находятся в выключенном состоянии и требуют отдельного включения через вызов метода Enable() .

Action Maps

Это объединение нескольких InputAction, которое отвечает за разные наборы действий в игре. Допустим, в меню, с помощью стика на контроллере, вы перемещаетесь между разными кнопками интерфейса, а в игре стик отвечает за перемещение персонажа.

Control Schema

Это абстракция над InputAction которая позволяет задавать разные триггеры для этих самых действий в зависимости от используемых физических устройств. Допустим, управление передвижением с клавиатуры осуществляется через WSAD, а с контроллера через левый стик.

Action Asset

Action Asset

Это новый тип ассета, который отвечает за комплексную настройку InputAction, ActionMap и схем управления, именно его можно встретить в виде большой панели из трёх блоков в большинстве других обзоров.

Итого

Фактически вся новая система вертится вокруг наборов InputAction, а всё остальное призвано структурировать работу с ними. В результате можно выделить четыре основных подхода к использованию новой системы:

  • Обращение к данным устройства напрямую - как в случае с Mouse.current.leftButton.wasPressedThisFrame
  • Использование базовых InputAction
  • Генерация класса с набором InputAction из Action Asset, что сильно упрощает настройку сложных систем управления, использующих разные схемы
  • Использование Action Asset через объект PlayerInput

Как можно заметить Action Asset вовсе не является необходимым условием для настройки управления и служит лишь для упрощения работы с действительно большими и сложными решениями.

Изменение управления

В предыдущей статье мы рассматривали шаблон “Команда”, который обычно используют для добавления возможности перенастройки управления в игре. Использование этого подхода в новой системе фактически добавит лишний слой абстракции над InputAction, так как они уже являются логическими элементами. В свою очередь, изменение управления становится не очень очевидным и реализуется через класс с подозрительным названием InputActionRebindingExtensions. По этой теме есть отдельный пункт в документации и довольно сложный пример под названием Rebinding UI, который поставляется с пакетом. Так как это очень важный элемент для системы управления, то мы рассмотрим его минималистичную реализацию ниже.

Практика

Примеры с полноценным созданием Action Asset довольно часто встречаются на просторах интернета и не имеет смысла повторять их ещё раз, так что, в качестве минималистичного примера, изменим управление магом из наших предыдущих статей на использование новой системы с применением InputAction, а также добавим возможность перенастройки управления.

Реализация

Input Handler

Для базового использования InputActions не требуется какой-либо дополнительной настройки кроме добавления кнопок

Action Settings

Результат

Для первого запуска заклинания используется левая кнопка мышки, а второй выстрел происходит после перенастройки действия на кнопку клавиатуры f

Заключение

Изначально работа с новой системой вызывает ассоциации с добавлением кастомного редактора на какой-нибудь компонент, как, например, в нашей статье о редакторе цветов для генератора Шума Перлина. Но понимание что это очень большое изменение текущего подхода и переход на совершенно новое api приходит очень быстро. Большой набор возможностей и новых компонентов может отпугнуть, но довольно неплохая для первой версии документация и примеры позволяют надеяться на быстрое погружение для её полноценного использования, если, конечно, текущие ограничения не являются для вас большой преградой. Надеюсь, эта статья немного прояснила для вас основы работы с New Input System. Пока! =)

Графический интерфейс пользователя (англ. Graphic User Interface, GUI) — это метод взаимодействия пользователя с игрой, в котором элементы интерфейса (меню, кнопки, значки, списки и т. п.), представленные пользователю на экране, выполнены в виде графических изображений.

Интерфейс игры

Интерфейс игры

Canvas

Все элементы интерфейса (кнопки, изображения) должны находиться внутри объекта Canvas. Canvas — это холст экрана, на котором размещаются элементы. Для создания холста в главном меню выбери GameObject → UI → Canvas. В окне Инспектор можно настроить параметры холста. Самая важная настройка — это Render Mode компонента Canvas. Он отвечает за режим отображения интерфейса.

Render Mode содержит следующие настройки на выбор:

  • Screen Space - Overlay. Элементы интерфейса показываются поверх экрана. Стоит по умолчанию;
  • Screen Space - Camera. Позволяет отображать интерфейс в определенной камере;
  • World Space. В этом режиме элементы интерфейса располагаются в 3D пространстве.

Rect Transform

Все элементы интерфейса имеют настройки Rect Transfrom. Разберем их на примере кнопки (выбрав в главном меню GameObject → UI → Button).

Во время редактирования пользовательского интерфейса в Unity нужно включить режим 2D. Для этого на панели Scene нажми кнопку 2D:

Переключение режимов

Переключение режимов

Включи инструмент редактирования пользовательского интерфейса на панели инструментов или нажми кнопку T. С помощью этого инструмента можно перемещать, вращать и изменять размер элементов интерфейса.

Режимы редактирования

Выбери элемент интерфейса. Схватив за одну из точек по краям можно изменить размер элемента.

Пример 1 Пример 2

Пример 3 Пример 4

Button

Создать кнопку можно в главном меню GameObject → UI → Button. Кнопка состоит из двух объектов, которые можно посмотреть в окне Иерархия - формы и текста внутри нее:

Объекты кнопки

Сначала разберем настройки надписи на кнопке. Выбери Text на панели Hierarchy. В Inspector будут следующие настройки текста:

Теперь, рассмотрим настройки формы кнопки. Выбери объект Button (название этого объекта можно изменить). В окне Инспектор находятся следующие основные настройки:

  • Normal Color. Цвет кнопки;
  • Highlighted Color. Цвет кнопки при наведении на нее мыши;
  • Pressed Color. Цвет нажатой кнопки.

Функции

Как ты уже знаешь, отдельным блокам кода можно задавать имена. Такой именованный блок кода называется функцией или методом (частный случай функций). Для примера создадим функцию с именем SayHello, которая выводит фразу Hello world! в консоль:

Обратите внимание, что функция не выполняется автоматически при создании. Чтобы функция выполнилась, ее требуется явно вызвать в желаемом месте. Для примера вызовем созданную функцию внутри функции Start():

Обработка события нажатия на кнопку

Для обработки события нажатия на кнопку необходимо выполнить два шага:

1. Создать функцию, которая должна выполняться при каждом нажатии.

2. Передать имя созданной функции в функцию AddListener() соответствующего события кнопки.

Весь код скрипта:

Обратите внимание, что имя функции может быть любым. Имя должно соответствовать действию, которое выполняет функция. А вот этот скрипт загрузит сцену с нужным именем по нажатию кнопки:

Создание кнопки со своим дизайном

Рассмотрим пример создания кнопки, состоящая из трех изображений: обычное состояние (Normal), состояние, когда на кнопку наведена мышь (Hovered) и состояние нажатия на кнопку (Pressed). Кнопки можно ⬇ скачать тут.

Сначала нужно загрузить все изображения кнопки в проект, лучше использовать одно изображение, разделенное с помощью инструмента Sprite Editor. Выбери загруженное изображение и в окне Инспектор в параметре Texture Type выбери Sprite (2D and UI). После чего нажми кнопку Apply.

Изменение типа изображения

Изменение типа изображения

Теперь в параметре Sprite Mode выбери Multiple и нажми кнопку Sprite Editor, чтобы открылся редактор Sprite Editor. Выбери в меню Slice → Type → Automatic и нажми кнопку Slice. Unity сам найдет все кнопки и разметит их.

Автоматическая разметка кнопок

Автоматическая разметка кнопок

Теперь нужно настроить фрагменты кнопки, которые не будут изменять свой размер при масштабировании элемента. Выбери кнопку, перетащи зеленые точки так, чтобы область находилась внутри кнопки и не затрагивала углы:

Выполни это действие для каждой кнопки. После этого можно закрыть окно и принять изменения.

Создай кнопку Выдели кнопку GameObject → UI → Button. Выбери ее и в окне Инспектор отредактируй следующие настройки:

  • в параметре Source Image выбери спрайт кнопки в обычном состоянии (normal);
  • в параметре Image Type выбери Sliced;
  • в параметре Transition выбери Sprite Swap;
  • в параметре Highlighted Sprite выбери спрайт подсвеченной кнопки (с нашими кнопками это normal состояние);
  • в параметре Pressed Sprite выбери спрайт нажатой кнопки.

Image

GameObject → UI → Image позволяет показывать на переднем плане экрана изображения такие как карта, значок жизни, магии и прочее.

Добавь в проект изображение, которое будет использоваться в графическом интерфейсе. В Окне Инспектор изображения для выбери следующие настройки:

Добавь на сцену GameObject → UI → Image. На панели Inspector выбери следующие настройки:

  • в параметре Source Image выбери загруженное изображение;
  • в параметре Color можно перекрасить изображение в определенный цвет;
  • в параметре Image Type выбери Simple. Если включить настройку Preserve Aspect, то изображение не будет деформироваться при изменении размера экрана. Если в настройке Image Type выбрать Tiled — изображение будет повторяться на экране.

Text

GameObject → UI → Text позволяет отображать текст в графическом интерфейсе (отображение диалогов, подсказок, надписей). Содержит следующие настройки:

  • Text — текст, который будет отображаться на экране;
  • Font — шрифт текста;
  • Font Style — тип шрифта. Принимает следующие значения:
  • Normal – обычный шрифт;
  • Bold – применение полужирного начертания к тексту;
  • Italic – применение курсивного начертания к тексту;
  • Bold and Italic – применение курсивного и полужирного начертания к текст.
  • Font Size — размер шрифта;
  • Alignment — выравнивание текста;
  • Color — цвет текста.

Текст можно менять с помощью скрипта. Это позволит в режиме реального времени отображать количество набранных очков, жизни, времени и т.п.

Читайте также: