Как сделать игру «три в ряд» на Unity

В этом уроке вы узнаете, как сделать игру «три в ряд» для Unity3D.

Как сделать игру Match3 в Unity3D

В игре «три в ряд» цель простая: меняйте местами фишки, пока не получится ряд из трех или больше. Когда происходит сочетание, то эти объекты удаляются, а пустые места заполняются. Это позволяет игрокам проделывать потенциальные комбинации и получать кучу игровых очков!

В этом уроке вы узнаете, как сделать следующее:

  • создать поверхность, заполненную игровыми фишками
  • выбирать и отменять выбор фишек при помощи мыши
  • определять соседние фишки
  • менять местами фишки
  • определять сочетания из трех и более фишек
  • заполнять пустые пространства после того, как произошло сочетание
  • вести игровой счет и подсчет ходов

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

Приступая к работе

Скачайте начальный проект Match 3 how-to и распакуйте его в каталог по вашему выбору.

Откройте этот проект в Unity. Игровые ресурсы отсортированы в нескольких папках:

Файлы игры Match3 в Unity3D

  • Animations: Содержит анимацию панели завершения игры. Если вам нужно узнать об анимации в Unity, то ознакомьтесь с учебным пособием по этой теме.
  • Audio: Содержит музыку и звуковые эффекты, используемые в игре.
  • Fonts: Содержит шрифты, используемые в игре.
  • Prefabs: Содержит различные менеджеры, UI (пользовательский интерфейс) и префабы тайлов.
  • Scenes: Содержит меню и игровую сцену.
  • Scripts: Содержит скрипты, используемые в игре. BoardManager.cs и Tile.cs — эти скрипты вы будете редактировать.
  • Sprites: Содержит ресурсы пользовательского интерфейса и различные спрайты персонажей, которые будут использоваться в качестве фишек на поверхности.

Создание игровой поверхности

Если у вас не открыта сцена Game, то откройте ее и нажмите на кнопку «play». Это просто чистый синий задний фон со счетчиками игровых очков и ходов. Нужно это поправить!

Для начала создайте пустой игровой объект и назовите его BoardManager.

Игровой объект BoardManager будет отвечать за генерацию поверхности и ее заполнение тайлами.

Далее, найдите скрипт BoardManager.cs в окне Project в папке Scripts\Board and Grid. Перетащите его на пустой игровой объект BoardManager в окне Hierarchy. Должно получиться следующее:

Объект BoardManager в окне Inspector

Пришло время окунуться в код. Откройте BoardManager.cs и взгляните на то, что уже находится в этом файле.

public static BoardManager instance;     // 1
    public List<Sprite> characters = new List<Sprite>();     // 2
    public GameObject tile;      // 3
    public int xSize, ySize;     // 4

    private GameObject[,] tiles;      // 5

    public bool IsShifting { get; set; }     // 6

    void Start () {
        instance = GetComponent<BoardManager>();     // 7

        Vector2 offset = tile.GetComponent<SpriteRenderer>().bounds.size;
        CreateBoard(offset.x, offset.y);     // 8
    }

    private void CreateBoard (float xOffset, float yOffset) {
        tiles = new GameObject[xSize, ySize];     // 9

        float startX = transform.position.x;     // 10
        float startY = transform.position.y;

        for (int x = 0; x < xSize; x++) {      // 11
            for (int y = 0; y < ySize; y++) {
                GameObject newTile = Instantiate(tile, new Vector3(startX + (xOffset * x), startY + (yOffset * y), 0), tile.transform.rotation);
                tiles[x, y] = newTile;
            }
        }
    }
  1. Другим скриптам будет нужен доступ к файлу BoardManager.cs, поэтому этот скрипт использует шаблон Singleton вместе со статичной переменной с именем instance, что позволяет вызывать его из любого скрипта.
  2. characters — это список спрайтов, который вы будете использовать в качестве игровых фишек.
  3. Игровой объект-префаб tile будет инициализирован при создании поверхности.
  4. xSize и ySize — это X и Y размеры поверхности.
  5. Имеется также двумерный массив с именем tiles, который будет использоваться для хранения тайлов на поверхности.
  6. Также есть инкапсулированная переменная IsShifting булевого типа, которая будет сообщать игре найдено ли совпадение и происходит ли перезаполнение игрового поля.
  7. Метод Start() устанавливает singleton со ссылкой на BoardManager.
  8. Вызов функции CreateBoard(), передача в границах размеров спрайта, который используется для отображения тайла.
  9. В CreateBoard() происходит инициализация двумерного массива tiles.
  10. Происходит поиск начальных позиций для генерации игрового поля.
  11. Выполнение цикла через xSize и ySize, создание экземпляра newTile в каждой итерации для получения сетки из рядов и колонок.

