Книга «Unity в действии. Мультиплатформенная разработка на C#. 3-е межд. издание»

image Привет, Хаброжители!

Создание игр — увлекательное дело, а Unity делает его простым и доступным! Unity возьмет на себя всю тяжелую работу, и вы можете сосредоточиться на игре, графике и взаимодействии с пользователем. Поддержка C# и огромная экосистема готовых компонентов позволят даже начинающим разработчикам быстро перейти от идеи к реализации. Осваивать и получать практический опыт работы с инструментами Unity и высокотехнологичным механизмом визуализации вы будете на примерах игр в двумерной, трехмерной и виртуальной/дополненной реальности.

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

Третье издание знаменитого бестселлера было полностью переработано. И самое главное — теперь в книге пойдет речь о дополненной и виртуальной реальности.

Осваивайте Unity и быстрее приступайте к созданию собственных игр!

Кому стоит прочитать эту книгу
Третье издание книги «Unity в действии» посвящено программированию игр с помощью Unity. Это руководство можно считать введением в Unity для опытных программистов. Цель его крайне проста: научить людей, имеющих опыт программирования, но ни разу не сталкивавшихся с Unity, разрабатывать игры с помощью этого инструмента.

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

В этой книге более подробно изложены материалы, касающиеся программирования, чем в большинстве других изданий по Unity (особенно предназначенных для начинающих). Зачастую Unity представляют как набор функций, не требующих особых навыков программирования, что в корне неверно, так как без них невозможно производство коммерчески успешных продуктов. Если вы пока не имеете соответствующего опыта, советую обратиться к одному из множества различных сайтов с «бесплатными интерактивными уроками кодирования» (например, learnprogramming.online) и после изучения основ вернуться к чтению этой книги.

Выбор языка программирования не имеет особого значения; все примеры в книге написаны на C#, но они легко переводятся на другие языки. Часть I посвящена знакомству с новыми понятиями, и материал намеренно изложен со всей возможной скрупулезностью. Темы из части II книги более динамичны: вы можете сразу выполнять проекты в различных игровых жанрах. В книге также описывается развертывание игр на различных платформах, в том числе в интернете и на мобильных устройствах, но в целом мы не будем заострять на этом внимание, так как Unity не зависит от платформы.

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


5.2. СОЗДАНИЕ КАРТ И ПРЕВРАЩЕНИЕ ИХ В ИНТЕРАКТИВНЫЕ ОБЪЕКТЫ


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

5.2.1. Объект из спрайтов


Перетащите в сцену одно изображение карты. Используйте рисунок лицевой стороны, так как поверх будет добавлена другая карта, скрывающая первую. Технически положение этого объекта пока не имеет значения, но в конечном счете карта должна будет оказаться в определенной точке, поэтому присвойте полям Position значения -3, 1, 0. Поместите в сцену спрайт card_back и сделайте его потомком предыдущего. Напоминаю, что для этого на вкладке Hierarchy нужно перетащить дочерний объект на родительский. Установите для нового спрайта положение 0, 0, -0.1. Это положение относительно предка, то есть фактически мы поместили рубашку карты в точку с теми же координатами X и Y, но чуть ближе по координате Z.

ПРИМЕЧАНИЕ
В этой настройке обратная и лицевая части карты являются отдельными объектами. Это упрощает настройку графики, и открыть «переднюю часть» так же просто, как выключить «заднюю». Однако поскольку Unity всегда находится в режиме 3D, даже если сцена выглядит в режиме 2D, можно создать 3D-карту, которая переворачивается. Настроить это будет сложнее, но такой вариант может иметь преимущества для определенных графических эффектов. Не существует единственно правильного способа, просто нужно взвесить различные плюсы и минусы.
СОВЕТ
Вместо инструментов Move, Rotate и Scale, которыми мы пользовались в трехмерном режиме, теперь доступен единый инструмент манипуляции Rect Tool. Он автоматически активируется после перехода в двумерный режим. Кроме того, его можно включить нажатием пятой кнопки управления в верхнем левом углу Unity. Когда этот инструмент активен, перетаскивание объектов приводит к выполнению всех трех операций в двух измерениях.
Когда вы расположите карту на столе рубашкой вверх, как показано на рис. 5.6, все будет готово для создания интерактивного объекта, реагирующего на щелчок кнопкой мыши.


image


5.2.2. Код ввода с помощью мыши


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

