originally i've posted this here but who cares?
This is a second article in a three-part tutorial. If you skipped the first part - [catch up here](https://dev.to/romanguivan17680f142e28/match-3-game-in-pixi-js-36hm)
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) => {});
Easy as that! If you want the cursor to change to pointer, add
sprite.buttonMode = true;
as well. The requirements for the next task are simple:
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} `;
}
console.log(row);
}
console.log('------');
}
We can chose any two sprites so far, not just the neighbours - we'll fix this later on.
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",
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', () => {
expect(matcher.matchGroupsHorizontal(TEST_GROUP_0).length)
.toBe(0);
expect(matcher.matchGroupsHorizontal(TEST_GROUP_1).length)
.toBe(0);
})
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']
]
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
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}]}
]
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.
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:
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:
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)](https://store.steampowered.com/app/1771240/PianoRocker/?l=german&beta=1)_