Далее, найдите спрайты персонажей в директории Sprites\Characters в окне Project. Выберите BoardManager в окне Hierarchy.

В окне Inspector измените значение Character Size для компонента скрипта BoardManager на 7. Это добавит семь элементов в массив Characters и отобразит ячейки для них в окне Inspector.

Теперь перетащите каждый символ в пустые ячейки. Наконец, выберите префаб Tile в папке Prefabs и поместите его в ячейку Tile.

Когда закончите, сцена должна выглядеть следующим образом:
Объект BoardManager в окне Inspector с заполненными элементами

Теперь выделите BoardManager снова. В компоненте BoardManager в окне Inspector, установите параметр X Size на 8 и Y Size на 12. Это размер игрового поля, с которым вы будете работать в этом уроке.

Настройка ширины и высоты игрового поля для игры Match3 в Unity3D
Нажмите на кнопку «play». Игровое поле сгенерировано, но странным образом выходит за пределы экрана.
Игровое поле для игры Match3 в Unity3D

Это происходит потому что игровое поле создает тайлы сверху направо, причем первый тайл начинается с позиции компонента BoardManager.

Чтобы это исправить, отрегулируйте позицию BoardManager так, чтобы он находился снизу слева от области обзора камеры. Установите позицию по X на -2.66, а позицию Y на 3.83.

Установка позиции игрового поля для игры Match3 в Unity3D

Нажмите на кнопку «play». Выглядит лучше, но это не совсем игра, когда все тайлы одинаковые. К счастью, есть легкий способ наполнить игровое поле случайными объектами.

Игровое поле, заполненное объектами для игры Match3 в Unity3D

Создание случайных объектов на игровом поле

Откройте скрипт BoardManager и добавьте эти строчки кода в метод CreateBoard, сразу под выражением tiles[x, y] = newTile;.

newTile.transform.parent = transform; // 1
Sprite newSprite = characters[Random.Range(0, characters.Count)]; // 2
newTile.GetComponent<SpriteRenderer>().sprite = newSprite; // 3

Эти строчки делают ключевые вещи:

  1. Устанавливают для всех тайлов компонент BoardManager в качестве родительского, чтобы упорядочить иерархию в редакторе.
  2. Случайно выбирается спрайт из нескольких, которые были ранее добавлены.
  3. Устанавливается заново созданный спрайт тайла в случайно выбранный спрайт.

Запустите игру, чтобы увидеть игровое поле с различными фишками.

Игровое поле для игры Match3 в Unity3D, заполненное случайными объектами

Как вы могли заметить, игровое поле может генерировать совпадающие комбинации из трех объектов в начале, и это немного забавно вообще-то.

Поправить повторяющиеся тайлы

Совпадающие объекты на игровом поле игры Match3 в Unity3D

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

Чтобы это сделать создайте две переменные типа Sprite в методе CreateBoard справа над двойным циклом for:

Sprite[] previousLeft = new Sprite[ySize];
Sprite previousBelow = null;

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

Размещение объектов на игровом поле игры Match3 в Unity3D

Цикл проходит по всем тайлам снизу слева и поднимается вверх по одному тайлу. Каждый проход получает значение, отображаемое слева и снизу текущего тайла, и удаляет их из списка новых возможных значений. Случайный символ вытягивается из списка и назначается левому и нижнему тайлу.

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

