Как сделать меню в unity

Обновлено: 03.07.2024

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

Создание префаба элемента интерфейса

Для того, чтобы с лёгкостью динамично инстанциировать элементы интерфейса, сначала нужно создать префаб для типа элемента интерфейса, который будете потом инстанциировать. Настройте нужный вам элемент интерфейса в сцене, затем перетащите его в Project View, чтобы конвертировать его в префаб.

К примеру префабом для кнопки может быть игровой объект (Game Object) с Image и Button компонентом, и дочерний игровой объект с Text компонентом. Ваши настройки могу отличаться между собой, в зависимости от того, что вам в данный момент нужно сделать.

Вы можете задаться вопросом, почему мы не используем API методы для создания разных типов контроллеров, включая визуальную часть и многое другое. Сделано так было, потому что существует бесконечное количество способов например чтобы сделать кнопку одним из элементов для настройки чего-либо. Использует она изображение, текст или и то и другое сразу? Или для работы ей нужно использовать одновременно сразу несколько изображений? Что из себя представляет к примеру шрифт, его цвет, размер или выравнивание? Какой спрайт или спрайты должно будет использовать изображение? Благодаря тому, что вы можете создать любой префаб и инстанциировать его, вы получаете возможность настроить его так, как вам нужно. Если же вам потом захочется изменить поведение и отображение какого-либо элемента интерфейса, вы просто сможете отредактировать нужный вам префаб и применённые изменения тут же отразятся в вашем интерфейсе, включая динамически созданный интерфейс.

Инстанциирование элемента интерфейса

Префабы элементов интерфейса инстанциируются как обычные используя метод Instantiate. При назначении родительского элемента в качестве инстанциируемого элемента интерфейса, рекомендуется делать это с использованием метода Transform.SetParent вкупе со значением параметра worldPositionStays выставленным в false.

Позиционирование элемента интерфейса

Элемент интерфейса позиционируется там где нужно используя компонент Rect Transform. Если элемент интерфейса является потомком компоновочной группы (Layout Group), тогда он будет автоматически позиционирован там где нужно без необходимости делать это вручную.

При позиционировании компонента Rect Transform бывает полезным сперва определить имеет или должен ли он иметь свойство растягиваемости. Данное свойство вступает в действие, когда параметры anchorMin и anchorMax не соответствуют друг другу.

Для того, чтобы сделать Rect Transform нерастягиваемым, его позиция задаётся попросту через свойства anchoredPosition и sizeDelta. Свойство anchoredPosition задаёт позицию точки опоры относительно привязок. Свойство sizeDelta представляет собой значение исходного размера тогда, когда нет никакого растяжения.

Для растяжения Rect Transform проще будет задать позицию используя свойства offsetMin и offsetMax. Свойство offsetMin задаёт угол левого нижнего угла прямоугольника относительно левой нижней привязки. Свойство offsetMax задаёт угол правого верхнего угла прямоугольника относительно правой верхней привязки.

настройка элемента интерфейса

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

Делается это путём получения различных компонентов и изменения их свойств. Для более подробной информации по компонентам Image и Text, ознакомьтесь с соответствующими разделами документации по скриптингу и тому как работать путём скриптинга с UnityEvents.

Unity3D - версия не имеет значения.
Интерфейс - знание движка в лицо, ну или хотя бы со стороны.
Прямые руки - с рождения.


Часть первая. Подготовка к работе.

1. Для начала, конечно же, откройте сам Unity3D и создайте новый, чистый проект. А как? File --> New Project.
2. Создайте его в пути, не имеющий кириллицы. И не отмечайте галочки, нам вполне сойдет стандартный набор инструментов.
3. Перед нами голый проект, на котором мы создадим ваше первое меню.

1 | Создание легкого меню или азы Unity скриптинга

4. Создайте новый скрипт нажатием ПКМ (правой кнопкой мыши), во вкладке Project.
Create --> JavaScript.
5. Назовем его TextControl. Щелкните два раза ЛКМ (левой кнопкой мыши) по названию скрипта.

Часть вторая. Сборка сцены.

6. В свежей сцене создайте Plane и четыре куба через вкладку GameObject --> Create Other --> Plane, Cube.
7. Так же добавьте Point Light для эффекта.

8. У вас должно получится что-то в этом духе.

2 | Создание легкого меню или азы Unity скриптинга

9. Теперь создайте три 3D текста. GameObject --> Create Other --> 3D Text. Не забудьте развернуть их по оси Y на 180 градусов.

10. Расположите текст так, чтобы они были в середке кубиков. Текст можно вписать в Inspector --> Text Mesh --> Text.
Получилось вот что.

3 | Создание легкого меню или азы Unity скриптинга

11. Выделите все объекты в Hierarchy и нажмите сочетание клавиш Ctrl+D.
12. Отодвиньте все объекты вправо и переименуйте текст в Fastest, Fantastic и Cancel.

4 | Создание легкого меню или азы Unity скриптинга

13. Камеру находящаяся над главный меню назовите Camera1, а вторую, находящуюся над настройками графики, Camera2.
14. Создайте папочку Scenes нажатием ПКМ во вкладке Project. Create --> Folder.
15. Сохраните сцену под названием MainMenu и поместите ее в папку Scenes. File --> Save Scene.
Если все идеально, должно получится прямо как у меня.

5 | Создание легкого меню или азы Unity скриптинга

Часть третья. Контроль текста.

16. Откройте скрипт TextControl и приготовьтесь писать.

17. Для начала давайте создадим переменную isNewGameButton, при которой объект будет загружать сцену.

Примечание: Почему false? Потому-что при true переменная будет срабатывать сразу.

18. Лучше не раскатывать тесто, и сразу выставить нужные переменные.

19. Теперь давайте разберем самое интересное. При входе на объект курсора мыши, его материал моментально изменяется.

Смена материала на серый цвет при входе курсора будет осуществляться таким образом:

А возвращение материала в прежний вид при выходе курсора выглядит так:

Примечание: Белый цвет, точнее Color.white, возвращает обычный цвет текстуре.

20. Сейчас мы начнем делать нажатие на объекты.

Нажатие осуществляется через:

А чтобы занять чем-то нажатие, мы составим свойства для каждой переменной.

Для начала возьмем переменную IsQuitButton:

Примечание: Application.Quit() осуществляет выход из игры полностью.

Теперь возьмем переменную ужасной графики.

Примечание: else дает возможность работать в OnMouseUp() много раз. А QualitySettings.currentLevel = QualityLevel.Fastest изменяет графику на уровне.

Примечание: Тоже самое что и выше, но теперь с максимальной графикой.

Теперь возьмем переменную загрузки уровня.

Примечание: Application.LoadLevel () дает возможность загружать уровень. А число в скобках пишут для точной загрузки нужного левела.

21. Теперь поработаем с камерами. Создайте новые две переменные:

А теперь продолжите таким образом:

Примечание: Мы работаем с нажатием, и таким образом, когда вы щелкаете на кнопке Options, первая камера отключается, а вторая начинает функционировать.

22. Ну и последнее что нам нужно, это возвращение из игры в меню.

Добавьте в конце закрывающуюся скобку >.

Часть четвертая. Сборка меню.

24. Назначьте готовый скрипт ко всем кнопкам в сцене.
25. Теперь смотрим в Inspector --> Text Control (Script) и разбираем.

Для начала выберите кубик с New Game и поставьте галочку Is New Game Button.
Затем щелкните кубик с Options и поставьте галку Is Options Button. А еще в поле Camera 1 вставьте первую камеру, а в поле Camera 2, вторую.
На кубике Quit галку Is Quit Button.

Сейчас мы переходим на место с настройками. Выделите куб Fastest и поставьте галочку Is Quality Button Fastest. Так же Fantastic, но уже Is Quality Button Fantastic.
Выделите кнопку Cancel, поставье галочку Is Options Button, и вставьте в поле Camera 1 вторую камеру, а в поле Camera 2, первую.
Примечание: Почему? Потому-что нам нужен обратный процесс.

Для загрузки сцены (1), то есть, Is New Game Button, при компиляции, выставьте уровень под нужным вам числом. И не забудьте изменить значение в скрипте!

Так же, если потребуется выйти из уровня обратно в меню, используйте переменную Is Main Menu Button.

26. Теперь посмотрим что получилось.

Если все в порядке, значит я не зря писал данный урок :)
А если возникли какие-то проблемы, пишите, помогу всем, чем смогу.
Спасибо за внимание, с вами был Валентин. Желаю успехов в игрострое!

Ну а в следующем уроке я расскажу, как улучшить меню в плане возможностей ;)

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

Добавить пункт меню

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



горячая клавиша

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

% -CTRL в Windows / CMD в OSX

& -Alt
клавиши курсора ВЛЕВО / ВПРАВО / ВВЕРХ / ВНИЗ
F1…F12
HOME,END,PGUP,PDDN
Буквенная клавиша не является частью последовательности клавиш. Чтобы добавить буквенную клавишу в последовательность клавиш, необходимо добавить подчеркивание впереди (например: _g соответствует сочетанию клавиш "G").
Комбинация сочетаний клавиш добавляется после пути к пункту меню и разделяется пробелом. Пример показан ниже:


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

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

Особый путь

Display, путь, переданный свойству MenuItem, определяет, в какое меню верхнего уровня будет помещен новый пункт меню. В Unity есть несколько специальных путей, которые действуют в контекстных меню (меню, доступных по щелчку правой кнопкой мыши):

CONTEXT / ComponentName - пункт меню появится в данном компоненте
в контекстном меню (меню, отображаемое при щелчке правой кнопкой мыши).
Ниже показаны некоторые примеры использования специальных путей:

Результат приведенного выше фрагмента кода выглядит следующим образом:

Проверка

Некоторые пункты меню доступны только при определенных обстоятельствах, в противном случае их следует отключить. Добавьте метод аутентификации, чтобы включить / отключить пункт меню в соответствии с его контекстом.
Метод проверки является статическим и использует метод, отмеченный свойством MenuItem, и это свойство передает значение true в качестве параметра проверки.
Этот метод проверки должен иметь тот же путь, что и метод команды меню, и он должен иметь логическое возвращаемое значение, чтобы подтвердить, активировано или отключено меню.
Например, метод проверки можно использовать для контекстного меню текстуры в представлении проекта:


Если щелкнуть правой кнопкой мыши непроизвольную текстуру, этот пункт меню будет отображаться серым цветом:

Приоритет меню управления

Приоритет - это номер, присвоенный пункту меню (третий параметр, переданный в MenuItemde), который управляет порядком отображения меню.
пункты меню также могут быть автоматически сгруппированы, одна группа на каждые 50:


В приведенном выше примере кода показана следующая структура, пункты меню разделены на две группы:

Другие родственные классы

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

MenuCommand

ContextMenu

ContextMenuItem

Это свойство добавляется в поле класса компонента .Выше показано, что свойство ContextMenu добавляет пункт меню в контекстное меню компонента, а затем свойство ContextMenuItem добавляет пункт меню в контекстное меню поля.
Поскольку этот атрибут добавляется в поле, метода нет, поэтому он принимает два параметра: один используется для отображения имени элемента меню, а другой используется для указания того, который будет вызываться при выборе меню. Метод экземпляра.
Пример. Добавление метода для случайной инициализации поля:


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

AddComponentMenu

GenericMenu

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

В один прекрасный момент вам приспичило создать меню. Контекстное или слитое c общим меню Unity. И тут вы поняли что ничего о том как это делается и не знаете. Или знаете не всё. Данная статья - обзор по способам построить меню в Unity. Этих способов благо достаточно - и каждый из них специфичен.

В чем сабж статьи?
Если вдруг вам приходится заниматься редакторами профессионально, как мне на день насущный, то данные знания для вас просто обязательны - меню применяются повсеместно. Когда я начинал только изучать это мне приходилось пилить велосипедные комбобоксы из юнити-вики, потому что я не знал как делать это правильно. Способ тот кривой и вам на такие вещи я натыкаться не советую - почему бы не начать сразу делать их правильно?

