Simple Merge Mechanic In Unity

Ece Sefercioğlu
6 min readAug 23, 2019

--

In merge games, as you merge, you see new elements in the game. In this simple demo, each merge leads us to convex polygons with more edges up to octagon.

I came up with the idea while thinking of how one can use Stack data structure in game development. Then merge games come to my mind, sprites can be held like a stack and each merge new sprite is popped from the stack to be shown. While thinking of an example merge game, Cat Condo was the first to come to my mind, which I based this demo on. Another popular merge game examples could be merge games of Gram Games and 2048. I wonder if I should go for 2048 the next time.

Do you also sketch your ideas before implementation?

The project became more simple than first I expected it to be. The game is in a 3 * 3 grid, has 6 sprite level. New element pops out to the mat in every 2 seconds(which can be variable). There is not exactly a game over but game end state. It is when the mat filled up with sprites and no more merges could be done.

There are 3 types of game objects with MonoBehaviour and one game object for creating the background.

TileSpriteHolder has the TileSpriteManager MonoBehaviour attached to it. It takes the sprites in increasing edge count and reverses these list to a stack. This stack is used by TileElements to upgrade their sprites and reset their appearance if needed.

TileSprites are still private but can be taken from the editor in Unity by using a SerializeField attribute. The only public member of this class is GetTileSpritesStack which returns a full stack of sprites.

TileMap holds all the Tiles as child objects and manipulates the actions of filling a new TileElement when needed.

All the TileElement objects’ references are held in this class inside FullTiles and EmptyTiles lists. At the start, TileMap sends a signal to its children’s first children, which are TileElements to initialize itself by filling their sprite stack from the TileSpriteManager object’s one and adds them into the EmptyTiles list.

In every 2 seconds objects checks if there is a spot among empty tiles to create a new one. This control done by calling CreateNewTile function using InvokeRepeating(“CreateNewTile”, 0.0f, 2.0f). If there are no empty tiles it means the game ended otherwise new tile is chosen among EmptyTiles list.

Putting a TileElement into the empty state is moving it from FullTiles to EmptyTiles. To fill a tile it is moved from EmptyTiles to FullTiles and it is signaled to show its sprite as well.

Tile objects are backgrounds and anchor points of TileElements. Moreover, they are used to iterate over their child objects with TileElement MonoBehaviour by TileMap. These do not have a MonoBehaviour attached to it.

Each TileElement object has a Rigidbody 2D and Box Collider 2D attached to it to enable trigger calls and mouse events on the object. These are the objects that are intractable inside the tilemap and can be merged or swapped.

As the project is very simple, TileElement is the most complex part it can ever have. It consists of the parts where merging, moving and utility functions are implemented.

Booleans are for checking movement and collision states. There is a reference for TileMap object to signal empty state of the object. The reference for TileSpriteManager is to get and reset Sprites stack. GameObject triggered is the game object that the current TileElement collides with.

Movement is controlled with OnMouseUp and OnMouseDown events. For them to work the object needs collider on. When the object is pressed on MoveWithMouse boolean set to true and the position of the game object set the position of the mouse on the screen if its SpriteRenderer component is enabled. SpriteRenderer is checked to not to move empty tiles. When the mouse pressed up if the object is in collision with another TileElement, the state is resolved in OntoAnotherOne function, otherwise, the game object’s position is set to the position of its parent.

While there is a staying collision between TileElements, CollidedWithOther boolean is set to be true until collision ends. The game object of the staying collision is kept in triggered game object.

When mouse pressed up and a TileElement is onto other TileElement OntoOtherOne function is called. This function decides if a swap or merge happens between the objects. For merge, the level of the sprites should be same, this is checked by sprite counts in the stacks. Sprite count being zero means TileElement is at the top level of sprite and the last sprite too popped out of the Sprites elements. So no more merges can happen.

When there is a merge either the current one should be hidden and the other one updated or vice versa. Hiding current one and updating the other one requires more simple actions so this approach was chosen. The sprite of other TileElement is leveled up while the current one should be hidden and wait for its respawn. It is sent to its parent position, sprite stack is reset, its appearance is hided and TileMap is informed on that this TileElement is no longer shows a sprite.

To swap two TileElements we simply swap their parents and send them to their new positions.

This function controls whether the sprite of the game object is shown or not. If it were to be shown, it means it is called from TileMap, and the sprite will be shown the first time. Otherwise, the object’s sprite is hidden by disabling its SpriteRenderer component.

Current sprite is updated as it is popped from Sprites stack.

This function is called when TileElements are set from TileMap for the first time. The stack is set to initial structure, the position is set to parent Tile ‘s position and the size of box collider set to the bounds of the sprite by peeking on the top object of the sprite stack.

To reset the Sprites stack it is filled up from TileSpriteManager ‘s full one.

BackToParent does what the name suggests, returns the game object where it was initially, in its parent’s position.

The final state of the game

Even though this project is developed in 2D it will be very easy to convert it in 3 if needed. You do not even need a grid setting to create a merge game. When two kinds of object collide with other a new one can be created.

After going through the development I think using stacks was not necessary for this implementation. There could be a list of the sprites and an index counter that would be reset when the TileElement needs to be hidden.

Using stacks is suitable for the idea if there is a constant increase in the count of TileElements. The updated TileElements could stay in an object pool and wait for its time to shine when new merges are done to its level. All in all, I am happy with how this project turned out. Using stacks worked well with the project too.

You can find the project here.

--

--

Ece Sefercioğlu

Indie Game Developer/Blogger/Student likes to tweak around the mechanics and share