Пишем сапёр на Unity. Настройка

Логические игры часто включают в себя клеточные поля, при этом клетки имеют определенные свойства и модели поведения. В этой серии уроков мы покажем вам, как создать простую версию классической игры Сапёр. Прилагаем список статей:

  1. Пишем сапёр на Unity. Настройка.
  2. Пишем Сапёр на Unity. Взаимодействие.
  3. Пишем Сапёр на Unity. Обработка конца игры.

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

  1. Настройка проекта.
  2. Механика мяча и платформы.
  3. Поведение блоков, префабы и дизайн уровней.
  4. Добавление звуков и новых уровней.

Правила игры

Цель игры — найти все мины и не взорваться. Мины спрятаны в клетках, а сами поля бывают разных размеров от 9×9 (легкий уровень) до 16×30 (сложный уровень). Впрочем, никто не запрещает использовать любой понравившийся размер поля.

При нажатии на клетку вы «раскрываете» её. Если там мина — вы проиграли. Если же мины есть в рядом стоящих клетках, то на месте клетки, на которую вы нажали, появляется число, означающее количество мин вокруг нее. А если вокруг безопасно, то все близлежащие «безопасные» клетки раскроются.

Если вы уверены, что в какой-то клетке находится мина, смело жмите на нее правой кнопкой — клетка отметится флажком и вы не сможете «раскрыть» клетку, пока не снимите флажок. Это делается для того, чтобы вы случайно не открыли клетку, в которой точно находится мина.

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

Базовая клетка

Создайте новый проект Unity, добавьте на сцену кубик (Cube) и назовите его Tile. Перетащите его в папку с проектом для того, чтобы превратить его в префаб. Кубик пока ничего не умеет, но мы воспользуемся им, чтобы построить игровое поле, а затем добавим ему новых возможностей.

Генератор клеток

Создайте новый пустой объект и назовите его Grid. Так же как и кубик, сделайте его префабом (перетащив в папку с проектом). Этот объект — наш будущий генератор клеток, который и будет создавать игровое поле.

Создайте новый сценарий (в качестве языка программирования будем на этот раз использовать JavaScript) и также назовите его Grid. Прикрепите его к нашему генератору и пропишите в скрипте:

Сохраните скрипт и перетащите наш кубик Tile в поле Tile Prefab компонента Script объекта Grid. У вас должно получится вот так:

ms_04

Названия переменных говорящие: numberOfTiles позволяет задать количество клеток на игровом поле, а distanceBetweenTiles задает расстояние между клетками.

В настоящий момент генератор клеток ничего не делает. Давайте добавим несколько строчек кода в метод CreateTiles:

Если вы нажмете на кнопку Play, то увидите нечто подобное:

ms_01

Функция CreateTiles создает копии префаба кубика (столько раз, сколько мы задали) и помещает их в линию, где расстояние между кубиками равно distanceBetweenTiles. Попробуйте подобрать оптимальное расстояние, чтобы будущее поле выглядело красиво.

Но для Сапёра нам нужно поле в виде сетки, а не линии. Чтобы достичь этого, добавьте в сценарий объекта Grid новую переменную, которая будет отвечать за количество кубиков в строке:

public var tilesPerRow: int = 4;

И перепишем метод CreateTiles:

Запустив игру, вы увидите такую картину:

ms_02

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

Добавление мин

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

public var isMined: boolean = false;

Этот параметр нам и скажет, есть ли в клетке мина. Далее нам нужно позволить генератору создавать новый объект, который мы только что создали. Для этого измените тип переменной tilePrefab с GameObject на Tile в скрипте объекта Grid:

public var tilePrefab: Tile;

А теперь добавим новые переменные:

И не забудем про инициализацию:

И немного изменим команду Instantiate:

А в конце сценария выполним метод AssignMines. Вот так будет выглядеть измененный метод CreateTiles:

Метод AssignMines случайным образом задаст некоторым клеткам мины:

Как работает функция AssignMines? Дело в том, что все созданные клетки в методе CreateTiles помещаются в массив tilesAll. И уже в методе AssignMines они копируются в массив tilesUnmined. Далее случайным образом отбирается numberOfMines плиток. Параметр isMined отобранных плиток устанавливается в true, а они сами удаляются из массива tilesUnmined и помещаются в tilesMined.

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

Дизайн плиток

Сейчас наши клетки выглядят как кубики (по сути это и есть кубики). Давайте изменим их внешний вид.

В исходных файлах вы найдете 3D модель puzzleObjects.fbx. Скопируйте файл в папку с проектом для дальнейшего использования (убедитесь в том, что он импортируется с размером, установленным в 1):

ms_02_01

Перейдите в настройки префаба Tile и поменяйте значение поля Mesh Filter на tileImproved.

ms_02_02

И здесь же сбросьте значения компонента Box Collider, нажав на него правой кнопкой мыши и выбрав Reset.

ms_02_03

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

ms_02_04

Обратите внимание, мы поменяли параметры префаба лишь раз, но изменениям будут подвергнуты все созданные из него объекты. В этом и есть преимущество этих объектов.

Добавление чисел

Для того, чтобы показывать количество мин вокруг открывшейся клетки, воспользуемся 3D текстом. Создайте, выбрав GameObject -> Create Other -> 3D Text, и добавьте его к объекту Tile. Вот, как это должно выглядеть:

ms_02_05

Поверните текст так, чтобы он лежал на клетке, установите значение текста в 0, а его размер измените так, чтобы он не выглядел размытым.

ms_02_06

ms_02_07

Теперь мы должны получить доступ к тексту из кода. Добавьте следующую строчку в скрипт объекта Tile:

public var displayText: TextMesh;

Перетащите объект текста в новое поле компонента Script объекта Tile:

ms_02_14

Вывод

И на этом наша статья подходит к концу. Мы создали функциональную основу для игры в жанре головоломок. Эта основа может быть использована вами не только для Сапёра.

В следующей части этой серии мы добавим больше возможностей клеткам и доведем игру до ума. Оставайтесь с нами!

Перевод статьи «Build a Grid-Based Puzzle Game Like Minesweeper in Unity: Setup»