Делаем простую игру с кнопками, ящиками и дверями на Unity. Как создать внутриигровое меню в Unity

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

Для самых нетерпеливых по ссылкам ниже находится готовый прототип.
Онлайн версия
Скомпилированная версия для Windows [Зеркало ] ~7.5 МБ

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

Подразумевается, что вы уже успели скачать Unity и поигрались немного в редакторе. Нет? Сейчас самое время, я подожду.

Грубый набросок

Я соврал, я не буду ждать. Создаём пустой проект без лишних пакетов и выбираем схему расположения окошек на свой вкус, я буду использовать Tall. Добавляем в иерархию сферу, перетаскиваем на неё главную камеру. Теперь камера будет следовать за нашей сферой, если она вдруг захочет погулять. Переименовываем сферу в «Player», перетаскиваем в Project, теперь у нас есть prefab , который мы можем использовать в любых новых сценах, если таковые будут. Не забывайте проверять координаты префабов при создании и использовании, если мы хотим делать игрушку в двух измерениях, то третья ось должна быть выставлена в ноль для всех взаимодействующих объектов.

Теперь добавим источник света, лезем в меню GameObject -> Create Other -> Directional light . Его координаты не имеют значения, он будет освещать наши объекты одинаково из любого места. Однако, имеет смысл поднять его немного над сценой, чтобы не мешался при выделении объектов, поэтому поставим ему координаты (0;0;-10). К слову о сцене, ось X у нас будет расти слева направо, Y - снизу вверх, а Z - от зрителя вглубь экрана. Покликайте по стрелочкам вокруг кубика в правом верхнем углу сцены и поверните её нужным образом.

Добавим на сцену кубик, назовём его «Wall» и перетащим в Assets. Одинокая кубическая стена рядом со сферическим колобком не очень-то впечатляет, да? Три поля Scale в инспекторе позволят нам вытягивать стенку, а комбинация клавиш Ctrl+D создаст её копию. В Unity есть много других полезных горячих клавиш , например зажатый Ctrl ограничивает перемещение объектов единичными интервалами, а клавиша V позволит тягать объект за вершины, и они будут липнуть к вершинам других объектов. Замечательно, не правда ли? И вы всё ещё пишете свой движок? Ну-ну.

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

Скрипты и физика

Нам нужно больше движения и цвета! Хотя, если ваше суровое детство было наполнено бетонными игрушками, то можно оставить всё как есть. Для всех остальных пришло время скриптов. Я буду приводить примеры на C#, но можно писать и на JS или Boo. На самом деле выбирать последние два смысла не имеет, они были добавлены в Юнити скорее как довесок, меньше поддерживаются, хуже расширяются и для них сложнее найти примеры. Особенно ужасен Boo, который по сути является unpythonic Python. Мерзость. Виват, питонисты!

Создаём C# Script, называем его «PlayerController», перетаскиваем на префаб Player и открываем с помощью Visual Studio любимого редактора. Сперва нужно потереть лишний мусор, оставим только нужное.

Using UnityEngine; public class PlayerController: MonoBehaviour { void Update() { } }
Функция Update вызывается в каждом кадре, что очень удобно для реализации движения, внутри неё мы и будем размещать код. Нажатия кнопок игроком можно получить с помощью класса Input . В комплекте с Unity идут замечательные настройки ввода, достаточно написать Input.GetAxis («Horizontal») и мы уже знаем нажал ли игрок на клавиатуре стрелку вправо или влево. Если у игрока подключён геймпад, то он может управлять и с него, нам даже не надо писать лишний код.


Такой нехитрой строчкой мы получаем информацию о действиях пользователя и создаём вектор движения. Для того, чтобы вектор куда-нибудь приложить, нам понадобится Rigidbody . Выделяем префаб Player и через меню Component -> Physics -> Rigidbody добавляем нужный компонент. Теперь мы можем на него ссылаться в нашём скрипте.

Rigidbody.AddForce(direction);
Функция AddForce имеет несколько интересных вариантов приложения силы , но для начала нам хватит и значений по умолчанию.

Готово! Сохраняем, жмём Play, тестируем.

Эээм, знаете, мне это напомнило тот момент из фильма Inception, где мужик бешено вращал глазами и катился кубарем то по стене, то по потолку. Наверное так он себя чувствовал.

Нам нужно запретить вращение и передвижение по оси Z. Выделяем префаб, смотрим на компонент Rigidbody и видим раздел Constraints. Оставляем неотмеченными только первые две галочки X и Y, остальные четыре включаем. Чуть выше снимаем галочку Use Gravity и прописываем Drag равный четырём (в разделе об эстетике я расскажу зачем это было сделано). Тестируем ещё раз.

Оно шевелится и не вертится! Ура! Но делает это слишком медленно. Добавим одну переменную к нашему скрипту и задействуем её в формуле нашего движения. Весь код будет в итоге выглядеть так:

Using UnityEngine; public class PlayerController: MonoBehaviour { public int acceleration; void Update() { var direction = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0); rigidbody.AddForce(direction * acceleration); } }
Заметили как в инспекторе появилось новое поле Acceleration у нашего скрипта? Эффектно, да? Вбиваем в поле тридцатку или что-нибудь на ваш вкус и проверяем в действии.

Материалы и коллайдеры

Пора уже сделать какую-нибудь кнопку, чтобы было на что нажимать. Дублируем префаб Wall и переименовываем его в «Button». В инспекторе у коллайдера ставим галочку Is Trigger. Это развоплотит нашу кнопку и заставит другие объекты проходить сквозь неё. Создаём скрипт «Button» и вешаем на кнопку.

У коллайдеров-триггеров есть события OnTriggerEnter и OnTriggerExit , которые вызываются всякий раз, когда что-то пересекает область триггера. На самом деле это не совсем так, ибо есть множество разных объектов и физический движок обрабатывает не все столкновения, подробнее читайте .

Для начала просто проверим как работают триггеры. Напишем что-нибудь в консольку Unity. Функция Debug.Log очень полезная, кроме текста она также умеет печатать разные игровые объекты.

Using UnityEngine; public class Button: MonoBehaviour { void OnTriggerEnter(Collider other) { Debug.Log("Hello"); } void OnTriggerExit(Collider other) { Debug.Log("Habr!"); } }
Кинули кнопку на сцену. Потестировали? Идём дальше. Было бы нагляднее, если бы наша кнопка меняла цвет при нажатии. Для цвета нам нужно прикрепить к кнопке материал . Create -> Material, назовём его «Button Mat» и накинем на кнопку. В свойствах материала выберем для Main Color зелёненький. Теперь в скрипте мы можем обращаться к цвету материала с помощью renderer.material.color и менять его как вздумается. Заставим кнопку краснеть от вхождения в неё нашего колобка. Как-то пошло вышло.

Using UnityEngine; public class Button: MonoBehaviour { void OnTriggerEnter(Collider other) { renderer.material.color = new Color(1, 0, 0); } void OnTriggerExit(Collider other) { renderer.material.color = new Color(0, 1, 0); } }
Класс Color может принимать кроме тройки RGB ещё и альфу, но у нас стоит обычный диффузный шейдер, поэтому она для нас не важна. Тестируем!

Если вы ещё не сделали это, то настала пора прибраться в нашем проекте, иначе мы заблудимся в мешанине префабов и скриптов. Например создадим папку «Levels» для хранения сцен, «Prefabs» для складирования заготовок, «Materials» для материалов и «Scripts» для скриптов, а потом рассортируем накопившееся богатство по папочкам.

Знаете, а ведь наша кнопка до сих пор не похожа на кнопку! Давайте её сплющим и заставим продавливаться под колобком. Выберите кнопку в иерархии, сделайте её толщиной в 0.3 единицы и положите на пол, т. е. выставьте координату Z в 0.35. Видите в инспекторе наверху три удобных кнопочки «Select», «Revert» и «Apply»? С помощью них можно взаимодействовать с префабом прямо на месте. Нажмите Apply и все кнопки отныне будут плоские и лежачие.

