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 ****/ // Tween plugin for card flip and reveal animations // --- Card symbols (use simple emojis for MVP) --- var cardSymbols = ["🍎", "🍌", "🍇", "🍉", "🍒", "🍋", "🍓", "🍍", "🥝", "🥑", "🍊", "🍐", "🍈", "🍑", "🥥", "🍅"]; // --- Game settings --- var gridCols = 4; var gridRows = 4; 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 and Timer --- 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) 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; 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 tween(firstCard, { scaleX: 1.15, scaleY: 1.15 }, { duration: 120, easing: tween.cubicOut, onFinish: function onFinish() { tween(firstCard, { scaleX: 1, scaleY: 1 }, { duration: 120 }); } }); tween(secondCard, { scaleX: 1.15, scaleY: 1.15 }, { duration: 120, easing: tween.cubicOut, onFinish: function onFinish() { tween(secondCard, { scaleX: 1, scaleY: 1 }, { duration: 120 }); } }); // Check for win if (matchesFound === totalPairs) { LK.clearInterval(timer); LK.setScore(moves); // Use moves as score (lower is better) 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 }; // --- Start game --- resetGame(); // --- Game over handler (reset on game over) --- game.onGameOver = function () { if (timer) LK.clearInterval(timer); resetGame(); }; // --- Win handler (reset on win) --- game.onYouWin = function () { if (timer) LK.clearInterval(timer); // Game will reset automatically by LK };
===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,344 @@
-/****
+/****
+* 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: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0x2e3a4f // Deep blue background
+});
+
+/****
+* Game Code
+****/
+// Tween plugin for card flip and reveal animations
+// --- Card symbols (use simple emojis for MVP) ---
+var cardSymbols = ["🍎", "🍌", "🍇", "🍉", "🍒", "🍋", "🍓", "🍍", "🥝", "🥑", "🍊", "🍐", "🍈", "🍑", "🥥", "🍅"];
+// --- Game settings ---
+var gridCols = 4;
+var gridRows = 4;
+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 and Timer ---
+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)
+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;
+ 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
+ tween(firstCard, {
+ scaleX: 1.15,
+ scaleY: 1.15
+ }, {
+ duration: 120,
+ easing: tween.cubicOut,
+ onFinish: function onFinish() {
+ tween(firstCard, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 120
+ });
+ }
+ });
+ tween(secondCard, {
+ scaleX: 1.15,
+ scaleY: 1.15
+ }, {
+ duration: 120,
+ easing: tween.cubicOut,
+ onFinish: function onFinish() {
+ tween(secondCard, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 120
+ });
+ }
+ });
+ // Check for win
+ if (matchesFound === totalPairs) {
+ LK.clearInterval(timer);
+ LK.setScore(moves); // Use moves as score (lower is better)
+ 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
+};
+// --- Start game ---
+resetGame();
+// --- Game over handler (reset on game over) ---
+game.onGameOver = function () {
+ if (timer) LK.clearInterval(timer);
+ resetGame();
+};
+// --- Win handler (reset on win) ---
+game.onYouWin = function () {
+ if (timer) LK.clearInterval(timer);
+ // Game will reset automatically by LK
+};
\ No newline at end of file