Unity

Разработка вашей первой игры с помощью Unity и C#. Часть 3

Адам Тьюлипер

Продукты и технологии:

Unity, C#, Microsoft .NET Framework

В статье рассматриваются:

  • вращение и перемещение объектов;
  • анимации и террейны;
  • системы координат в Unity;
  • потоки и сопрограммы (coroutines).

Вы все еще со мной в этом цикле статей? Хорошо. В первой статье я рассказал о некоторых основах Unity (msdn.microsoft.com/magazine/dn759441). Во второй статье я сосредоточился на двухмерной графике (2D) в Unity (msdn.microsoft.com/magazine/dn781360). Теперь я перейду к своей любимой части разработки игр — к трехмерной графике (3D). Трехмерный мир — это воистину волшебное место: потрясающие среды, создающие эффект полного погружения в мир игры, богатые звуковые эффекты и красивые визуальные средства; даже простая игра-головоломка с реалистичной физикой может зацепить так, что вы часами будете в нее играть.

3D-игры определенно добавляют уровень сложности по сравнению с 2D, но шаг за шагом вы постепенно создаете интересную 3D-игру. Новые параметры проектов как для 2D, так и для 3D, в Unity поддерживают 3D. В 2D-игре могут быть 3D-объекты и наоборот.

Из чего состоит трехмерная сцена?

Трехмерная (3D) сцена состоит главным образом из трех основных визуальных компонентов: источников света (lights), рендеров мешей (mesh renderers) и шейдеров. Unity поддерживает четыре типа источников света. Вы найдете их в меню GameObject. Поэкспериментируйте, добавляя разные типы и изменяя их свойства. Самый простой из них — источник направленного света (directional light), который подобен солнцу на небе.

Меш (mesh), или модель, — это набор вершин, образующие многоугольники, из которых состоит какой-либо объект. Шейдер является скомпилированной подпрограммой, содержащей код для управления тем, как будет отображаться ваш объект или как он будет взаимодействовать с источником света. Некоторые шейдеры просто берут освещение и отражают его подобно зеркалу, другие — принимают текстуру (изображение, применяемое к мешу) и могут создавать тени и глубину, а некоторые даже позволяют прорезать визуальные дыры в ваших моделях (пример — изгородь).

Модели обычно содержатся в файлах .fbx или .obj, экспортируемых из другого программного пакета для моделирования. FBX-файлы также могут хранить данные анимаций, поэтому вы можете получить один FBX-файл для своей модели и еще один, который содержит несколько анимаций. Также поддерживается ряд сторонних файловых форматов, таких как Autodesk Maya (.ma) и Blender. Как правило, сторонняя программа должна быть установлена в той же системе, если вы хотите, чтобы Unity импортировал эти файлы, а затем предоставлял возможность их простого перетаскивания в проект Unity подобно любым другим файлам. «За кулисами» Unity будет преобразовывать другие файловые форматы (при импорте или обнаружении изменений в файлах) в формат .fbx.

Asset Store

Я вкратце рассказывал об Asset Store (магазине ресурсов) в своей первой статье, но по-настоящему полезным он становится при разработке 3D-игр. Я не художник и, поскольку это технический журнал, полагаю, что большинство из вас тоже не являются художниками. (Если же вы художник, пожалуйста, примите мои поздравления — это редкий дар.) Но, если вы хотите создать игру, например, с буйной растительностью и старыми полуразрушенными зданиями, это не проблема. Я могу купить то, что мне нужно, в Asset Store. Если мне нужны 15 видов зомби, я могу приобрести пакет от Mixamo в Asset Store. Потенциально возможные комбинации почти бесконечны, так что не волнуйтесь о том, что чья-то игра будет выглядеть похожей на вашу. А самое главное в том, что Asset Store интегрирован с Unity. Вы можете обновлять свои пакеты, щелкая Window | Asset Store, а затем значок корзины. Вы также можете просматривать рецензии и комментарии, чтобы быстрее понять, подойдет ли конкретный элемент для вашего проекта, например оптимизирован ли он для мобильного устройства. Настольные игры обычно используют гораздо больше объектов, текстур, вершин, памяти, чем мобильная игра, хотя некоторые из более новых чипов делают сегодня мобильные устройства близкими по своим возможностям к Xbox 360.