Для реализации программной анимации мы будет использовать класс Transform . У него есть свойство localPosition , которое позволит нам двигать кнопку:

Transform.localPosition += new Vector3(0, 0, 0.3f);
Этот код нажмёт кнопку. В целом это выглядит так:

Using UnityEngine; public class Button: MonoBehaviour { void OnTriggerEnter(Collider other) { transform.localPosition += new Vector3(0, 0, 0.3f); renderer.material.color = new Color(1, 0, 0); } void OnTriggerExit(Collider other) { transform.localPosition -= new Vector3(0, 0, 0.3f); renderer.material.color = new Color(0, 1, 0); } }
Протестировали. Наезд на кнопку вынуждает её нехило колбасить из-за сферического коллайдера колобка, который не всегда будет соприкасаться в утопленной кнопкой. Как это решить? Вспоминаем, что игры наполовину состоят из лжи, а значит размеры коллайдера совсем не обязательно должны совпадать с моделькой. Смотрим в инпекторе свойства коллайдера, учетверяем его размер по оси Z и смещаем его на -1.5 в том же направлении. Тестируем! Так гораздо лучше.

Двери, ящики и магниты

Теперь, когда у нас есть полнофункциональная кнопка, можно заставить её что-нибудь делать. Склонируем префаб стенки, назовём его «Door», создадим красненький материал «Door Mat», повесим его куда нужно и закинем свежеиспечённую дверь на сцену. Для того, чтобы как-то воздействовать на дверь, нам нужно иметь ссылку на её объект, поэтому создадим у кнопки новую переменную.

Public GameObject door;
GameObject это класс, в который заворачиваются все-все-все объекты на сцене, а значит у них у всех есть функция SetActive , которая представлена в инспекторе галочкой в левом верхнем углу. Если вы ещё пользуетесь Unity третьей версии, то вам придётся воспользоваться альтернативами . С помощью свойства активности можно прятать объекты не удаляя их. Они как бы пропадают со сцены и их коллайдеры перестают участвовать в расчётах. Прям то, что надо для двери. Приводим код к следующему виду:

Using UnityEngine; public class Button: MonoBehaviour { public GameObject door; void OnTriggerEnter(Collider other) { door.SetActive(false); transform.localPosition += new Vector3(0, 0, 0.3f); renderer.material.color = new Color(1, 0, 0); } void OnTriggerExit(Collider other) { door.SetActive(true); transform.localPosition -= new Vector3(0, 0, 0.3f); renderer.material.color = new Color(0, 1, 0); } }
Выбираем кнопку на сцене, перетаскиваем дверь из иерархии на появившееся поле в свойствах скрипта кнопки. Проверяем код в действии.

Наезд колобком на кнопку автомагически растворяет дверь и возвращает её на место после разъезда. Но какой толк нам от кнопки, которая постоянно выключается и запирает нам дверь? Настал час ящиков! Копируем префаб стенки, называем его «Box», добавляем к нему Rigidbody, не забываем проделать те же самые операции, что и с Player"ом, а затем кидаем его на сцену.

Как вы наверное заметили, толкать ящик не очень-то удобно. Кроме того, если он застрянет в углу комнаты, то достать его будет невозможно. Как вариант, мы можем сделать зоны-телепорты по углам комнаты, которые будут перемещать все попавшие в них ящики, но это немного мудрёно. Добавим в PlayerController магнит, который будет притягивать все близлежащие ящики. Функция Input.GetButton в отличие от Input.GetButtonDown будет возвращать true до тех пор, пока нажата запрашиваемая кнопка. То, что нам нужно.

If (Input.GetButton("Jump"))
Как мы будем находить ящики? Вариантов множество, например, мы можем прицепить к Player"у ещё один коллайдер и регистрировать OnTriggerEnter или OnTriggerStay , но тогда нужно будет решать проблему раннего реагирования триггера кнопки. Помните ту ссылку на матрицу с разными коллайдерами? Вот-вот. К тому же магнит должен работать только по нажатию кнопки, в остальное время он не нужен. Поэтому мы будем вручную проверять столкновения с помощью . Transform.position даст нам координаты центра колобка. Поищем объекты поблизости:

Var big = Physics.OverlapSphere(transform.position, 2.1f);
Поищем объекты, практически касающиеся колобка.

Var small = Physics.OverlapSphere(transform.position, 0.6f);
Две полученные сферы захватят все объекты, в том числе стены и кнопки. Чтобы отсеять лишнее, воспользуемся метками , они нам ещё не раз пригодятся. Идём в Edit -> Project Settings -> Tags и создаём метки на все случаи жизни: «Box», «Wall», «Button», «Door». «Player» уже есть. Выбираем префабы и метим их с помощью выпадающего списка вверху инспектора. Теперь мы можем отсеять нужные нам коробки:

Foreach (var body in big) if (System.Array.IndexOf(small, body) == -1 && body.tag == "Box") body.rigidbody.AddForce((transform.position - body.transform.position) * 20);
Нашли объект в большой сфере, проверили его наличие в малой сфере, проверили метку, двинули к себе. Немного математики с векторами, ничего сложного для тех, кто не прогуливал школу.

Ящик всё равно нас немного толкает, не не будем сейчас на этом заморачиваться, в движении всё равно это не мешает.

Внимательные читатели давно уже заметили один баг кнопки. Внимательные и усердные его уже исправили. Какой же это баг?

Если в поле коллайдера попадает два объекта, а потом один объект уходит по своим делам, то срабатывает лишнее выключение кнопки. Что делать? Нам нужно считать количество колобков и ящиков в области коллайдера и выключать кнопку, когда рядом никого нет, а включать при любом количестве тех и других. К сожалению, в Unity нет списка текущих столкновений. Очень жаль. Возможно, у разработчиков ещё не дошли до этого руки. В любом случае это решается парой строчек кода. Мы можем сделать свой список и складывать в него все приходящие объекты, вынимать все уходящие, а состояние кнопки менять в Update.

Плохой вариант

using UnityEngine; using System.Collections.Generic; public class Button: MonoBehaviour { public GameObject door; public bool pressed = false; private List colliders = new List(); void Update() { if (colliders.Count > 0 && !pressed) { door.SetActive(false); transform.localPosition += new Vector3(0, 0, 0.3f); renderer.material.color = new Color(1, 0, 0); pressed = true; } else if (colliders.Count == 0 && pressed) { door.SetActive(true); transform.localPosition -= new Vector3(0, 0, 0.3f); renderer.material.color = new Color(0, 1, 0); pressed = false; } } void OnTriggerEnter(Collider other) { colliders.Add(other); } void OnTriggerExit(Collider other) { colliders.Remove(other); } }


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

Using UnityEngine; public class Button: MonoBehaviour { public GameObject door; private int colliderCount = 0; void OnTriggerEnter(Collider other) { if (colliderCount == 0) { door.SetActive(false); transform.localPosition += new Vector3(0, 0, 0.3f); renderer.material.color = new Color(1, 0, 0); } colliderCount++; } void OnTriggerExit(Collider other) { colliderCount--; if (colliderCount == 0) { door.SetActive(true); transform.localPosition -= new Vector3(0, 0, 0.3f); renderer.material.color = new Color(0, 1, 0); } } }

Сцены, частицы и шейдеры

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

Дублируем префаб стенки, переименовываем в «Finish», меняем метку на одноимённую, превращаем коллайдер в триггер. Создадим материал «Finish Mat» с манящим голубеньким цветом и повесим на финиш.

Вся семья в сборе. Но как-то не очень маняще и слишком похоже на стенку. И на дверь. И на кубик. На помощь приходят шейдеры ! Сейчас у нас для всех материалов используется обычный матовый диффузный шейдер . В свойствах материала выберем для финиша Transparent/Specular . Этот шейдер будет учитывать альфу цвета и отсвечивать вторым цветом, который мы укажем. Поставим у голубенького альфу в половину, а отблеск сделаем белым. Тестируем.

