Step 2 - Flipping Cards: Click Detection
Let's make cards flip when clicked and manage the flipping logic!
Understanding Flip Logic
When clicking cards:
- Detect which card was clicked
- Flip it face-up (if allowed)
- Track how many cards are currently flipped
- Only allow 2 cards flipped at once
- Later we'll check if they match
Step 1: Add Click Tracking Variables
Add variables to track flipped cards:
let cols = 4;
let rows = 4;
let cardWidth;
let cardHeight;
let cards = [];
let flippedCards = []; // Track currently flipped cards
Breaking it down
flippedCards = []- Array to hold references to cards currently face-up- Will contain 0, 1, or 2 cards
- When 2 cards are flipped, we'll check if they match
Step 2: Detect Card Clicks
Add mousePressed() function:
function mousePressed() {
// Loop through all cards
for (let card of cards) {
// Check if click is inside this card
if (mouseX > card.x && mouseX < card.x + cardWidth &&
mouseY > card.y && mouseY < card.y + cardHeight) {
console.log("Clicked card with value:", card.value);
}
}
}
Breaking it down
mousePressed()- Called once when mouse is clicked- Loop through all cards to find which was clicked
- Boundary check:
mouseX > card.x- Click is to the right of card's left edgemouseX < card.x + cardWidth- Click is to the left of card's right edgemouseY > card.y- Click is below card's top edgemouseY < card.y + cardHeight- Click is above card's bottom edge- All four must be true = click is inside card
- Console logs which card was clicked
Test it: Click cards - console should show their values!
Step 3: Flip Cards on Click
Now actually flip the card:
function mousePressed() {
for (let card of cards) {
if (mouseX > card.x && mouseX < card.x + cardWidth &&
mouseY > card.y && mouseY < card.y + cardHeight) {
// Don't flip if already matched
if (card.matched) {
continue;
}
// Don't flip if already flipped
if (card.flipped) {
continue;
}
// Flip the card
card.flipped = true;
flippedCards.push(card);
console.log("Flipped cards:", flippedCards.length);
}
}
}
Breaking it down
if (card.matched) continue;- Skip matched cards (can't flip them again)if (card.flipped) continue;- Skip already flipped cardscard.flipped = true- Set card to face-upflippedCards.push(card)- Add to array of flipped cards- Console shows how many cards are currently flipped
Test it: Click cards - they should flip face-up!
Step 4: Limit to Two Cards
Only allow two cards to be flipped at once:
function mousePressed() {
// Don't flip more cards if 2 are already flipped
if (flippedCards.length >= 2) {
return;
}
for (let card of cards) {
if (mouseX > card.x && mouseX < card.x + cardWidth &&
mouseY > card.y && mouseY < card.y + cardHeight) {
if (card.matched) {
continue;
}
if (card.flipped) {
continue;
}
card.flipped = true;
flippedCards.push(card);
console.log("Flipped cards:", flippedCards.length);
}
}
}
Breaking it down
if (flippedCards.length >= 2) return;- Check at start of function- If 2 cards are already flipped, exit function immediately
- Player can't flip more cards until we handle the current pair
- This prevents flipping 3+ cards at once
Test it: You can only flip 2 cards, then clicks don't work!
Step 5: Add Temporary Match Checking
Let's check if two flipped cards match:
function mousePressed() {
if (flippedCards.length >= 2) {
return;
}
for (let card of cards) {
if (mouseX > card.x && mouseX < card.x + cardWidth &&
mouseY > card.y && mouseY < card.y + cardHeight) {
if (card.matched) {
continue;
}
if (card.flipped) {
continue;
}
card.flipped = true;
flippedCards.push(card);
// Check for match when 2 cards are flipped
if (flippedCards.length === 2) {
checkMatch();
}
}
}
}
function checkMatch() {
let card1 = flippedCards[0];
let card2 = flippedCards[1];
if (card1.value === card2.value) {
console.log("Match!");
} else {
console.log("No match!");
}
}
Breaking it down
if (flippedCards.length === 2)- When second card is flippedcheckMatch()- New function to compare cardslet card1 = flippedCards[0]- First flipped cardlet card2 = flippedCards[1]- Second flipped cardif (card1.value === card2.value)- Compare their values- Console shows if they match
Test it: Flip 2 cards - console says if they match!
Step 6: Handle Matches
When cards match, mark them as matched:
function checkMatch() {
let card1 = flippedCards[0];
let card2 = flippedCards[1];
if (card1.value === card2.value) {
// Cards match!
card1.matched = true;
card2.matched = true;
console.log("Match! Cards marked as matched.");
// Clear flipped cards array
flippedCards = [];
} else {
console.log("No match!");
}
}
Breaking it down
- When match is found:
card1.matched = true- Mark first card as matchedcard2.matched = true- Mark second card as matched- Matched cards stay face-up (remember our draw code checks
card.matched) flippedCards = []- Clear the array so we can flip more cards
- Player can now flip next pair
Test it: Find matching pairs - they stay face-up!
Step 7: Flip Non-Matching Cards Back
Cards that don't match should flip back after a delay:
let cols = 4;
let rows = 4;
let cardWidth;
let cardHeight;
let cards = [];
let flippedCards = [];
let canClick = true; // Prevent clicking during delay
Update checkMatch():
function checkMatch() {
let card1 = flippedCards[0];
let card2 = flippedCards[1];
if (card1.value === card2.value) {
card1.matched = true;
card2.matched = true;
flippedCards = [];
} else {
// No match - flip back after delay
canClick = false; // Prevent clicking during delay
setTimeout(function() {
card1.flipped = false;
card2.flipped = false;
flippedCards = [];
canClick = true; // Allow clicking again
}, 1000); // 1 second delay
}
}
Update mousePressed() to check canClick:
function mousePressed() {
// Don't allow clicks during delay
if (!canClick) {
return;
}
if (flippedCards.length >= 2) {
return;
}
// ... rest of code stays the same ...
}
Breaking it down
canClick = true- Variable to control when clicks are allowed- When cards don't match:
canClick = false- Disable clickingsetTimeout(function() {...}, 1000)- Wait 1 second (1000 milliseconds)- Inside setTimeout:
card1.flipped = false- Flip first card backcard2.flipped = false- Flip second card backflippedCards = []- Clear arraycanClick = true- Re-enable clicking
if (!canClick) return;- Exit mousePressed if clicking disabled- Player sees cards for 1 second before they flip back
Test it: Click non-matching cards - they flip back after 1 second!
Step 8: Add Visual Feedback for Hover
Show which card you're about to click:
function draw() {
background(50);
for (let card of cards) {
// Check if mouse is over this card
let hovering = false;
if (canClick && !card.flipped && !card.matched) {
if (mouseX > card.x && mouseX < card.x + cardWidth &&
mouseY > card.y && mouseY < card.y + cardHeight) {
hovering = true;
}
}
if (card.flipped || card.matched) {
// Face-up card
if (hovering) {
fill(220, 120, 120); // Brighter if hovering
} else {
fill(200, 100, 100);
}
stroke(255);
strokeWeight(3);
rect(card.x, card.y, cardWidth, cardHeight, 10);
fill(255);
noStroke();
textSize(40);
textAlign(CENTER, CENTER);
text(card.value, card.x + cardWidth / 2, card.y + cardHeight / 2);
} else {
// Face-down card
if (hovering) {
fill(120, 170, 220); // Brighter blue if hovering
} else {
fill(100, 150, 200);
}
stroke(255);
strokeWeight(3);
rect(card.x, card.y, cardWidth, cardHeight, 10);
stroke(80, 120, 160);
strokeWeight(2);
line(card.x + 10, card.y + 10,
card.x + cardWidth - 10, card.y + cardHeight - 10);
line(card.x + cardWidth - 10, card.y + 10,
card.x + 10, card.y + cardHeight - 10);
}
}
}
Breaking it down
let hovering = false- Track if mouse is over this card- Check for hover:
- Only if clicking is allowed (
canClick) - Only if card is not flipped or matched
- Check if mouse is inside card boundaries
- Only if clicking is allowed (
- If hovering, use brighter color
- Face-down:
fill(120, 170, 220)instead offill(100, 150, 200) - Face-up:
fill(220, 120, 120)instead offill(200, 100, 100)
- Face-down:
- Gives clear feedback about which card will be clicked
Test it: Move mouse over cards - they brighten!
Step 9: Change Cursor on Hover
Make cursor change to pointer:
function draw() {
background(50);
let anyHovering = false;
for (let card of cards) {
let hovering = false;
if (canClick && !card.flipped && !card.matched) {
if (mouseX > card.x && mouseX < card.x + cardWidth &&
mouseY > card.y && mouseY < card.y + cardHeight) {
hovering = true;
anyHovering = true;
}
}
// ... rest of card drawing code ...
}
// Set cursor based on whether hovering any card
if (anyHovering) {
cursor(HAND);
} else {
cursor(ARROW);
}
}
Breaking it down
let anyHovering = false- Track if hovering ANY card- Set to
truewhen hovering any clickable card cursor(HAND)- Show pointing hand cursorcursor(ARROW)- Normal arrow cursor- Clear feedback that cards are clickable
Complete Step 2 Code
Here's everything together:
let cols = 4;
let rows = 4;
let cardWidth;
let cardHeight;
let cards = [];
let flippedCards = [];
let canClick = true;
function setup() {
createCanvas(600, 600);
cardWidth = width / cols - 20;
cardHeight = height / rows - 20;
let values = [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8];
values = shuffle(values);
let index = 0;
for (let i = 0; i < cols; i++) {
for (let j = 0; j < rows; j++) {
let card = {
value: values[index],
x: i * (cardWidth + 20) + 10,
y: j * (cardHeight + 20) + 10,
flipped: false,
matched: false
};
cards.push(card);
index++;
}
}
}
function draw() {
background(50);
let anyHovering = false;
for (let card of cards) {
let hovering = false;
if (canClick && !card.flipped && !card.matched) {
if (mouseX > card.x && mouseX < card.x + cardWidth &&
mouseY > card.y && mouseY < card.y + cardHeight) {
hovering = true;
anyHovering = true;
}
}
if (card.flipped || card.matched) {
if (hovering) {
fill(220, 120, 120);
} else {
fill(200, 100, 100);
}
stroke(255);
strokeWeight(3);
rect(card.x, card.y, cardWidth, cardHeight, 10);
fill(255);
noStroke();
textSize(40);
textAlign(CENTER, CENTER);
text(card.value, card.x + cardWidth / 2, card.y + cardHeight / 2);
} else {
if (hovering) {
fill(120, 170, 220);
} else {
fill(100, 150, 200);
}
stroke(255);
strokeWeight(3);
rect(card.x, card.y, cardWidth, cardHeight, 10);
stroke(80, 120, 160);
strokeWeight(2);
line(card.x + 10, card.y + 10,
card.x + cardWidth - 10, card.y + cardHeight - 10);
line(card.x + cardWidth - 10, card.y + 10,
card.x + 10, card.y + cardHeight - 10);
}
}
if (anyHovering) {
cursor(HAND);
} else {
cursor(ARROW);
}
}
function mousePressed() {
if (!canClick) {
return;
}
if (flippedCards.length >= 2) {
return;
}
for (let card of cards) {
if (mouseX > card.x && mouseX < card.x + cardWidth &&
mouseY > card.y && mouseY < card.y + cardHeight) {
if (card.matched) {
continue;
}
if (card.flipped) {
continue;
}
card.flipped = true;
flippedCards.push(card);
if (flippedCards.length === 2) {
checkMatch();
}
}
}
}
function checkMatch() {
let card1 = flippedCards[0];
let card2 = flippedCards[1];
if (card1.value === card2.value) {
card1.matched = true;
card2.matched = true;
flippedCards = [];
} else {
canClick = false;
setTimeout(function() {
card1.flipped = false;
card2.flipped = false;
flippedCards = [];
canClick = true;
}, 1000);
}
}
What You Learned
In this step, you:
- ✅ Detected clicks on specific cards
- ✅ Flipped cards face-up when clicked
- ✅ Limited flipping to 2 cards at once
- ✅ Checked if two cards match
- ✅ Kept matched cards face-up
- ✅ Flipped non-matching cards back with delay
- ✅ Prevented clicking during flip-back delay
- ✅ Added hover effects for better UX
- ✅ Changed cursor to show clickable cards
- ✅ Learned about setTimeout for delays
What's Next?
The game is playable! But we're missing some key features: tracking moves, detecting when the game is won, and ability to restart. Next, we'll add these finishing touches.
Coming up: Move counter, win detection, and restart!