Добавить в корзинуПозвонить
Найти в Дзене
Joseph & Adolf

Unity Basics. Игровые объекты и скрипты.

В этой статье: Это первое руководство из серии, посвященной изучению основ работы с Unity. В нем мы создадим простые часы и запрограммируем компонент, который будет отображать текущее время. Вам пока не нужно иметь никакого опыта работы с редактором Unity, но предполагается, что у вас есть некоторый опыт работы с многооконными редакторами. Это руководство создано с использованием Unity 2020.3.6f1. Прежде чем мы сможем начать работу с редактором Unity, мы должны сначала создать проект. Когда вы откроете Unity, вам будет представлен Unity Hub. Это приложение для запуска и установки, с помощью которого вы можете создавать или открывать проекты, устанавливать версии Unity и выполнять некоторые другие действия. Если у вас не установлена Unity 2020.3 или выше, установите её сейчас.
Когда вы создаете новый проект, вы можете выбрать его версию Unity и шаблон. Мы будем использовать стандартный 3D-шаблон. После создания он добавляется в список проектов и открывается в соответствующей версии ре
Оглавление

Создание часов

В этой статье:

  • Создадим часы из простых объектов
  • Напишем C# скрипт
  • Заставим стрелки часов вращаться
  • Анимируем часы.

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

Это руководство создано с использованием Unity 2020.3.6f1.

Пришло время создать часы
Пришло время создать часы

1. Создание проекта

Прежде чем мы сможем начать работу с редактором Unity, мы должны сначала создать проект.

1.1 Новый проект

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

Когда вы создаете новый проект, вы можете выбрать его версию Unity и шаблон. Мы будем использовать стандартный 3D-шаблон. После создания он добавляется в список проектов и открывается в соответствующей версии редактора Unity.

1.2 Макет редактора

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

Макет редактора по умолчанию.
Макет редактора по умолчанию.

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

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

1.3 Пакеты

Функциональность Unity является модульной. Помимо основной функциональности, существуют дополнительные пакеты, которые можно загрузить и включить в свой проект. Стандартный 3D-проект в настоящее время включает в себя несколько пакетов по умолчанию, которые вы можете увидеть в окне проекта в разделе Пакеты (Packages).

Пакеты по умолчанию.
Пакеты по умолчанию.

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

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

Менеджер пакетов, показывающий только пакеты в проекте.
Менеджер пакетов, показывающий только пакеты в проекте.

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

Самый простой способ удалить пакеты - сначала использовать панель инструментов, чтобы ограничить список пакетов только в проекте. Затем выберите пакеты по одному и нажмите кнопку Remove в правом нижнем углу окна. Unity будет пересобираться после каждого удаления.

После удаления всего, кроме
Visual Studio Editor, в окне проекта остаются три пакета: Custom NUnit, Test Framework и Visual Studio Editor. Два других остались, потому что от них зависит Visual Studio Editor.

Вы можете сделать зависимости и неявно импортированные пакеты видимыми в менеджере пакетов через окно настроек проекта, открываемое через
Edit / Project Settings... Выберите категорию "Package Manager", а затем включите отображение зависимостей (Show Dependencies) в разделе "Advanced Settings".

Настройки проекта диспетчера пакетов; Включено отображение зависимостей.
Настройки проекта диспетчера пакетов; Включено отображение зависимостей.

1.4 Цветовое пространство

В настоящее время рендеринг обычно выполняется в линейном цветовом пространстве, но Unity по умолчанию по-прежнему настроен на использование цветового пространства gamma. Для лучших визуальных результатов выберите Player в окне настроек проекта, откройте панель Other Settings и прокрутите вниз до раздела Rendering. Убедитесь, что цветовое пространство (Color Space) выбрано линейным (Linear). Unity выдаст предупреждение о том, что для этого понадобится много времени, но это не относится к почти пустому проекту. Подтвердите переключение.

Цветовое пространство задано как линейное.
Цветовое пространство задано как линейное.