Выделите корневой объект на вкладке Hierarchy (щелкнув на карте в сцене, вы выделите рубашку, так как она располагается сверху). Затем нажмите Add Component в нижней части панели Inspector. Выделите строку Physics 2D (не Physics!) и выберите вариант Box collider.

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

image


СОВЕТ
Приучайте себя распределять ресурсы по папкам, если у вас пока нет такой привычки. Создайте для скриптов отдельную папку и перетащите туда все сценарии. Это можно сделать непосредственно на вкладке Project. Избегайте имен, на которые реагирует Unity: Resources, Plugins, Editor и Gizmos. О назначении этих папок мы поговорим позднее, а пока просто помните, что пользовательские папки так называть нельзя.
Отлично, теперь мы можем щелкать на карте! Подобно методу Update (), функция OnMouseDown () также происходит от класса MonoBehaviour, именно она вызывает реакцию на щелчок мыши. Запустите воспроизведение игры и убедитесь, что на консоли стало появляться сообщение. Но вывод на консоль был сделан только с целью тестирования, мы же хотим перевернуть карту.


5.2.3. Открытие карты по щелчку


Отредактируйте код в соответствии с листингом 5.2 (запустить его пока невозможно, но это не повод для беспокойства).

В этом скрипте мы сделали два ключевых дополнения: ссылку на объект сцены и деактивирующий данный объект метод SetActive (). Из предыдущих глав вы уже знаете, как сделать ссылку: переменная помечается как сериализованная, а затем на эту переменную на панели Inspector перетаскивается объект с вкладки Hierarchy. После этого код начнет влиять на объект сцены.

image


Метод SetActive деактивирует любой объект GameObject, делая его невидимым. Если теперь перетащить объект card_back на переменную этого сценария на панели Inspector, в процессе игры карта начнет исчезать после щелчка по ней. Исчезнувшая рубашка открывает лицевую сторону карты. Как видите, мы решили еще одну важную задачу! Но на столе всего одна карта, давайте исправим этот недостаток.

СОВЕТ
Забыть перетащить объект, когда в сценарии есть сериализованная переменная, — довольно распространенная ошибка. Поэтому полезно пользоваться вкладкой Console, на которой выводятся сообщения об ошибках. Код, использующий сериализованную переменную, которая не была установлена, выдаст ошибку нулевой ссылки (null). Фактически ошибка нулевой ссылки возникает каждый раз, когда код пытается использовать переменную, которая еще не установлена, независимо от того, сериализованная она или нет.


5.3. ОТОБРАЖЕНИЕ НАБОРА КАРТ


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

5.3.1. Программная загрузка изображений


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

Напишем простой тестовый код (который позже мы заменим), чтобы продемонстрировать процесс программной загрузки изображений. Первым делом добавьте в сценарий MemoryCard код из следующего листинга.

image

После того как вы сохраните этот скрипт, на панели Inspector появится новая сериализованная переменная. Перетащите на ячейку Image какой-либо спрайт с вкладки Project (выберите одно из лицевых изображений, кроме того, которое уже есть в сцене). Запустите воспроизведение сцены, и вы увидите на карте новый рисунок.

Чтобы понять смысл кода, необходимо разобраться в компоненте SpriteRenderer. На рис. 5.7 мы видим, что рубашка карты имеет всего два компонента: стандартный Transform, присутствующий у всех объектов сцены, и новый SpriteRenderer. Последний делает из объекта GameObject спрайт и определяет, какой ресурс будет на нем отображен. Обратите внимание, что первое свойство этого компонента называется Sprite и связано с одним из спрайтов на вкладке Project. Этим свойством можно управлять программно, что, собственно, и происходит внутри нашего сценария.

image


Работая с CharacterController и нашими скриптами в предыдущих главах, вы замечали, что метод GetComponent () возвращает другие компоненты того же объекта, поэтому воспользуемся им для ссылки на объект SpriteRenderer. Свойство Sprite объекта SpriteRenderer может быть применено к любому ресурсу, поэтому в коде ему присваивается объявленная в верхней части переменная Sprite (в редакторе мы заполнили ее одним из спрайтов).

Как видите, не слишком сложно! Но это всего одно изображение. У нас их четыре, поэтому удалите новый фрагмент кода из листинга 5.3 (он был нужен только для демонстрации), чтобы подготовиться к следующему разделу.

5.3.2. Выбор изображения из компонента SceneController


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