Пока финиш выглядит не очень прозрачным, нужно как-то намекнуть, что он бесплотный. Для этого добавим к финишу систему частиц , которые будут плавать внутри и манить игрока. Component -> Effects -> Particle System. Если выбрать финиш на сцене, то можно смотреть на симуляцию, чтобы было проще создать желаемый эффект. В первую очередь поставим галочку Prewarm, тогда в игре частицы появятся заранее и будут продолжать свою нехитрую жизнь, а не возникнут на глазах игрока. Start Lifetime на единичку. Start Speed сделаем поменьше, например 0.1. Start Size 0.1. Цвет выставим голубенький. На вкладке Emission меняем Rate на две сотни. На вкладке Shape поставим Shape равным Box, это заставит частицы появляться на всём объёме финиша. Потом установим галочку Random Direction, чтобы частицы летали в разные стороны. Активируем вкладку Size over Lifetime, выбираем там какую-нибудь восходящую линию. На вкладке Randerer меняем стандартный Renderer Mode на Mesh. Меняем Mesh на сферу. Готово! Много-много маленьких пузыриков появляются и исчезают, а финиш теперь выглядит гораздо веселее.

Осталось заставить финиш перемещать игрока на следующий уровень. Для управления сценами в Unity есть несколько полезных функций и переменных. Application.loadedLevel покажет нам текущий уровень, Application.levelCount покажет их количество, а Application.LoadLevel загрузит желаемый. Кроме того, нам нужно указать в Build Settings все сцены, в которые мы хотим попасть. Создадим новый скрипт «Finish», повесим на префаб и напишем внутри следующее:

Using UnityEngine; public class Finish: MonoBehaviour { void OnTriggerEnter(Collider other) { if (other.tag == "Player") if (Application.loadedLevel + 1 != Application.levelCount) Application.LoadLevel(Application.loadedLevel + 1); else Application.LoadLevel(0); } }
Мы проверяем, что на финиш попал игрок, а потом перемещаемся на следующий или первый уровень. Дегустируем наш новый полнофункциональный финиш.

Эстетика, динамика и механика

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

Посмотрим на наше творение. Самая часто используемая механика - передвижение. Давайте внимательно посмотрим всё ли у неё в порядке. Открываем код.

Var direction = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0);
Если приглядеться, то видно, что по диагоналям наш вектор длиннее, а значит больше и прилагаемая сила. Можно исправить это

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

По окончанию первого урока у вас получится меню игры с работающими кнопками:

В уроке будут использоваться функции:
1) public int - назначение переменной числовым значением
2) GUI.BeginGroup - создание GUI группы
3) GUI.Button - Создание GUI кнопки
4) Debug.Log - вывод сообщения в консоли
5) Application.LoadLevel - загрузка уровня
6) Application.Quit - закрытие игры

Итак, начнем:
Шаг 1: Создаем в окне Project C# скрипт и называем его по своему.
Шаг 2: Создаем игровую камеру:
* На верхнем меню программы нажимаем пункт GameObject
* После нажатия появляется выпадающее меню, в нем нажимаем пункт Create Other
* В появившемся списке нажимаем на строку с названием Camera и после этого действия в окне Hierarchy появляется объект Camera

Шаг 3: Назначаем объекту Camera скрипт, который создали в первом шаге. Для этого в окне Project находим ваш скрипт, у меня он называется Menu, и перетягиваем его в окно Hierarchy на объект Camera.
Чтобы удостовериться в правильности хода нужно: нажать на объект Camera в окне Hierarchy. В окне Inspector вы у объекта увидите такое одержимое:

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

Шаг 4: Откроем скрипт в редакторе. Для этого нажмите на ваш скрипт двойным нажатием левой кнопки мыши в окне Project. У вас откроется редактор скриптов, в моем случае это MonoDevelop. Открыв редактор, перед вами появится вас скрипт, который будет абсолютно пустой, но с базовым содержанием:

200?"200px":""+(this.scrollHeight+5)+"px");">
using UnityEngine;
using System.Collections;
public class Menu1: MonoBehaviour {
// Use this for initialization
void Start () {
}
void Update () {
}
}


Строка

200?"200px":""+(this.scrollHeight+5)+"px");">public class [u]Menu : MonoBehaviour {

В замен слова Menu будет содержать название вашего скрипта. Строку трогать и изменять не нужно. По крайней мере в данном уроке.

Шаг 5: Отредактируем скрипт под меню, для этого можно удалить некоторые строки, которые вам не понадобятся в этом уроке:

200?"200px":""+(this.scrollHeight+5)+"px");">
// Use this for initialization - этот комментарий нам не нужен

// Update is called once per frame
void Update () {
} - метод Void нам тоже не понадобится

Шаг 6: Наш скрипт подготовлен для создания на нем меню игры.
Перед методом void Start создадим переменную для нумерации окон в меню игры.
Содержании строки такое:

200?"200px":""+(this.scrollHeight+5)+"px");">public int window;


public int - задаем числовое значение переменной
window - название переменной, которая будет использоваться в скрипте с числовым значением

Шаг 6: Для правильной работы меню, при старте работы скрипта у нас должно отображаться одно содержимое, для этого в метод void Start добавим стартовое значением переменной window . Весь метод будет выглядеть так:

200?"200px":""+(this.scrollHeight+5)+"px");">
void Start () {
window = 1;
}

Метод при старте исполнения скрипта будет назначать переменной window значение 1

[b]Шаг 7:
Начнем саму работу с GUI выводом меню, для этого создадим ниже метода void Start, метод в выводом GUI. Выглядеть он будет так:

200?"200px":""+(this.scrollHeight+5)+"px");">
void OnGUI () {
}

Данный метод в программе Unity3D и в вашем создаваемом приложении вызовет вывод графических элементов.

Шаг 8: Чтобы меню отображалось по центру экрана и не приходилось для каждой кнопки рассчитывать местоположение, создадим группу, которая будет выводить свое содержимое по центру экрана:

200?"200px":""+(this.scrollHeight+5)+"px");">
GUI.EndGroup ();

GUI.BeginGroup - создаем группу
(new Rect - задаем значение, что дальше будут даны данные о расположении самой группы
(Screen.width / 2 - 100, - задаем расположение группы относительно ширины экрана
Screen.height / 2 - 100, - задаем расположение группы относительно высоты экрана
200 - задаем ширину группы
200 - задаем высоту группы

Значения ширины и высоты можно свои ставить, но чтобы все было по центру аккуратно в Screen.width / 2 - 100, Screen.height / 2 - 100 значение 100 заменяем на свое значение. То есть если же ваша группа будет иметь ширину и высоту 300, то в замен 100 вы должны ввести половину от ширины 300. Вводимое значение будет 150.

Шаг 9: Создаем вывод меню, если переменная window = 1. Для этого, между началом и концом группы, созданной в шаге №8, то есть

200?"200px":""+(this.scrollHeight+5)+"px");">
GUI.BeginGroup (new Rect (Screen.width / 2 - 100, Screen.height / 2 - 100, 200, 200));
Сюда!!!
GUI.EndGroup ();


Напишем выдаваемое значение при window = 1:

200?"200px":""+(this.scrollHeight+5)+"px");">
if(window == 1)
{
if(GUI.Button (new Rect (10,30,180,30), "Играть"))
{
window = 2;
}
if(GUI.Button (new Rect (10,70,180,30), "Настройки"))
{
window = 3;
}
if(GUI.Button (new Rect (10,110,180,30), "Об Игре"))
{
window = 4;
}
if(GUI.Button (new Rect (10,150,180,30), "Выход"))
{
window = 5;
}
}

if(window == 1) - если windows равно значению 1, то создадим вывод
if(GUI.Button (new Rect (10,30,180,30), "Играть")) - создаем кнопку "Играть"
{window = 2;} - если нажата кнопка "Играть", то window получит значение 2
С остальными кнопка так же.

Шаг 10: Создадим вывод, если переменная window равно 2

200?"200px":""+(this.scrollHeight+5)+"px");">
if(window == 2)
{

{
Application.LoadLevel(1);
}
{
Application.LoadLevel(2);
}
{
Application.LoadLevel(3);
}
{
window = 1;
}
}