Для этого нужно добавить следующие строчки кода сразу над
Sprite newSprite = characters[Random.Range(0, characters.Count)];:

List<Sprite> possibleCharacters = new List<Sprite>(); // 1
possibleCharacters.AddRange(characters); // 2

possibleCharacters.Remove(previousLeft[y]); // 3
possibleCharacters.Remove(previousBelow);
  1. Создается список возможных символов для этого спрайта.
  2. Добавляются все символы в список.
  3. Удаляются символы, которые находятся слева и снизу текущего спрайта из списка возможных символов.

Далее замените эту строчку:

Sprite newSprite = characters[Random.Range(0, characters.Count)];

на эту

Sprite newSprite = possibleCharacters[Random.Range(0, possibleCharacters.Count)];

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

Наконец, добавьте эти строчки ниже этого выражения newTile.GetComponent().sprite = newSprite;:

previousLeft[y] = newSprite;
previousBelow = newSprite;

В этом коде назначается newSprite левому и нижнему тайлу от текущего для использования в следующем проходе цикла.

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

Объекты с уникальным распожением на игровом поле игры Match3 в Unity3D

Меняем тайлы местами

Самая важная игровая механика в игре жанра «три в ряд» — это выбор и обмен тайла с соседним для составления ряда из трех одинаковых фишек. Чтобы это сделать нужно написать дополнительный код. Сначала это будет выбор тайла.

Откройте файл Tile.cs в редакторе кода. Для удобства этот скрипт уже имеет несколько переменных и два метода: Select и Deselect.

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

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

В среде разработки Unity есть встроенный метод MonoBehaviour готовый к использованию: OnMouseDown.

Добавьте этот метод в скрипт Tile.cs под метод Deselect:

void OnMouseDown() {
	// 1
    if (render.sprite == null || BoardManager.instance.IsShifting) {
        return;
    }

    if (isSelected) { // 2 Is it already selected?
        Deselect();
    } else {
        if (previousSelected == null) { // 3 Is it the first tile selected?
            Select();
        } else {
            previousSelected.Deselect(); // 4
        }
    }
}
  1. Проверяем разрешено ли игрой выбирать тайлы при помощи мыши. Бывает, что нужно ограничивать игроков в этом, например, когда игра заканчивается или тайл не содержит объект.
  2. Выражение if (isSelected) определяет выделить или отменить выделение тайла. Если тайл уже выбран, то снимается выделение.
  3. Проверяем есть ли уже какой-нибудь выбранный тайл. Когда переменная previousSelected равна null, то этот тайл первый, поэтому выделяем его.
  4. Если это был не первый выбранный тайл, то снимаем выделение со всех тайлов.

Сохраните этот скрипт и вернитесь в редактор. Теперь вы сможете выделять и снимать выделение с тайлов при помощи нажатия кнопкой мыши на них.

Выделение объектов курсором мыши на игровом поле игры Match3 в Unity3D
Все в порядке? Теперь можно добавить механизм обмена для фишек.

Своппинг тайлов

Начните с открытия файла Tile.cs и добавления следующего метода SwapSprite сразу под методом OnMouseDown:

public void SwapSprite(SpriteRenderer render2) { // 1
    if (render.sprite == render2.sprite) { // 2
        return;
    }

    Sprite tempSprite = render2.sprite; // 3
    render2.sprite = render.sprite; // 4
    render.sprite = tempSprite; // 5
    SFXManager.instance.PlaySFX(Clip.Swap); // 6
}

Этот метод будет менять местами спрайты двух тайлов. Вот как это работает:

  1. Устанавливается SpriteRenderer с именем render2 в качестве параметра, который будет использоваться вместе с render для обмена спрайтами.
  2. Проверяется render2 с SpriteRenderer текущего тайла. Если они одинаковые, то ничего не делать, так как обмен местами двух одинаковых спрайтов не будет иметь смысла.
  3. Создается tempSprite для хранения спрайта render2.
  4. Происходит обмен второго спрайта установкой его на место первого.
  5. Первый спрайт заменяется на второй также (который был назначен в tempSprite).
  6. Воспроизведение звукового эффекта.