В типичной 3D-игре применяются многие из тех же концепций, что и в 2D-играх: коллайдеры, триггеры, абсолютно твердые тела (rigid bodies), игровые объекты/преобразования, компоненты и др. Независимо от типа 3D-игры обычно вам нужно контролировать ввод, перемещения и персонажей, использовать анимации и эффекты частиц, а также выстраивать воображаемый мир так, чтобы он был и фантастическим, и реалистическим. Мы обсудим некоторые способы, которыми Unity может помочь добиться всего этого.

Контроллеры ввода, перемещения и персонажей

Чтение ввода для перемещения в 3D немного усложняется, потому что вместо простого движения в плоскостях X и Y теперь можно двигаться в трех измерениях: X, Y и Z. Варианты 3D-перемещения включают (но не исчерпываются ими) движение сверху вниз, где персонаж двигается только горизонтально и вертикально; поворачивание камеры или персонажа при считывании ввода от мыши, как это делается во многих шутерах от первого лица (first-person shooter, FPS); смещение влево и вправо при чтении ввода по горизонтали; вращение вокруг себя при чтении ввода по горизонтали или простое движение в обратном направлении. Вариантов перемещения очень много, так что вам есть из чего выбирать.

Перемещая объект, вы не сообщаете ему позицию, в которую он должен переместиться, как вы, возможно, ожидали. Вспомните, что вы выполняете код на каждом кадре, поэтому должны смещать объект с небольшим приращением. Можно либо отдать это на откуп ядру поддержки физики, приложив силу к абсолютно твердому телу, либо создать переходную анимацию (tween) объекта. Последнее, по сути, означает переход между значениями, т. е. движение из точки A в точку B. Есть разные способы создания соответствующего набора значений в Unity, в том числе применение бесплатных сторонних библиотек, таких как iTween. На рис. 1 показаны некоторые ручные способы перемещения объекта в Unity. Заметьте, что для простоты они не оптимизированы (для оптимизации мне пришлось бы хранить ссылку на преобразование в переменной, чтобы избежать частого переключения между управляемым и «родным» кодом).

Рис. 1. Различные методы перемещения объектов

// Метод 1
void Update()
{
  // Перемещение из точки A в точку B на 0.2 в каждом кадре.
  // Предполагается вызов в Update. Чтобы не промахнуться
  // мимо точки назначения, максимальное смещение - 0.2.
  transform.position = 
Vector3.MoveTowards(transform.position, new Vector3(10, 1, 100), .2f);
}
// Метод 2
void Update()
{
  // Интерполяция от точки A к точке B на процентную долю
  // на каждом кадре – в данном случае на 10% (0.1 float)
  var targetPosition = new Vector3(10,0,15);
  transform.position = Vector3.Lerp(parentRig.position, targetPosition, .1f);
}
// Метод 3
void Update()
{
  // Телепортируем объект в направлении его поворота.
  // Если вы поворачиваете объект на 90 градусов, он сместится
  // в направлении, в котором он сейчас смотрит. По сути, это
  // преобразование локальных координат в мировые для смещения
  // объекта в направлении и на расстояние, указанные вектором.
  // См. раздел "Системы координат в Unity" в этой статье.
  transform.Translate(Vector3.forward * Time.deltaTime);
}
// Метод 4
void FixedUpdate()
{
  // Заставляет объект действовать так, словно его толкнули
  // вправо (положительная ось X). Вы также можете использовать
  // (Vector.right * someForce) вместо нового Vector().
  rigidbody.AddForce( new Vector3(7, 0, 0), ForceMode.Force);
}
// Метод 5
void FixedUpdate()
{
  // Заставляет объект действовать так, словно его толкнули
  // по положительной оси X (в мировых координатах)
  // со скоростью примерно 7 м/с. Объект будет замедляться
  // из-за трения.
  rigidbody.velocity = new Vector3(7,0,0);
}
// Метод 6
// Перемещаем позицию абсолютно твердого тела
// (заметьте, что это не делается через преобразование).
// В этом случае объект будет расталкивать другие объекты
// у него на пути и перемещаться вправо в мировых координатах
// со скоростью примерно 3 единицы/с.
private Vector3 speed = new Vector3(3, 0, 0);
void FixedUpdate()
{
  rigidbody.MovePosition(rigidbody.position + speed * Time.deltaTime);
}
// Метод 7
void FixedUpdate()
{
  // Vector3.forward - 0,0,1. Вы могли бы переместить персонаж
  // в 0,0,1, но на самом деле вам нужно переместить объект
  // независимо от его вращения. Используется, когда персонаж
  // должен двигаться в направлении, в котором он смотрит
  // независимо от его вращения.
  // Вы должны преобразовать содержимое этого вектора из
  // локального пространства (0,0,1) в мировое; для этого
  // можно использовать TransformDirection и присвоить
  // данному вектору его скорость.
  rigidbody.velocity = transform.TransformDirection(Vector3.forward * speed);
}