Для начала подготовьте пустой объект GameObject (для этого в GameObject нужно выбрать команду Create Empty). Затем на вкладке Project создайте сценарий SceneController.cs и перетащите его на контроллер GameObject. Прежде чем приступать к написанию кода для нового сценария, добавьте содержимое листинга 5.4 в скрипт MemoryCard вместо того, что было в листинге 5.3.

image


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

ПРИМЕЧАНИЕ
Возможно, раньше вы пользовались языком программирования, в котором отсутствовали такие понятия, как геттер (getter) или, иначе говоря, метод чтения, и сеттер (setter) — устанавливающий метод. Это функции, которые запускаются при попытке получить доступ к связанному с ними свойству (например, получить значение card.id). Они применяются для разных целей, но в рассматриваемом случае свойство Id предназначено только для чтения, поэтому вы не можете задать значение для него.


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

Теперь, когда дополнительный код появился в MemoryCard, впишите в скрипт SceneController содержимое следующего листинга.

image


Этот короткий фрагмент демонстрирует принцип управления картами со стороны контроллера SceneController. Большая часть представленного кода уже должна быть вам знакома (например, перетаскивание объекта-карты в редакторе Unity на ячейку сериализованной переменной на панели Inspector), но с массивами изображений вы раньше не сталкивались. Как показано на рис. 5.8, на панели Inspector можно задать набор элементов. Введите 4 в поле для размера массива, а затем перетащите спрайты с рисунками карт на ячейки элементов массива. Эти спрайты станут доступными, как и все остальные ссылки на объекты.

Метод Random.Range () применялся в главе 3, надеюсь, вы помните, как с ним работать. Точные граничные значения в данном случае неважны, но имейте в виду, что минимальное значение входит в диапазон и может быть возвращено, в то время как возвращаемое всегда меньше максимального.

image


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

5.3.3. Экземпляры карт


У компонента SceneController ссылка на объект-карту уже есть, поэтому остается воспользоваться методом Instantiate () (листинг 5.6) и получить набор карт. Аналогичным способом мы генерировали объекты в главе 3.

image


Этот сценарий намного длиннее предыдущих, но объяснять тут особо нечего, потому что большинство дополнений представляют собой обычные объявления переменных и математические вычисления. Самым странным фрагментом кода является, вероятно, условный оператор if (i == 0 && j == 0). Он заставляет или выбрать исходную карту для первой ячейки сетки, или клонировать его для остальных ячеек. Изначальный объект в сцене уже есть, поэтому, копируя его на каждом шаге цикла, мы получим слишком много карт. Они раскладываются путем смещения в соответствии с количеством итераций.

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

image


СОВЕТ
Так же как и при перемещении 3D-объектов, у двумерных значение transform.position может многократно увеличиваться внутри метода Update () для достижения большей плавности. Когда мы занимались перемещениями объекта-игрока, вы видели, что при прямом редактировании параметра transform.position нельзя добавить распознавание столкновений. Для получения 2D-объектов с распознаванием столкновений нужно добавить к ним компоненты группы Physics2D и отредактировать, например, параметр rigidbody2D.velocity.


5.3.4. Тасуем карты


Вместо того чтобы делать каждую карту случайной, мы создадим из их идентификаторов (чисел от 0 до 3, сопоставленных с каждой парой карт) массив и перемешаем его элементы (листинг 5.7). На основе этого массива и будет осуществляться раскладка карт.

image


Нажмите кнопку Play, и сетка заполнится картами из набора, содержащего ровно по две карты каждого вида. Массив карт пропущен через алгоритм тасования Кнута (еще его называют алгоритмом Фишера — Йетса). Это простой, но эффективный способ перемешать элементы массива. Данный алгоритм перебирает массив и случайным образом меняет местами каждый из его элементов.

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

Об авторе
Джо Хокинг (Joe Hocking) — инженер-программист, специализирующийся на разработке программного обеспечения (ПО) для интерактивных медиа. В настоящее время является сотрудником Qualcomm. Первое издание этой книги Джо написал, работая в Synapse Games, а большую часть книги, которую вы сейчас держите в руках, — уже будучи сотрудником BUNDLAR. Кроме того, он вел занятия в Университете Иллинойса в Чикаго, школе Института искусств Чикаго и Колумбийском колледже Чикаго. Джо живет в пригороде Чикаго с женой и двумя детьми. Его сайт: newarteest.com.


Более подробно с книгой можно ознакомиться на сайте издательства:
» Оглавление
» Отрывок

По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
Для Хаброжителей скидка 25% по купону — Unity

© Habrahabr.ru