Используя реализованный метод SwapSprite вы можете вызывать его из метода OnMouseDown.

Добавьте эту строчку над previousSelected.Deselect(); в операторе else метода OnMouseDown:

SwapSprite(previousSelected.render);

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

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

Выбор и своппинг объектов на игровом поле игры Match3 в Unity3D

Поиск соседних тайлов

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

Но как легко найти соседние тайлы?

Откройте Tile.cs и добавьте следующий метод под методом SwapSprite:

private GameObject GetAdjacent(Vector2 castDir) {
    RaycastHit2D hit = Physics2D.Raycast(transform.position, castDir);
    if (hit.collider != null) {
        return hit.collider.gameObject;
    }
    return null;
}

Этот метод будет возвращать один соседний тайл, посылая луч в сторону, указанную параметром castDir. Если тайл найден в этом направлении, то возаращается его экземпляр типа GameObject.

Пример поиска сочетаний объектов на игровом поле игры Match3 в Unity3D
Далее, добавьте следующий метод ниже метода GetAdjacent:

private List<GameObject> GetAllAdjacentTiles() {
    List<GameObject> adjacentTiles = new List<GameObject>();
    for (int i = 0; i < adjacentDirections.Length; i++) {
        adjacentTiles.Add(GetAdjacent(adjacentDirections[i]));
    }
    return adjacentTiles;
}

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

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

Замените следующий код в методе OnMouseDown:

else {
    SwapSprite(previousSelected.render);
    previousSelected.Deselect();
}

на этот:

else {
    if (GetAllAdjacentTiles().Contains(previousSelected.gameObject)) { // 1
        SwapSprite(previousSelected.render); // 2
        previousSelected.Deselect();
    } else { // 3
        previousSelected.GetComponent<Tile>().Deselect();
        Select();
    }
}
  1. Вызывается GetAllAdjacentTiles и проверяется, есть ли выбранный игровой объект previousSelected в списке возвращенных соседних тайлов.
  2. Меняются местами спрайты тайлов.
  3. Тайл не находится рядом с ранее выбранным, отменяется выбор предыдущего тайла и выбирается новый тайл.

Сохраните скрипт и вернитесь в редактор Unity.

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

Выделение и своппинг объектов на игровом поле игры Match3 в Unity3D
Теперь нужно разобраться с важным элементом игры — сочетанием фишек.

Сочетания фишек

Сочетание фишек по цвету можно разделить на следующие шаги:

  1. Найти три и больше одинаковых спрайтов рядом друг с другом и учесть это как комбинацию.
  2. Удалить тайлы, которые совпадают по цвету.
  3. Спустить тайлы вниз, чтобы заполнить пустое пространство.
  4. Заполнить пустые тайлы сверху новыми фишками.
  5. Проверить есть ли еще совпадения.
  6. Повторять до тех пор, пока имеются совпадения.

Откройте файл Tile.cs и добавьте следующий метод ниже метода GetAllAdjacentTiles:

private List<GameObject> FindMatch(Vector2 castDir) { // 1
    List<GameObject> matchingTiles = new List<GameObject>(); // 2
    RaycastHit2D hit = Physics2D.Raycast(transform.position, castDir); // 3
    while (hit.collider != null && hit.collider.GetComponent<SpriteRenderer>().sprite == render.sprite) { // 4
        matchingTiles.Add(hit.collider.gameObject);
        hit = Physics2D.Raycast(hit.collider.transform.position, castDir);
    }
    return matchingTiles; // 5
}

Итак, что же здесь проихсодит?

  1. Данный метод назначает Vector2 в качестве параметра, который будет направлением, в котором будут запускаться все лучи.
  2. Создается новый список экзепляров класса GameObjects для хранения всех сочетающихся тайлов.
  3. Выпускаем луч из тайла в направлении castDir.
  4. Продолжаем выпускать новые лучи, пока они не найдут то, что нужно, или пока спрайт тайлов будет отличаться от возвращаемого спрайта объекта. Если оба условия соблюдены, то рассматриваем это как комбинацию фишек и добавляем в список.
  5. Возвращаем список комбинаций фишек.