У каждого подхода есть свои плюсы и минусы. При перемещении только с помощью преобразования производительность может пострадать (методы 1–2), хотя это очень простой способ перемещения. Unity предполагает: если у объекта нет компонента rigidbody, он, вероятно, не является перемещаемым объектом. Она создает на внутреннем уровне статическую матрицу столкновений (static collision matrix), чтобы знать, где находятся объекты; это повышает производительность. Когда вы перемещаете объекты с помощью преобразования, эту матрицу нужно пересчитывать, что приводит к уменьшению производительности. В простых играх вы никогда не заметите этого падения производительности, и такие варианты могут оказаться для вас самыми простыми, хотя по мере усложнения ваших игр важно перемещать само абсолютно твердое тело, как в методах 4–6.

Поворачивание объектов

Поворачивание объектов — операция сравнительно несложная, во многом аналогичная перемещению объектов, но теперь векторы представляют градусы, а не позицию или нормализованный вектор. Нормализованный вектор — это просто вектор с максимальным значением 1 для любого значения, и он может использоваться, когда вам нужно лишь ссылаться на направление по вектору. Вам доступны некоторые ключевые слова, относящиеся к векторам, например Vector3.right, back, forward, down, up, left, right, zero и one. Все, что будет перемещаться или поворачиваться в положительном горизонтальном направлении, может использовать Vector.right, под которым подразумевается (1,0,0), или одна единица вправо, а в случае поворачиваемого объекта это один градус. На рис. 2 я просто понемногу поворачиваю объект в каждом кадре.

Рис. 2. Метод для поворачивания объекта

// Предполагается, что любой код, использующий _player,
// размещается до _player, чтобы кешировать ссылку на него
private GameObject _player;
void Start()
{
  _player = GameObject.FindGameObjectWithTag("Player");
}
// Метод 1
void Update () {
  // В каждом кадре происходит поворот вокруг оси X
  // на 1 градус в секунду (Vector3.right = (1,0,0))
  transform.Rotate(Vector3.right * Time.deltaTime);
}
// Метод 2
void Update () {
  // Независимо от того, куда движется игрок,
  // поворачиваемся к нему подобно пулеметной
  // турели, следящей за целью
  transform.LookAt(_player.transform);
}
// Метод 3
void Update()
{
  Vector3 relativePos = _player.transform.position -  transform.position;
  // Если вы напрямую задаете поворачивание,
  // делайте это через Quaternion
  transform.rotation = Quaternion.LookRotation(relativePos);
}

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

Вы обратили внимание на Quaternion в методе 3? Unity использует на внутреннем уровне эти Quaternion для представления всех поворачиваний. Quaternion — эффективные структуры, предотвращающие эффект, который называется шарнирной блокировкой (gimbal lock). Он возможен, если для поворачивания вы используете обычные углы Эйлера (Euler angles). Шарнирная блокировка происходит, когда две поворачиваемые оси оказываются в одной плоскости, после чего их нельзя разделить. (Наглядную демонстрацию см. в видеоролике по ссылке bit.ly/1mKgdFI.) Чтобы избежать этой проблемы, Unity использует структуры Quaternion вместо углов Эйлера, хотя вы можете задавать углы Эйлера в Unity Editor и он в конечном счете будет выполнять их преобразование в Quaternion. Многие никогда не сталкиваются с шарнирной блокировкой, но я хотел указать на то, что, если вы хотите напрямую задавать поворачивание в коде, то должны делать это через Quaternion; кроме того, вы можете сами преобразовывать углы Эйлера с помощью Quaternion.Euler.