1.5 Экземпляр сцены

Новый проект содержит экземпляр сцены с именем SampleScene, который открыт по умолчанию. Вы можете найти его ресурсы (ассеты) в разделе Assets / Scenes в окне проекта.

Экземпляр сцены в окне проекта.
Экземпляр сцены в окне проекта.

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

Макет в одну колонку.
Макет в одну колонку.

Экземпляр сцены содержит основную камеру (main camera) и направленный источник света (directional light). Это игровые объекты. Они перечислены в окне иерархии под сценой.

Иерархия объектов внутри сцены.
Иерархия объектов внутри сцены.

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

Значки в окне сцены.
Значки в окне сцены.

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

Скрытые объекты
Скрытые объекты

2. Создание простых часов

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


2.1 Создаем игровой объект

Нам нужен игровой объект для представления часов. Мы начнем с самого простого из возможных игровых объектов, который является пустым. Его можно создать с помощью пункта меню GameObject / Create Empty. В качестве альтернативы вы можете воспользоваться опцией "Create Empty" в контекстном меню окна "Hierarchy", которое обычно открывается щелчком правой кнопкой мыши. Добавится игровой объект в сцену. Он сразу же выбирается в окне "Hierarchy" в разделе "SampleScene", который теперь помечен звездочкой, чтобы указать, что в нем есть несохраненные изменения. Вы также можете изменить его название сейчас или оставить это на потом.

Иерархия с выбранным новым игровым объектом.
Иерархия с выбранным новым игровым объектом.

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

Окно инспектора с выбранными часами.
Окно инспектора с выбранными часами.

Под заголовком находится список всех компонентов игрового объекта. Вверху списка всегда находится компонент Transform, который на данный момент является всем, что есть в наших часах. Он управляет положением, поворотом и масштабом игрового объекта. Убедитесь, что все значения положения и поворота часов установлены на 0. Их шкала должна быть равномерно равна 1.

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

Объект выбран с помощью инструмента перемещения.
Объект выбран с помощью инструмента перемещения.

Какой инструмент активен, можно определить с помощью кнопок в левом верхнем углу панели инструментов редактора. Режимы также можно активировать с помощью клавиш Q, W, E, R, T и Y. Самая правая кнопка в группе предназначена для включения пользовательских инструментов редактирования, которых у нас нет. Инструмент перемещения активен по умолчанию.

Панель инструментов.
Панель инструментов.

Рядом с кнопками режимов находятся еще три кнопки для управления размещением, ориентацией и привязкой инструментов манипуляции.

2.2 Создаем циферблат часов

Хотя у нас есть объект clock, мы пока ничего не видим. Нам нужно добавить к нему 3D-модели, чтобы что-то получилось. Unity содержит несколько примитивных объектов, которые мы можем использовать для создания простых часов. Давайте начнем с добавления цилиндра в сцену с помощью GameObject / 3D Object / Cylinder. Убедитесь, что он имеет те же значения в Transform, что и наши часы.

Игровой объект, представляющий собой цилиндр.
Игровой объект, представляющий собой цилиндр.

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

Элемент MeshFilter, установленный в виде цилиндра.
Элемент MeshFilter, установленный в виде цилиндра.

Второй - это MeshRenderer. Цель этого компонента - обеспечить визуализацию сетки объекта. Он также определяет, какой материал используется для рендеринга, который является материалом по умолчанию. Этот материал также отображается в инспекторе под списком компонентов.

Компонент MeshRenderer, для которого установлен материал по умолчанию.
Компонент MeshRenderer, для которого установлен материал по умолчанию.

Третий - CapsuleCollider, который предназначен для 3D-физики. Объект представляет собой цилиндр, но у него есть капсульный коллайдер, потому что в Unity нет примитивного цилиндрического коллайдера. Он нам не нужен, поэтому мы можем удалить этот компонент. Если вы хотите использовать физику в своих часах, вам лучше использовать компонент MeshCollider. Компоненты можно удалить с помощью выпадающего меню с тремя точками в правом верхнем углу.

Цилиндр без коллайдера.
Цилиндр без коллайдера.

Мы превратим цилиндр в циферблат часов, сплющив его. Для этого уменьшим Y-составляющую шкалы. Уменьшим ее до 0,2. Поскольку высота ячейки цилиндра составляет две единицы, ее эффективная высота составит 0,4. Давайте также сделаем большие часы, поэтому увеличим значения X и Z в их масштабе до 10.

Предполагается, что наши часы должны стоять или висеть на стене, но в данный момент их циферблат находится в горизонтальном положении. Мы можем исправить это, повернув цилиндр на четверть оборота. В Unity ось X направлена вправо, ось Y - вверх, а ось Z - вперед. Итак, давайте спроектируем наши часы с учетом той же ориентации, а это означает, что мы видим их лицевую сторону, когда смотрим на них вдоль оси Z. Установите угол поворота цилиндра по оси X на 90 и отрегулируйте вид сцены таким образом, чтобы была видна лицевая сторона часов, а синяя стрелка Z инструмента перемещения указывала от вас в сторону экрана.

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

Циферблат как потомок Clock
Циферблат как потомок Clock

Потомки подвержены преобразованию. Это означает, что когда часы меняют положение, то же самое происходит и с циферблатом. Создается впечатление, что они представляют собой единое целое. То же самое касается поворота и масштабирования. Это можно использовать для создания сложных иерархий объектов.

2.3 Создание периферии часов

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

Добавьте объект cube в сцену с помощью
GameObject / 3D Object / Cube, назовите его Hour Indicator 12, а также сделайте его потомком Clock. Порядок расположения потомков в иерархии не имеет значения, вы можете разместить его как над циферблатом, так и под ним.

Hour Indicator 12 потомок Clock
Hour Indicator 12 потомок Clock

Установите его масштаб по оси X равным 0,5, по оси Y равным 1, а по оси Z равным 0,1, чтобы он превратился в плоский длинный блок. Затем установите его положение по оси X равным 0, по оси Y равным 4, а по оси Z равным -0,25. Он поместится поверх циферблата, указывая на 12-й час. Также удалите компонент BoxCollider.

Индикатор на 12-м часу.
Индикатор на 12-м часу.

Индикатор плохо виден, потому что он того же цвета, что и циферблат. Давайте создадим для него отдельный материал, используя Assets / Create / Material, или с помощью кнопки "плюс", или контекстного меню окна проекта. В результате мы получаем ассет материала, который является дубликатом материала по умолчанию. Измените его название на Hour Indicator.

Выберите материал и измените его альбедо на что-то другое, щелкнув по его цветовому полю. Откроется всплывающее окно "Цвет", в котором предлагаются различные способы выбора цвета. Я выбрал темно-серый цвет, соответствующий шестнадцатеричному 494949, который соответствует 73 для RGB 0-255. Мы не используем альфа-канал, поэтому его значение не имеет значения. Мы также можем оставить все остальные свойства материала без изменений.

Темно-серое альбедо.
Темно-серое альбедо.

Сделайте так, чтобы часовой индикатор использовал этот материал. Вы можете сделать это, перетащив материал на объект в окне "Scene" или "Hierarchy". Вы также можете перетащить его в нижнюю часть окна инспектора, когда выбран игровой объект индикатора, или изменить Element 0 массива Materials его MeshRenderer.

Индикатор "темного часа".
Индикатор "темного часа".

2.4 Двенадцать часовых индикаторов

Мы могли бы обойтись одним индикатором для 12-го часа, но давайте включим по одному на каждый час. Для начала сориентируем камеру для просмотра сцены так, чтобы она смотрела прямо вниз по оси Z. Это можно сделать, щелкнув по конусам осей на приспособлении для просмотра камеры в правом верхнем углу экрана. Вы также можете изменить ось сетки сцены на Z с помощью кнопки "grid" на панели инструментов.