Добавьте следующую булевую переменную в начале файла, сразу вверху метода Awake:

private bool matchFound = false;

Когда комбинация будет найдена, то эта переменная примет значение true.

Теперь добавьте следующий метод под методом FindMatch:

private void ClearMatch(Vector2[] paths) // 1
{
    List<GameObject> matchingTiles = new List<GameObject>(); // 2
    for (int i = 0; i < paths.Length; i++) // 3
    {
        matchingTiles.AddRange(FindMatch(paths[i]));
    }
    if (matchingTiles.Count >= 2) // 4
    {
        for (int i = 0; i < matchingTiles.Count; i++) // 5
        {
            matchingTiles[i].GetComponent<SpriteRenderer>().sprite = null;
        }
        matchFound = true; // 6
    }
}

Данный метод ищет все совпадающие тайлы среди имеющихся путей, а затем очищает эти совпадения соответственно.

  1. Берем массив путей Vector2; это пути, по которым тайл проведет луч.
  2. Создаем список экземпляров класса GameObject для хранения комбинаций.
  3. Перебираем список путей и добавляем комбинаци в спискок matchingTiles.
  4. Продолжаем, если найдены совпадения двумя и более тайлами. Вы можете удивиться, почему с двумя тайлами здесь достаточно, потому что третий это начальный тайл.
  5. Перебираем все совпадающие тайлы и удаляем их спрайты, устанавливая им значения null.
  6. Устанавливаем для matchFound значение true.

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

public void ClearAllMatches() {
    if (render.sprite == null)
        return;

    ClearMatch(new Vector2[2] { Vector2.left, Vector2.right });
    ClearMatch(new Vector2[2] { Vector2.up, Vector2.down });
    if (matchFound) {
        render.sprite = null;
        matchFound = false;
        SFXManager.instance.PlaySFX(Clip.Clear);
    }
}

Это запустит метод домино. Он вызывает ClearMatch для вертикальных и горизонтальных комбинаций. ClearMatch будет вызывать FindMatch для каждого направления, влево и вправо, или вверх и вниз.

Если вы нашли комбинацию по горизонтали или вертикали, тогда устанавливаете текущему спрайту значение null, сбрасываете переменную matchFound на false, и воспроизводите звуковой эффект «комбинации».

Чтобы все это заработало, нужно вызывать метод ClearAllMatches() каждый раз, когда фишки обмениваются местами.

В методе OnMouseDown добавьте следующую строчку сразу перед previousSelected.Deselect(); :

previousSelected.ClearAllMatches();

Теперь добавьте следующий код после строчки previousSelected.Deselect(); :

ClearAllMatches();

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

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

Сочетание и очищение объектов на игровом поле игры Match3 в Unity3D

Чтобы заполнить пустое пространство нужно сдвинуть и снова заполнить игровое поле.

Перенос и повторное заполнение игрового поля тайлами

Перед тем, как передвинуть тайлы нужно найти пустые тайлы. Откройте файл BoardManager.cs и добавьте следующую подпрограмму ниже метода CreateBoard:

public IEnumerator FindNullTiles() {
    for (int x = 0; x < xSize; x++) {
        for (int y = 0; y < ySize; y++) {
            if (tiles[x, y].GetComponent<SpriteRenderer>().sprite == null) {
                yield return StartCoroutine(ShiftTilesDown(x, y));
                break;
            }
        }
    }
}

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

Данная подпрограмма будет перебирать все игровое поле и искать фишки со спрайтами, значение которых null. Когда будет найден пустой тайл, запустится другая подпрограмма ShiftTilesDown для обработки фактическое перемещения тайлов.

Добавьте следующую подпрограмму ниже предыдущей:

