মূল বিষয়বস্তু
কম্পিউটার প্রোগ্রামিং
কোর্স: কম্পিউটার প্রোগ্রামিং > অধ্যায় 4
পাঠ 5: স্মৃতিশক্তি গেমটি তৈরিMemory game: Flipping tiles
ঠিক আছে, এখন টাইলসগুলোর ছক ঊর্ধ্বমুখী বা নিম্নমুখী করে দেখানো যায়। কিন্তু গেমটি খেলা যাচ্ছে না। লক্ষ্য করা যাক, গেমটি মূলত এভাবে কাজ করবে:
- গেমের শুরু থেকে সকল টাইলসকে নিম্নমুখী করে রাখা হবে।
- খেলোয়াড় ক্লিক করে দুইটি টাইলস উল্টাবে।
- যদি দুইটি টাইলসে কার্টুনের ছবি মিলে যায় তাহলে তারা এভাবেই ঊর্ধ্বমুখে উল্টানো অবস্থায় থাকবে নইলে কিছুক্ষণ পরেই পূর্বের নিম্নমুখে উল্টানো অবস্থায় ফেরত যাবে।
ক্লিক করে টাইলসকে উল্টানো
Right now, we have a program that draws a grid of tiles and then never draws anything else. Going forward, we want our program to draw different things over time - it'll start off drawing face down tiles, but then it'll display clicked tiles, and if all goes well for the player (fingers crossed!), it'll display a win screen.
So let's move all our drawing code into the ProcessingJS
draw
function. The computer will keep calling draw()
while the program is running, so the tiles will keep getting drawn according to whether they're face up or face down:draw = function() {
background(255, 255, 255);
for (var i = 0; i < tiles.length; i++) {
tiles[i].draw();
}
};
Let's get some of those tiles face up now! To flip a tile, the player must click on it. To respond to clicking in ProcessingJS programs, we can define a
mouseClicked
function, and the computer will execute that code every time the mouse is clicked.mouseClicked = function() {
// process click somehow
};
When our program sees that the player has clicked somewhere, we want to check if they've clicked on a tile, using
mouseX
and mouseY
. Let's start by adding an isUnderMouse
method to Tile
that returns true
if a given x and y is within a tile's area.With the way we've drawn the tiles, the x and y of the tile correspond to the upper left corner of the tile, so we should return true only if the given x is between
this.x
and this.x + this.width
, and if the given y is between this.y
and this.y + this.width
:Tile.prototype.isUnderMouse = function(x, y) {
return x >= this.x && x <= this.x + this.width &&
y >= this.y && y <= this.y + this.width;
};
Now that we have that method, we can use a
for
loop in mouseClicked
to check if each tile is under the mouseX
and mouseY
. If so, we set the tile's isFaceUp
property to true
:mouseClicked = function() {
for (var i = 0; i < tiles.length; i++) {
if (tiles[i].isUnderMouse(mouseX, mouseY)) {
tiles[i].isFaceUp = true;
}
}
};
এটা দেখতে এমন। একাধিক টাইলসে ক্লিক করে দেখি কি হয়:
Restricting tile flips
কিছু কি লক্ষ্য করা গেছে? আমরা গেমের একটি অংশে কাজ করা শেষ করেছি, যে খেলোয়াড় ক্লিক করার মাধ্যমে টাইলস উল্টাতে পারবে, কিন্তু গুরুত্বপূর্ণ একটি বিষয় আমরা এখানে উপেক্ষা করে গেছি: সেটা হল খেলোয়াড় যেন দুইটির বেশি টাইলস উল্টাতে না পারে।
We will need to keep track of the number of flipped tiles somehow. One simple way would be a global
numFlipped
variable that we increment each time the player turns a card face up. We only flip a tile over if numFlipped
is less than 2 and the tile isn't already face up:var numFlipped = 0;
mouseClicked = function() {
for (var i = 0; i < tiles.length; i++) {
var tile = tiles[i];
if (tiles.isUnderMouse(mouseX, mouseY)) {
if (numFlipped < 2 && !tile.isFaceUp) {
tile.isFaceUp = true;
numFlipped++;
}
}
}
};
টাইলস উলটানোয় বিলম্ব করা
এখন, আমাদের দুইটি টাইলস উল্টানোর লজিক সম্পূর্ণ হয়েছে। এরপর কি করবো? গেমের কার্যক্রমগুলো আরেকবার দেখে নেই:
যদি দুইটি টাইলসের ছবি এক হয়, তাহলে তারা ঊর্ধ্বমুখী হয়ে থাকবে। নয়তো, টাইলসগুলো কিছুক্ষণ পর আবার তাদের পূর্বের অবস্থায় চলে যাবে।
টাইলসগুলো কিছুক্ষণ পর পূর্বের অবস্থায় উল্টে যাওয়ার কাজ, অর্থাৎ দ্বিতীয় অংশটির কাজ আমরা আগে করবো, কারণ টাইলসগুলো যদি নিজে থেকে উল্টে না যায় তাহলে প্রথম অংশটি ঠিক মত কাজ করছে কিনা না তা সহজে আমরা পরীক্ষা করে দেখতে পারব না।
We know how to flip tiles back over, by setting
isFaceUp
to false
, but how do we do that after some period of time? Every language and environment has a different approach to delaying execution of code, and we need to figure out how to do it in ProcessingJS. We need some way of keeping track of time - whether the delay period has passed - and a way of calling code after the period of time has passed. Here's what I'd suggest:- We create a global variable called
delayStartFC
, initially null. - In the
mouseClicked
function, right after we've flipped over a second tile, we store the current value offrameCount
indelayStartFC
. That variable tells us how many frames have passed since the program started running, and is one way of telling time in our programs. - In the
draw
function, we check if the new value offrameCount
is significantly higher than the old one, and if so, we flip all of the tiles over and setnumFlipped
to 0. We also resetdelayStartFC
tonull
.
It's actually a nice solution that doesn't require too much code to implement. As a performance optimization, we can use the
loop
and noLoop
functions to make sure that the draw
code is only being called when there's a delay happening. Here it all is:var numFlipped = 0;
var delayStartFC = null;
mouseClicked = function() {
for (var i = 0; i < tiles.length; i++) {
var tile = tiles[i];
if (tile.isUnderMouse(mouseX, mouseY)) {
if (numFlipped < 2 && !tile.isFaceUp) {
tile.isFaceUp = true;
numFlipped++;
if (numFlipped === 2) {
delayStartFC = frameCount;
}
loop();
}
}
}
};
draw = function() {
if (delayStartFC &&
(frameCount - delayStartFC) > 30) {
for (var i = 0; i < tiles.length; i++) {
tiles[i].isFaceUp = false;
}
numFlipped = 0;
delayStartFC = null;
noLoop();
}
background(255, 255, 255);
for (var i = 0; i < tiles.length; i++) {
tiles[i].draw();
}
};
Flip some tiles below - it's pretty cool how the tiles automatically flip back over, aye? To help you understand it more, try changing how long the delay waits and how many tiles have to be flipped over before the delay starts.
Checking matches
If you managed to match any tiles above, you were probably sad when they flipped back over, because, hey, you made a match! So now it's time to implement this rule of the game:
If two tiles match, then they should stay face up.
That means that we should check for matching tiles whenever there are 2 flipped over, and before we set up the delay. In pseudo-code, that'd be:
if there are two tiles flipped over:
if first tile has same face as second tile:
keep the tiles face up
We already have a check for whether there are two tiles flipped over (
numFlipped === 2
), so how do we check if the tiles have the same face? First, we need some way of accessing the two flipped over tiles. How do we find them?We could iterate through our array each time, find all the tiles with
isFaceUp
set to true, and then store those into an array.Here's a shortcut: let's just always store our flipped tiles in an array, for easy access. That way, we don't have to iterate through our whole
tiles
array every time the player flips a tile.As a first step, we can replace
numFlipped
with an array, and then use flippedTiles.length
everywhere we previously used numFlipped
. Our mouseClick
function looks like this:var flippedTiles = [];
var delayStartFC = null;
mouseClicked = function() {
for (var i = 0; i < tiles.length; i++) {
var tile = tiles[i];
if (tile.isUnderMouse(mouseX, mouseY)) {
if (flippedTiles.length < 2 && !tile.isFaceUp) {
tile.isFaceUp = true;
flippedTiles.push(tile);
if (flippedTiles.length === 2) {
delayStartFC = frameCount;
loop();
}
}
}
}
};
এখন, দুইটি টাইলসের ছবি একই কিনা তা
flippedTiles
অ্যারে থেকে বের করতে হবে। আসলে, face
(ছবি সম্বলিত টাইলসের মুখ) এর বৈশিষ্ট্যটি কি? এটা হল একটি অবজেক্ট - আসলে, মিলে যাওয়া টাইলসের face অবজেক্ট একই হবে, কারণ, চলকটি কম্পিউটারের মেমোরিতে একই অবস্থানকে নির্দেশ করছে। কারণ আমরা প্রতিটি ছবিতে একবার করে অবজেক্ট তৈরি করেছি (যেমন getImage("avatars/old-spice-man")
এবং তারপর face এর অ্যারেতে একই অবজেক্ট দুইবার দিয়েছি:var face = possibleFaces[randomInd];
selected.push(face);
selected.push(face);
অন্ততপক্ষে জাভাস্ক্রিপ্টে, সমান অপারেটর দুইটি চলকে একই অবজেক্ট নিলে সত্য রিটার্ন করবে এবং উভয় চলকই মেমোরিতে একই অবজেক্টে নির্দেশ করবে। তাহলে যাচাই করা সহজ হবে - কারণ প্রত্যেকটি টাইলসের
face
বৈশিষ্ট্যে শুধু সমান অপারেটর ব্যবহার করতে হবে:if (flippedTiles[0].face === flippedTiles[1].face) {
...
}
Now that we know the tiles match, we need to keep them up. Currently, they'd all get turned over after a delay. We could just not set up the animation in this case, but remember, there will be an animation in later turns - so we can't rely on that.
Instead, we need a way of knowing "hey, when we turn all of them back over, we shouldn't turn these particular ones over." Sounds like a good use for a boolean property! Let's add an
isMatch
property to the Tile
constructor, and then set isMatch
to true
only inside that if
condition:if (flippedTiles[0].face === flippedTiles[1].face) {
flippedTiles[0].isMatch = true;
flippedTiles[1].isMatch = true;
}
Now we can use that property to decide whether to turn the tiles over after the delay.
for (var i = 0; i < tiles.length; i++) {
var tile = tiles[i];
if (!tile.isMatch) {
tile.isFaceUp = false;
}
}
Play with it below! When you find two matching tiles below, they should stay up after the delay (and after future turns):
আলোচনায় অংশ নিতে চাও?
কোন আলাপচারিতা নেই।