Выводим кнопки, которые доступны при нажатии на кнопку "Играть". Вывод ни чем не отличается о предыдущих кнопок, расшифрую только новые функции:
Debug.Log("Уровень 1 загружен"); -
Application.LoadLevel(1); - вызываем функцию, которая загружает уровень игры. 1 - можно менять на нужный вам уровень. Числовое значение можно брать, если нажать на сочетании клавиш Ctrl + Shift + B.

Шаг 11: Создаем вывод, если window имеет значение 3:

200?"200px":""+(this.scrollHeight+5)+"px");">
if(window == 3)
{

{
}
{
}
{
}
if(GUI.Button (new Rect (10,160,180,30), "Назад"))
{
window = 1;
}
}


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

Шаг 12: Выводим содержимое, если значение у window 4

200?"200px":""+(this.scrollHeight+5)+"px");">
if(window == 4)
{

{
window = 1;
}
}

Шаг 13: Выводим содержимое, если переменная window имеет значение 5 и нажата кнопка "Выход"

200?"200px":""+(this.scrollHeight+5)+"px");">
if(window == 5)
{

{
Application.Quit();
}
{
window = 1;
}
}


В данном выводе из новых функций, только она:
Application.Quit(); - данная функция выключает приложении при нажатии кнопки "Да".
P.S. Функция не работает в редакторе Unity3D, она работает только в скомпилированном проекте.

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

If(window == 2)
{
GUI.Label(new Rect(50, 10, 180, 30), "Выберите уровень");
if(GUI.Button (new Rect (10,40,180,30), "Уровень 1"))
{
Debug.Log("Уровень 1 загружен");
Application.LoadLevel(1);
}
if(GUI.Button (new Rect (10,80,180,30), "Уровень 2"))
{
Debug.Log("Уровень 2 загружен");
Application.LoadLevel(2);
}
if(GUI.Button (new Rect (10,120,180,30), "Уровень 3"))
{
Debug.Log("Уровень 3 загружен");
Application.LoadLevel(3);
}
if(GUI.Button (new Rect (10,160,180,30), "Назад"))
{
window = 1;
}
}

If(window == 3)
{
GUI.Label(new Rect(50, 10, 180, 30), "Настройки Игры");
if(GUI.Button (new Rect (10,40,180,30), "Игра"))
{
}
if(GUI.Button (new Rect (10,80,180,30), "Аудио"))
{
}
if(GUI.Button (new Rect (10,120,180,30), "Видео"))
{
}
if(GUI.Button (new Rect (10,160,180,30), "Назад"))
{
window = 1;
}
}

If(window == 4)
{
GUI.Label(new Rect(50, 10, 180, 30), "Об Игре");
GUI.Label(new Rect(10, 40, 180, 40), "Информация об разработчике и об игре");
if(GUI.Button (new Rect (10,90,180,30), "Назад"))
{
window = 1;
}
}

If(window == 5)
{
GUI.Label(new Rect(50, 10, 180, 30), "Вы уже выходите?");
if(GUI.Button (new Rect (10,40,180,30), "Да"))
{
Application.Quit();
}
if(GUI.Button (new Rect (10,80,180,30), "Нет"))
{
window = 1;
}
}
GUI.EndGroup ();
}
}


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


Unity позволяет не делать меню настроек, так как есть встроенный диалог настроек качества и разрешения перед запуском игры. Однако если вы хотите сделать вашу игру неповторимой и показать, на что способны, как дизайнер, то эта статья именно для вас. После прохождения этого туториала вы сможете создавать свое собственное меню с таким функционалом – настройки громкости, разрешения, качества изображения, переключения полноэкранного и оконного режима. Интересно? Тогда запускай Unity и поехали!
Создание и дизайн главного меню и меню настроек
1. Создаем две сцены: Menu и Game
2. Открываем File->BuildSettings и перетаскиваем созданные сцены в Scenes in build.

Теперь можем приступать к созданию пользовательского интерфейса. Откройте сцену Menu и добавьте Panel. Сразу добавляется Canvas(Холст) и дочерним объектом к нему добавляется Panel (Панель).


Обратим внимание на инспектор для Canvas. А именно на компонент Canvas.


Render Mode автоматически выставлен на Screen Space – Overlay.

Немного информации:

Screen Space – Overlay:

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

Важен порядок размещения объектов в иерархии. Холст Screen Space – Overlay должен находиться в самом верху иерархии, иначе он может пропасть из виду.


Screen Space – Camera:

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

Так же для Холста с такими настройками важно размещение относительно других объектов. На переднем плане будут объекты, которые находятся ближе к камере, не зависимо от того, это UI или другие GameObjects.


World Space:

Холст размещается, как любой другой объект без привязки к камере или экрану, он может быть ориентирован как вам угодно, размер Холста задается с помощью RectTransform, но то, как его будет видно во время игры, будет зависеть от положения камеры.
В данном задании мы будем использовать Screen Space – Overlay.
Давайте настроим цвет панели. Можно также добавить картинку на фон. Для этого нужно обратить внимание на компонент Image в Инспекторе панели.


Как видите, картинка добавляется в поле Source Image. Картинку можно просто перетащить из файлов проекта, либо кликнуть на кружочек справа от поля.

Цвет выбирается в поле Color, либо с помощью инструмента пипетки.
Важно знать, что пипетка позволяет выбрать цвет за пределами Unity.
После того, как выбрали цвет или картинку, нам нужно разместить кнопки, которые всем будут управлять, а так же текст. Чтобы упростить себе задачу, для Panel мы добавим еще один компонент, который называется Vertical Layout Group. И сразу настроим его.

Нам необходимо разместить все кнопки и текст по центру экрана. Для этого находим в компоненте Vertical Layout Group пункт Child Alignment и выбираем Middle Center. Теперь все наши элементы, будь то кнопки или текст, будут выравниваться по центру, независимо от разрешения экрана.

Так же убираем галочки с ChildForceExpand. Должно получиться так:


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

В нашем меню должны быть следующие кнопки:

1. Start Game
2. Settings
3. Exit

Сейчас, добавляем Text дочерним элементом нашей Панели. Можете заметить, как он прикрепляется по центру. Иногда для этого требуется созданный элемент в окне Scene просто передвинуть на Panel и тогда он выровняется. Настраиваем текст так, как хочется с помощью компонента Text(Script) в инспекторе.


После добавляем Button. Кнопка добавится под текст.

Разберем компонент Button нашей кнопки. Нас интересует Transition – то, как кнопка будет реагировать на действия мышки. В Unity реализовано несколько способов. Пока рассмотрим довольно простой Color Tint. Это значит, что кнопка будет менять свой цвет при определенных состояниях. Интерфейс для настройки довольно удобный. Настраиваем так, как нравится.


Так же у объекта Button есть дочерний объект Text – это текст который будет отображаться на кнопке. В тексте прописываем Play.

Кажется, Текст и Кнопки находятся слишком близко друг к другу.

Что бы это исправить для нашего Текста Menu добавляем еще один компонент, который называется Layout Element. И ставим галочку напротив Ignore Layout.


После этого выставляем Anchor на Middle-Center.


Потом добавляем еще три кнопки и называем их Settings, Credits, Exit.
Можно поменять размеры кнопок. На этом этапе меню выглядит так:

Переходы между main menu и settings
Что бы переходить на меню опций не обязательно делать новую сцену.
Для начала создаем пустой GameObject (Create Empty) как Child нашего Холста. Назовем его Main menu. Потом нашу панель, со всеми инструментами сделаем дочерними элементами этого объекта. Должно получиться так:


Выбираем наш MainMenu объект и сделаем его дубликат. С выбранным элементом нажимаем комбинацию клавиш Ctrl+D. У нас появится новый объект.


Переименовываем новый объект в Settings. Для удобства управления инактивируем MainMenu.


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

За контроль Fullscreen будет отвечать элемент Toggle.
За громкость – Slider.
За качество изображения и разрешение – Dropdown.

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