private IEnumerator ShiftTilesDown(int x, int yStart, float shiftDelay = .03f) {
    IsShifting = true;
    List<SpriteRenderer>  renders = new List<SpriteRenderer>();
    int nullCount = 0;

    for (int y = yStart; y < ySize; y++) {  // 1
        SpriteRenderer render = tiles[x, y].GetComponent<SpriteRenderer>();
        if (render.sprite == null) { // 2
            nullCount++;
        }
        renders.Add(render);
    }

    for (int i = 0; i < nullCount; i++) { // 3
        yield return new WaitForSeconds(shiftDelay);// 4
        for (int k = 0; k < renders.Count - 1; k++) { // 5
            renders[k].sprite = renders[k + 1].sprite;
            renders[k + 1].sprite = null; // 6
        }
    }
    IsShifting = false;
}

ShiftTilesDown работает, принимая x-позицию и y-позицию, и задержку. Параметры x и y используются для определения тайла, который нужно передвинуть. Нужно чтобы тайлы перемещались вниз, поэтому параметр x будет неизменным, в то время как y будет изменяться.

Подпрограмма делает следующее:

  1. Происходит перебор и поиск количества свободного пространства, нужного для того, чтобы спустить тайлы.
  2. Сохраняется количество свободных тайлов в целочисленной переменной nullCount.
  3. Снова проходит перебор, чтобы начать фактическое перемещение тайлов.
  4. Срабатывает пауза в секундах, определяемая параметром shiftDelay.
  5. Цикл перебирает каждый SpriteRenderer в списке renders.
  6. Меняются местами спрайты с верхними, пока не будет достигнут конец и последний спрайт не установлен на значение null.

Теперь нужно остановиться и запустить подпрограмму FindNullTiles как только найдена комбинация фишек.

Сохраните скрипт BoardManager и откройте файл Tile.cs. Добавьте следующие строчки в метод ClearAllMatches(), над выражением SFXManager.instance.PlaySFX(Clip.Clear); :

StopCoroutine(BoardManager.instance.FindNullTiles());
StartCoroutine(BoardManager.instance.FindNullTiles());

Это остановит подпрограмму FindNullTiles и запустит снова с самого начала.

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

Заполнение игрового поля объектами для игры Match3 в Unity3D
Откройте файл BoardManager.cs и добавьте следующий метод ниже ShiftTilesDown:

private Sprite GetNewSprite(int x, int y) {
    List<Sprite> possibleCharacters = new List<Sprite>();
    possibleCharacters.AddRange(characters);

    if (x > 0) {
        possibleCharacters.Remove(tiles[x - 1, y].GetComponent<SpriteRenderer>().sprite);
    }
    if (x < xSize - 1) {
        possibleCharacters.Remove(tiles[x + 1, y].GetComponent<SpriteRenderer>().sprite);
    }
    if (y > 0) {
        possibleCharacters.Remove(tiles[x, y - 1].GetComponent<SpriteRenderer>().sprite);
    }

    return possibleCharacters[Random.Range(0, possibleCharacters.Count)];
}

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

В подпрограмме ShiftTilesDown замените строчку:

renders[k + 1].sprite = null;

на следующую:

renders[k + 1].sprite = GetNewSprite(x, ySize - 1);

Это будет гарантировать то, что игровое поле всегда заполнено тайлами.

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

Схема проверки новых комбинаций для игры Match3 в Unity3D

Комбинации сочетаний

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

Откройте файл BoardManager.cs и найдите метод FindNullTiles().

Добавьте следующий оператор for внизу метода, ниже цикла for:

for (int x = 0; x < xSize; x++) {
    for (int y = 0; y < ySize; y++) {
        tiles[x, y].GetComponent<Tile>().ClearAllMatches();
    }
}

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

Сохраните работу и запустите игру. Начните менять местами тайлы и обратите внимание, как бесконечный запас новых тайлов продолжает заполнять игровое поле во время игры.
Заполнение игрового поля новыми объектами для игры Match3 в Unity3D

Движение счетчика и подсчет игровых очков

Пришло время отследить движения игрока и игровой счет.

