Как сделать стены в unity3d

Создание игры Tower Defense в Unity, часть 1

Туториал создавался в Unity 2018.3.0f2.

Как сделать стены в unity3d

Поле, готовое к использованию в тайловой игре жанра tower defense.

Игра жанра Tower Defense

Tower defense — это жанр, в которой целью игрока является уничтожение толп врагов, пока они не добрались до своей конечной точки. Игрок выполняет свою цель, строя башни, которые атакуют врагов. У этого жанра очень много вариаций. Мы будем создавать игру с тайловым полем. Враги будут двигаться по полю в сторону своей конечной точки, а игрок будет создавать им препятствия.

Я буду считать, что вы уже изучили серию туториалов по управлению объектами.

Кроме того, мы визуализируем поле одним четырёхугольником (quad), который будет обозначать землю. Мы не будем делать четырёхугольником сам объект поля, а добавим ему дочерний объект quad. При инициализации мы сделаем масштаб XY земли равным размеру поля. То есть каждый тайл будет иметь размер в одну квадратную единицу измерения движка.

Создадим объект поля в новой сцене и добавим ему дочерний quad с материалом, который выглядит как земля. Так как мы создаём простую игру-прототип, вполне достаточно будет однородного зелёного материала. Повернём quad на 90° по оси X, чтобы он лежал на плоскости XZ.

Как сделать стены в unity3d

Как сделать стены в unity3d

Как сделать стены в unity3d

Если он существует, то редактор Unity вызывает его для компонентов после их изменения. В том числе при добавлении их к game object, после загрузки сцены, после рекомпиляции, после изменения в редакторе, после отмены/повтора и после сброса компонента.

OnValidate — это единственное место в коде, где допускается присвоение значений полям конфигурации компонентов.

Как сделать стены в unity3d

Теперь при запуске режима игры мы будем получать поле с верным размером. Во время игры расположите камеру так, чтобы была видна вся доска, скопируйте её компонент transformation, выйдите из режима игры (play mode) и вставьте значения компонента. В случае поля размером 11×11, находящегося в начале координат, для получения удобного вида сверху можно расположить камеру в позиции (0,10,0) и повернув её на 90° по оси X. Мы оставим камеру в этом фиксированном положении, но возможно изменим его в будущем.

Как сделать стены в unity3d

Префаб тайла

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

Как сделать стены в unity3d

Стрелка на чёрном фоне.

Поместите текстуру стрелки в свой проект и включите опцию Alpha As Transparency. Затем создайте для стрелки материал, который может быть стандартным материалом (default material), для которого выбран режим cutout, а в качестве основной текстуры выберите стрелку.

Как сделать стены в unity3d

Для обозначения каждого тайла в игре мы будем использовать game object. Каждый из них будет иметь свой quad с материалом стрелки, так же, как у поля есть quad земли. Также мы добавим тайлам компонент GameTile со ссылкой на их стрелку.

Создайте объект тайла и превратите его в префаб. Тайлы будут находиться вровень с землёй, поэтому приподнимите стрелку немного вверх, чтобы избежать проблем с глубиной при рендеринге. Также немного уменьшите масштаб стрелки, чтобы между соседними стрелками было немного пространства. Подойдёт смещение по Y 0.001 и одинаковый для всех осей масштаб 0.8.

Как сделать стены в unity3d

Как сделать стены в unity3d

Как сделать стены в unity3d

Учтите, что сами тайлы необязательно должны быть game objects. Они нужны только для того, чтобы отслеживать состояние поля. Мы могли бы использовать тот же подход, что и для поведения в серии туториалов Object Management. Но на ранних этапах простых игр или прототипов game objects вполне нас устраивают. В будущем это можно будет изменить.

Располагаем тайлы

Для создания тайлов GameBoard должен иметь ссылку на префаб тайла.

Как сделать стены в unity3d

Ссылка на префаб тайла.

Затем он может создать его экземляры с помощью двойного цикла по двум измерениям сетки. Хоть размер и выражен как X и Y, мы будем располагать тайлы на плоскости XZ, как и само поле. Так как поле центрировано относительно точки начала координат, нам нужно вычесть из компонентов позиции тайла соответствующий размер минус один, разделённый на два. Учтите, что это должно быть деление с плавающей запятой, в противном случае для чётных размеров оно не сработает.

Как сделать стены в unity3d