Можно настроить Spacing в Vertical layout group, чтобы между элементами было немного пространства. Добавим на панель картинку и в итоге получим такой результат:

Программирование кнопок
Перейдем к написанию скрипта меню.

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

Это мы и пропишем в нашем скрипте.

Для MainMenu добавляем новый компонент MenuControls.cs и отрываем его.

Первое что надо сделать – удалить существующие методы Start() и Update() – тут они нам не нужны.

Using UnityEngine.SceneManagement;
После этого напишем свой метод для нажатия кнопки Play. Метод должен быть public - нам нужно иметь возможность видеть его за пределами нашего скрипта.

За загрузку сцены отвечает SceneManager и у него есть метод LoadScene. Существует несколько перегрузок метода. Можно передавать имя сцены, которую вы хотите загрузить. В нашем случае это сцена «Game».

В итоге функция будет выглядеть следующим образом.

Public void PlayPressed() { SceneManager.LoadScene("Game"); }
Так же создаем метод для выхода из игры:

Public void ExitPressed() { Application.Quit(); }
Однако в Unity мы не увидим результата работы этого метода, так как подобное работает только в билде. Для того что бы проверить, что все работает правильно, добавляем в метод строчку

Debug.Log("Exit pressed!");
Теперь необходимо прикрепить события кнопок к этим методам. Выбираем кнопку Play и находим в инспекторе следующее:


Это событие кнопки, которое по нажатию будет вызывать подписанные на событие методы. Добавляем метод нажимая на +.

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

После этого нужно выбрать скрипт MenuControls и найти метод PlayPressed().


Точно также делаем для кнопки Exit. Только теперь выбираем метод ExitPressed().
Для кнопки Settings нам не нужно писать никакой код, так как некоторый функционал уже встроен.

Суть в том, что мы будем активировать GameObject. На этом этапе у вас должен быть активным MainMenu, а Settings не должно быть видно. Так же видим, что когда мы активируем Settings, он полностью перекрывает Menu. Суть в том, что играет роль порядок расположения дочерних объектов Холста – в каком порядке они расположены в иерархии в том порядке они и будут прорисовываться. Поскольку Settings у нас находятся над Main Menu, то они перекрывают меню.
Это мы и будем использовать.

Выбираем кнопку Settings и в OnClick() перетаскиваем наш объект Settings. В функциях выбираем GameObject ->SetActive(); и ставим галочку. Вот так:


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

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

Настройки
Настройки полного экрана

Первое что мы пропишем это переключение полноэкранного и оконного режимов.
Нужно убрать галочку с пункта Is On нашего Toggle элемента.
Создаем скрипт для объекта Settings. Назовем его Settings.cs.

Для начала нам надо хранить переменную типа bool которая будет отображать текущее состояние – полноэкранный режим или нет. А потом, по изменению toggle эта переменная будет переключаться на противоположное значение.

У экрана есть свойство Screen.fullScreen типа bool. Можно просто будем присваивать значение нашей переменной isFullScreen этому свойству.

Код выглядит так:

Public void FullScreenToggle() { isFullScreen = !isFullScreen; Screen.fullScreen = isFullScreen; }
Увидеть результат можно только в билде. Давайте сейчас это сделаем. Учтите, что для того что бы билд был правильным нужно оставить активным только объект MainMenu, а Settings отключить. Если это сделано, то запускаем билд через File->BuildSettings и нажимаем кнопку Build.

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

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

Добавим эти два элемента. Сначала добавляем AudioMixer. Правой кнопкой мыши в окне Project ->Create->AudioMixer.

Называем его GameSettings. После этого открываем окно AudioMixer: Window->Audio Mixer (Ctrl + 8).

Что бы контролировать параметры миксера через скрипт, их нужно сделать видимыми для этого скрипта. Эта процедура называется ExposeParameters. Для этого кликаем на Mixer и в инспекторе находим volume и кликаем правой кнопкой мыши. Выбираем Expose to script:


Теперь в окне Audio Mixer обратите внимание на пункт Exposed Parameters в верхней левой части.
Теперь там есть параметр. Кликаем на него и называем наш параметр masterVolume. Следует запомнить имя, которое ему присваиваем – его нужно будет указать в коде.

Переходим в Settings.cs и создаем поле AudioMixer, чтобы получить ссылку на него в коде.

Public AudioMixer am;
потом создаем метод

Public void AudioVolume(float sliderValue) { am.SetFloat("masterVolume", sliderValue); }
Метод SetFloat будет принимать значения нашего слайдера и присваивать это значение параметру “masterVolume”.

Осталось прикрепить наш метод к событиям слайдера. Находим в инспекторе слайдера поле On Value Changed и точно так же прикрепляем объект. Вот только теперь нам надо не просто выбирать метод из списка, а использовать поле Dynamic float. Как видите, там уже есть наш метод, и он будет получать переменную от самого слайдера. Также нужно не забыть перетащить AudioMixer в соответствующее поле в компоненте Settings.cs.


Обратите внимание, что мы напрямую привязываем значение слайдера к значениям аудио-миксера. В аудио миксере громкость изменяется от -80 до 20. Нам же достаточно менять от -80(нет звука) до 0(нормальный звук). В настройках слайдера минимальное значение выставляем на -80, максимальное на 0.
Теперь добавим звуки в нашу игру, чтобы проверить работу скрипта.
На canvas добавим компонент Audio Source.
Настроим его следующим образом:


Audio Clip – саундтрек
Output – Мастер нашего миксера (дочерний объект)
Loop – поставить галочку – зациклить композицию, чтобы она играла постоянно.
Качество изображения
В Unity уже встроены настройки качества изображения. Edit->Project Settings->Quality. В инспекторе видим Quality settings. Их можно добавлять и настраивать.

Особенностью работы с настройками качества является следующее:
Каждой настройке соответствует индекс, который мы можем получить из Dropdown. Все что нужно сделать – переписать соответствующие пункты в нужные индексы в нашем UI элементе. Открываем его и в инспекторе находим Dropdown(Script) и в нем пункт Options. Дальше вписываем настройки в нужном порядке. У меня получилось так:


Дальше нужно прописать код. Мы продолжаем дополнять методами наш скрипт Settings.cs
Создаем метод, который будет принимать int – индекс выбранного пункта.

Public void Quality(int q) { QualitySettings.SetQualityLevel(q); }
Сохраняем скрипт и подключаем метод к событию на нашем меню. На этот раз это событие Dropdown – On Value Changed.

Поскольку наш метод будет получать значение от самого UI элемента, то мы выбираем название метода из группы Dymanic int. по аналогии с предыдущим пунктом.

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

Первое что нам понадобится – массив типа Resolution где мы будем хранить значения разрешений экрана.

Однако для пунктов выпадающего списка тип – string. Поэтому создаем список List<> в который мы будем сохранять значения возможных разрешений. Для работы со списками необходимо подключить:

Using System.Collections.Generic;
Также нам понадобится ссылка на соответствующий Dropdown. Для работы с UI элементами следует также прописать:

Using UnityEngine.UI;
В скрипте получим следующие поля:

Resolution rsl; List resolutions; public Dropdown dropdown;
Инициализацию и заполнение проводим в методе Awake. Этот метод вызывается при запуске объекта, соответственно выполняется раньше, чем все остальные методы.

Получаем значения и каждое из них добавляем в List в формате ширина*высота. После этого очищаем список Dropdown и заполняем его новыми опциями.

Public void Awake() { resolutions = new List(); rsl = Screen.resolutions; foreach (var i in rsl) { resolutions.Add(i.width +"x" + i.height); } dropdown.ClearOptions(); dropdown.AddOptions(resolutions); }
Теперь нужно создать метод, который будет менять разрешение экрана. Как и в предыдущих пунктах – принимать значение будем от UI элемента. Создаем функцию, которая принимает int

