Хабра уже имеет за плечами пару туториалов на тему кроссплатформенного 3D движка «Unity 3D», но до сих пор не имеет уроков на тему 3D меню.
На эту статью меня натолкнул пост Unity3d script basics и .
Так же пару людей поругалось на автора на тему «Уже не в том веке живём», поэтому вот вам щепоточка знаний.
Делая этот туториал, я предполагал, что вы уже знакомы с основами JS"a и интерфейса Unity 3D.
Работать я буду с Виндусовской версией Unity 3D. Обладатели других ОС думаю разберутся.

Приготовления

1) Иметь основную мысль на тему меню, так сказать мысленную заготовку.
2) Нужные вам модели. Можно найти (Google Sketchup). Нужный нам формат - collada.
3) Иметь шрифт под текст меню. Можно взять стандартные из шрифтов винды.

Первые шаги

Я придумал сценку на темы «Вестерн».
Начнём с приготовления сцены.
Первым создайте terrain и подберите размер/высоту/качество текстурирования(Terrain->Set Resolution/Flatten Heightmap).
Дальше подберите текстуру и сделайте основной ландшафт сцены

После поставьте камеру под нужный угол обзора, добавьте свет(Derectional light) и тени он него же.
Добавьте туман и небо(skybox), всё это можно найти в Edit->Render Settings.




Модели

Для начала разархивируйте скачанный архив, и перенесите все текстуры из images в папку Materials вашего проекта(просто выделите и перетащите на папку в окне unity).
Дальше импортируйте модель *.dae в Unity (Assets->Import New Asset), и добавьте её на сцену, так же измените размер до вам угодного.
ВАЖНО! Если вы сделаете не по порядку, то получите незатекстурированную модель.

Меню Основа

Меню можно сделать с помощью моделей или 3д текста, а можно и из всего сразу.
Я расскажу о текстовом варианте, т.к. это попривычнее будет.
Что бы создать 3д текст со своим шрифтом, нужно добавить его в проект (Assets->Import New Asset).
После, выделив его, перейти в (GameObjects->Create Other->3D Text).
У вас создастся мыльный текст. Чтобы повысить качество, поменяйте Font Size, а потом подгоните размер.

Function OnMouseDown () { //1 if(name=="Play Game") { Application.LoadLevel("Test Scene"); } //2 if(name=="Options") { } if(name=="Quit") { Application.Quit(); } //3 } function OnMouseOver () { //1 animation.Play(); //4 }

1) События при манипуляции с мышью.
2) Загрузит сцену под названием Test Scene, если вы укажете её при компиляции проекта(так же можно указать порядковый номер).
3) Выход из приложения, если оно скомпилировано не под web, или не запускается в редакторе.
4) Метод анимации меню, если таковое вам хочется (Слишком муторно, поэтому рассказывать не буду. Можно будет посмотреть в моём готовом проекте).
* name - имя объекта, на котором лежит скрипт.

Переходы по меню

Создайте цилиндр, и переименуйте его в Menu. Сделайте его прозрачным, применив любой частично прозрачный материал (можно как на скрине).


Вложим все наши объекты «3д текст» в объект Menu(окно Hierarchy).

Так же на другой стороне цилиндра создайте два 3д текста, и переместите их по иерархии в Menu. Это будет подпункт меню.


Обновим скрипт.

Var menuRotation: GameObject; //1 function OnMouseDown () { if(name=="Play Game") { Application.LoadLevel("Test Scene"); } if(name=="Options" || name=="Back") { menuRotation.transform.Rotate(0, 180, 0); } //2 if(name=="Quit") { Application.Quit(); } } function OnMouseOver () { animation.Play(); }

1) Создаст переменную, содержащую объект.
2) При нажатии на объект с названием Options, команда повернёт объект menuRotation на 180*.

Перенесите цилиндр в поле Menu Rotation кнопки Options.
Теперь вы имеем почти готовое меню. Осталось только создать слайдер.

Слайдеры

Слайдеры - самая муторная часть урока. Приготовьтесь к страданиям и шаманству.
*Буду рад, если кто-нибудь выложит свою версию слайдера, т.к. мой на самом деле Шаманский.
Создайте приметив Cube и растяните его до подобия ниточки. Cоздайте приметив Sphere и поместите его в центре Cube.
Всё это располагаем противоположно основному меню, а именно к подменю Options.
*Для удобства я переименовал их в line_slider и sphere_slider.


Создайте новый скрипт JS, и прицепите его к Sphere(sphere_slider).

Function OnMouseDrag () { //1 var translation: float = Input.GetAxis ("Mouse X"); //2 transform.Translate(translation, 0, 0); //3 print(transform.position.x.ToString()); //4 }

1) Событие будет активироваться при нажатии (на Sphere/sphere_slider) и передвижении мыши.
2) Создаст переменную translation, в которую передаются Х координаты мыши.
3) Перемещает Sphere/sphere_slider за мышкой.
ВНИМАНИЕ. Координата, по которой будет двигаться шар, у всех разная (легче всего повернуть его координатой Х / красной стрелкой по траектории движения)
4) Строка transform.position.x выдаст нам координату, на которой в данный момент находится объект.
ВНИМАНИЕ. Координата всё так же разная (transform.position.x || y || z; либо поворот Sphere/sphere_slider).

По выводу в консоль определим минимум и максимум, на который будет двигаться будущий слайдер.

Как только вы засечёте нужные координаты, обновляйте старый скрипт.

Function OnMouseDrag () { var translation: float = Input.GetAxis ("Mouse X") * 0.18; //1 if (transform.position.x < min || transform.position.x > < min) { transform.Translate(0.1, 0, 0); } //3 if (transform.position.x > max) { transform.Translate(-0.1, 0, 0); } //3 } else { transform.Translate(translation, 0, 0); } }

1) Всё так же ловим координату Х, но с коэффициентом, который понижаетувеличивает скорость передвижения шара.
ВНИМАНИЕ! Так же уникален для каждого пользователя.
2) Максимум и минимум движения слайдера по осям.
3) При выходе за пределы координата немного убавляется (во избежание застревания слайдера на месте).
*меняем min и max на полученные ранее пределы.