Созданные экземпляры тайлов.

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

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

Поиск пути

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

Соседи тайлов

Пути идут от тайла к тайлу, в северном, восточном, южном или западном направлении. Чтобы упростить поиск, заставим GameTile отслеживать ссылки на четырёх его соседей.

Отношения между соседями симметричны. Если тайл является восточным соседом второго тайла, то второй является западным соседом первого. Добавим к GameTile общий статический метод для определения этих отношений между двумя тайлами.

Добавим аналогичный метод для создания отношений между северными и южными соседями.

Расстояние и направление

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

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

Оператор-стрелку => также можно использовать по отдельности для геттера и сеттера свойств, для тел методов, конструкторов и в некоторых других местах.

Выращиваем путь

Если у нас есть тайл с путём, то мы можем позволить ему вырастить путь по направлению к одному из соседей. Изначально единственным тайлом с путём является конечная точка, поэтому мы начинаем с нулевого расстояния и увеличиваем его отсюда, перемещаясь в противоположном движению врагов направлении. То есть все непосредственные соседи конечной точки будут иметь расстояние 1, а все соседи этих тайлов — расстояние 2, и так далее.

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

Теперь добавим методы для выращивания пути в конкретных направлениях.

Поиск в ширину

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

Повторяем этот этап, пока в границе есть тайлы.

Отображаем пути

Теперь у нас есть поле, содержащее верные пути, но пока мы этого не видим. Надо настроить стрелки так, чтобы они указывали вдоль пути через их тайлы. Это можно сделать, повернув их. Так как эти повороты всегда одинаковы, добавим в GameTile по одному статическому полю Quaternion для каждого из направлений.

Как сделать стены в unity3d

Изменяем приоритет поиска

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

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

Как сделать стены в unity3d

Конечная точка в центре.

Результат кажется логичным, если вспомнить, как работает поиск. Так как мы добавляем соседей в порядке «север-восток-юг-запад», наивысший приоритет имеет север. Так как мы выполняем поиск в обратном порядке, это значит, что последним пройденным направлением оказывается юг. Именно поэтому всего несколько стрелок указывает на юг и многие указывают на восток.

Изменить результат можно, настроив приоритеты направлений. Давайте поменяем местами восток и юг. Так мы должны получить симметрию «север-юг» и «восток-запад».

Как сделать стены в unity3d

Порядок поиска «север-юг-восток-запад».

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

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

Одиночный амперсанд — это двоичный оператор И (AND). Он выполняет логическую операцию И для каждой отдельной пары битов его операндов. Поэтому чтобы конечный бит был равен 1, оба бита пары должны быть равны 1. Например 10101010 и 00001111 дают нам 00001010.

В компьютерах числа хранятся в двоичном виде. В них могут использоваться только 0 и 1. В двоичном виде последовательность 1, 2, 3, 4 записывается как 1, 10, 11, 100. Как видите, самый младший разряд чётных чисел равен нулю.

Мы используем двоичное AND как маску, игнорируя всё, кроме самого младшего разряда. Если результат равен нулю, то мы имеем дело с чётным числом.

Во-вторых, изменим знак результата, если их координата Y чётная. Так мы создадим шахматный узор.

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

Как сделать стены в unity3d

Переменный порядок поиска.

Изменяем тайлы

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

Содержимое тайла

Затем создадим префабы для двух типов контента, у каждого из которых компонент GameTileContent с соответствующим заданным типом. Давайте для обозначения тайлов конечных точек воспользуемся голубым сплющенным кубом. Так как он почти плоский, коллайдер ему не нужен. Для префаба пустого содержимого используем пустой game object.

Как сделать стены в unity3d

Как сделать стены в unity3d

Префабы конечной точки и пустого содержимого.

Фабрика содержимого

Добавим фабрике скрытый метод Get с префабом в качестве параметра. Здесь мы снова пропустим многократное применение объектов. Он создаёт экземпляр объекта, задаёт его исходную фабрику, перемещает его на сцену фабрики и возвращает его.

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

У нас есть только два типа содержимого, поэтому просто добавим для них два поля конфигурации префабов.

Создадим ассет фабрики и настроим её ссылки на префабы.

Как сделать стены в unity3d

А затем передадим Game ссылку на фабрику.

Как сделать стены в unity3d

Касание тайла

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

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

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

Изменение содержимого

Теперь мы можем превратить любой тайл в конечную точку нажатием курсора.