Public void Resolution(int r) { Screen.SetResolution(rsl[r].width, rsl[r].height, isFullScreen); }
В SetResolution необходимо передать параметры – ширина, высота и булевскую переменную, отвечающую за полный экран. У нас такая уже есть – это isFullScreen. Передаем ее в функцию.
Дальше не забываем подключить к соответствующему событию наш метод Resolution из группы Dynamic Int, а так же добавить ссылку на нужный Dropdown. 

Этот урок даст всем желающим основное понимание того, как будет работать 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);

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

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

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

Анимация кнопки

Перейдем сразу к практике, добавив парочку красивых анимаций. На то есть несколько причин. Во-первых, анимация - это круто! Во-вторых, в этом проекте без них просто не обойтись. Мы сделаем так, чтобы кнопка выезжала из сцены, поэтому у нас будет место для отображения нового диалогового окна, которое мы создадим позже.

Начнем создание анимации и аниматора. Анимация кнопки ничем не отличается от анимации любого другого объекта Unity. Вы должны будете добавить компонент Animator , создать несколько анимаций и настроить состояния и переходы между ними. Вот, что нам нужно сделать:



В дополнение к созданию самой анимации, Unity также любезно добавляет компонент аниматора к btn_start и создает контроллер аниматора.


Если вы не знакомы с анимацией и контроллерами Аниматора в Unity , пожалуйста, изучите урок под названием Ваяем игру в стиле Jetpack Joyride в Unity 2D .

Анимирование кнопки, выезжающей за пределы экрана

Хотя фактически нам нужны две анимации technically - кнопка, выезжающая из сцены, и кнопка, выезжающая обратно в сцену - мы схитрим и создадим всего одну, а потом прокрутим ее задом наперед.

Чтобы создать слайд из анимации, выполните следующие действия:

  1. Выберите btn_start в Иерархии
  2. Убедитесь, что при этом появилась вкладка Анимация (Animation view).
  3. Нажмите на 1:00 на временной шкале и убедитесь в том, что запись анимации запущена. Это должно происходить автоматически.
  4. Измените Anchors (Якоря) на top-stretch (сверху-тянется).
  5. Измените Pos Y (Позиция по оси Y) на 60 в Инспекторе (Inspector).
  6. Остановите запись, нажав на кнопку с красным кружком.

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

Обратите внимание на два следующих пунктов, отмеченных буквами на скриншоте:

A: Ключевому кадр была автоматически выставлена метка 0:00 . В этот момент кнопка находится в исходном положении, в том самом, что вы установили в предыдущем учебнике.

B: Хотя визуальное отображение якорей не изменяется на красный цвет, вы можете увидеть, что числовые значения изменились и стали красными, указывая, что вы также анимировали якоря.


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


Вы заметили анимацию якорей? Если и нет, то вам наверняка бросилась в глаза эта раздражающе мигающая красная стрелочка. Жаль графический дизайн не моя сильная сторона. Хотя, если немного подумать, все становится на свои места: положение кнопки соответствует расстоянию до якорей. В случае btn_start , это было расстояние от нижней границы. В данный момент вы работаете только с движением по вертикали, поэтому только нижняя граница имеет значение. Чтобы убедиться, что кнопка выезжает за пределы экрана, двигайте ее вверх, пока она не пропадет из поля зрения. Что делать, если вам неизвестна высота экрана? Как сделать так, чтобы кнопка остановилась в тот самый момент, когда вы больше ее не видите? Ответ: изменив ее якоря. Установив якоря на верхнюю границу экрана, вы просто устанавливаете расстояние от этой границы. Таким образом, кнопка всегда будет выше края экрана и не зависеть от ее высоты, так как она позиционируется относительно верхнего края экрана.

Анимирование кнопки, въезжающей в сцену

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

  1. При загрузке сцены кнопка должна не просто появляться, а еще и как бы скользить.
  2. При закрытии окна настроек кнопки должны вернуться в исходное положение

Это довольно легко сделать. Во-первых, нужно отключить зацикливание анимации, так как кнопка должна двигаться либо вверх, либо вниз, а не скакать туда-сюда как шарик от пинг-понга. Чтобы отключить цикл, откройте папку Animations в Браузере проекта и выберите анимацию btn_start_slide_out , а в Инспекторе снимите галочку с пункта Loop Time .

Теперь выберите btn_start в Иерархии и откройте Animator view (вкладку Аниматор). Щелкните правой кнопкой мыши на состояние btn_start_slide_out и выберите Copy .


Щелкните правой кнопкой мыши где-нибудь внутри вкладки Аниматор, и выберите Paste чтобы продублировать состояние btn_start_slide_out .


Теперь выберите этот дубликат состояния, скорее всего он называется btn_start_slide_out 0 , и переименуйте его в Инспекторе на btn_start_slide_in . Кроме того, задайте Speed значение, равное -1 .

Затем, внутри вкладки Аниматор щелкните правой кнопкой мыши на btn_start_slide_in и выберите Set As Default , т.к. нам надо, чтобы жизненный цикл кнопки начинался с ее появления на экране, а не наоборот.



Наконец, добавьте два перехода между состояниями. Чтобы сделать это, щелкните правой кнопкой мыши на состоянии btn_start_slide_out и выберите Make Transition (сделать переход). Далее нажмите на btn_start_slide_in , чтобы сделать переход. После этого создайте обратный переход, щелкнув правой кнопкой мыши на btn_start_slide_in , выбрав Make Transition , а затем нажав на btn_start_slide_out . Вот, что у вас должно получиться:


Вы близки к цели, но нужно дополнительно настроить переходы. Вместо этого вы должны использовать параметр isHidden . Выберите transition из btn_start_slide_out в btn_start_slide_in и измените Conditions в Инспекторе чтобы isHidden стала равной false .

Затем выберите переход, который идет в обратном направлении, из btn_start_slide_in в btn_start_slide_out и изменитеs Conditions чтобы isHidden стала равной true .

Запустите сцену. Кнопка должна аккуратно въехать в сцену. Потом измените isHidden вручную, чтобы кнопка выехала за пределы экрана.


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

Анимация кнопки настроек

Кнопка Настройки должна скользить вниз по экрану, чтобы освободить место в центре для диалога. Как вы думаете, вам хватит умения анимировать кнопку Настройки самостоятельно? Все, что вам нужно знать, это:

  • Offscreen Pos Y should be -50
  • Нет необходимости изменять якоря, поскольку кнопка уже спозиционирована по отношению к нижнему краю экрана.

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

  1. Выберите btn_settings в Иерархии.
  2. Откройте вкладку Анимация (Animation view).
  3. Откройте раскрывающийся список анимаций и выберите .
  4. Назовите анимацию btn_settings_slide_out и сохраните ее в папке Animations . Это также приведет к созданию контроллера анимации и добавит компонент аниматора в btn_settings .
  5. На вкладке Анимация нажмите на 1:00 mark at the timeline and make sure recording is on . Запись включится автоматически, но вы можете нажать на красный кружок и начать запись вручную.
  6. В Инспекторе для btn_settings измените Pos Y на -50 . На этот раз Вам не нужно менять якоря.
  7. Остановите запись, нажав на кнопку с красным круго, или сохранив сцену.
  8. Откройте папку Animations в Браузере проекта (Project browser). Выберите btn_settings_slide_out и снимите галочку в Инспекторе напротив пункта Loop Time .
  9. Теперь откройте вкладку Аниматор (Animator).
  10. Скопируйте и вставьте состояние btn_settings_slide_out для дублирования.
  11. Выберите продублированное состояние, которое названо btn_settings_slide_out 0 . В Инспекторе измените имя на btn_settings_slide_in и измените Speed на -1.
  12. Щелкните правой кнопкой мыши на состояние btn_settings_slide_in и выберите Set As Default .
  13. Создайте переход из btn_settings_slide_in to btn_settings_slide_out , затем создайте обратный переход.
  14. Добавьте новый параметр Bool под именем isHidden .
  15. Выберите переход из btn_settings_slide_out to btn_settings_slide_in , зайдите в Инспектор и измените Conditions , переключив isHidden на false .
  16. Выберите переход из btn_settings_slide_in to btn_settings_slide_out и измените Conditions, переключив isHidden на true .