Смотрим прямо на часы, вдоль оси Z.
Смотрим прямо на часы, вдоль оси Z.

Дублируйте игровой объект Hour Indicator 12. Вы можете сделать это с помощью Edit / Duplicate, с помощью указанного сочетания клавиш или через контекстное меню в окне иерархии. Дубликат появится под оригиналом в окне иерархии, также являясь потомком Clock. Он будет называться Hour Indicator 12 (1). Переименуйте его в Hour Indicator 6 и уберите Y-составляющую его положения, чтобы он указывал на 6-й час.

Создайте индикаторы для 3 и 9 часов таким же образом. В этом случае их позиции по оси X должны быть равны 4 и -4, а позиции по оси Y должны быть равны нулю. Кроме того, установите их поворот по оси Z на 90 градусов, чтобы они были повернуты на четверть круга.

Четыре часовых индикатора.
Четыре часовых индикатора.

Затем создайте еще один дубликат Hour Indicator 12, на этот раз для первого часа. Установите его положение по оси X равным 2, положение по оси Y равным 3,464, а угол поворота по оси Z равным -30. Затем продублируйте его на 2-й час, поменяйте местами позиции X и Y и удвоьте вращение по Z до -60.

Индикаторы для 1-го и 2-го часов.
Индикаторы для 1-го и 2-го часов.

Дублируйте эти два индикатора и измените их положение по оси Y и их повороты, чтобы создать индикаторы для часов 4 и 5. Затем используйте тот же прием для часов 1, 2, 4 и 5, чтобы создать остальные индикаторы, на этот раз изменив их положение по оси X и снова их повороты.

Все часовые индикаторы.
Все часовые индикаторы.

2.5 Создание стрелок

Следующим шагом будет создание стрелок часов. Мы начнем с часовой стрелки. Снова продублируем Hour Indicator 12 и назовем его Hours Arm. Затем создадим материал Clock Arm и используем его в качестве стрелки. В данном случае я сделал его полностью чёрным, в шестнадцатеричном формате: 000000. Уменьшите масштаб стрелки по X до 0,3 и увеличьте по Y до 2,5. Затем измените положение по Y на 0,75, чтобы она указывала на 12-й час, но оставьте стрелку немного в противоположном направлении. Это создаст впечатление, что при вращении стрелка имеет небольшой противовес.

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

Стрелка часов вращается вокруг своего центра.
Стрелка часов вращается вокруг своего центра.

Это происходит потому, что вращение происходит относительно локального положения игрового объекта. Чтобы создать соответствующее вращение, мы должны ввести вращающийся объект и вращать его вместо стрелки. Поэтому создадим новый пустой игровой объект и сделаем его потомком Clock. Можно сделать это напрямую, создав объект через контекстное меню Clock в окне иерархии. Назовите его Hours Arm Pivot и убедитесь, что его положение и поворот равны нулю, а масштаб равен 1. Затем сделайте Hours Arm потомком Hours Arm Pivot.

Часовая стрелка с поворотным механизмом.
Часовая стрелка с поворотным механизмом.

Теперь попробуйте повернуть объект вращения (pivot). Если вы делаете это в режиме просмотра сцены, убедитесь, что режим положения ручки инструмента установлен на Pivot, а не на Center.

Стрелка часов вращается вокруг оси.
Стрелка часов вращается вокруг оси.

Дважды продублируйте часовой механизм (Hours Arm Pivot), чтобы создать минутный (Minutes Arm Pivot) и секундный (Seconds Arm Pivot). Переименуйте их соответствующим образом, включая дублированных потомков стрелок (Arm).

Все иерархии стрелок.
Все иерархии стрелок.

Минутная стрелка (Minutes Arm) должна быть более узкой и длинной, чем часовая (Hours Arm), поэтому установите её шкалу X на 0,2, а шкалу Y на 4, затем увеличьте положение Y до 1. Также измените положение Z на -0,35, чтобы она располагалась поверх часовой. Обратите внимание, что это относится к стрелке, а не к его оси вращения.

