Code edit (1 edits merged)
Please save this source code
User prompt
Add DEV button to the game. This button must be at center bottom and always visible. This button take you to next level immediately
User prompt
Next button always appear at bottom
User prompt
Add next button to the game. This button take you to next level
User prompt
After matching cards, matching cards must disappear
User prompt
Please fix the bug: 'TypeError: Cannot use 'in' operator to search for 'scaleX' in null' in or related to this line: 'tween(firstCard, {' Line Number: 356
User prompt
Please fix the bug: 'TypeError: Cannot use 'in' operator to search for 'scaleX' in null' in or related to this line: 'tween(firstCard, {' Line Number: 355
User prompt
Align level indicator to top middle
User prompt
There must be level system for the game. If you match all cards you will go next level. Maximum level number must be 25.
Code edit (1 edits merged)
Please save this source code
User prompt
Card Match Mania
Initial prompt
I want card match game
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Card class: represents a single card in the memory game
var Card = Container.expand(function () {
var self = Container.call(this);
// Card properties
self.symbol = null; // Symbol or id for matching
self.isFaceUp = false;
self.isMatched = false;
self.index = -1; // Position in the grid
// Card dimensions (will be set in game code)
self.cardWidth = 0;
self.cardHeight = 0;
// Face-down asset (back of card)
var back = self.attachAsset('cardBack', {
anchorX: 0.5,
anchorY: 0.5
});
// Face-up asset (front of card, shows symbol)
var front = self.attachAsset('cardFront', {
anchorX: 0.5,
anchorY: 0.5
});
// Symbol text (shows symbol when face up)
var symbolText = new Text2('', {
size: 90,
fill: 0x222222
});
symbolText.anchor.set(0.5, 0.5);
front.addChild(symbolText);
// Set card size
self.setSize = function (w, h) {
self.cardWidth = w;
self.cardHeight = h;
back.width = w;
back.height = h;
front.width = w;
front.height = h;
};
// Set the symbol for this card
self.setSymbol = function (symbol) {
self.symbol = symbol;
symbolText.setText(symbol);
};
// Flip the card to face up (with animation)
self.flipUp = function (_onFinish) {
if (self.isFaceUp || self.isMatched) {
return;
}
self.isFaceUp = true;
// Animate flip: scaleX 1 -> 0, swap, 0 -> 1
tween(self, {
scaleX: 0
}, {
duration: 120,
easing: tween.cubicIn,
onFinish: function onFinish() {
back.visible = false;
front.visible = true;
tween(self, {
scaleX: 1
}, {
duration: 120,
easing: tween.cubicOut,
onFinish: function onFinish() {
if (_onFinish) {
_onFinish();
}
}
});
}
});
};
// Flip the card to face down (with animation)
self.flipDown = function (_onFinish2) {
if (!self.isFaceUp || self.isMatched) {
return;
}
self.isFaceUp = false;
// Animate flip: scaleX 1 -> 0, swap, 0 -> 1
tween(self, {
scaleX: 0
}, {
duration: 120,
easing: tween.cubicIn,
onFinish: function onFinish() {
front.visible = false;
back.visible = true;
tween(self, {
scaleX: 1
}, {
duration: 120,
easing: tween.cubicOut,
onFinish: function onFinish() {
if (_onFinish2) {
_onFinish2();
}
}
});
}
});
};
// Instantly show face up (no animation)
self.showFaceUp = function () {
self.isFaceUp = true;
self.scaleX = 1;
back.visible = false;
front.visible = true;
};
// Instantly show face down (no animation)
self.showFaceDown = function () {
self.isFaceUp = false;
self.scaleX = 1;
front.visible = false;
back.visible = true;
};
// Mark as matched (disable interaction, highlight)
self.setMatched = function () {
self.isMatched = true;
// Subtle highlight
tween(self, {
tint: 0xA0FFA0
}, {
duration: 300,
easing: tween.linear
});
};
// Handle tap/click
self.down = function (x, y, obj) {
if (self.isFaceUp || self.isMatched || game.lockInput) {
return;
}
game.onCardTapped(self);
};
// Initialize: show face down
self.showFaceDown();
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2e3a4f // Deep blue background
});
/****
* Game Code
****/
// --- Card symbols (use simple emojis for MVP) ---
// Tween plugin for card flip and reveal animations
var cardSymbols = ["๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐", "๐ฅ", "๐ฅ", "๐", "๐", "๐", "๐", "๐ฅฅ", "๐
"];
// --- Game settings ---
var maxLevel = 25;
var currentLevel = 1;
// Level configuration: grid size per level (increase difficulty)
function getLevelConfig(level) {
// Level 1-5: 4x4, 6-10: 5x4, 11-15: 6x5, 16-20: 6x6, 21-25: 7x6
if (level <= 5) {
return {
cols: 4,
rows: 4
};
}
if (level <= 10) {
return {
cols: 5,
rows: 4
};
}
if (level <= 15) {
return {
cols: 6,
rows: 5
};
}
if (level <= 20) {
return {
cols: 6,
rows: 6
};
}
return {
cols: 7,
rows: 6
};
}
var gridCols = getLevelConfig(currentLevel).cols;
var gridRows = getLevelConfig(currentLevel).rows;
var totalPairs = gridCols * gridRows / 2;
var cardSpacing = 36; // px between cards
// --- Card assets (simple colored rectangles) ---
// --- Game state ---
var cards = []; // All card objects
var firstCard = null;
var secondCard = null;
var lockInput = false; // Prevent input during animations
var moves = 0;
var matchesFound = 0;
var timer = null;
var elapsedTime = 0; // in seconds
var timerText = null;
var movesText = null;
// --- GUI: Moves, Timer, Level ---
var levelText = new Text2('Level: 1', {
size: 80,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelText);
movesText = new Text2('Moves: 0', {
size: 80,
fill: 0xFFFFFF
});
movesText.anchor.set(0.5, 0);
LK.gui.top.addChild(movesText);
timerText = new Text2('Time: 0s', {
size: 80,
fill: 0xFFFFFF
});
timerText.anchor.set(0.5, 0);
LK.gui.top.addChild(timerText);
// Position GUI elements (moves left, timer right, both at top, avoid top-left 100x100)
// Align level indicator to top middle using LK.gui.top
levelText.x = LK.gui.width / 2;
levelText.y = 20;
movesText.x = 400;
movesText.y = 20;
timerText.x = LK.gui.width - 400;
timerText.y = 20;
// --- Helper: Shuffle array (Fisher-Yates) ---
function shuffleArray(arr) {
for (var i = arr.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// --- Layout cards ---
function layoutCards() {
// Remove old cards if any
for (var i = 0; i < cards.length; i++) {
cards[i].destroy();
}
cards = [];
// Prepare symbols (duplicate and shuffle)
var symbols = [];
for (var i = 0; i < totalPairs; i++) {
symbols.push(cardSymbols[i]);
symbols.push(cardSymbols[i]);
}
shuffleArray(symbols);
// Card size: fit grid to 2048x2732 with spacing
var availableWidth = 2048 - cardSpacing * (gridCols + 1);
var availableHeight = 1800 - cardSpacing * (gridRows + 1); // leave space for GUI
var cardWidth = Math.floor(availableWidth / gridCols);
var cardHeight = Math.floor(availableHeight / gridRows);
// Center grid
var gridPixelWidth = cardWidth * gridCols + cardSpacing * (gridCols - 1);
var gridPixelHeight = cardHeight * gridRows + cardSpacing * (gridRows - 1);
var startX = Math.floor((2048 - gridPixelWidth) / 2) + cardWidth / 2;
var startY = 350 + cardHeight / 2; // leave space for GUI
// Create cards
for (var row = 0; row < gridRows; row++) {
for (var col = 0; col < gridCols; col++) {
var idx = row * gridCols + col;
var card = new Card();
card.setSize(cardWidth, cardHeight);
card.setSymbol(symbols[idx]);
card.index = idx;
card.x = startX + col * (cardWidth + cardSpacing);
card.y = startY + row * (cardHeight + cardSpacing);
card.showFaceDown();
game.addChild(card);
cards.push(card);
}
}
}
// --- Reset game state ---
function resetGame() {
firstCard = null;
secondCard = null;
lockInput = false;
moves = 0;
matchesFound = 0;
elapsedTime = 0;
if (typeof levelText !== "undefined") {
levelText.setText('Level: ' + currentLevel);
}
// Update grid size for this level
var config = getLevelConfig(currentLevel);
gridCols = config.cols;
gridRows = config.rows;
totalPairs = gridCols * gridRows / 2;
movesText.setText('Moves: 0');
timerText.setText('Time: 0s');
layoutCards();
if (timer) {
LK.clearInterval(timer);
}
timer = LK.setInterval(function () {
elapsedTime++;
timerText.setText('Time: ' + elapsedTime + 's');
}, 1000);
}
// --- Card tap handler ---
game.onCardTapped = function (card) {
if (lockInput || card.isFaceUp || card.isMatched) {
return;
}
if (!firstCard) {
firstCard = card;
card.flipUp();
} else if (!secondCard && card !== firstCard) {
secondCard = card;
lockInput = true;
card.flipUp(function () {
// Check for match after both are face up
LK.setTimeout(function () {
checkMatch();
}, 350);
});
}
};
// --- Check for match ---
function checkMatch() {
moves++;
movesText.setText('Moves: ' + moves);
if (firstCard.symbol === secondCard.symbol) {
// Match!
firstCard.setMatched();
secondCard.setMatched();
matchesFound++;
// Subtle scale animation for matched cards, then disappear
if (firstCard && secondCard) {
var matched1 = firstCard;
var matched2 = secondCard;
if (matched1) {
tween(matched1, {
scaleX: 1.15,
scaleY: 1.15
}, {
duration: 120,
easing: tween.cubicOut,
onFinish: function onFinish() {
if (matched1) {
tween(matched1, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 180,
onFinish: function onFinish() {
if (matched1) {
matched1.destroy();
}
}
});
}
}
});
}
if (matched2) {
tween(matched2, {
scaleX: 1.15,
scaleY: 1.15
}, {
duration: 120,
easing: tween.cubicOut,
onFinish: function onFinish() {
if (matched2) {
tween(matched2, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 180,
onFinish: function onFinish() {
if (matched2) {
matched2.destroy();
}
}
});
}
}
});
}
}
// Check for win
if (matchesFound === totalPairs) {
LK.clearInterval(timer);
LK.setScore(moves); // Use moves as score (lower is better)
if (currentLevel < maxLevel) {
currentLevel++;
// Short delay before next level
LK.setTimeout(function () {
resetGame();
}, 1200);
} else {
LK.showYouWin();
}
}
// Reset selection
firstCard = null;
secondCard = null;
lockInput = false;
} else {
// Not a match: flip both back after short delay
LK.setTimeout(function () {
firstCard.flipDown();
secondCard.flipDown(function () {
firstCard = null;
secondCard = null;
lockInput = false;
});
}, 600);
}
}
// --- Game update (not used for logic, but required) ---
game.update = function () {
// No per-frame logic needed for MVP
};
// --- DEV Button: Center bottom, always visible, go to next level ---
var devBtn = new Text2('DEV', {
size: 90,
fill: 0xFF00FF,
font: "Impact"
});
devBtn.anchor.set(0.5, 1);
devBtn.x = LK.gui.width / 2;
devBtn.y = 100;
LK.gui.bottom.addChild(devBtn);
// DEV button handler: go to next level immediately (max 25)
devBtn.down = function (x, y, obj) {
if (currentLevel < maxLevel) {
currentLevel++;
resetGame();
} else {
LK.showYouWin();
}
};
// --- Start game ---
resetGame();
// --- Game over handler (reset on game over) ---
game.onGameOver = function () {
if (timer) {
LK.clearInterval(timer);
}
currentLevel = 1;
resetGame();
};
// --- Win handler (reset on win) ---
game.onYouWin = function () {
if (timer) {
LK.clearInterval(timer);
}
currentLevel = 1;
// Game will reset automatically by LK
}; ===================================================================
--- original.js
+++ change.js
@@ -49,9 +49,11 @@
symbolText.setText(symbol);
};
// Flip the card to face up (with animation)
self.flipUp = function (_onFinish) {
- if (self.isFaceUp || self.isMatched) return;
+ if (self.isFaceUp || self.isMatched) {
+ return;
+ }
self.isFaceUp = true;
// Animate flip: scaleX 1 -> 0, swap, 0 -> 1
tween(self, {
scaleX: 0
@@ -66,17 +68,21 @@
}, {
duration: 120,
easing: tween.cubicOut,
onFinish: function onFinish() {
- if (_onFinish) _onFinish();
+ if (_onFinish) {
+ _onFinish();
+ }
}
});
}
});
};
// Flip the card to face down (with animation)
self.flipDown = function (_onFinish2) {
- if (!self.isFaceUp || self.isMatched) return;
+ if (!self.isFaceUp || self.isMatched) {
+ return;
+ }
self.isFaceUp = false;
// Animate flip: scaleX 1 -> 0, swap, 0 -> 1
tween(self, {
scaleX: 0
@@ -91,9 +97,11 @@
}, {
duration: 120,
easing: tween.cubicOut,
onFinish: function onFinish() {
- if (_onFinish2) _onFinish2();
+ if (_onFinish2) {
+ _onFinish2();
+ }
}
});
}
});
@@ -124,9 +132,11 @@
});
};
// Handle tap/click
self.down = function (x, y, obj) {
- if (self.isFaceUp || self.isMatched || game.lockInput) return;
+ if (self.isFaceUp || self.isMatched || game.lockInput) {
+ return;
+ }
game.onCardTapped(self);
};
// Initialize: show face down
self.showFaceDown();
@@ -151,24 +161,32 @@
var currentLevel = 1;
// Level configuration: grid size per level (increase difficulty)
function getLevelConfig(level) {
// Level 1-5: 4x4, 6-10: 5x4, 11-15: 6x5, 16-20: 6x6, 21-25: 7x6
- if (level <= 5) return {
- cols: 4,
- rows: 4
- };
- if (level <= 10) return {
- cols: 5,
- rows: 4
- };
- if (level <= 15) return {
- cols: 6,
- rows: 5
- };
- if (level <= 20) return {
- cols: 6,
- rows: 6
- };
+ if (level <= 5) {
+ return {
+ cols: 4,
+ rows: 4
+ };
+ }
+ if (level <= 10) {
+ return {
+ cols: 5,
+ rows: 4
+ };
+ }
+ if (level <= 15) {
+ return {
+ cols: 6,
+ rows: 5
+ };
+ }
+ if (level <= 20) {
+ return {
+ cols: 6,
+ rows: 6
+ };
+ }
return {
cols: 7,
rows: 6
};
@@ -283,17 +301,21 @@
totalPairs = gridCols * gridRows / 2;
movesText.setText('Moves: 0');
timerText.setText('Time: 0s');
layoutCards();
- if (timer) LK.clearInterval(timer);
+ if (timer) {
+ LK.clearInterval(timer);
+ }
timer = LK.setInterval(function () {
elapsedTime++;
timerText.setText('Time: ' + elapsedTime + 's');
}, 1000);
}
// --- Card tap handler ---
game.onCardTapped = function (card) {
- if (lockInput || card.isFaceUp || card.isMatched) return;
+ if (lockInput || card.isFaceUp || card.isMatched) {
+ return;
+ }
if (!firstCard) {
firstCard = card;
card.flipUp();
} else if (!secondCard && card !== firstCard) {
@@ -335,9 +357,11 @@
alpha: 0
}, {
duration: 180,
onFinish: function onFinish() {
- if (matched1) matched1.destroy();
+ if (matched1) {
+ matched1.destroy();
+ }
}
});
}
}
@@ -358,9 +382,11 @@
alpha: 0
}, {
duration: 180,
onFinish: function onFinish() {
- if (matched2) matched2.destroy();
+ if (matched2) {
+ matched2.destroy();
+ }
}
});
}
}
@@ -408,9 +434,9 @@
font: "Impact"
});
devBtn.anchor.set(0.5, 1);
devBtn.x = LK.gui.width / 2;
-devBtn.y = LK.gui.height - 40;
+devBtn.y = 100;
LK.gui.bottom.addChild(devBtn);
// DEV button handler: go to next level immediately (max 25)
devBtn.down = function (x, y, obj) {
if (currentLevel < maxLevel) {
@@ -423,14 +449,18 @@
// --- Start game ---
resetGame();
// --- Game over handler (reset on game over) ---
game.onGameOver = function () {
- if (timer) LK.clearInterval(timer);
+ if (timer) {
+ LK.clearInterval(timer);
+ }
currentLevel = 1;
resetGame();
};
// --- Win handler (reset on win) ---
game.onYouWin = function () {
- if (timer) LK.clearInterval(timer);
+ if (timer) {
+ LK.clearInterval(timer);
+ }
currentLevel = 1;
// Game will reset automatically by LK
};
\ No newline at end of file