Теперь, когда вы увидели множество вариантов, должен отметить, что самым простым методом я нахожу использование rigidbody и простое применение .AddForce к персонажу. По возможности я предпочитаю повторно использовать код, и, к счастью, Unity предоставляет целый ряд заготовок (prefabs).

Давайте не будем изобретать колесо

Unity предоставляет пакет Sample Assets в Asset Store (bit.ly/1twX0Kr), который содержит кросс-платформенный диспетчер ввода, элементы управления для контроля джойстиков на мобильных устройствах, некоторые анимации, эффекты частиц и, что самое важное, ряд заранее скомпилированных контроллеров персонажей.

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

Заготовка персонажа от третьего лица
Рис. 3. Заготовка персонажа от третьего лица

Анимации

Системе анимации Mecanim в Unity можно было бы посвятить целую книгу (и такая книга уже написана, кстати). Анимации в 3D, как правило, сложнее, чем в 2D. В 2D файл анимации обычно изменяет спрайт для рендеринга в каждом ключевом кадре. В 3D данные, связанные с анимацией, гораздо сложнее. Вспомните: в предыдущей статье я говорил, что файлы анимации содержат ключевые кадры. В 3D может быть много ключевых кадров, каждый из которых содержит массу точек данных для движений пальцев, руки или ноги или для выполнения любого количества и типа перемещений. Кроме того, меши могут определять кости в них и использовать компоненты, называемые рендерами мешей со скинами (skinned mesh renderers), которые деформируют меши на основе того, как движутся кости; это во многом напоминает то, как движутся живые существа.

Файлы анимации обычно создаются в сторонней системе моделирования/анимации, хотя их можно создавать и в Unity.

Базовая поза персонажа в системе 3D-анимации — в виде буквы «T» (T-pose), и понимать это следует буквально: персонаж стоит вертикально с вытянутыми в стороны руками. Такой вариант применяется практически ко всем моделям, имеющим гуманоидную форму. Затем вы можете вдохнуть жизнь в этот базовый персонаж, назначив ему с помощью Mecanim практически любой файл анимации. Можно сделать так, чтобы зомби, эльф и человек танцевали совершенно одинаково. Также можно смешивать и подбирать файлы анимации любым подходящим для вас способом и назначать их через состояния во многом по аналогии с тем, что вы делали в 2D. Для этого применяется контроллер анимации, подобный показанному на рис. 4.

Контроллер анимации для управления состояниями анимации персонажа
Рис. 4. Контроллер анимации для управления состояниями анимации персонажа

{Рисунок, здесь текст не переводится — это не надписи}

Помните, что персонажи и анимации можно получать из Unity Asset Store, создавать с помощью средств моделирования и использовать сторонние продукты вроде Fuse от Mixamo, которые позволяют быстро генерировать адаптированные под ваши потребности персонажи. Просмотрите мои видеоролики на Channel 9 — они дадут вам начальное представление об анимации в Unity.

Создание мира

В Unity встроена система террейнов для генерации миров. Можно создать террейн, а затем использовать имеющиеся инструменты для приданию террейну нужных форм, создания гор, размещения деревьев и травы, рисования текстур и др. Можно добавить в мир небо, импортировав пакет скайбоксов (Assets | Import Package | Skyboxes) и назначив их в Edit | Render Settings | Skybox Material. У меня ушло около пары минут на создание террейна с динамической отражающей водой, деревьями, песком, горами и травой (рис. 5).

Быстро созданный террейн
Рис. 5. Быстро созданный террейн

Системы координат в Unity

В Unity есть четыре метода для ссылки на какую-либо точку в игре или на экране (рис. 6). Существует экранное пространство, диапазон которого простирается от 0 до количества пикселей; оно обычно используется для получения местоположения, где пользователь коснулся экрана или щелкнул мышью.