Преобразование минутной стрелки.
Преобразование минутной стрелки.

Также отрегулируйте секундную стрелку. На этот раз используйте значения 0,1 и 5 для шкалы X, Y и 1,25 и -0,45 для шкалы Y, Z соответственно.

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

Давайте выделим секундную стрелку, создав для нее отдельный материал. Я придал ей темно-красный цвет, в шестнадцатеричном виде: B30000. Кроме того, я отключил сетку в окне сцены, когда мы закончили создавать наши часы.

Часы с тремя стрелками.
Часы с тремя стрелками.

Если вы еще этого не сделали, то сейчас самое время сохранить сцену с помощью команды File / Save.

Также рекомендуется систематизировать ресурсы проекта. Поскольку у нас есть три материала, давайте поместим их в папку материалов (
Materials), которую мы создадим с помощью Assets / Create / Folder или через окно проекта. Затем вы можете перетащить материалы туда.

3. Анимация часов

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


3.1 C# Script Asset

Добавьте новый скриптовый ассет в проект с помощью Assets / Create / C# Script и назовите его Clock. C# - это язык программирования, используемый для скриптов Unity. Давайте также сразу же поместим его в новую папку Scripts, чтобы поддерживать порядок в проекте.

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

Инспектор ресурса часов C#.
Инспектор ресурса часов C#.

3.2 Определение типа компонента

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

Пустой файл ничего не определяет. Он должен содержать определение нашего компонента clock. Мы собираемся определить не отдельный экземпляр компонента. Вместо этого мы определяем общий класс или тип, известный как Clock. Как только это будет установлено, мы сможем создать несколько таких компонентов в Unity, хотя в этом руководстве мы ограничимся одним clock.

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

class Clock

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

public class Clock

На данный момент у нас еще нет корректного синтаксиса C#. Если вы сохраните файл и вернетесь в редактор Unity, то в его консольном окне появятся ошибки компиляции.

Мы указали, что определяем тип, поэтому должны определить, что он из себя представляет. Это делается с помощью блока кода, который следует за объявлением. Границы блока кода обозначены фигурными скобками. Пока что мы оставляем их пустыми, поэтому просто напишите {}.

public class Clock {}

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

Бескомпонентный скрипт.
Бескомпонентный скрипт.

Это означает, что мы не можем использовать этот скрипт для создания компонентов в Unity. На данный момент наш Clock определяет базовый тип объекта C#. Наш пользовательский тип компонента должен расширять тип MonoBehaviour Unity, наследуя его данные и функциональность.

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

public class Clock : MonoBehaviour {}

Однако это приведет к ошибке после компиляции. Компилятор жалуется, что не может найти тип MonoBehaviour. Это происходит потому, что тип содержится в пространстве имен, которым является UnityEngine. Чтобы получить к нему доступ, мы должны использовать его полное название UnityEngine.MonoBehaviour.

public class Clock : UnityEngine.MonoBehaviour {}

Неудобно постоянно добавлять префикс UnityEngine при обращении к типам Unity. К счастью, мы можем сделать, чтобы компилятор автоматически использовал всё что находится в пространстве имён. Это делается путем добавления using UnityEngine; в самом начале файла. Точка с запятой необходима для обозначения конца инструкции.

using UnityEngine;
public class Clock :
MonoBehaviour {}

Теперь мы можем добавить наш пользовательский компонент к игровому объекту Clock в Unity. Это можно сделать либо перетащив скрипт ассет на объект, либо нажав кнопку "Add Component" в нижней части инспектора объектов.

Игровой объект Clock с нашим компонентом Clock.
Игровой объект Clock с нашим компонентом Clock.

3.3 Запускаем стрелку