Вот, что вы должны получить после этого:


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

Запуск анимации кнопки с помощью скрипта

В MonoDevelop откройте UIManagerScript , который мы создали в первой части, и добавьте следующие переменные экземпляра прямо внутри определения класса:

Public Animator startButton; public Animator settingsButton;

После этого добавьте следующий метод:

Public void OpenSettings() { startButton.SetBool("isHidden", true); settingsButton.SetBool("isHidden", true); }

Вот и весь код, который нам нужен. Сохраните скрипт и вернитесь обратно в Unity . В Unity выберите UIManager в Иерархии (Hierarchy). Перетащите btn_start из Иерархии в поле Start Button в Инспекторе, а btn_settings в поле Settings Button .


Теперь выделите btn_settings в Иерархии и нажмите на клавишу + в списке On Click (Button) . Перетащите UIManager из иерархии в новый пункт в списке. После этого откройте меню выбора функций и выберите UIManagerScript\OpenSettings () .

Запустите сцену. Подождите когда кнопки закончат движение и нажмите на кнопку Settings . Вы должны увидеть как обе кнопки одновременно выходят за пределы экрана в противоположных направлениях.


Кнопки выходят за пределы экрана в противоположных направлениях

Добавление окна с настройками

Посмотрите, сколько у нас теперь свободного места! Идеально подходит для выезжающего диалогового окна. Ведь природа не терпит пустоты:] Обычно, диалоги содержат некоторые другие элементы управления, которые должны появиться и двигаться с диалогом. Таким образом, наиболее эффективным будет создать диалоговое окно как панель и назначить другие элементы интерфейса его детскими объектами. Для создания панели выберите GameObject\Create UI\Panel в меню. Это создаст полноэкранную панель с полупрозрачным белым фоном.

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

  1. Выберите Panel в Иерархии и переименуйте на dlg_settings .
  2. Установите его якоря в middle-right , так как вы будете позиционировать диалог относительно правого края от экрана, поэтому он не виден, когда вы запускаете сцену.
  3. Установите Width равной 400 , а Height - 150 .
  4. Установите Pos X равной 220 , а Pos Y - 0 .

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

Установка фонового изображения для диалога

Вы собираетесь использовать 9й срез изображения в качестве фона диалогового окна, поэтому в первую очередь вы должны установить границу в настройках импорта. Откройте папку Menu в браузере проекта и выберите settings_panel_bg_9slice . В Инспекторе кликните на Sprite Editor чтобы открыть вкладку Редактора спрайтов (Sprite Editor view). Установите толщину рамок (Border) равной 20 и нажмите на кнопку Применить (Apply) на самом верху.

Теперь вы можете использовать это изображение для фона диалогового окна. Выберите dlg_settings в Иерархии и перетащите settings_panel_bg_9slice в поле Source Image в Инспекторе. Дважды щелкните на Color сядом с полем Source Image и установите A значение 255 чтобы убрать прозрачность.


Вот как должен выглядеть диалоговое окно после установки фонового изображения:


Добавление метки

В своем нынешнем состоянии, трудно утверждать, что этот зеленый прямоугольник - ни что иное, как диалоговое окно с настройками, но есть простой способ чтобы это исправить. Все, что вам нужно сделать, это написать на нем "Настройки"). Веберите GameObject\Create UI\Text для создания нового текстового UI элемента. Выберите Text в Иерархии и переименуйте в lbl_settings . Теперь переместите lbl_settings на dlg_settings чтобы добавить его в качестве дочернего объекта.

После этого, выберите в иерархии lbl_settings и сделайте следующие изменения:

  1. Установите Якоря Anchors в top-center .
  2. Задайте Pos X равной 0 , а Pos Y - -40 .
  3. Измените Text на Settings .
  4. Откройте папку Fonts в Браузере проекта и перетащите шрифт DCC – Dreamer в поле Инспектора Font .
  5. задайте Font Size значение 30 .
  6. Отцентрируйте, установите выравнивание Alignment в Center Align .
  7. Покрасьте текст в белый цвет, задав Color белую палитру и установив A (Alpha) значение 255 чтобы убрать прозрачность.

Анимация окна с настройками

Теперь у вас есть настоящее окно с Параметрами и наш следующий шаг будет заключаться в том, чтобы при нажатии пользователем на кнопку Настройка происходило появление этого окна. Мы проделаем все тоже самое, что и для скользящих кнопок, разница будет только в том, что диалоговое окно не будет скользить автоматически после запуска сцены. Выберите в Иерархии dlg_settings и откройте вкладку Animation (Анимация). Затем создайте новую анимацию, нажав на (Создать новый клип) в выпадающем списке.


Создаем новую анимацию для окна с настройками

Назовем анимацию dlg_settings_slide_in и сохранем ее в папке Animations.


Затем щелкните по отметке 1:00 на шкале и убедитесь в том, что запись началась, или запустите ее вручную, нажав на кнопку записи.


В инспекторе установите Якоря Anchors в middle-center , а Pos X в 0 .

Нажмите на Stop recording чтобы остановить запись.

Откройте папку Animations в Браузере проекта и выберите dlg_settings_slide_in . В Инспекторе снимите галку напротив Loop Time .

Теперь переключитесь на вкладку Animator . Скопируйте и вставьте состояние dlg_settings_slide_in чтобы дублировать его. Переименуйте его в dlg_settings_slide_out , установите его скорость Speed в -1 .

Примечание: На этот раз, не изменяйте состояние по умолчанию! Дефолтное состояние диалогового окна должно остаться таким: dlg_settings_slide_in .

Нажмите на кнопку + и добавьте новый параметр Bool под именем isHidden . Создайте два перехода между состояниями, точно так же, как вы делали с кнопками. Затем добавьте параметр isHidden типа Bool. Измените условия перехода dlg_settings_slide_out => dlg_settings_slide_in , переключив isHidden на false , а для перехода dlg_settings_slide_in => dlg_settings_slide_out переключите isHidden на true .


Запустите сцену и вы увидите скольжение диалогового окна в начале, а затем происходит перекрытие кнопок.

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

Отображение диалога при нажатии на кнопку.

Когда вы запустите сцену с меню, диалоговое окно не появится сразу. Это хорошо. Впрочем, оно не будет отображаться даже при нажатии на кнопку Настройки. А вот это уже не хорошо. Откройте UIManagerScript в MonoDevelop и добавьте следующую переменную экземпляра:

Public Animator dialog;

Затем добавьте следующий код в конец OpenSettings:

