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

Идея пришла мне в голову, когда я думал о том, как можно использовать структуру данных Stack в разработке игр. Затем мне приходят на ум игры слияния, спрайты можно хранить как стек, и каждый новый спрайт слияния извлекается из стека для отображения. Когда я думал о примере игры со слиянием, мне в голову пришла Cat Condo, на которой я основал эту демоверсию. Другими популярными примерами игр слияния могут быть игры слияния Gram Games и 2048. Интересно, стоит ли мне переходить на 2048 в следующий раз.

Проект стал проще, чем я ожидал. Игра в сетке 3 * 3, имеет 6 уровней спрайтов. Новый элемент появляется на коврике каждые 2 секунды (может изменяться). Это не совсем игра, а конечное состояние игры. Это когда коврик заполняется спрайтами, и слияние больше невозможно.

Есть 3 типа игровых объектов с MonoBehaviour и один игровой объект для создания фона.

TileSpriteHolder имеет прикрепленный к нему TileSpriteManager MonoBehaviour. Он берет спрайты в увеличивающемся количестве граней и переворачивает этот список в стек. Этот стек используется TileElements для обновления своих спрайтов и сброса их внешнего вида при необходимости.

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

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

Все ссылки на объекты TileElement хранятся в этом классе внутри списков FullTiles и EmptyTiles. Вначале TileMap отправляет сигнал своим первым дочерним элементам, которые являются TileElements, для инициализации, заполняя свой стек спрайтов из объекта TileSpriteManager, и добавляет их в список EmptyTiles.

Каждые 2 секунды объекты проверяют, есть ли среди пустых плиток место для создания нового. Этот элемент управления выполняется путем вызова функции CreateNewTile с использованием InvokeRepeating («CreateNewTile», 0.0f, 2.0f). Если пустых плиток нет, это означает, что игра окончена, в противном случае из списка EmptyTiles выбирается новая плитка.

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

Объекты Tile - это фон и точки привязки TileElements. Более того, они используются для перебора своих дочерних объектов с помощью TileElement MonoBehaviour от TileMap. К ним не прикреплено MonoBehaviour.

К каждому объекту TileElement прикреплены Rigidbody 2D и Box Collider 2D, которые позволяют запускать вызовы триггеров и события мыши на объекте. Это объекты, которые невозможно изменить внутри тайловой карты, и их можно объединить или поменять местами.

Поскольку проект очень прост, TileElement - самая сложная его часть. Он состоит из частей, в которых реализованы функции слияния, перемещения и полезности.

Логические значения предназначены для проверки состояния движения и столкновения. Имеется ссылка на объект TileMap, сигнализирующая о пустом состоянии объекта. Справка для TileSpriteManager - получить и сбросить стек спрайтов. GameObject triggered - игровой объект, с которым сталкивается текущий TileElement.

Движение контролируется с помощью событий OnMouseUp и OnMouseDown. Чтобы они работали, объекту нужен коллайдер. Когда объект нажимается на MoveWithMouse, логическое значение имеет значение true, а позиция игрового объекта устанавливает положение мыши на экране, если его компонент SpriteRenderer включен. SpriteRenderer отмечен, чтобы не перемещать пустые плитки. При нажатии мыши, если объект находится в столкновении с другим TileElement, состояние разрешается в функции OntoAnotherOne, в противном случае положение игрового объекта устанавливается равным положению его родитель.

Хотя существует постоянная коллизия между элементами TileElement, логическое значение CollidedWithOther принимает значение true до тех пор, пока коллизия не закончится. Игровой объект остающегося столкновения сохраняется в игровом объекте triggered.

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

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

Чтобы поменять местами два TileElement, мы просто меняем местами их родителей и отправляем их на новые позиции.

Эта функция определяет, будет ли отображаться спрайт игрового объекта. Если он должен быть показан, это означает, что он вызывается из TileMap, и спрайт будет показан в первый раз. В противном случае спрайт объекта скрывается путем отключения его компонента SpriteRenderer.

Текущий спрайт обновляется по мере извлечения из стека спрайтов.

Эта функция вызывается, когда TileElements устанавливается из TileMap в первый раз. Стек устанавливается в исходную структуру, позиция устанавливается в положение родительского элемента Tile, а размер коллайдера бокса устанавливается в соответствии с границами спрайта путем взгляда на верхний объект стека спрайтов.

Чтобы сбросить стек спрайтов, он заполняется из полного стека TileSpriteManager.

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

Несмотря на то, что этот проект разработан в 2D, при необходимости его будет очень легко преобразовать в 3. Для создания игры слияния вам даже не требуется настройка сетки. Когда два типа объектов сталкиваются друг с другом, может быть создан новый.

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

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

Вы можете найти проект здесь.