Пространство области просмотра (viewport space) — просто значение от 0 до 1, благодаря которому можно сообщить, что полпути — это .5, а не делить пиксели на 2. Поэтому я могу разместить объект посреди экрана, указав в качестве его позиции координаты (.5, .5).

Мировое пространство (world space) относится к абсолютному позиционированию объекта в игре на основе трех координат: (0, 0, 0). Все игровые объекты верхнего уровня в сцене имеют свои координаты, перечисленные в мировом пространстве.

Наконец, локальное пространство (local space) всегда относительно родительскому игровому объекту. В случае игрового объекта верхнего уровня оно идентично мировому пространству. Все дочерние игровые объекты перечисляются в Editor с координатами относительно их предку, поэтому, например, в вашей модели у дома могут быть мировые координаты (200, 0, 35), тогда как у его передней двери (предполагая, что это дочерний игровой объект, принадлежащий дому) — только (1.5, 0, 0), поскольку они относительны предку. В коде, когда вы ссылаетесь на transform.position, всегда используются мировые координаты, даже если это дочерний объект. В данном примере у двери были бы мировые координаты (201.5, 0, 35), но, если вместо этого вы ссылаетесь на transform.localPosition, вы получили бы (1.5, 0, 0). В Unity есть функции для преобразований между различными системами координат.

Координаты в Unity
Рис. 6. Координаты в Unity

Screen space: Экранное пространство:
Viewport space: Пространство области просмотра:
World space: Мировое пространство:

В предыдущих примерах перемещения я выполнял эти операции, используя в основном мировое пространство, но в некоторых случаях применял локальное. Вернитесь к методу 7 на рис. 1. В этом примере я принимаю локальный нормализованный вектор Vector.forward, который представляет собой (0,0,1). Само по себе это ничего особенно не значит. Однако это показывает намерение что-то переместить по оси Z, т. е. вперед. А как быть, если объект поворачивается на 90 градусов от (0,0,0)? Слово «вперед» теперь может иметь два значения: оригинальную абсолютную ось Z (в мировом пространстве) или ось Z, относительную поворачиваемому объекту, что всегда указывает вперед для объекта. Если нужно, чтобы объект всегда двигался вперед независимо от угла поворота, можно просто транслировать значения между локальным forward и мировым вектором forward, используя transform.TransformDirection(Vector3.forward * speed), как и показано в том примере.

Потоки и сопрограммы

Unity использует систему сопрограмм (coroutine system) для управления своими потоками. Если вам нужно, чтобы что-то происходило в другом потоке, вы запускаете сопрограмму вместо создания нового потока. Unity управляем всем «за кулисами». Сопрограмма приостанавливается, встречая метод yield. В примере на рис. 7 проигрывается анимация атаки, потом следует пауза в течение случайно выбранного интервала, а затем анимация атаки воспроизводится снова.

Рис. 7. Применение сопрограммы для приостановки действия

void Start()
{
  // Запускаем отдельную сопрограмму,
  // которая действует как отдельный поток
  StartCoroutine(Attack());
}
IEnumerator Attack()
{
  // Запускаем анимацию атаки
  _animator.SetTrigger("Attack");
  // Ждем от .5 до 4 секунд до повторного
  // проигрывания анимации атаки
  float randomTime = Random.Range(.5f, 4f);
  yield return new WaitForSeconds(randomTime);
}

Физика и обнаружение коллизий

Физика и средства обнаружения коллизий в 3D почти такие же, как в 2D, с тем исключением, что коллайдеры имеют другую форму и у компонента rigidbody есть несколько других свойств, таких как способность свободного вращения или перемещения по осям X, Y и Z. В 3D теперь имеется коллайдер меша, который обертывает всю фигуру модели как зону распознавания коллизии. Звучит грандиозно, и для коллизий это все весьма хорошо, но плохо для производительности. В идеале, нужно упростить формы коллайдеров и ограничить процессорные ресурсы, необходимые для их использования. У вас есть зомби? Нет проблем — используйте коллайдер капсулы (capsule collider). Сложный объект? Используйте несколько коллайдеров. По возможности избегайте коллайдера меша.

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

