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) => {});
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:
- 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} `;
}
console.log(row);
}
console.log('------');
}
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",
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.
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)