ContextMenu

Самый первый способ, с которым, вы вероятно даже сталкивались - это ContextMenu.
Следующий пример демонстрирует принцип работы данного атрибута:

  • только в скриптах, наследуемых от класса Behaviour (всех сценариях и компонентах)
  • только для методов экземпляра (никакого 'static'!)
  • метод не должен иметь параметров

Способ применения: когда нужно быстренько вызвать какой-нибудь метод из компонента. В таких случаях переписывать инспектор объекта с целью добавить пару кнопок - нецелесообразно. Да и некоторые пунктики лучше иметь именно там.

Этот способ используется в игровом билде (что логично, так как все компоненты лежат там).
Для данного способа чуть ниже (в разделе "Параметры строки меню") будет расписано чуть подробней.

ContextMenuItem

Данный способ похож на предыдущий - это тоже атрибут. Но пишется он не туда и не так.
Пример написания:

Данный способ добавляет контекстное меню для нашего поля(в примере - поле ff). При щелчке правой кнопкой мыши мы увидим меню с данным пунктом:

  • используется только в скриптах, наследуемых от класса Behaviour (всех сценариях и компонентах)
  • только для методов экземпляра (никакого 'static'!)
  • метод не должен иметь параметров

Способ применения: аналогично первому способу, но с учетом того, что выполнение метода относится к определенному полю, а не компоненту в целом.

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

MenuItem

С атрибутами, однако, мы еще не закончили. Остался у нас в запасе один, о котором я умолчал. Это атрибут MenuItem, добавляющие пункты в основное меню редактора.

  • функция, которой принадлежит атрибут, обязательно должна быть статичной
  • функция не должна иметь параметров

Прошу заметить - в официальной документации Unity предоставлен ошибочный пример применения данной функции. Там она используется в компонентах MonoBehaviour, которые для редактора в принципе не нужны. Естественно, что пример безумно дурной и если вы в реальной ситуации заюзаете данный метод вот таким образом ваша игра просто не скомпилируется (нельзя использовать методы редактора в игровом билде, но если вы не удержались то должны предварить это соответствующей ограничивающей директивой).

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

Данный способ всегда должен указывать на раздел, то есть нельзя просто добавить пункт в меню, он обязательно должен находиться в каком-то месте. Помимо этого данный способ позволяет встраивать меню в существующие уже места. Это создает подобный пункт не только в основном меню, но и в контекстном меню в иерархии или проекте, если вы определили элемент туда.

Помимо этого данный атрибут может по желанию реализовывать несколько параметров:
isValidateFunction - помечает функцию как функцию-валидатор. Это значит, что данная функция не показывает пункт меню, а проверяет его доступность. Такая функция должна возвращать bool, говорящий, задисейблена данная кнопка или активна. Используется в паре с обычным пунктом меню (не валидатором с точно таким же путем, включая дополнительные параметры).
Пример:

priority - порядок пункта меню. Обычно стандартные элементы отсчитывают порядок от 0 и выше и, следовательно, указав "-1" мы установим наш пункт в самом верху списка.

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

Так же в рамках данного способа вы можете изменять контекстные меню инспектора, с таким же результатом как у способа ContextMenu. Однако, тут есть свой сахар - вы можете изменять пункты уже написанных компонентов, таких как Transform, Rigidbody и так далее.
Для этого способа зарезервировано меню CONTEXT, после которого вы вводите название нужного вам компонента:

  • 'Assets/' - для добавления пункта в контекстное меню в окне ассетов
  • 'Assets/Create/' - для добавления пункта в меню кнопки 'Create' в окне с ассетами

GUI методы

  • EditorGUILayout.Popup - строковый попап. Принцип работы: узнаем индекс строки из массива
  • EditorGUILayout.IntPopup - числовой попап. Принцип работы: узнаем индекс числа из массива, при том можно подменить числа "строковым отображением" (показывать текст, подразумевать число)
  • EditorGUILayout.EnumPopup - попап для перечислений. Принцип работы: указываем текущее значение перечислителя, получаем новое.
  • EditorGUILayout.MaskField - попап, позволяющий выбрать несколько значений, компонуя их в маску (обычно так делают выборки слоев/тегов)

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