Как сделать стены в unity3d

Несколько конечных точек.

Делаем поле правильным

Теперь Game должен передать свою фабрику полю.

Так как теперь у нас есть несколько конечных точек, изменим GameBoard.FindPaths так, чтобы он вызывал BecomeDestination для каждой и добавлял их все в границу. И это всё, что нужно для поддержки нескольких конечных точек. Все остальные тайлы как обычно очищаются. Затем мы удаляем жёстко заданную конечную точку в центре.

Наконец, заставим Game вызывать ToggleDestination вместо того, чтобы задавать само содержимое тайла.

Как сделать стены в unity3d

Несколько конечных точек с правильными путями.

Стены

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

Содержимое

Стены — это ещё один тип содержимого, поэтому добавим в GameTileContentType элемент для них.

Затем создадим префаб стены. На этот раз создадим game object содержимого тайла и добавим ему дочерний куб, который будет находиться поверх поля и заполнять тайл целиком. Сделаем его высотой в половину единицы и сохраним коллайдер, потому что стены могут визуально перекрывать часть тайлов за ним. Поэтому когда игрок касается стены, он будет влиять на соответствующий тайл.

Как сделать стены в unity3d

Как сделать стены в unity3d

Как сделать стены в unity3d

Добавим префаб стены в фабрику, и в коде, и в инспекторе.

Как сделать стены в unity3d

Фабрика с префабом стены.

Включение и отключение стен

Добавим в GameBoard метод включения-отключения стен, как мы сделали это для конечной точки. Изначально проверять неверное состояние поля мы не будем.

Включение-отключение стен будет использоваться гораздо чаще, чем включение-отключение конечных точек, поэтому сделаем так, чтобы переключение стен в Game выполнялось основным касанием. Конечные точки можно переключать дополнительным касанием (обычно это правая клавиша мыши), которое можно распознать, передав в Input.GetMouseButtonDown значение 1.

Как сделать стены в unity3d

Теперь у нас есть стены.

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

Блокировка поиска пути

Чтобы стены блокировали поиск пути, нам достаточно не добавлять тайлы со стенами в границу поиска. Это можно сделать, заставив GameTile.GrowPathTo не возвращать тайлы со стенами. Но путь всё равно должен вырастать по направлению стены, чтобы все тайлы на поле имели путь. Это необходимо, потому что существует возможность того, что тайл с врагами внезапно превратится в стену.

Как сделать стены в unity3d

Стены влияют на пути.

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

Как сделать стены в unity3d

Учтите, что требование правильности всех путей не позволяет оградить стенами часть поля, в котором нет конечной точки. Мы можем разделить карту, но только если в каждой части есть хотя бы одна конечная точка. Кроме того, каждая стена должна быть соседней с пустым тайлом или конечной точкой, в противном случае она не сможет сама иметь путь. Например, невозможно сделать сплошной блок из стен размером 3×3.

Скрываем пути

Теперь метод FindPaths должен показывать обновлённые пути, только если включена визуализация.

По умолчанию визуализация путей отключена. Отключим стрелку в префабе тайла.

Как сделать стены в unity3d

Стрелка префаба по умолчанию неактивна.

Сделаем так, чтобы Game переключал состояние визуализации при нажатии клавиши. Логично было бы использовать клавишу P, но она также является горячей клавишей включения-отключения режима игры в редакторе Unity. В результате визуализация будет переключаться, когда использована горячая клавиша выхода из режима игры, что выглядит не очень красиво. Поэтому давайте используем клавишу V (сокращение от visualization).

Как сделать стены в unity3d

Отображение сетки

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

Как сделать стены в unity3d

Мы не будем добавлять эту текстуру по отдельности к каждому тайлу, а применим её к земле. Но сделаем эту сетку необязательной, как и визуализацию путей. Поэтому добавим в GameBoard поле конфигурации Texture2D и выберем для него текстуру сетки.

Как сделать стены в unity3d

Поле с текстурой сетки.

Сделаем так, чтобы Game переключал визуализацию сетки клавишей G.

Как сделать стены в unity3d

Как сделать стены в unity3d

Как сделать стены в unity3d

Отмасштабированная сетка с отключенной и включённой визуализацией путей.

Итак, на данном этапе мы получили функционирующее поле для тайловой игры жанра tower defense. В следующем туториале мы добавим врагов.

Источник

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *