Trending
Opinion: How will Project 2025 impact game developers?
The Heritage Foundation's manifesto for the possible next administration could do great harm to many, including large portions of the game development community.
Featured Blog | This community-written post highlights the best of what the game industry has to offer. Read more like it on the Game Developer Blogs or learn how to Submit Your Own Blog Post
What goes into a match-3 game that takes 4 years to make? In this post I'll show you my methods and a host of games and creators that helped shape an original way to match-3.
Back in early 2014 I embarked on some research into the Match 3 genre in preparation for my own foray. I laid out my findings in a quippy Gamasutra post. Consider that article a primer on what I'm going to go into here. I feel I was somewhat remiss not to have known about Zuma at the time (perhaps unsurprising given that E.A. won't give it its own page on their site), a game which has been popular enough to spawn various clones that take the mechanic in new directions. One interesting example being Marble Duel, a Puzzle Quest riff.
Since then I've not seen any games doing what I was trying to do with the Match 3 board. My method was to combine sliding-tile-puzzles with match-3. The closest was perhaps Swap Sword, which has you moving a character around the board, but still able to swipe other match patterns.
The mechanic I was exploring was much simpler. It started with a game I made built in Steven Lavelle's PuzzleScript: a browser based tool where you make games using some simple methods of describing the content, and some pattern conversion rules. Let's look at some of the pattern-code I used in my first match-3 PuzzleScript game, Mandy Crush. In this game your task is to clear the board of items, which you can only move by swapping places with them. (All the PuzzleScript games I'm linking to have a "hack" link at the bottom that will allow you to inspect and modify the code.)
[ RedMatch | RedMatch | RedMatch ] -> [ RedMatch DebrisDelay | RedMatch DebrisDelay | RedMatch DebrisDelay ]
Three items in a row are converted by adding a new tile on top that marks them for destruction (DebrisDelay in this case). The reason we only mark for destruction is that we want to be able to match four or more in a row as well. Converting the matchable items straight away will result in only part of a match-4 being destroyed. The code describes a RedMatch instead of a RedItem because this game also features wild-card items that will match with any colour. This is something I've rarely seen in match-3 games, and I'm not sure why that's the case. There's a lot of valuable features in a few match-3 games that aren't common to all. It's as if by flooding the market with so many iterations, we're unable to see innovations that are being made. By explaining my process, I hope to reduce the amount of trial and error other match-3 designers may be going through.
My motives stem from the fact that many of my solutions are not my own. Using the debris marker to allow matching of 3+ items was not my idea, that honor goes to Alan Hazelden, the creator of How to Build a Good Snowman. The evidence is in this discussion over an early version of Mandy Crush on the Google Groups forum for PuzzleScript. The point is that I don't make advances in isolation. I talk to people, show them prototypes and people tell me how to improve on them. If you should learn anything from my methods, the most important is sharing your discoveries and being steered to a better destination.
I followed Mandy Crush with a prototype that behaved more like a traditional match-3 game: Drop Swap. Destroyed items were now replaced by new ones dropping in and in its first incarnation I rewarded a match with a dash. This early prototype was a bit annoying to play, as you can experience for yourself. I can't take credit for the limited turns system that came later. That goes to Kalev Tait (whose only work I can find is this devious riff on Sokoban). In the thread where I developed the prototype they posted this:
I find the 'charge' a little frustrating to use... I set something up, and suddenly I'm accidentally charged and then I need to destroy a bunch of blocks rather than continue with my current set up.
What I would recommend, is:
- drop the 'charge' power
- add a 'life counter'
- each turn reduces the 'life counter' by one
- making a match recharges the 'life counter' (and gives points)
- if the 'life counter' reaches zero, you lose
In the very next post I dismissed this advice. (I dismiss all advice when I first receive it, only after forgetting my hubris do I realise someone is telling me something valuable.) Then I implemented it and started discussing new possibilities. Dennis Au - a man who could spin gold thread out of PuzzleScript - showed me how to detect junction matches. This is when one has two matches that overlap one another. Candy Crush rewards you for such a match with a special item and I wanted to do the same:
Right [ Collect | Collect ] -> [ > Collect | > Collect ] Down [ Collect | Collect ] -> [ > Collect | > Collect ] [ Down Collect | Right Collect ] [ Collect OldPos ] -> [ Collect | Collect ] [ Bomb OldPos ] sfx6
This code stitches two existing matches together and creates a reward where the player used to be. I followed this up with rewards for matching four and five before cobbling together a few quests. The final result compiled into a few thousand instructions in PuzzleScript's compiler. Stephen told me at a later date that when he improved the engine he used Drop Swap as a case study and got those thousands down to less than 200.
Drop Swap's warm reception spurred me to do the research into the match-3 genre I mentioned at the beginning of this article. I then went about translating the code to Unity. The code for examining a match is a simple sweep leftwards, then downwards, before collating groups. But to set my game apart from others I wanted a feature I had seen in the game Zookeeper.
Zookeeper is rare among match-3 games for a something that is seldom replicated. I call this feature Board Locking. When the player makes a match in Candy Crush, you cannot interact with the game until everything comes to a halt. Zookeeper is wiser than this, you can match, match, match, and as the new items drop in you can keep on matching. How are they managing this in a turn based game? Through careful study I noticed that parts of the board did indeed become "locked" but elsewhere the player is free to interact. One of my early prototypes illustrates how this works:
Usually when one makes a game, we take its state and work bottom-up to create an animation so that the player can understand what is going on. When we Board Lock, we work top-down as well, the animation manager declares a section of the board locked whilst it is being animated. We rule out any calculations on locked sections of the board until they are free again. The result is that play is almost free from interruption and we can also do the impossible, allow two people to play a turn based game at the same time.
This success was my undoing. Possibilities for what the game could be exploded. I tried to support multiplayer so my code bloated to account for the potential. Even showing off this branch of gameplay at local developer meet-ups. The prevalence of quest-based match-3 games convinced me that I had to make a level based game that allowed for many objectives. More bloat. This was my first attempt at a game in Unity so I structured the code matryoshka-style to keep my pure board state calculations free of Unity's cumbersome components (in hindsight, this allowed Six Match's best feature to flourish). Work on the project ground to a halt as I lost sight of what I was making. For a few years afterwards I struggled to focus on anything. Now and again I would tinker with the project but concluded that it was not casual enough and the quests I'd assembled were not interesting.
I briefly toyed with match-3 again by making a sequel to an old PuzzleScript game of mine using some of Drop Swap's code: Drop Kick. Though I feel like it never became as solid a game as it should have been due to how quiet the forums had become. Twitter and likewise Discord and Slack have provided an easier alternative to forums, but they are terrible places for long form design. There are no dedicated threads, no code formatting, and no in depth review because any lengthy response will be interrupted by a change of subject. The forums still breathe but they are a shadow of their former selves.
Only last year did I find myself between projects thinking, "what was it about Drop Swap that made it fun?" I had a good game, people had told me so. I realised it wasn't in quests, or competition, but just getting to the next match. It didn't matter what the over-arching goal was, what mattered was finding a pattern on the board and having enough moves to convert it. Like Tetris, you can plan ahead, but it is this piece, this move that you are making right now that interests you. From the wreckage I assembled a simple highscore chase. Only six moves felt right, just outside of what the average human can comfortably calculate. To keep things interesting I would introduce a new complication after so many items dropped in. My folly in adding so many features to the game proved to be my fortune as I kept discovering more and more mechanics I'd completely forgotten were in the game. But before I could share my creation I needed some kind of tutorial, a guide. This was where my compact model of the board proved its worth.
With the state of the board taking up so little memory, I was able to run hundreds of branching simulations of what could happen when the player moved. I spread this calculation across many frames to avoid causing the engine to stutter, and this gave the player a chance to find their own solution before the guide was ready to animate. I uploaded this build to the indie games platform itch.io for the public to try and comment on. After the page was shared by an individual on reddit I was told that my guide was annoying! Being proud of my ruthlessly perfect A.I. it never occurred to me I'd destroyed all challenge in the game by having it interrupt when people were struggling. Over a few iterations I developed a help-system. First presenting some guides for free, then presenting a button for a limited number of guides. This was much better, but further into the game one would encounter unsolvable boards and people were asking for some kind of solution to a situation that was beyond their control. I didn't want the dreaded "shuffle" mechanic. In Candy Crush an unsolvable board will rearrange everything to give you a viable match. Later levels will shuffle every few moves, giving the rightful impression that the game is rigged. Alternatively match-3 games will goad you into buying boosters - converting one of the items on the board into something that destroys many items. This seemed an acceptable alternative. When no real help was available, you would spend it on a booster to survive. I'd accidentally added a lives system. Electron Dance would later muse on this when I released the game on Android.
Finally the end-game suffered due to my implementation of creeping death. I had to put a hard limit on how long people could play for, but it never felt quite right. It still doesn't. Even though I've given the player the option to suicide after they've ran out of lives for some bonus score. For now, I've treated the situation as an unfortunate consequence of Six Match's short term goals. Despite this, the game has been well received.
I hope the story of Six Match gives you ideas or solutions for your own work. Perhaps it will encourage you to share your own process so that others may learn from you as well.
Looking through my notes I noticed I missed a very important point about wild card matching. It's perhaps overlooked because it's a little tricky. In PuzzleScript we can simply define a group of three and the engine does the rest, hitting all tiles at once. But I'm using C# and I have do do things procedurally, one by one. What follows is for programmers looking for the same answer.
A naive search simply looks for three or more: A B A B B B -match found with B - Pretty easy to write an algorithm for. Just count the same ID appearing.
But if we introduce W our wildcard. We can get a match-5 with this: A A W B B
We can't simply pick a colour and say it's that kind of match anymore. Most match-3 games use this to lock rewards to a specific colour. Which is fine. But wild cards are fun, so how do we fix our algorithm?
By approaching it a similar way to PuzzleScript. We cycle through, always looking at just three items. If it's a legal match we mark them. Ignorant of the last step, we examine the left most coin (we're walking right) if it's marked we add it to the current group. If the middle coin is not marked, we tie off the group. The marking of coins can happen at any time, but all we care about is our left-most coin being marked, that's all we add each step. At the right edge of the board, we do a hacky clean up. Below is the C# for just the horizontal sweep, I've added comments for clarity.
public bool CheckMatch() { // Tiles are cells that can hold a coin Tile start, a, b, c; // Each ID is a bitwise flag, the wild card ID is simply a mask of all colour IDs Coin.ID aMasked, bMasked, cMasked; List<Tile> matchTiles = new List<Tile>(); // we mark coins ready to collect with this number // it's like having a boolean called "collect" on the coin // but we don't have to reset every coin, just increment the number // (pro-tip, you can remove the need for a closed-list in A* with this trick) sweepCount++; // match 3 sweep horizontal a = start = tiles[0]; while(a != null) { b = a.right; c = b.right; while(c != null) { // match 3? // the left most tile is added to the current group aMasked = (a.coin.id & Coin.matchMask); bMasked = (b.coin.id & Coin.matchMask); cMasked = (c.coin.id & Coin.matchMask);// matchMask filters out things like diamonds and stampers if( // remember Board Locking? // we don't want to look at locked Tiles a.locked == 0 && b.locked == 0 && c.locked == 0 && (aMasked & bMasked) != 0 && (bMasked & cMasked) != 0 && (cMasked & aMasked) != 0 ) { a.collect = b.collect = c.collect = sweepCount; matchTiles.Add(a); } else { if(a.collect == sweepCount) { matchTiles.Add(a); // terminate group? if(b.collect != sweepCount) { CreateMatch(matchTiles, Match.Dir.HORIZ); matchTiles = new List<Tile>(); } } } // right edge of board reached - clean up if(c.right == null) { if(a.collect == sweepCount && b.collect == sweepCount) { matchTiles.Add(b); if(c.collect == sweepCount) { matchTiles.Add(c); } } if(matchTiles.Count > 2) { CreateMatch(matchTiles, Match.Dir.HORIZ); matchTiles = new List<Tile>(); } } a = b; b = a.right; c = b.right; } start = start.down; a = start; } sweepCount++; // vertical next, which involves looking for junctions // System.Linq comes in handy // And there's a bunch of other stuff ;) // ... }
This one had me scratching my head for a while at the time. Perhaps you would have figured it out faster than I. My findings are here so others may not waste time as I did.
Read more about:
Featured BlogsYou May Also Like