void OnCollisionEnter(Collision collision)
{
  // Вызывается, когда происходит физическое столкновение
  Debug.Log("Collided with " + collision.gameObject.name);
}
void OnTriggerEnter(Collider collider)
{
  // Вызывается, когда другой объект попадает в зону триггера
  Debug.Log("Triggered by " + collider.gameObject.name);
}

Методов гораздо больше, чем перечислено здесь, в частности есть методы OnTriggerExit и OnCollisionExit, почти идентичные своим аналогам в 2D.

Создание объектов

Когда вам нужно создать в период выполнения новые элементы на основе GameObject, не пользуйтесь конструкторами. Вместо них применяйте Instantiate. У вас определенно могут быть классы с конструкторами, которые в скриптах неявно наследуются от MonoBehavior; это происходит во всех скриптах верхнего уровня, назначенных любому GameObject. Однако эти скрипты могут вызывать конструкторы для других объектов:

// Предполагаем, что эта ссылка назначена в Editor
[SerializeField]
private GameObject _zombie;
void Start()
{
  // Создаем новый экземпляр этого игрового объекта. Им может
  // быть заготовка из вашего проекта или объект,
  // уже присутствующий в сцене.
  Instantiate(zombie, transform.position, Quaternion.identity);
}

Эффекты частиц

Если вы хотите, чтобы у вас были мерцающие звезды, пыль, снег, взрывы, огонь, туман, поднимающийся от водопада, всякие кровавые и другие эффекты, нужно использовать эффект частиц (particle effect). В Unity есть старая система частиц и более новая, лучше оптимизированная — Shuriken. С помощью Shuriken в Unity можно делать массу потрясающий вещей, в том числе заставить падающие частицы поддерживать коллизии. Поскольку на этот счет существует множество учебных пособий, например по ссылке bit.ly/1pZ71it, и эффекты, как правило, создаются в Editor с дизайнером, здесь я просто покажу, как можно создавать их экземпляры, когда, скажем, некий персонаж попадает в область триггера монеты, которую он должен подобрать.

Чтобы начать работу с частицами, выберите Game Object | Particle System и вы сразу же увидите, что в вашу сцену добавлен один из эффектов, как на рис. 8.

Эффект частиц
Рис. 8. Эффект частиц

Я предпочитаю создавать из своих систем частиц заготовки (о которых я рассказывал во второй статье), чтобы их можно было легко использовать повторно. Достаточно создать их экземпляры в коде, сначала назначив скрипт игровому объекту (предполагая, что его класс наследует от MonoBehavior, как и все компоненты script игровых объектов), а затем перетащив нужный эффект частиц в Editor из сцены или заготовки в проект, например, на предоставляемое свойство SmokeEffect (рис. 9).

Рис. 9. Предоставляемое свойство SmokeEffect

[SerializeField]
private ParticleSystem _smokeEffect;
void OnTriggerEnter(Collider collider)
{
  // Удостоверяемся, что частицы показываются, только если
  // игрок попадает в соответствующую зону
  if (collider.gameObject.tag == "Player")
  {
    GameController.Score++;
    // Создаем систему частиц в позиции игровых объектов
    // безо всякого поворачивания
    Instantiate(_smokeEffect, transform.position,Quaternion.identity);
    // НЕ вызывайте Destroy(this), потому что this - это
    // компонент script в игровом объекте, а значит, следует
    // использовать this.gameObject, т. е. просто gameObject
    Destroy(gameObject);
  }
}

Создание UI

В Unity 4.6 добавили совершенно новую систему UI для создания элементов HUD (heads-up display) в игре с использованием текста, панелей, виджетов и др. Добавление текста в HUD вашей игры сводится к выбору GameObject | UI | Text и заданию шрифта и текста. Если вам нужно позднее управлять текстом в коде, например для обновления счета, вы просто используете:

// Получаем компонент UnityEngine.UI.Text
var score = GetComponent<Text>();
score.text = "Score:0";

