Компьютерная графика → Pac-Man 2D на Unity 3D

Эта статья будет переводом статьи по туториалу с сайта noobtuts (оригинал https://noobtuts.com/unity/2d-pacman-game), но на более новой версии Unity.

 Оригинальная игра Pac-Man была выпущена в октябре 1980 года и вскоре стала самой известной аркадной игрой всех времен. Игра стала настолько популярной, что даже Unity включила крошечную ее часть в свой игровой движок:

Вот предварительный просмотр финальной игры:

Данное руководство было сделано на версии Unity 2018.3.11f1. Более новые версии также должны работать нормально, более старые версии могут работать или не работать.

Настройка проекта

Запускаем Unity и создаем новый проект.  Задаем название проекта, выбираем 2D и создаем проект.

В Иерархии выбираем Main Camera и меняем Background на черный. Также настроем Size и Position, как показано на скрине:

Лабиринт

Создадим типичный лабиринт Pac-Man. Для начала, необходимо создать в папке Assets папку Sprites, далее щелкаем правой кнопкой мыши на картинку выше, выбираем "Сохранить картинку как..." и сохраняем ее в папке Sprites.

После сохранения в папке мы можем выбрать его в области Project.

Далее необходимо изменить параметры импорта в Inspector.

Значение «Pixel Per Unit», равное 8, означает, что 8 х 8 пикселей поместятся в одну единицу в игровом мире. Мы будем использовать это значение для всех наших текстур. Мы выбрали значение 8, потому что расстояние между двумя Pac-точками (едой) всегда равно 8 px, и мы хотим, чтобы это расстояние составляло единицу в нашей игре. Мы выбрали Bottom-Left для Pivot, потому что это облегчит выравнивание.

После настройки в инспекторе необходимо применить изменения нажав на "Apply".

Теперь мы можем перетащить лабиринт на сцену (убедитесь чтоб вкладка была на Scene, а не на Game) или можно перетащить его в Иерархию. В Inspector  меняем Position лабиринта на (0,0), чтобы в дальнейшем было проще.

Физика лабиринта

Сейчас  лабиринт - это всего лишь картинка. Она не имеет физики, объекты не столкнутся со стенками, и Pac-Man сможет пройти прямо сквозь стены. Чтобы изменить это необходимо добавить коллайдер для каждой стены в лабиринте: Add Component -> Physics 2D -> Box Collider 2D в Инспекторе.  

Если мы посмотрим на сцену, то увидим, что Unity обернула коллайдер вокруг всего лабиринта, что не совсем то, что нам нужно. Нам нужно иметь коллайдер вокруг каждой стены лабиринта. Дляя этого в инспекторе нажимаем на кнопку "Edit Collider" и изменять коллайдер в сцене, используя зеленые точки. Или же в ручную в инспекторе менять Offset и Size:

Необходимо повторить этот процесс для каждой стенки в лабиринте. Все, что нам нужно сделать, это выбрать Add Component -> Physics 2D -> Box Collider 2D , нажать кнопку Edit Collider или в менять Offset и Size, и затем изменять его в сцене. 

Очень важно, чтобы каждый коллайдер был абсолютно точным. Необходимо чтобы Offset и Size коллайдера были равны 1,25 или 1,5 или 1,75 или 2,00 , а не 1,24687 или 1,25788 . Вот несколько примеров:

Если же Pac-Man будет застревать в лабиринте или испытывать затруднения при движении, то это потому, что Коллайдеры лабиринта не совсем точны.

Конечный результат лабиринта:

Коллайдер будет очень много, настройка займет время.

Добавление Pac-Man

 Нам понадобится одна анимация для каждого направления движения: 
вправо
влево
вверх
вниз

Это все будет в одной картинке:

Щелкаем правой кнопкой мыши на картинку выше, выбираем "Сохранить картинку как..." и сохраняем ее в папке Sprites.

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

После настройки в инспекторе необходимо применить изменения нажав на "Apply".  Значение  Multiple в Sprite Mode, означает что в нашем изображении более одного пакмена. Для того, чтобы разделить пакманов, необходимо открыть "Sprite Editor":

Теперь мы можем разделить наших пакменов, нажав на Slice (у некоторых оно может обозначаться Sl) и выбрать тип Grid By Cell Size, задав Pixel Size (16,16):

Нажимаем Slice чтобы изменения применились, и далее "Apply" чтобы сохранить изменения.

В результате теперь есть 12 срезов под пакмана, проверить можно в области Project:

Создание анимации Pac-Man

Теперь, когда у нас есть фрагменты анимации, мы можем создать наши 4 анимации из этого. Напомним, что анимации нам понадобятся:

Right (срез 0, 1 и 2)

Left (срез 3, 4 и 5)

Up (срезы 6, 7 и 8)

Down (срез 9, 10 и 11)

Начнем с анимации Right. Выбираем первые три среза, и перетаскиваем их на сцену.

Когда мы перетаскиваем несколько фрагментов на сцену, Unity создает из них анимацию, и поэтому автоматически спрашивает нас, где сохранить анимацию. Сохраняем анимацию как right.anim в новой папке PacmanAnim. Unity просто добавила игровой объект pacman_0 на сцену и два файла в область Project:

Первый файл - это конечный автомат анимации, который определяет скорость анимации и еще что-то. Второй - это сама анимация.

Мы повторим этот процесс для остальной части анимации (срезы 3, 4, 5 слева; срезы 6, 7, 8 вверх и срезы 9, 10, 11 вниз). Сохраняем в ту же папке что и right.anim.

Как будет выглядеть иерархия после этих действий:

Уберем ненужное. Unity создала один GameObject для каждой анимации, но нам нужен только первый. Выбираем остальные 3, а затем щелкните правой кнопкой мыши и удалите их:

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

Создание переходов анимации

Сейчас у нас есть 4 файла анимации, но Unity пока не знает, когда и какую анимацию воспроизводить. Для этого понадобится конечный автомат анимации, который имеет 4 состояния:

right

left

up

down

Также нужны переходы, чтобы Unity знала, когда переключаться из одного состояния анимации в другое. Он будет использовать Transitions (переходы), чтобы знать, когда переключаться в другое состояние. Unity делает все это автоматически, все, что нам нужно сделать, это уведомить его о направлении движения Pac-Man из скрипта позже.

Дважды щелкаем на pacman_0 в области Project:

Открывается Animator где показан один переход:

Перетаскиваем наши остальные состояния (left, up, down) из Project на Animator:

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

У Pac-Man будет только 4 направления движения (вверх, вниз, влево, вправо) . Это означает, что нам нужны только следующие 4 перехода в нашей машине состояний анимации:

Если DirY> 0, переходите наверх (например, DirY = 1, DirY = 2, DirY = 3 и т. Д.)

Если DirY <0, то идти вниз (например, DirY = -1, DirY = -2, DirY = -3 и т. Д.)

Если DirX> 0, тогда идите направо (например, DirX = 1, DirX = 2, DirX = 3 и т. Д.)

Если DirX <0, перейдите влево (например, DirX = -1, DirX = -2, DirX = -3 и т. Д.)

Если позиция Pac-Mac будет меняться по одной из координат, будет включаться соответствующаяя анимация.

Создадим DirX и DirY. Для этого переходим во вкладку Parameters, нажимаем на "+" справа и затем добавим один параметр типа Float с именем DirX и другой параметр типа Float с именем DirY:

Переходы используются для переключался на разные состояния анимации на основе параметров. Например, мы могли бы добавить переход от left на right с условием, что DirX> 0 . Однако рекомендуется чтобы была небольшая погрешность, поскольку сравнение с плавающей запятой не всегда идеально, поэтому вместо этого  будем использовать DirX> 0,1 .

Для перехода из любого состояния (right, left, up, down) в  другое будем использовать Any State. Щалекаем ПКМ на Any State и выбираем "Make Transition", после этого перетаскиваем белую стрелку в нужное состояние:

Теперь нажимаем на белую стрелку и смотрим на Inspector, где мы можем изменить Условие (Conditions) перехода. Нажимаем на "+" и установим DirX> 0.1  и отключим Can Transition To Self (это позволяет избежать странных ситуаций, когда анимация будет перезапускаться все время, удерживая нажатой клавишу перемещения): 

В результате, когда Pac-Man идет вправо (DirX> 0,1), аниматор переключается в состояние right и воспроизводит анимацию.

Теперь необходимо добавить еще три переходя для каждого состояния (и у всех отключить Can Transition To Self):

из Any State (любого состояния), в состояние left с условием DirX <-0.1

из Any State (любого состояния), в состояние up c условем DirY> 0,1

из Any State (любого состояния), в состояние down c условем DirY <-0,1

Вот как  сейчас выглядит Animator :

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

Если нажать на Play, то можно увидеть анимацию right.

Для красоты, переименуем pacman_0 на pacman выбрав его в Иерархии, и изменив название в Inspector, так же изменим его позицию на (14,14):

Физика Pac-Man

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

Pac-Man также должен двигаться. RigidBody заботится о таких вещах, как гравитация, скорость и другие силы, которые заставляют вещи двигаться. Как правило, все объекты в мире физики, которое должны двигаться, нуждаются в Rigidbody.

Давайте выберем pacman в Иерархии , выберите Add Component -> Phusics 2D -> Circle Collider 2D, а также Add Component -> Phusics 2D -> Rigidbody 2D . И используем  настройки, как показано на следующем рисунке:

Pac-Man теперь является частью мира физики. Он столкнется с другими вещами, и другие вещи столкнутся с ним. Это также приведет к вызову функции OnCollisionEnter2D в любом скрипте, подключенном к Pac-Man.

Скрипт движения

Самый простой способ заставить Pac-Man двигаться - создать скрипт, который просто проверяет нажатия клавиш со стрелками, а затем перемещает Pac-Man  вверх / вниз / влево / вправо, когда это необходимо. Работать это будет так: когда игрок нажимает одну из клавиш со стрелками, Pac-Man будет двигаться ровно на 1 единицу в нужном направлении. Pac-Dots (еда) также будет располагаться на расстоянии 1 единица между ними.

Выбираем pacman в иерархии, и нажимаем Add Component->New Script, назовем его PacmanMove и нажимаем “Create and Add”.

Скрипт появится в области Project в папке Assets, можем создать папку Scripts в Assets и перенести наш скрипт в эту папку.

Дважды кликаем по скрипту PacmanMove, чтобы он открылся.

Функция Start автоматически вызывается Unity при запуске игры. Функция Update вызывается автоматически снова и снова, примерно 60 раз в секунду (это зависит от текущей частоты кадров, она может быть ниже, если на экране много объектов).

Существует еще один тип функции обновления - FixedUpdate. Он также вызывается снова и снова, но через фиксированный интервал времени. Лучше использовать FixedUpdate при работе с физикой:

Нам понадобится public переменная скорости, чтобы в дальнейшем мы могли изменять ее в Inspector:

Нам также понадобится способ выяснить, может ли Pac-Man двигаться в определенном направлении или есть же. Так, например, если бы мы хотели выяснить, есть ли стена сверху Pac-Man’а, мы могли бы просто провести линию на одну единицу выше Pac-Man и посмотреть, ударит ли она что-нибудь.

Вот эта функция:

Нам также понадобится функция, которая заставит Pac-Man переместиться на 1 единицу в нужном направлении. Необходимо чтобы он пошел туда плавно, поэтому сохраним точку назначения перемещения в переменной, а затем используем FixedUpdate для постепенного перехода к этой точке:

Мы использовали GetComponent для доступа к компоненту Rigidbody Pac-Man. Затем мы используем его для перемещения.

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

Вот наша функция FixedUpdate с проверками ввода:

Если мы сохраним Script и нажмем Play, то теперь сможем перемещать Pac-Man с помощью клавиш со стрелками.

Установка параметров анимации

Прямо сейчас мы можем прекрасно перемещать Pac-Man по лабиринту, но аниматор пока не воспроизводит все анимации. Изменим наш код, чтобы вычислить текущее направление движения, а затем установить параметры аниматора:

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

Если мы сохраним скрипт и нажмем кнопку « Play», то увидим правильную анимацию.

Теперь нам необходимо чтобы Pac-Man всегда рисовался поверх карты и Pac-Dots (еды). Для этого выбираем panman в иерархии и выставляем Order in Layer на 1:

Pac-Dots

Маленькие точки, которые может съесть Pac-Man, называются Pac-Dots. В нашем случае это будет изображение размером 2x2 px:

Щелкаем правой кнопкой мыши на картинку выше, выбираем "Сохранить картинку как..." и сохраняем ее в папке Sprites.

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

После этого мы можем перетащить его в сцену. Нам надо получать уведомления, когда Pac-Man идет по Pac-Dot. Все, что нам нужно сделать, это выбрать Add Component -> Physics 2D -> Box Collider 2D в Inspector, а затем выбрать Is Trigger : 

Unity будет автоматически вызывать функцию OnTriggerEnter2D всякий раз, когда Pac-Man пересечет Pac-Dot.

Добавим скрипт нашему pacdot. Для этого выбираем его в иерархии, Add Component -> New Script , назовем его Pacdot, переместим его в нашу папку Scripts и затем откроем:

Нам не понадобится функция Start или Update , поэтому удалим их. Вместо этого мы будем использовать ранее упомянутую функцию OnTriggerEnter2D:

В функции мы будем проверять, был ли объект, который прошел через Pac-Dot, и если так, то мы уничтожим Pac-Dot:

Сохраняем наш скрипт, и в иерархии дублируем Pac-Dot. Важно чтобы координаты были округленными, такими как (1,2), а не (1.003,2.05).

Результат после дублирования и размещения всех Pac-Dot результат будет таким:

Перенесем все pacdot в maze в иерархии и в последствии свернуть maze:

The Ghosts

В оригинальной игре Pac-Man были враги Ghosts (призраки). Добавим их и в нашу игру.

Загружаем Sprite красного призрака, известного как Blinky:

Щелкаем правой кнопкой мыши на картинку выше, выбираем "Сохранить картинку как..." и сохраняем ее в папке Sprites.

Далее выбираем его в области Project, и меняем настройки импорта в Inspector:

Создаем анимацию. В Inspector необходимо чтобы Sprite Mode был Multiple, так как в нашей картинке есть разные вариации одного изображения. Нажимаем на Sprite Editor в инспекторе, и меняем Slice как на картинке:

После этого мы можем закрыть Sprite Editor и нажать «Apply». Теперь пришло время создавать анимацию, перетаскивая фрагменты в сцену, как мы делали с Pac-Man.

Сначала мы перетащим фрагменты 0 и 1 в сцену и сохраним анимацию как right.anim в новой папке BlinkyAnim.

Мы повторим этот процесс для:

2 и 3 как влево

4 и 5 как вверх

6 и 7 как вниз

Теперь можно очистить Иерархию, удалив blinky_2, blinky_4, blinky_6:

Также можно удалить blinky_2, blinky_4, blinky_6 из папки BlinkyAnim в области Project.

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

Дважды щелкаем на файл blinky_0, чтобы открылся Animator:

Необходимо создать такой же автомат анимации, который мы использовали для Pac-Man раньше. Все, что нам нужно сделать, это перетащить анимацию, создать параметры (DirX, DirY) и затем установить переходы:

Any State для перехода right с условием DirX> 0,1

Any State для перехода left с условием DirX <-0.1

Any State для перехода up с условием DirY> 0,1

Any State для перехода down с условием DirY <-0,1

Конечный автомат анимации будет таким:

Для красоты переименуем blinky_0 на blinky в иерархии.

Также меняем положение в Инспекторе, чтобы Блинки находился в середине лабиринта:

Физика для призраков

Блинки тоже должен стать частью мира физики. Выбираем Add Component -> Physics 2D -> Circle Collider 2D в Инспекторе и назначим следующие свойства: 

Включение Is Trigger нужно потому что Blinky - это призрак, и призраки могут ходить сквозь объекты.

Блинки также должен перемещаться по лабиринту, а это значит, что нам нужно добавить к нему Rigidbody. Выбираем Add Component -> Physics 2D -> Rigidbody 2D:

Точно так же, как Pac-Man, необходимо чтобы Blinky был нарисован на переднем плане, перед Pac-Dots. Все, что нужно сделать, это установить значение Order in Layer в 1:

Движение призраков

Создадим сценарий движения по точкам, который заставит Блинки идти по определенному пути. То, что звучит почти слишком просто, на самом деле является довольно приличным решением нашей проблемы с ИИ. Чем длиннее и сложнее путь, тем сложнее игроку избежать Блинки.

Выбираем Add Component -> New Scriptназовем его GhostMove и переместим в папку Scripts. Дважды щелкаем по нему, чтобы он открылся.

Нам не понадобится функция Start и Update, вместо этого мы будем использовать функцию FixedUpdate.

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

Используем функцию FixedUpdate, чтобы приблизиться к текущей путевой точке, или выбрать следующую, как только мы ее достигнем:

Функция Vector2.MoveTowards нужна чтобы вычислить точку, которая ближе к путевой точке. После этого устанавливаем позицию призрака с помощью solidbody2D.MovePosition . Если путевая точка достигнута, то мы увеличиваем переменную cur на единицу. Также надо сбросить переменную cur на 0, если она превышает длину массива.

В этом методе устанавливаем параметры анимации:

 

Есть еще одна вещь, которую нужно добавить в скрипт. Призраки должны уничтожить Pac-Man при столкновении с ним:

Добавление путевых точек

Чтобы добавить точку, выбираем Game Object-> Create Empty из верхнего меню. Переименуем его в Blinky_Waypoint0, а затем назначим ему Gizmo (Gizmo - это просто визуальный помощник, мы его не видим, когда играем в игру), чтобы нам было легче видеть его в сцене: 

Разместим его в (15, 20). Теперь можно продублировать путевую точку, переименовать ее в Blinky_Waypoint1 и расположить ее в (10, 20). Следующую Blinky_Waypoint2 в (10, 14), Blinky_Waypoint3 в (19, 14) и наконец Blinky_Waypoint4 в (19, 20). Вот что должно получиться:

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

Выбираем blinky в Иерархии, а затем перетаскиваем одну путевую точку за другой в слот Waypoints нашего скрипта GhostMove:

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

Пинки, Инки и Клайд

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

 

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

1. Звук

2. Рекорд

3. Продвинутый ИИ

4. Порталы

5. Больше уровней

7. Жизни

P.S. В одном из скринов скрыта ссылка на Git репозиторий данной игры, с готовым кодом и префабами.

2852 0
Ихтияр Каримов