DEV Community

Roman Guivan
Roman Guivan

Posted on

match 3 game in pixi.js 102: Gameplay

Once you've finished this long-read, your game will become playable! The hype is real!

This is a second article in a three-part tutorial. If you skipped the first part - catch up here

Selecting a sprite

Most basic interaction for a match-3 game would be selecting an animal and making it trade places with neighbour. Thats what we are gonna implement right now!

To make a sprite "interactive" in pixi, you set it's interactive attribute to true. This allows you to add a click event.

  sprite.interactive = true;
  sprite.on('pointerdown', (e) => {});
Enter fullscreen mode Exit fullscreen mode

Easy as that! If you want the cursor to change to pointer, add

  sprite.buttonMode = true; 
Enter fullscreen mode Exit fullscreen mode

as well. The requirements for the next task are simple:

  • if there is no selection, clicking on an animal makes it "selected", which means it changes it's scale, or tint (like in previous animation example)
  • if there's already a selection - two animals trade places

Now please stop reading and go solve it yourself, i'll wait!

In case you got stuck somewhere: here is my solution, as diff on github

Sprites are now stored in a two-dimensional array, and every second click - just makes two selected elements trade their x and y coordinates, both on-screen and in the array itself.

To make sure I have not forgot to swap the sprites in array too, i debugged a bit with a trace function executed each time i click:

  function printSpriteNames() {
      for (let y = 0; y < TILES_OY; y++) {
          let row = '';
          for (let x = 0; x < TILES_OX; x++) {
              row+=`${sprites[x][y].name} `;
Enter fullscreen mode Exit fullscreen mode

We can chose any two sprites so far, not just the neighbours - we'll fix this later on.

Detecting groups

The player wants to group animals in lines of 3 and more, horizontally or vertically. Once a line like that is formed - the group is destroyed, and new elements are added onto the screen.

Seems like the theme of this article is iterating arrays, everyone's first programming assignment ever. To make it more interesting, I suggest we write our pattern-recognition util using TDD.

Unit-tests can be cool when used wisely. In this example i'll use Jest. If you haven't worked with Jest or unit-tests before (or had bad confusing experiences in the past) - no worries, i'll cover all the basics right now.

To add jest, run npm install jest --save-dev
Once jest is installed - add a test script to your package.json

    "scripts": {
      "test": "jest",
Enter fullscreen mode Exit fullscreen mode

In our /scripts folder i'll create two new files, next to index.js. patterns.js and patterns.spec.js. First one being our "pattern-matcher" implementation (utility function that would find patterns on sprites array) and second - the unit-test for it!

Here's how the files look like As you can see, pattern-matcher implementation just has three empty methods for now, that are supposed to find groups of repeating elements horizontally and vertically (third method would aggregate results of first two). Whats more interesting is the .spec.js file now.

Inside the describe('') block we have our first test-case

  it('reads 3 and more in a horizontal line', () => {
Enter fullscreen mode Exit fullscreen mode

In our sprites array, each column is represented as nested array, so for a field 3x3, a horizontal group of 3 'cow' sprites in first row would look like

  const TEST_GROUP = 
     ['cow', 'cat, 'doge'],
     ['cow', 'rhino', 'frog'],
     ['cow', 'snake', 'frog']
Enter fullscreen mode Exit fullscreen mode

It's a bit confusing, but array values are flipped 90 degrees compared to screen representation. I did it in favour of storing coordinates as [x][y] and not other way around.

Running npm test would result in

Image description

TEST_GROUP_1 has the row of matching values, but there is no implementation to find it yet! Go to pattern.js and implement it yourself. The format i chose for "groups" looks like this:

     { name: "cow", points: [{0,0}, {1,0}, {2,0}]}
Enter fullscreen mode Exit fullscreen mode

It's completely up to you HOW you chose to solve this problem. You can use regular expressions or implement some fancy algorithm. I recommend you to take this unit test and implement such a patterns.js that would make it pass.

In case you're lost: my dumbest solution is available here

Once you're done - it's time to use our "well-tested" :p logic in the game.

Matching groups

Our index.js was in a sad sad shape for a 100-lines file. I've extracted some of the blocks into separate functions so the file is a bit easier to navigate. Now to the final task of this chapter:

On first render AND after each swap run pattern-matcher. If there are groups found:

  • delete each sprite from the group
  • replace these spots with new animals

Use app.stage.removeChild to remove sprites and our pattern matcher for pattern matching. Another pro-tip - limit the number of possible random animals to 10 or less, not to animals.length, otherwise finding 3 of a kind will become hard.

You can find my solution here. Congratulations! You've made it! It's a match-three game! Well, not quite, not yet. There are still things that have to be done, to make it a real game:

  • there has to be a condition on which the player LOSES the game
  • "destruction" and "creation" of our sprites have to be animated, at the moment it all looks too instantaneous
  • sound and visual effects have to be added to make it all feel really interactive.
    • ah and another round of pattern-matching has to be ran after new elements are inserted

Guess what? - thats exactly what we're gonna do in part three! See you there, real soon! And for now: enjoy

If you haven't whishlisted PIANO ROCKER on steam yet - do it right now! (or i'll call the police)

Top comments (0)