Чтобы стрелки вращались, объекты Clock должны знать о них. Давайте начнем с часовой стрелки. Как и все игровые объекты, его можно вращать, изменив его компонент Transform. Поэтому мы должны добавить в Clock знания о компоненте Transform для поворота стрелки. Это можно сделать, добавив поле данных в его блок кода, определяемое как имя, за которым следует точка с запятой.

Для этого поля было бы подходящим название hours pivot. Однако названия должны состоять из одного слова. Принято делать первое слово в названии поля строчным, а все остальные слова - заглавными, а затем соединять их вместе. Поэтому мы назовем его hoursPivot.

public class Clock : MonoBehaviour {

hoursPivot;
}

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

Transform hoursPivot;

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

Поля по умолчанию являются приватными (private), что означает, что доступ к ним возможен только с помощью кода, принадлежащего Clock. Но класс не знает о нашей Unity сцене, поэтому нет прямого способа связать поле с нужным объектом. Мы можем изменить это, объявив поле сериализуемым. Это означает, что он должен быть включен в данные сцены, когда Unity сохраняет сцену, что происходит путем помещения всех данных в последовательность, их сериализации и записи в файл.

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

[SerializeField]
Transform hoursPivot;

Как только поле будет сериализуемым, Unity обнаружит это и отобразит его в окне инспектора компонента Clock нашего игрового объекта Clock.

Поле поворота часов.
Поле поворота часов.

Чтобы установить правильное соединение, перетащите Hours Arm Pivot из иерархии в поле Hours Pivot. В качестве альтернативы, воспользуйтесь круглой кнопкой справа от поля и найдите pivot в появившемся списке. В обоих случаях редактор Unity захватывает компонент Transform от Hours Arm Pivot и помещает ссылку на него в наше поле.

Часы соединены между собой.
Часы соединены между собой.

3.4 Зная все три стрелки часов

Мы должны сделать то же самое для минутной и секундной стрелок. Поэтому добавьте в Clock еще два сериализуемых поля Transform с соответствующими названиями.

[SerializeField]
Transform hoursPivot;
[SerializeField]
Transform minutesPivot;
[SerializeField]
Transform secondsPivot;

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

[SerializeField]
Transform hoursPivot, minutesPivot, secondsPivot;

Подключите также две другие стрелки в редакторе.

Все три механизма соединены.
Все три механизма соединены.

3.5 Пробуждение

Теперь, когда у нас есть доступ к осям вращения стрелок, следующим шагом будет их вращение. Для этого нам нужно сказать Clock исполнить код. Это делается путем добавления в класс блока кода, известного как метод. Блок должен иметь префикс с именем, которое по традиции пишется с заглавной буквы. Мы назовем его Awake, предполагая, что код должен выполняться при пробуждении компонента.

public class Clock : MonoBehaviour {

[
SerializeField]
Transform hoursPivot, minutesPivot, secondsPivot;

Awake {}
}

Методы чем-то похожи на математические функции, например, f(x)=2x+3. Эта функция принимает число, представленное переменным параметром x, удваивает его, а затем прибавляет три. Она оперирует с одним числом, и ее результатом также является одно число. В случае метода это больше похоже на f(p)=c, где p представляет входные параметры, а c представляет собой код, который он выполняет.

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

void Awake {}

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

void Awake () {}

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

3.6 Вращение с помощью кода

Чтобы вращать стрелки, мы должны создать новое вращение. Мы можем изменить вращение Transform, присвоив новое вращение ее свойству localRotation.

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

Мы можем создать кватернион на основе углов Эйлера, вызвав метод Quaternion.Euler. Для этого запишите его в Awake, а в конце инструкции поставьте точку с запятой.

void Awake () {
Quaternion.Euler;
}

У метода есть параметры, используемые для описания желаемого вращения. В этом случае мы приведем разделенный запятыми список, содержащий три аргумента, все в круглых скобках, после названия метода. Мы указываем три числа для вращений по X, Y и Z. Используйте ноль для первых двух и -30 для поворота по оси Z.

Quaternion.Euler(0, 0, -30);