Если мне требуется некое изображение в UI, я открываю GameObject | UI | Image и назначаю спрайтовое 2D-изображение этому новому компоненту. Значения задаются, как и в случае любого другого игрового объекта. Надеюсь, что к этому моменту вы уже заметили шаблон. Чтобы создать простой GUI, создайте UI-объекты через меню GameObject | UI, укажите начальные значения в Editor и позже контролируйте их, получая ссылки на данные UI-компоненты и изменяя значения или даже анимируя их. Я сформировал базовый GUI, показанный на рис. 10, создав элементы под новым компонентом Canvas. Новая UI-система в Unity 4.6 содержит ряд базовых типов объектов, таких как Panel, Button, Text, Image, Slider, Scrollbar и Toggle, и с ними невероятно легко работать при создании UI.

UI с изображением и текстов в HUD
Рис. 10. UI с изображением и текстов в HUD

Искусственный интеллект в вашей игре

Было бы нечестно не упомянуть об искусственном интеллекте (AI), хотя здесь я не стану вдаваться в детали создания AI (хотя строительные блоки для него присутствуют в более ранних примерах кода для find/move/rotate). Но я кратко расскажу о нескольких доступных вам вариантах. Не уверен, что игровой AI правильно называть искусственным интеллектом, потому что весь этот интеллект сводится не более чем к самым базовым действиям. Я показывал, как поворачивать преобразование в сторону другого объекта и перемещать этот объект. Это базовый AI во многих играх. В Unity встроены некоторые средства нахождения пути за счет поддержки NavMesh, который заранее рассчитывает все пути вокруг объектов. NavMesh работает весьма хорошо и теперь включен в бесплатную редакцию Unity, хотя многие выбирают вместо него A* Pathfinding Project (arongranberg.com/astar) — алгоритм, который можно реализовать самостоятельно или, экономя время, приобрести соответствующий пакет ресурсов. На момент написания этой статьи 2D-поддержка поиска путей (pathfinding) не была встроена в Unity — только для 3D, хотя A* имеет такую поддержку. Есть также Behave 2.0 от AngryAnt — популярный плагин AI для Unity с некоторыми по-настоящему впечатляющими средствами и RAIN — бесплатный инструментальный набор для создания AI от rivaltheory.com, который тоже весьма неплох и имеет встроенные поведения для follow, find, интеграции с Mecanim и др.

Заключение

3D-мир вносит дополнительный уровень сложности по сравнению с 2D, так как в нем вы имеете дело с полноценными мешами и еще одним измерением. Asset Store — важнейший источник ресурсов как для начинающих, так и для продвинутых разработчиков игр, и вы действительно можете значительно ускорить создание своей игры, используя готовые ресурсы.

Когда я только начинал разрабатывать игры, я едва не сошел с ума в поисках множества моделей и текстур в Интернете. В Интернете есть некоторые великолепные торговые площадки, предлагающие такие ресурсы, но вы быстро поймете, что не все они годятся для игр. Как-то раз я скачал модель небольшого валуна, в которой оказалось почти 100 000 вершин! Ищите ресурсы, оптимизированные под мобильные устройства, или проверяйте количество вершин/многоугольников, чтобы они гарантированно работали в ваших играх. А иначе они значительно замедлят вашу игру. Существуют средства оптимизации моделей, в том числе Cruncher, включенный в Unity. В следующей статье я рассмотрю, как перенести игру или приложение из Unity на платформу Windows. Заходите в мой блог на Channel 9 (aka.ms/AdamChannel9), где я периодически выкладываю свои учебные видеоролики и ссылки на различный контент для скачивания.


Адам Тьюлипер  (Adam Tuliper) — старший идеолог по технологиям в Microsoft; живет в солнечной Южной Калифорнии. Разработчик инди-игр, один из администраторов Orange County Unity Meetup и автор на pluralsight.com. Скоро в его семье появится третий ребенок, так что связывайтесь с ним, пока у него еще есть свободные минуты, по адресу adamt@microsoft.com или через twitter.com/AdamTuliper.

{Для верстки: в последней фамилии (литовской) есть диакритический знак, не потеряйте}

Выражаю благодарность за рецензирование статьи экспертам Мэтту Ньюмену (Matt Newman) из Subscience Studios и Тоутвидасу Цилису (Tautvydas Žilys) из Unity.