Public void OpenSettings() { //..пропускается.. dialog.enabled = true; dialog.SetBool("isHidden", false); }

Это активирует компонент Animator и устанавливает правильное значение параметра isHidden . И в заключении добавим новый метод CloseSettings следующим образом:

Public void CloseSettings() { startButton.SetBool("isHidden", false); settingsButton.SetBool("isHidden", false); dialog.SetBool("isHidden", true); }

Это возвращает кнопки и скрывает диалоговое окно. Сейчас мы добавим элемент UI, вызывающий этот метод. Сохраните UIManagerScript и переключитесь обратно в Unity. Выберите UIManager в Иерархии и перетащите dlg_settings в поле Dialog в Инспекторе.


Запустите сцену, затем нажмите на кнопку Настройка и проследите, как кнопки выезжают из сцены в то время, как в нее заезжает диалоговое окно. Мы здорово продвинулись, но еще многое предстоит сделать, ведь мы не можем закрыть диалоговое окно. Чтобы это исправить добавим кнопку Закрыть. Выберите GameObject\Create UI\Button . Переименуйте новую кнопку в btn_close и перетащите ее на dlg_settings в Иерархии чтобы добавить кнопку в качестве дочернего объекта. тек как эта кнопка не имеет текста, то удалите текст из btn_close . Вот что вы должны иметь в Иерархии на данный момент:

Теперь выберите btn_close и выполните следующие действия:

  1. Установите Якоря Anchors в top-right .
  2. Установите значения Pos X и Pos Y равными 0 .
  3. Откройте папку Menu в Браузере проекта и перетащите settings_btn_close в поле Source Image в Инспекторе.
  4. Нажмите на Set Native Size .

Теперь наше диалоговое окно на вкладке Сцена должно выглядеть так:

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

Кнопка выглядит стильно, но она ничего не делает. К счастью, мы уже добавили метод, который должна вызывать наша кнопка. Выберите btn_close в Иерархии, прокрутите вниз список до On Click (Button) и нажмите + . Перетащите UIManager Из иерархии к новому пункту, а затем выберите в выпадающем списке метод UIManagerScript\CloseSettings() .

Запустите сцену. Нажмите на кнопку насткройка, а затем на кнопку закрыть, когда диалоговое окно появится на сцене.


Добавление настроек звука

Сейчас в нашем диалоговом окне нет никакого толка. Пришло время добавить в него какие-нибудь настройки. Пусть это будет включение/выключение музыки. Музыки? Да, что за удовольствие играть в игру без звуковой дорожки?

Добавление музыки к сцене с Меню

Вы можете порыться в своей коллекции для поиска подходящей мелодии, но это не обязательно, потому что проект уже содержит музыкальный трек. Все, что вам нужно сделать, это проиграть его. Выберите Main Camera в Иерархии и добавьте компонент Audio Source . Затем, в Браузере проекта откройте папку Audio и перетащите music в поле Audio Clip в Инспекторе. Поставьте галочку справа от Play On Awake .

Включение и выключение музыки

Для включения и выключения музыки вы собираетесь использовать a…wait for it…toggle control. Выберите GameObect\Create UI\Toggle чтобы добавить переключатель на сцену.

Управление переключателя состоит из root object (корневой объект) с добавленным Toggle script (скрипт переключения) и несколькими дочерними объектами:

  • Background: Изображение, которое всегда видно (i.e. in both On and Off states)
  • Checkmark (галочка): Изображение, которое отображается только при включении (ON).
  • Label: Метки, отображается рядом с переключателем.

Метка нам не понадобится, поэтому дочерний объект Label можете удалить. Переименуйте Toggle в tgl_sound и перетащите его на dlg_settings , чтобы вставить в диалоговое окно. Вот, что вы должны увидеть в Иерархии после всех этих действий:

Выберите tgl_sound в Иерархии. Установите Якоря Anchors в положение middle-left , Pos X в 115 , а Pos Y в -10 .

Примечание: Помните, как якоря и позиция устанавливаются по отношению к родителю? Поэтому важно сначала добавить tgl_sound в качестве детского объекта dlg_settings и только после этого установите его позицию.

Помните, изменение якорей и точки отсчета не влияет на положение элементов UI - вместо этого изменяются значения Pos X, Pos Y, Left, Right и т.д., в результате чего элемент остается на том же месте, но с использованием новых якорей и точки отсчета. Установите их в первую очередь, и поиграйте с ними, чтобы установить правильное положение.

Ах да, под компонентом Toggle (Script) есть окошко Active - нужно убрать из него галочку. Теперь вам нужно настроить изображение для фона и галочки, а также дочерние объекты. Так же, как и другие изображения, мы возьмем из папки Menu folder в Браузере проекта. Нам потребуются два изображения:

  • settings_btn_sound для фона
  • settings_btn_sound_checkmark для Checkmark

Выберите Background из tgl_sound в иерархии и перетащите settings_btn_sound из браузера проекта в Source Image в Инспектор. Затем нажмите на Set Native Size .

Теперь выберите Checkmark и повторите предыдущие шаги, но на этот раз используйте изображение settings_btn_sound_checkmark из Браузера проекта.

Вот, что вы должны увидеть во вкладке Сцена:


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

Выключение музыки

Сильной стороной обработчиков событий элементов UI является то, что иногда вы можете обойтись без написания какого-либо кода. Вместо этого, вы можете установить элемент пользовательского интерфейса, чтобы изменить свойство или непосредственно вызвать функцию компонента, подключенную к объекту. Вот как вы можете изменить свойство mute компонента Audio Source , прикрепленого к основной камере. Выберите tgl_sound в Иерархии, а в Инспекторе в списке найдите On Value Changed (Boolean) . Нажмите на + чтобы добавить новый элемент.

Перетащите MainCamera из иерархии на добавленный элемент. Откройте выпадающее меню с функциями и выберите AudioSource\mute из секции Dynamic bool на верху.


Примечание: Если вы внимательно посмотрите на варианты выбора функции, вы увидите два свойства mute: один в разделе Dynamic bool , а другой - в Static Parameters . Давайте разберем в чем разница. Если вы отключите звук в разделе Dynamic bool, значение функции будет изменяться на текущее значение свойства Active переключателя каждый раз, когда вы будете его переключать. Если вы отключите звук в разделе Static Parameters, откроется новое поле, и вы сможете установить постоянное значение функции в Инспекторе. Конечно, в разделе Dynamic bool находятся только свойства и методы, в основе которых лежат значения Bool, т.к. это активный тип переключателя. Но поскольку вы можете определить каждое значение как статический параметр, в разделе Static Parameters вы найдете все публичные свойства и методы.

Следовательно, когда для переключателя установлено свойство Active, он изменяет свойство Mute у AudioSource на true и выключает музыку. Запустите сцену, откройте окно с настройками и попробуйте выключить, а потом включить музыку.

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

То, что переключатель может синхронизировать свое активное и неактивное состояние с полем какого-нибудь другого объекта – это, конечно, хорошо, но что делать, если у вас диапазон значений? В этом случае, вы можете использовать элемент Slider UI. Выберите GameObject\Create UI\Slider для добавления слайдера. Переименуйте его в sdr_volume и положите его в dlg_settings .

Выберите sdr_volume в Иерархии и установите его Якоря Anchors в положение middle-right . Установите его Pivot раной (1, 0.5) так что вы могли бы позиционировать sdr_volume с помощью средней точки своего правого края. Наконец установите его Pos X , равной -20 , Pos Y - -10 , Width - 270 , а Height - 35 .

Теперь диалог Настройки должен выглядеть так, как сейчас:


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

  • Background: Изображение, которое показывает границы ползунка и его внутренней области, когда она не заполнена (например, когда рукоятка слева).
  • Handle: Изображение для рукоятки. Вы перетаскиваете ее чтобы изменить значение слайдера.
  • Fill: Изображение, которое тянется, чтобы показать значение ползунка.

На самом деле это изображение – не единственная часть, которая может тянуться, поэтому лучше использовать изображения, которые можно увеличить как минимум в 9 раз, для всех трех частей. Откройте папку Menu в Браузере проекта и найти три изображения, соответствующие каждой части слайдера: slider_background , slider_fill и slider_handle .

Для каждого изображения, откройте Sprite Editor в Инспекторе и задайте все значения Border равными восьми. Нажмите на Apply .

Теперь вам нужно установить соответствующее изображение для каждой части слайдера:

  1. Выберите sdr_volume и перетащите slider_background на Source Image в Инспекторе.
  2. Выберите Fill (не Fill Area) и перетащите slider_fill на Source Image .
  3. Выберите Handle и перетащите slider_handle на Source Image .

Если вы запустите сцену сейчас и откройте диалоговое окно Настройки, вы должны увидеть что-то вроде этого:

Изменение уровня громкости

Изменение громкости музыки с помощью ползунка похоже на то, что вы сделали с переключателем. Выберите sdr_volume в Иерархии. В инспекторе, прокрутите вниз, чтобы увидеть On Value Changed (Single) и нажмите на + чтобы добавить новый пункт. Перетащите MainCamera из Иерархии к этому новому пункту в списке, откройте выпадающий список функций и выберите AudioSource\volume в разделе Dynamic float .

Запустите сцену, откройте диалоговое окно Настройки (Settings) и измените значение слайдера. Вы должны услышать как изменяется громкость звука при перетаскивании ползунка. Лично я считаю, что это супер-крутая функция.

Что будет дальше?

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