Результатом этого вызова является значение структуры кватернионов, содержащее поворот на 30° по часовой стрелке вокруг оси Z, соответствующий 1 часу на наших часах.

Чтобы применить это вращение к часовой стрелке, присвойте результат Quaternion.Euler к hoursPivots.localRotation, используя оператор присваивания =.

hoursPivot.localRotation = Quaternion.Euler(0, 0, -30);

Теперь войдите в режим воспроизведения в редакторе. Это можно сделать с помощью Edit / Play или нажав кнопку play в верхней центральной части окна редактора. Unity переключит фокус на игровое окно, которое отображает то, что видит основная камера в сцене. Активируется компонент часов, и часы будут установлены на один час.

В режиме воспроизведения всегда один час.
В режиме воспроизведения всегда один час.

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


3.7 Получаем текущее время

Следующий шаг - определить текущее время. Мы можем использовать структуру DateTime для доступа к системному времени устройства, на котором мы работаем. DateTime не является типом Unity, он находится в пространстве имен System. Он является частью основной функциональностью фреймворка .NET, который используется в Unity для поддержки скриптов.

У
DateTime есть свойство Now, которое выдает значение DateTime, содержащее текущую системную дату и время. Чтобы проверить правильность этого значения, мы выведем его в консоль при запуске Awake. Мы можем сделать это, передав его в метод Debug.Log.

using System;
using UnityEngine;

public class Clock :
MonoBehaviour {

[
SerializeField]
Transform hoursPivot, minutesPivot, secondsPivot;

void
Awake () {
Debug.Log(DateTime.Now);
hoursPivot.localRotation =
Quaternion.Euler(0, 0, -30);
}
}

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


3.8 Вращение стрелок

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

Debug.Log(DateTime.Now.Hour);

Таким образом, чтобы стрелка часов показывала текущий час, мы должны умножить поворот на -30° на текущий час. Умножение выполняется с помощью символа звездочки *. Нам также больше не нужно регистрировать текущее время, поэтому мы можем избавиться от этого оператора.

hoursPivot.localRotation = Quaternion.Euler(0, 0, -30 * DateTime.Now.Hour);
Сейчас в режиме воспроизведения четыре часа.
Сейчас в режиме воспроизведения четыре часа.

Чтобы было понятно, что мы переводим часы в градусы, мы можем задать поле hoursToDegrees, содержащее коэффициент преобразования. Углы в Quaternion.Euler определяются как значения с плавающей точкой, поэтому мы будем использовать тип float. Поскольку мы уже знаем число, мы можем сразу же присвоить его как часть описания поля. Затем умножим на поле вместо буквального значения -30 в Awake.

float hoursToDegrees = -30;
[SerializeField]
Transform hoursPivot, minutesPivot, secondsPivot;
void Awake () {
hoursPivot.localRotation =
Quaternion.Euler(0, 0, hoursToDegrees * DateTime.Now.Hour);
}

Если мы объявляем целое число без суффикса, то предполагается, что это целое число, которое относится к другому типу значений. Хотя компилятор преобразует их автоматически, давайте явно укажем, что все наши числа имеют тип float, добавив к ним суффикс f.

float hoursToDegrees = -30f;
[SerializeField]
Transform hoursPivot, minutesPivot, secondsPivot;
void Awake () {
hoursPivot.localRotation =
Quaternion.Euler(0f, 0f, hoursToDegrees * DateTime.Now.Hour);
}

Количество градусов в час всегда одинаково. Мы можем обеспечить это, добавив префикс const к объявлению hoursToDegrees. Это превратит его в константу, а не в поле.

const float hoursToDegrees = -30f;

Давайте проделаем то же самое с двумя другими стрелками, используя соответствующие свойства DateTime. И минута, и секунда представлены вращением на отрицательные шесть градусов.