ShowAsDropDown

Следующий способ - использовать отдельное окно. Однако вызывать его не просто так, а "по-особенному", через метод ShowAsDropDown. Соль этого окна в том, что оно закрывается при щелчке вне.
Для того, чтобы результат "ушел обратно" при успешных действиях - обычно делается делегат, который обрабатывается у родителя. Однако это не единственный способ вернуть значение - можно юзать функторы, а может даже втупую забить код для конкретной ситуации. Но опять же в целях универсальности лучше использовать делегат, гарантирующий обратный вызов.
Пример:

Соответственно далее в такое окно дописывается метод OnGUI где рисуется абсолютно кастомный попап под наши потребности.
Метод "ShowAsDropDown" просит указать место своего появления в абсолютных координатах. Часто нам нужно отображать такой попап на месте щелчка мышью. Такие координаты мыши можно достать следующей командой:

Соль этого способа в том, что вид попапа может быть любым - вы можете сделать, например, выбор иконок или оформить окно по своему желанию.

PopupWindow

Данный способ по сути является автоматизацией предыдущего, предоставляя методы OnOpen/OnClose и заготовленный метод Show.
Суть в том чтобы пронаследовать наш класс от класса PopupWindow. Однако запомните - данная реализация ограничена в плане возврата обратного значения. Если вы используете попап для конкретных целей - данное окно вам подойдет, но для универсальных попапов желательно использовать ShowAsDropDown.

DisplayPopupMenu

Следующий способ - обычный попап, который используется во всех стандартных способах. По сути он лежит "в корне" работы GUI методов, которые я описывал немногим выше.
Этот способ использует делегат для обратного вызова.
Соль в вызове метода EditorUtility.DisplayPopupMenu, при вызове которого мы указываем ссылку на метод. Если вы хотите запаковать вызов пункта в метод без делегата вам понадобится создать класс аля:

Данный класс содержит методы, необходимые для манипуляций с данными попапа. Если коротко - то в коде GUI вы должны завести id для вашего контрола и затем вызвать EditorUtility.DisplayPopupMenu. В данный метод вы подставляете на место делегата метод PopupCallback.SetValueStandart. После проверяете, равен ли id инстанса в PopupCallback вашему id (это уже идет код чтобы отловить обратный вызов). В положительном случае - достаете из инстанса данные.
Не совсем очевидно, но это именно то что делают Unity для формирования таких методов.

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

GenericMenu

Завершает данный список класс GenericMenu. Его синтаксис необычайно прост - вы создаете экземпляр этого класса и добавляете через методы новые пункты. По завершению вызываете ShowAsContext. Каждый пункт может иметь свой обратный вызов, что упрощает создание меню с командами.

Кастомный попап без фокуса

Отдельной темой является создание попапов без фокуса. Дело в том, что на данный момент это нельзя сделать напрямую. Несмотря на это - потребность в таком попапе есть, притом существенная - это кастомные подсказки со встроенными картинками, это "умная строка", не сбивающая ввод текста и предлагающая варианты для окончания и прочие случаи, при которых новое окно не должно влиять на поведение.
Для таких целей я портировал костыль. Чтобы его использовать скопируйте целиком следующий код:

Некоторые из указанных выше способов поддерживают расширенное форматирование для строки.
Если вы укажете в качестве пункта пустую строку, вместо неё будет задействован сепаратор (вертикальная черта).
Если вы разделите части строки дробной чертой /, вы создадите подпункт с иерархией.

Таким образом, если вы хотите назначить пункту меню клавишу G вы должны написать что-то подобное следующему:

Естественно, что не всем клавиши можно представить буквой, потому для некоторых клавиш используются специальные ключевые слова LEFT, RIGHT, UP, DOWN, F1 .. F12, HOME, END, PGUP, PGDN.

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

Никогда не думал что чтобы рассказать про меню нужно столько написать. А ведь я еще кастомные меню особо не расписывал

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