Откройте файл GUIManager.cs, расположенный под Scripts\Managers в редакторе кода. Этот скрипт обрабатывает пользовательский интерфейс игры, включая двигающийся счетчик и игровой счет.

Чтобы начать добавьте эту переменную в начале файла, под переменной private int score;:

private int moveCounter;

Теперь добавьте следующий код в начале метода Awake(), чтобы инициализировать количество ходов, которые может сделать игрок:

moveCounter = 60;
moveCounterTxt.text = moveCounter.ToString();

Теперь нужно инкапсулировать эти переменные, чтобы можно было обновлять UI Text каждый раз, когда изменяется значение. Добавьте следующий код сверху метода Awake():

public int Score {
    get {
        return score;
    }

    set {
        score = value;
        scoreTxt.text = score.ToString();
    }
}

public int MoveCounter {
    get {
        return moveCounter;
    }

    set {
        moveCounter = value;
        moveCounterTxt.text = moveCounter.ToString();
    }
}

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

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

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

Сохраните скрипт и откройте файл BoardManager.cs. Добавьте следующий код в метод ShiftTilesDown, прямо над выражением yield return new WaitForSeconds(shiftDelay);:

GUIManager.instance.Score += 50;

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

В Tile.cs добавьте следующую строчку кода под SFXManager.instance.PlaySFX(Clip.Swap); в методе SwapSprite:

GUIManager.instance.MoveCounter--;

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

Сохраните скрипт и проверьте работу игровых счетчиков. Каждое движение должно будет уменьшать счетчик движения, а каждая комбинация фишек будет давать несколько игровых очков.

Подсчет игровых очков для игры Match3 в Unity3D

Экран завершения игры

Игра должна заканчиваться, когда счетчик движения достигает нуля. Откройте файл GUIManager.cs и добавьте следующий оператор if для установщика MoveCounter, под выражением moveCounter = value;:

if (moveCounter <= 0) {
    moveCounter = 0;
    GameOver();
}

В основном это будет работать. Так как GameOver() вызывается сразу на последнем ходу, комбинации не будут засчитываться в окончательный игровой счет. И конечно, это даст хотя отзыв об игре с оценкой в одну звезду.

Чтобы не допустить этого, нужно создать подпрограмму, которая будет ждать пока BoardManager.cs не завершит все передвижения фишек. Затем можно будет вызывать GameOver().

Добавьте следующую подпрограмму в GUIManager.cs под методом GameOver():

private IEnumerator WaitForShifting() {
    yield return new WaitUntil(()=> !BoardManager.instance.IsShifting);
    yield return new WaitForSeconds(.25f);
    GameOver();
}

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

GameOver();

На следующую:

StartCoroutine(WaitForShifting());

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

Игровой счет для игры Match3 в Unity3D

Куда двигаться дальше?

Вы можете скачать весь проект здесь.

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

  • Синхронизированный режим
  • Различные уровни с разнообразными размерами игрового поля
  • Бонусные очки за комбинации
  • Добавьте несколько систем частиц для клевых эффектов

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

Вот несколько ресурсов, чтобы узнать еще больше:

  • Unity scripting tutorials: Видео уроки, которые обучают основам написания скриптов в Unity.
  • Introduction to Unity UI: Система пользовательского интерфейса Unity в этой серии уроков.
  • Introduction To Unity Particle Systems: Изучите, как работают системы частиц и привнесите в игру интересные эффекты.

Автор перевода: Jean Winters
Источник: How to Make a Match 3 Game in Unity

Смотрите также:

Как сделать интерфейс в Юнити для игрыВведение в создание пользовательского интерфейса на Unity


как импортировать 2d-ресурсы в UnityИмпортирование 2D-ресурсов в Unity


Комментариев: 3 на “Как сделать игру «три в ряд» на Unity

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

    1. Хотелось бы еще уроков как сделать игру, например стратегию или игру для смартфона, и как продавать в плэймаркете

  2. Урок хорошо проработан, это моя первая игра в этом стиле и мне понравился результат, всё объясняется понятно и легко для восприятия

Комментировать

Почта не публикуется.Обязательные поля отмечены *