const float hoursToDegrees = -30f, minutesToDegrees = -6f, secondsToDegrees = -6f;
[SerializeField]
Transform hoursPivot, minutesPivot, secondsPivot;
void Awake () {
hoursPivot.localRotation =
Quaternion.Euler(0f, 0f, hoursToDegrees * DateTime.Now.Hour);
minutesPivot.localRotation =
Quaternion.Euler(0f, 0f, minutesToDegrees * DateTime.Now.Minute);
secondsPivot.localRotation =
Quaternion.Euler(0f, 0f, secondsToDegrees * DateTime.Now.Second);
}
Сейчас 5:16:31.
Сейчас 5:16:31.

Мы используем DateTime.Now три раза, чтобы получить час, минуту и секунду. Каждый раз мы снова проверяем свойство, что требует некоторой доработки, которая теоретически может привести к разным значениям времени. Чтобы этого не произошло, мы должны получать время только один раз. Мы можем сделать это, объявив переменную внутри метода и присвоив ей время, а затем использовать это значение в дальнейшем. Назовем ее time.

void Awake () {
DateTime time = DateTime.Now;
hoursPivot.localRotation =
Quaternion.Euler(0f, 0f, hoursToDegrees * time.Hour);
minutesPivot.localRotation =
Quaternion.Euler(0f, 0f, minutesToDegrees * time.Minute);
secondsPivot.localRotation =
Quaternion.Euler(0f, 0f, secondsToDegrees * time.Second);
}

В случае переменной можно опустить объявление типа, заменив его ключевым словом var. Это может сократить код, но возможно только в том случае, если тип переменной можно определить из того, что ей присваивается при объявлении. Кроме того, я предпочитаю делать это только в том случае, если тип явно указан в инструкции, что и имеет место в данном случае.

var time = DateTime.Now;

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

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

void Update () {
var time =
DateTime.Now;
hoursPivot.localRotation =
Quaternion.Euler(0f, 0f, hoursToDegrees * time.Hour);
minutesPivot.localRotation =
Quaternion.Euler(0f, 0f, minutesToDegrees * time.Minute);
secondsPivot.localRotation =
Quaternion.Euler(0f, 0f, secondsToDegrees * time.Second);
}
Обновление часов.
Обновление часов.

Обратите внимание, что наш компонент Clock получил переключатель перед своим названием в инспекторе. Это позволит нам отключить его, что не позволит Unity вызывать его метод Update.

Компонент Clock, который может быть отключен.
Компонент Clock, который может быть отключен.

3.10 Непрерывное вращение

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

DateTime не содержит дробных данных. К счастью, у него есть свойство TimeOfDay. Это дает нам значение TimeSpan, которое содержит данные в нужном нам формате, используя свойства TotalHours, TotalMinutes и TotalSeconds.

Начнём с получения значения структуры TimeOfDay из
DateTime.Now и сохраним его в переменной. Поскольку тип TimeSpan в этом заявлении не упоминается, я сделаю тип переменной явным. Затем изменим свойства, которые мы используем для вращения стрелок.

void Update () {
TimeSpan time = DateTime.Now.TimeOfDay;
hoursPivot.localRotation =
Quaternion.Euler(0f, 0f, hoursToDegrees * time.TotalHours);
minutesPivot.localRotation =
Quaternion.Euler(0f, 0f, minutesToDegrees * time.TotalMinutes);
secondsPivot.localRotation =
Quaternion.Euler(0f, 0f, secondsToDegrees * time.TotalSeconds);
}

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

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

hoursPivot.localRotation = Quaternion.Euler(0f, 0f, hoursToDegrees * (float)time.TotalHours);
minutesPivot.localRotation =
Quaternion.Euler(0f, 0f, minutesToDegrees * (float)time.TotalMinutes);
secondsPivot.localRotation =
Quaternion.Euler(0f, 0f, secondsToDegrees * (float)time.TotalSeconds);
Аналоговые часы.
Аналоговые часы.

Теперь вы знаете основы создания объектов и написания кода в Unity. Следующее руководство будет посвящено построению графика.