Вот слайдер почти и готов. Осталось только научить его возвращать значение.

Var bullet: float = 0; //1 function OnMouseDrag () { var translation: float = Input.GetAxis ("Mouse X") * 0.18; if (transform.position.x < min || transform.position.x > max) { //2 if (transform.position.x < min) { transform.Translate(0.1, 0, 0); } //3 if (transform.position.x > max) { transform.Translate(-0.1, 0, 0); } //3 } else { transform.Translate(translation, 0, 0); } bullet = (transform.position.x - min)*250;//2 }

1) Создаст переменную bullet.
2) Записывает значения слайдера в переменную bullet.

Вот и всё. Теперь у вас есть более-менее функционирующее меню Unity 3D.
То что получилось у меня - тык .
Благодарю за прочитанный урок.

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

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

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

var isNewGameButton = false;

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

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

var isOptionsButton = false; var isQualityButtonFastest = false; var isQualityButtonFantastic = false; var isMainMenuButton = false; var isQuitButton = false;

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

function OnMouseEnter() {

function OnMouseExit() {

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

function OnMouseEnter() { renderer.material.color = Color.gray; }

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

function OnMouseExit() { renderer.material.color = Color.white; }

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

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

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

function OnMouseUp() {

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

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

function OnMouseUp() { if (isQuitButton) { Application.Quit(); }

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

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

else if (isQualityButtonFastest) { QualitySettings.currentLevel = QualityLevel.Fastest; }

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

else if (isQualityButtonFantastic) { QualitySettings.currentLevel = QualityLevel.Fantastic; }

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

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

else if (isNewGameButton) { Application.LoadLevel (1); }

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

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

var camera1:Camera; var camera2:Camera;

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

else if (isOptionsButton) { camera1.enabled = false; camera2.enabled = true; }

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

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

else if (isMainMenuButton) { Application.LoadLevel (0); }

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

23. Проверяем.

var isNewGameButton = false; var isOptionsButton = false; var isQualityButtonFastest = false; var isQualityButtonFantastic = false; var isMainMenuButton = false; var isQuitButton = false; var camera1:Camera; var camera2:Camera; function OnMouseEnter() { //color.X - any color that you like renderer.material.color = Color.gray; } function OnMouseExit() { //color.X - any color that you like renderer.material.color = Color.white; } function OnMouseUp() { if (isQuitButton) { Application.Quit(); } else if (isQualityButtonFastest) { QualitySettings.currentLevel = QualityLevel.Fastest; } else if (isQualityButtonFantastic) { QualitySettings.currentLevel = QualityLevel.Fantastic; } else if (isNewGameButton) { //it can be any level Application.LoadLevel (1); } else if (isOptionsButton) { camera1.enabled = false; camera2.enabled = true; } else if (isMainMenuButton) { //main menu level Application.LoadLevel (0); } }

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

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. Теперь посмотрим что получилось.

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

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

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

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

Итак, способы

ContextMenu

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

Using UnityEngine; public class MyClass: MonoBehaviour { void SayMessage() { Debug.Log ("I love you, DevTribe"); } }

В результате мы получим пункт меню в следующем месте:

При нажатии на данный пункт выполнится наш метод SayMessage() .

Однако, данный способ применяется в очень узком месте:

  • только в скриптах, наследуемых от класса Behaviour (всех сценариях и компонентах)

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

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

ContextMenuItem

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

Using UnityEngine; public class MyClass: MonoBehaviour { public string ff; void SayMessage() { Debug.Log("I love you, DevTribe"); } }

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

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

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

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

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

Лично по мне - данный способ весьма не очевиден, на практике я им ни разу и не пользовался. Однако раз уж статья про меню - странно было бы упустить этот способ, тем более что в документации нет примера на C# (хотя, он и очевиден, но принцип действия не очень).

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

MenuItem

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

Пример кода:

Using UnityEditor; using UnityEngine; public static class MyClass { static void SayMessage() { Debug.Log("I love you, DevTribe"); } }

Данный способ приведет к следующему результату:

Естественно, у данного способа есть свои ограничения, но они отличаются от тех, что вы прочли ранее:

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

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

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

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

Static bool CanBeShow() { return "ILove".Length == 5; } static void ShowMessage() { Debug.Log("I love you, DevTribe"); }

Опять же, про это почему то в документации особо-то и не расписывали.

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

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

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

Using UnityEditor; using UnityEngine; public static class MyClass { static void ShowMessage(MenuCommand command) { Debug.Log("I love you, DevTribe"); } }

Как вы могли заметить, я применил в функции параметр - он не обязателен, но возможен при таком раскладе. Данный параметр содержит в себе поле "context", содержащее ссылку на компонент.
У данного способа так же нет ограничений в виде конкретного типа - например в качестве пути вы можете указать CONTEXT/MonoBehaviour/ и прикрепить пункт сразу ко всем наследникам данного класса. Указывать можно любой тип, наследуемый от Component (включая сам тип Component ).
Помимо "CONTEXT/" вы так же можете использовать следующие "специальные пути":

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

GUI методы

Нельзя обойти стороной GUI методы, существующие для вызова попапов - однако среди стандартных это, зачастую, выборка значения.
Итак, попапы собственно есть следующие:

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

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

Особо за них говорить нечего, всё можно узнать в документации.

ShowAsDropDown

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

Public class AutocompleteMenu: EditorWindow { public int visibleCount = 6; public static void Open(Vector2 position, OnControlEventDelegate onControlEvent, params GUIContent variants) { var window = CreateInstance(); window.variants = variants; window.onControlEvent = onControlEvent; window.ShowAsDropDown(new Rect(position.x, position.y, 1, 1), new Vector2(200, 20*window.visibleCount)); } public delegate void OnControlEventDelegate(Event @event); public OnControlEventDelegate onControlEvent; }

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

Var screenMousePos = GUIUtility.GUIToScreenPoint(Event.current.mousePosition);

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

PopupWindow

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

DisplayPopupMenu

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

Public class PopupCallback { public static PopupCallback instance; public int controlId; public bool change; public delegate void SetValueDelegate(object data); public static void Init(int controlId) { instance = new PopupCallback { controlId = controlId }; } public static bool Its(int controlId) { return instance.w(x => x.change) && instance.controlId == controlId; } public object obj; public static EditorUtility.SelectMenuItemFunction SetValueStandart(int controlId) { Init(controlId); return instance._SetValue; } public static SetValueDelegate SetValue(int controlId) { Init(controlId); return instance._SetValue; } public void _SetValue(object userData, string options, int selected) { change = true; obj = selected; } public void _SetValue(object data) { change = true; obj = data; } public static object GetValue() { var result = instance.obj; instance = null; return result; } public static T GetValue() { var result = (T)instance.obj; instance = null; return result; } }

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

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

GenericMenu

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

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

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

PopupWindowWithoutFocus.cs

Using System; using System.Collections.Generic; using System.Reflection; using UnityEditor; using UnityEngine; internal class PopupWindowWithoutFocus: EditorWindow { // Fields private Rect m_ActivatorRect; private const float m_BorderWidth = 1f; private Vector2 m_LastWantedSize = Vector2.zero; private PopupLocationHelper.PopupLocation m_LocationPriorityOrder; private PopupWindowContent m_WindowContent; private static Rect s_LastActivatorRect; private static double s_LastClosedTime; private static PopupWindowWithoutFocus s_PopupWindowWithoutFocus; // Methods private PopupWindowWithoutFocus() { hideFlags = HideFlags.DontSave; } private void FitWindowToContent() { Vector2 windowSize = m_WindowContent.GetWindowSize(); if (m_LastWantedSize != windowSize) { m_LastWantedSize = windowSize; Vector2 minSize = windowSize + new Vector2(2f * m_BorderWidth, 2f * m_BorderWidth); Rect rect = PopupLocationHelper.GetDropDownRect(m_ActivatorRect, minSize, minSize, null, m_LocationPriorityOrder); var baseType = GetType().BaseType; if (baseType != null) { var fieldInfo = baseType.GetField("m_Pos", BindingFlags.Instance | BindingFlags.NonPublic); if (fieldInfo != null) fieldInfo.SetValue(this, rect); } var vector3 = new Vector2(rect.width, rect.height); maxSize = vector3; base.minSize = vector3; } } public static void Hide() { if (s_PopupWindowWithoutFocus != null) { s_PopupWindowWithoutFocus.Close(); } } private void Init(Rect activatorRect, PopupWindowContent windowContent, PopupLocationHelper.PopupLocation locationPriorityOrder) { m_WindowContent = windowContent; m_WindowContent.GetType() .GetProperty("editorWindow") .GetSetMethod(true) .Invoke(m_WindowContent, new object {this}); m_ActivatorRect = GUIToScreenRect(activatorRect); m_LastWantedSize = windowContent.GetWindowSize(); m_LocationPriorityOrder = locationPriorityOrder; Vector2 minSize = windowContent.GetWindowSize() + new Vector2(m_BorderWidth * 2f, m_BorderWidth * 2f); position = PopupLocationHelper.GetDropDownRect(m_ActivatorRect, minSize, minSize, null, m_LocationPriorityOrder); ShowPopup(); Repaint(); } internal static Rect GUIToScreenRect(Rect guiRect) { Vector2 vector = GUIUtility.GUIToScreenPoint(new Vector2(guiRect.x, guiRect.y)); guiRect.x = vector.x; guiRect.y = vector.y; return guiRect; } public static bool IsVisible() { return (s_PopupWindowWithoutFocus != null); } private void OnDisable() { s_LastClosedTime = EditorApplication.timeSinceStartup; if (m_WindowContent != null) { m_WindowContent.OnClose(); } s_PopupWindowWithoutFocus = null; } private void OnEnable() { s_PopupWindowWithoutFocus = this; } private static bool OnGlobalMouseOrKeyEvent(EventType type, KeyCode keyCode, Vector2 mousePosition) { if (s_PopupWindowWithoutFocus != null) { if ((type == EventType.KeyDown) && (keyCode == KeyCode.Escape)) { s_PopupWindowWithoutFocus.Close(); return true; } if ((type == EventType.MouseDown) && !s_PopupWindowWithoutFocus.position.Contains(mousePosition)) { s_PopupWindowWithoutFocus.Close(); return true; } } return false; } internal void OnGUI() { FitWindowToContent(); var rect = new Rect(m_BorderWidth, m_BorderWidth, position.width - (2f * m_BorderWidth), position.height - (2f * m_BorderWidth)); m_WindowContent.OnGUI(rect); GUI.Label(new Rect(0f, 0f, position.width, position.height), GUIContent.none, "grey_border"); } private static bool ShouldShowWindow(Rect activatorRect) { if (((EditorApplication.timeSinceStartup - s_LastClosedTime) < 0.2) && !(activatorRect != s_LastActivatorRect)) { return false; } s_LastActivatorRect = activatorRect; return true; } public static void Show(Rect activatorRect, PopupWindowContent windowContent) { Show(activatorRect, windowContent, null); } internal static void Show(Rect activatorRect, PopupWindowContent windowContent, PopupLocationHelper.PopupLocation locationPriorityOrder) { if (ShouldShowWindow(activatorRect)) { if (s_PopupWindowWithoutFocus == null) { s_PopupWindowWithoutFocus = CreateInstance(); } s_PopupWindowWithoutFocus.Init(activatorRect, windowContent, locationPriorityOrder); } } } internal static class PopupLocationHelper { private static Type _typePopupLocationHelper; private static Type typePopupLocationHelper { get { if (_typePopupLocationHelper == null) _typePopupLocationHelper = typeof(EditorGUI).Assembly.GetType("UnityEditor.PopupLocationHelper"); return _typePopupLocationHelper; } } private static Type _typeContainerWindow; private static Type typeContainerWindow { get { if (_typeContainerWindow == null) _typeContainerWindow = typeof(EditorGUI).Assembly.GetType("UnityEditor.ContainerWindow"); return _typeContainerWindow; } } private static MethodInfo _methodFitWindowRectToScreen; private static MethodInfo methodFitWindowRectToScreen { get { if (_methodFitWindowRectToScreen == null) _methodFitWindowRectToScreen = typeContainerWindow.GetMethod("FitWindowRectToScreen", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); return _methodFitWindowRectToScreen; } } private static MethodInfo _methodFitRectToScreen; private static MethodInfo methodFitRectToScreen { get { if (_methodFitRectToScreen == null) _methodFitRectToScreen = typeContainerWindow.GetMethod("FitRectToScreen", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); return _methodFitRectToScreen; } } // Methods private static Rect FitRect(Rect rect, object popupContainerWindow) { if (popupContainerWindow != null) { return (Rect)methodFitWindowRectToScreen.Invoke(popupContainerWindow, new object {rect, true, true}); } return (Rect) methodFitRectToScreen.Invoke(null, new object {rect, true, true}); } public static Rect GetDropDownRect(Rect buttonRect, Vector2 minSize, Vector2 maxSize, object popupContainerWindow) { return GetDropDownRect(buttonRect, minSize, maxSize, popupContainerWindow, null); } public static Rect GetDropDownRect(Rect buttonRect, Vector2 minSize, Vector2 maxSize, object popupContainerWindow, PopupLocation locationPriorityOrder) { if (locationPriorityOrder == null) { PopupLocation locationArray1 = new PopupLocation; locationArray1 = PopupLocation.Above; locationPriorityOrder = locationArray1; } List rects = new List(); PopupLocation locationArray = locationPriorityOrder; for (int i = 0; i < locationArray.Length; i++) { Rect rect; switch (locationArray[i]) { case PopupLocation.Below: if (!PopupBelow(buttonRect, minSize, maxSize, popupContainerWindow, out rect)) { break; } return rect; case PopupLocation.Above: if (!PopupAbove(buttonRect, minSize, maxSize, popupContainerWindow, out rect)) { goto Label_0079; } return rect; case PopupLocation.Left: if (!PopupLeft(buttonRect, minSize, maxSize, popupContainerWindow, out rect)) { goto Label_0099; } return rect; case PopupLocation.Right: if (!PopupRight(buttonRect, minSize, maxSize, popupContainerWindow, out rect)) { goto Label_00B9; } return rect; default: { continue; } } rects.Add(rect); continue; Label_0079: rects.Add(rect); continue; Label_0099: rects.Add(rect); continue; Label_00B9: rects.Add(rect); } return GetLargestRect(rects); } private static Rect GetLargestRect(IEnumerable rects) { var rect = new Rect(); foreach (Rect rect2 in rects) { if ((rect2.height * rect2.width) > (rect.height * rect.width)) { rect = rect2; } } return rect; } private static bool PopupAbove(Rect buttonRect, Vector2 minSize, Vector2 maxSize, object popupContainerWindow, out Rect resultRect) { Rect rect = new Rect(buttonRect.x, buttonRect.y - maxSize.y, maxSize.x, maxSize.y); float num = 0f; rect.yMin -= num; rect = FitRect(rect, popupContainerWindow); float a = Mathf.Max((buttonRect.y - rect.y) - num, 0f); if (a >= minSize.y) { float height = Mathf.Min(a, maxSize.y); resultRect = new Rect(rect.x, buttonRect.y - height, rect.width, height); return true; } resultRect = new Rect(rect.x, buttonRect.y - a, rect.width, a); return false; } private static bool PopupBelow(Rect buttonRect, Vector2 minSize, Vector2 maxSize, object popupContainerWindow, out Rect resultRect) { var rect = new Rect(buttonRect.x, buttonRect.yMax, maxSize.x, maxSize.y) { height = maxSize.y + k_SpaceFromBottom }; rect = FitRect(rect, popupContainerWindow); float a = Mathf.Max((rect.yMax - buttonRect.yMax) - k_SpaceFromBottom, 0f); if (a >= minSize.y) { float height = Mathf.Min(a, maxSize.y); resultRect = new Rect(rect.x, buttonRect.yMax, rect.width, height); return true; } resultRect = new Rect(rect.x, buttonRect.yMax, rect.width, a); return false; } private static bool PopupLeft(Rect buttonRect, Vector2 minSize, Vector2 maxSize, object popupContainerWindow, out Rect resultRect) { var rect = new Rect(buttonRect.x - maxSize.x, buttonRect.y, maxSize.x, maxSize.y); const float num = 0f; rect.xMin -= num; rect.height += k_SpaceFromBottom; rect = FitRect(rect, popupContainerWindow); float a = Mathf.Max((buttonRect.x - rect.x) - num, 0f); float width = Mathf.Min(a, maxSize.x); resultRect = new Rect(rect.x, rect.y, width, rect.height - k_SpaceFromBottom); return (a >= minSize.x); } private static bool PopupRight(Rect buttonRect, Vector2 minSize, Vector2 maxSize, object popupContainerWindow, out Rect resultRect) { var rect = new Rect(buttonRect.xMax, buttonRect.y, maxSize.x, maxSize.y); const float num = 0f; rect.xMax += num; rect.height += k_SpaceFromBottom; rect = FitRect(rect, popupContainerWindow); float a = Mathf.Max((rect.xMax - buttonRect.xMax) - num, 0f); float width = Mathf.Min(a, maxSize.x); resultRect = new Rect(rect.x, rect.y, width, rect.height - k_SpaceFromBottom); return (a >= minSize.x); } // Properties private static float k_SpaceFromBottom { get { if (Application.platform == RuntimePlatform.OSXEditor) { return 10f; } return 0f; } } // Nested Types public enum PopupLocation { Below, Above, Left, Right } }

В качестве примера используйте этот код:

Using UnityEditor; using UnityEngine; public class ExampleUnfocusWindow: PopupWindowContent { public static void Open() { Open(new Vector2()); } public static void Open(Vector2 position) { var locationPriorityOrder = new PopupLocationHelper.PopupLocation; locationPriorityOrder = PopupLocationHelper.PopupLocation.Left; locationPriorityOrder = PopupLocationHelper.PopupLocation.Right; PopupWindowWithoutFocus.Show(new Rect(position.x, position.y, 6, 6), new ExampleUnfocusWindow(), locationPriorityOrder); } public override Vector2 GetWindowSize() { return new Vector2(200, 200); } public override void OnOpen() { base.OnOpen(); } public override void OnClose() { base.OnClose(); } public override void OnGUI(Rect rect) { GUI.Box(rect, "Hello world"); } }

Настройка строки меню

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

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

% - ctrl
# - shift
& - alt
_ - отсутствие спецсимвола

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

Так же пункты можно комбинировать, чтобы добиться сочетания аля "ctrl+shift".

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

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

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

Заготовки using UnityEditor; using UnityEngine; public class Popup: EditorWindow { public static void Open(Vector2 size) { var window = CreateInstance(); var screenMousePos = GUIUtility.GUIToScreenPoint(Event.current.mousePosition); window.ShowAsDropDown(new Rect(screenMousePos.x, screenMousePos.y, 1, 1), size); } }

Этот урок даст всем желающим основное понимание того, как будет работать GUI в Unity на примере создания простого меню. Это очень простой урок, не требующий специальных навыков, кроме начального знания Unity Java Script (или, в целом, вообще какого-либо скрипта). Приступим же.

Начнём с создания новой сцены с игрой. Эта сцена будет названа нами «Level_1». Сцену для меню мы назовём «Menu». В дальнейшем, перемещение между этими сценами будет возможно с помощью команды.
Application.LoadLevel (Номер сцены);
Однако, к этой команде мы вернёмся позже. В данный момент нас больше должно интересовать «Номер сцены». Проставить и посмотреть номер сцены возможно с помощью окна Build Settings (File

%D0%92%20%D1%8D%D1%82%D0%BE%D0%BC%20%D1%83%D1%80%D0%BE%D0%BA%D0%B5%20%D1%8F%20%D1%80%D0%B0%D1%81%D1%81%D0%BA%D0%B0%D0%B6%D1%83,%20%D0%BA%D0%B0%D0%BA%20%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0%D0%B5%D1%82%20GUI%20%D0%B2%20Unity,%20%D0%B8%20%D0%BF%D0%BE%20%D1%85%D0%BE%D0%B4%D1%83%20%D0%B4%D0%B5%D0%BB%D0%B0%20%D0%B1%D1%83%D0%B4%D1%83%20%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%B2%D0%B0%D1%82%D1%8C%20%D0%BF%D1%80%D0%BE%D1%81%D1%82%D0%BE%D0%B5%20%D0%BC%D0%B5%D0%BD%D1%8E.%20%D0%AD%D1%82%D0%BE%D1%82%20%D1%83%D1%80%D0%BE%D0%BA%20%D0%B4%D0%BB%D1%8F%20%D0%BD%D0%BE%D0%B2%D0%B8%D1%87%D0%BA%D0%BE%D0%B2,%20%D0%BD%D0%BE%20%D0%B6%D0%B5%D0%BB%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%20%D1%81%20%D0%BD%D0%B0%D1%87%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%BC%20%D0%B7%D0%BD%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC%20Unity%20Java%20Script,%20%D0%B4%D0%B0%20%D0%B8%D0%BB%D0%B8%20%D0%B2%D0%BE%D0%BE%D0%B1%D1%89%D0%B5%20%D0%BA%D0%B0%D0%BA%D0%BE%D0%B3%D0%BE%20%D0%BB%D0%B8%D0%B1%D0%BE%20%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%B0.%20%D0%9D%D1%83%20%D1%87%D1%82%D0%BE,%20%D0%BD%D0%B0%D1%87%D0%BD%D1%91%D0%BC.

%D0%90%20%D0%BD%D0%B0%D1%87%D0%B0%D1%82%D1%8C%20%D1%8F%20%D0%B4%D1%83%D0%BC%D0%B0%D1%8E%20%D1%81%20%D1%82%D0%BE%D0%B3%D0%BE,%20%D1%87%D1%82%D0%BE%20%D0%BC%D1%8B%20%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%B4%D0%B8%D0%BC%20%D0%BD%D0%BE%D0%B2%D1%83%D1%8E%20%D1%81%D1%86%D0%B5%D0%BD%D1%83.%20%D0%9D%D0%B0%D0%B7%D0%BE%D0%B2%D1%91%D0%BC%20%D1%81%D1%86%D0%B5%D0%BD%D1%83%20%D1%81%20%D0%B8%D0%B3%D1%80%D0%BE%D0%B9%20" level_1="">

Application.LoadLevel (Номер сцены);

К команде мы вернёмся чуть позже. Сейчас нас больше интересует "Номер сцены". Как его поставить? Где его посмотреть?
Поставить и посмотреть номер можно в окошке Build settings (File/Build settings...):
/img/origs/647.jpg" alt="Фото" />
Номер обведён красным.
Для того, чтобы добавить сцену, необходимо нажать на Add Current (обведено зелёным). В какой сцене вы находитесь, такая сцена и добавится. При запуске игры, запускается сцена "0", то есть "Menu".

Сцена создана. Теперь создадим скрипт, например "menu.js" и повесим его на камеру. Далее будем работать практически только с этим скриптом. Подготовка завершена.

Практически самый простой GUI объект - Окно. Давайте рассмотрим комманду в скрипте по подробнее.

GUI.Box (Rect (A, B, C, D), "Текст");

A - количество пикселей от левого края экрана к левому краю окна.
B - количество пикселей от верхнего края экрана к верхнему краю окна.
C - ширина окна.
D - высота окна.
А далее следует название окна. Эта надпись будет висеть сверху окна.
Давайте создадим наше первое окно:

Function OnGUI () { GUI.Box (Rect (5,5,200,200), "Текст"); }

Номер обведён красным цветом.
Если Вы хотите добавить сцену, то Вам необходимо будет нажать на кнопку AddCurrent, которая обведена зелёным. Добавится та самая сцена, в которой на данный момент Вы находитесь. При запуске игровой программы, будет запущена сцена «0», значит «Menu».
Теперь сцена создана. Далее следует создать скрипт, назовём его «menu.js» и повесить его на камеру. Дальнейшая работа пойдёт в основном с этим скриптом. Необходимая подготовка завершена.
Самый простой GUI объект – это Окно. Команда в скрипте выглядит так:
GUI.Box (Rect (A, B, C, D), "Текст");
, где А – это количество пикселей от левого края экрана к левому краю окна,
В – то же, от правого края экрана к верхнему краю окна,
С – это ширина окна,
D – это высота окна,
Далее следует название окна. Надпись будет располагаться сверху Вашего окна.
Теперь давайте создадим первое окно, в сцене оно будет выглядеть таким образом:


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

Screen.width Screen.height

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

Function OnGUI () { GUI.Box (Rect (5,5,50,50), "1"); //верхний левый угол GUI.Box (Rect (Screen.width - 55,5,50,50), "2"); // верхний правй угол GUI.Box (Rect (5,Screen.height - 55,50,50), "3"); // нижний левый угол GUI.Box (Rect (Screen.width - 55,Screen.height - 55,50,50), "4"); // нижний правый угол GUI.Box (Rect (Screen.width/2 - 25,Screen.height/2 - 25,50,50), "5"); // центр }


В зависимости от разрешения экрана теперь можно увеличивать и уменьшать окна.

По мнению многих пользователей Unity самый используемый GUI объект – это кнопка. Написать скрипт для кнопки очень просто.

If (тут) { }

Сейчас будет создана кнопка, которая сможет отправить нас в сцену с игрой:

Function OnGUI () { GUI.Box (Rect (Screen.width/2 - 100,Screen.height/2 - 100,200,180), "Главное меню"); // окно if (GUI.Button (Rect (Screen.width/2 - 90,Screen.height/2 - 80,180,30), "Играть")) { // наша кнопка Application.LoadLevel (1); // загружаем уровень Level_1 } }

При нажатии на кнопку «Играть», будет загружена первая цена (Level_1).
Также стоит создать ещё пару функциональных кнопок и кнопку «выход».

Function OnGUI () { GUI.Box (Rect (Screen.width/2 - 100,Screen.height/2 - 100,200,180), "Главное меню"); if (GUI.Button (Rect (Screen.width/2 - 90,Screen.height/2 - 80,180,30), "Играть")) { Application.LoadLevel (1); } if (GUI.Button (Rect (Screen.width/2 - 90,Screen.height/2 - 40,180,30), "Настройки")) { } if (GUI.Button (Rect (Screen.width/2 - 90,Screen.height/2 - 0,180,30), "Помощь")) { } if (GUI.Button (Rect (Screen.width/2 - 90,Screen.height/2 + 40,180,30), "Выход")) { Application.Quit(); // выход } }


Команда "Application.Quit();" она не будет работать в редакторе.
Также стоит настроить дополнительные окна для производства настроек и помощи. Для их создания будем использовать переменную, к примеру «window».
Добавляем ещё пару окон.

Var window = 0; function OnGUI () { if (window == 0) { // теперь главное меню активировано при window = 0 GUI.Box (Rect (Screen.width/2 - 100,Screen.height/2 - 100,200,180), "Главное меню"); if (GUI.Button (Rect (Screen.width/2 - 90,Screen.height/2 - 80,180,30), "Играть")) { Application.LoadLevel (1); } if (GUI.Button (Rect (Screen.width/2 - 90,Screen.height/2 - 40,180,30), "Настройки")) { window = 1; // активируем окно "настройки" } if (GUI.Button (Rect (Screen.width/2 - 90,Screen.height/2 - 0,180,30), "Помощь")) { window = 2; //активируем окно "помощь" } if (GUI.Button (Rect (Screen.width/2 - 90,Screen.height/2 + 40,180,30), "Выход")) { Application.Quit(); } } if (window == 1) { // наши настройки GUI.Box (Rect (Screen.width/2 - 100,Screen.height/2 - 100,200,180), "Настройки"); if (GUI.Button (Rect (Screen.width/2 - 90,Screen.height/2 + 40,180,30), "Назад")) { window = 0; } } if (window == 2) { // наша помощь GUI.Box (Rect (Screen.width/2 - 100,Screen.height/2 - 100,200,180), "Помощь"); if (GUI.Button (Rect (Screen.width/2 - 90,Screen.height/2 + 40,180,30), "Назад")) { window = 0; } } }

Подключаем клавиатурные кнопки к управлению:

If (GUI.Button (Rect (Screen.width/2 - 90,Screen.height/2 + 40,180,30), "Назад") || Input.GetKey ("escape")) { window = 0; }

Большую важность в создании меню имеет текст. Можно сказать, что скрипт текста совсем не отличается от скрипта окна. Раздел «Помощь» требует наполнения:

If (window == 2) { GUI.Box (Rect (Screen.width/2 - 100,Screen.height/2 - 100,200,180), "Помощь"); GUI.Label (Rect (Screen.width/2 - 100,Screen.height/2 - 80,200,140), "Здесь должна быть помощь "); // текст if (GUI.Button (Rect (Screen.width/2 - 90,Screen.height/2 + 40,180,30), "Назад") || Input.GetKey ("escape")) { window = 0; } }

Теперь в разделе «Помощь» есть текст.
Также к этому тексту Вы можете применить стиль. Для этого его нужно создать.

Var style: GUIStyle;

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

Красным подчёркнуты часто используемые опции:
1 - Цвет текста.
2 - Шрифт.
3 - Якорь.
4 - Размер.
5 - Жирный, Курсив, Жирный курсив.

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

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

Var toggle: boolean = false; toggle = GUI.Toggle (Rect (20, 20, 100, 20), toggle, "Тумблер");

Тумблер применяется для переменных типа boolean, которые могут находиться только в двух значениях true и false.

Добавляем в раздел «Настройки» дополнительное окно:

If (window == 1) { GUI.Box (Rect (Screen.width/2 - 100,Screen.height/2 - 100,200,180), "Настройки"); toggle = GUI.Toggle (Rect (Screen.width/2 - 90,Screen.height/2 - 70, 80, 20), toggle, "Тумблер"); // наш тумблер if (toggle == true) { GUI.Box (Rect (Screen.width/2 - 130,Screen.height/2 - 100,20,20), ""); // левое окошко GUI.Box (Rect (Screen.width/2 + 130,Screen.height/2 - 100,20,20), ""); // правое окошко } if (GUI.Button (Rect (Screen.width/2 - 90,Screen.height/2 + 40,180,30), "Назад") || Input.GetKey ("escape")) { window = 0; } }

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

Также стоит познакомиться с такой полезной функцией, как ползунок. При помощи ползунка Вы сможете изменить громкость и т.д. По написанию, ползунок практически не отличается от тумблера, только используется другой переменной: float, int.

Var sider: int = 0; sider = GUI.HorizontalSlider (Rect (25, 25, 100, 30), sider, A, B);

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

If (window == 1) { GUI.Box (Rect (Screen.width/2 - 100,Screen.height/2 - 100,200,180), "Настройки"); toggle = GUI.Toggle (Rect (Screen.width/2 - 90,Screen.height/2 - 70, 180, 20), toggle, "Тумблер"); if (toggle == true) { sider = GUI.HorizontalSlider (Rect (Screen.width/2 - 90,Screen.height/2 - 40, 180, 20), sider, 0, 160); //ползунок GUI.Box (Rect (Screen.width/2 - 130,Screen.height/2 - 100,20,20 + sider), ""); GUI.Box (Rect (Screen.width/2 + 110,Screen.height/2 - 100,20,20 + sider), ""); } if (GUI.Button (Rect (Screen.width/2 - 90,Screen.height/2 + 40,180,30), "Назад") || Input.GetKey ("escape")) { window = 0; } }


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

If (window == 1) { GUI.Box (Rect (Screen.width/2 - 100,Screen.height/2 - 100,200,180), "Настройки"); toggle = GUI.Toggle (Rect (Screen.width/2 - 90,Screen.height/2 - 70, 180, 20), toggle, "Тумблер"); if (toggle == true) { sider = GUI.HorizontalSlider (Rect (Screen.width/2 - 90,Screen.height/2 - 40, 180, 20), sider, 0, 160); //горизонтальный sider1 = GUI.VerticalSlider (Rect (Screen.width/2 - 90,Screen.height/2 - 20, 20, 50), sider1, 0, 160); //вертикальный GUI.Box (Rect (Screen.width/2 - 130 - sider,Screen.height/2 - 100,20 + sider,20 + sider1), ""); GUI.Box (Rect (Screen.width/2 + 110,Screen.height/2 - 100,20 + sider,20 + sider1), ""); } if (GUI.Button (Rect (Screen.width/2 - 90,Screen.height/2 + 40,180,30), "Назад") || Input.GetKey ("escape")) { window = 0; } }

Стоит не забыть добавить все необходимые переменные?

Var toggle = false; //тумблер var sider: int = 0; // горизонтальный ползунок var sider1: int = 0; // вертикальный ползунок

Разрешение экрана
Разрешение экрана меняется при помощи команды:

Screen.SetResolution (A, B, C);

Где A - ширина.
B - высота.
С - полноэкранный или оконный.
Пример:

Screen.SetResolution (1280, 1024, true);

Громкость звука
Для изменения громкости применяется команда:

Audio.volume = переменная (желательно float);

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

, Как добавить врагов , Контроллер врагов .
5. Простейшая игра (3) : Система частиц , Добавление звуков , Создание текста на экране , Публикация .
6. Трехмерный платформер. (в процессе создания)

2. Интерфейс программы

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

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

(Если эта тема не интересна, вы замечаете, что начинаете быстро утомляться при чтении, то можете пропустить целый раздел «Интерфейс», и сразу перейти к разделу «3. Создание простейшей игры ». Многим трудно даётся теория, гораздо эффективнее получается обучение сразу на практике. В 3-ей главе вы сразу же будете видеть результат своей работы, и обучение начнет приносить положительные эмоции. Но чтобы всё осознать, вам придётся периодически возвращаться ко второму разделу).

Ну а мы продолжаем.

Общий вид

Рабочее окно Unity разбито на 6 взаимосвязанных областей. (По умолчанию в Unity включается вид «Default» или «Tall». Если у вас другой вид рабочего окна, то можете переключить его в меню «Layout» в верхнем правом углу окна).



Перечень всех рабочих областей окна Unity:


1. Main menu
(Главное меню)
Строчка текста сверху, где располагаются все команды , доступные в программе. Многие команды продублированы кнопками и меню в рабочих областях, поэтому главное меню не обязательно использовать.
2. Project View
(Обзор проекта)
Список, в котором показаны все используемые файлы в игре: файл сцены, файл программного кода, графические и аудиофайлы.
3. Hierarchy
(Иерархия)
Список, где перечислены все объекты , добавленные на сцену. Здесь можно работать с объектами, копировать их, переименовывать, удалять.
4. Scene
(Сцена)
Область, где отображается игровой мир или игровая сцена. Здесь мы можем добавлять новые объекты, перетаскивать их, менять вид.
5. Game
(Игра)
Область предпросмотра, где видно, как сцена будет выглядеть в игре . Здесь можно настраивать различные настройки экрана и видеорежима.
6. Inspector
(Инспектор)
Список, состоящий из нескольких различных по виду разделов. Показывает все свойства выбранного объекта : размеры, модели, текстуры, скрипты.

Подробнее о каждой области будет описано ниже.

2.1. Главное меню (Main menu)


Главное меню Unity3D стандартно располагается в верхнем левом углу окна. Здесь есть обычные команды, типа «сохранить», «загрузить», но есть и уникальные для этой программы строчки.


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

2.2. Обзор проекта (Project View)


Каждый проект содержит папку «Assets». Содержимое этой папки представлено в области «Project View». Это ресурсы игры: файлы-скрипты, 3D-модели, текстуры, аудиофайлы, префабы (объекты, которые можно клонировать).


(Перемещение файлов можно выполнять в папке и через стандартный Проводник Windows, щёлкнув правой кнопкой мыши по нему и выбрав «Reveal in Exlorer». Но при перемещении ресурсов в Проводнике Windows будут потеряны все ссылки между объектами. Лучше всегда перемещать файлы внутри проекта только в «Project View»).


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

Перетащить файл из Проводника в Project View.

Команда «Assets» > «Import New Assets».


Проекты игры состоят из одного или нескольких файлов сцены. Каждая отдельная сцена – это отдельный уровень игры. Сцены так же хранятся в папке «Assets» и отображаются в «Project View».


Некоторые игровые ресурсы не являются файлами, и создаются непосредственно в Unity. Для создания ресурса используется выпадающее меню «Create» в «Project View» или (ПКМ) > «Create».

Меню «Create» позволяет добавлять в проект скрипты, префабы, папки и прочее. Любой ресурс или папку можно переименовать, нажав «F2» или сделав два клика по имени. Если зажать «Alt», то при раскрытии директории будут раскрыты и все поддиректории.

2.3. Иерархия (Hierarchy)


Иерархия содержит все объекты (GameObject) открытой сцены. Объекты, добавляемые в сцену или удаляемые из неё, отображаются или наоборот перестают отображаться в Hierarchy.


Наследование (Parenting). В иерархии Unity объектам можно задавать наследование. Любой объект может быть дочерним по отношению к другому. Дочерний объект будет двигаться и вращаться вместе с родителем. Для создания дочерней связи достаточно перетащить объект на «родителя» в Hierarchy.

2.4. Сцена (Scene View)


Игровая сцена используется для расстановки объектов на уровне (окружение, персонажи, камеры, системы частиц и прочее). Сцена может являться игровым уровнем, главным меню, заставкой.


В 2D режиме управление интуитивно понятно. В 3D режиме есть множество приёмов для перемещения по сцене:

* Зажатый ПКМ активирует режим свободного полёта.

* Перемещаться можно клавишами WASD на манер игры в жанре FPS.

* Выберите объект в списке иерархии и нажмите «F». Вид сцены будет центрирован и масштабирован по выбранному объекту.

* При зажатом Alt, ЛКМ будет крутить камеру вокруг текущей точки опоры.

* При зажатом Alt, СКМ будет перемещать камеру.

* При зажатом Alt, ПКМ будет масштабировать вид сцены.

* Альтернативный режим перемещений - клавиша Q.

2.5. Игровой вид (Game View) Сайт сайт


Game View - предпросмотр игры (рендер из игровой камеры).

В расположении окон по умолчанию «Игровой вид» отсутствует, для его включения нужно выбрать вкладку «Game» над игровой сценой. В других режима окон «Игровой вид» занимает своё отдельное место.


Три кнопки в верхней части окна Юнити отвечают за управление предпросмотром игры: «Play», «Pause» и «Step». (Все изменения, произведённые во время предпросмотра, сбрасываются при выходе из него. Исключением являются изменения в префабах).


Game View Control Bar

Первое выпадающее меню в «Game View» - это контроль пропорций изображения (Aspect Drop-down). На некоторых дисплеях это соотношение отличается от стандартного 4:3 (например, на широкоформатных мониторах - 16:10).

Кнопка «Gizmos» включает отображение контейнеров гизмо в «Game View».

Последняя кнопка - «Stats». Она показывает статистку рендеринга (Rendering Statistics), полезную при оптимизации.

2.6. Инспектор (Inspector)


GameObject – это любой объект в игре. Следует понимать, что сам по себе объект ничего не делает. Ему требуются специальные свойства, прежде чем он станет персонажем, окружением или визуальным эффектом.

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


Перечень объектов содержится в области «Hierarchy». Если в списке иерархии мы выберем любой объект, то в области «Inspector» (Инспектор) отобразятся все свойства этого конкретного объекта. Здесь же можно редактировать все эти свойства или добавлять новые.


Объект может содержать в себе такие типы компонентов: расположение в пространстве (Transform), меши (meshes), скрипты (scripts), звуки, ИС (Lights) и другие элементы.

Status Bar

Строка состояния (Status Bar) расположена в нижней части окна редактора. Она отображает ошибки компиляции и логи дебага. Если возникают проблемы с игрой, стоит заглянуть в строку состояния. Двойное нажатие на неё вызовет окно консоли (Console), в котором отображаются все ошибки.

2.7. Настройка рабочего окна

Можно настраивать расположение (Layout) секторов, перетаскивая их за закладки. Если перетащить закладку в область закладок уже существующего окна, то она будет добавлена к присутствующим там закладкам. Также можно прикрепить сектор к краю экрана или краю другого сектора.

Сектор может быть пристыкован к одной из сторон существующего окна.

Закладки могут открепляться от главного окна редактора и включаться в состав плавающего окна редактора. Плавающее окно может содержать сектора и закладки так же, как и главное окно.

Плавающее окно редактор похоже на главное окно, но не имеет панели инструментов (Toolbar).

Когда расположение секторов задано, его можно сохранить и загрузить в нужный момент через выпадающее меню Layout (Save и Load).

Полностью настроенное расположение секторов.

В любое время можно правым кликом по закладке вызвать меню с дополнительными возможностями, такими как максимизация сектора или добавление новых закладок.

Достижение "Почётный читатель сайт"
Понравилась статья? В благодарность можно поставить лайк через любую социальную сеть. Для вас это - один клик, для нас - очередной шаг вверх в рейтинге игровых сайтов.
Достижение "Почётный спонсор сайт"
Для особо щедрых есть возможность перевести деньги на счет сайта. В этом случае вы можете повлиять на выбор новой темы для статьи или прохождения.
money.yandex.ru/to/410011922382680