User prompt
The person who made the game in the beginning menu is written as Osman Eren
User prompt
In the beginning menu, the person who made the game is Osman Erendir.
User prompt
Let your fruits write ouan values
User prompt
Write with score color black
User prompt
Let the score you made in the upper corner write the score
User prompt
Let him write the score in the game
User prompt
Add scoreboard to start page and
User prompt
Name the game fruit dance
User prompt
Add start page to game
User prompt
Let the clouds in the back move
User prompt
Remove the transparent moving fruits on the back
User prompt
Let the clouds in the background move constantly and let the plane pass in between
User prompt
No explosion effect
User prompt
Enlarge your background image
User prompt
Make the box a little more beautiful
User prompt
Remove text
User prompt
Take the fruits into the box
User prompt
Make a little bigger box from the grill
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Tile class for each grid cell var Tile = Container.expand(function () { var self = Container.call(this); // Properties self.value = 2; // Default value self.row = 0; self.col = 0; self.asset = null; self.text = null; // Set tile value and update appearance self.setValue = function (val) { self.value = val; // Remove old asset if exists if (self.asset) { self.removeChild(self.asset); } // Choose asset by value var assetId = 'tile' + val; var supportedAssets = { 'tile2': true, 'tile4': true, 'tile8': true, 'tile16': true, 'tile32': true, 'tile64': true, 'tile128': true, 'tile256': true, 'tile512': true, 'tile1024': true, 'tile2048': true }; if (!supportedAssets[assetId]) assetId = 'tile2048'; // fallback self.asset = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Remove old text if exists if (self.text) { self.removeChild(self.text); } // Add value text (empty label, no fruit names) var label = ''; self.text = new Text2(label, { size: 60, fill: 0xFFFFFF, align: "center" }); self.text.anchor.set(0.5, 0.5); self.text.y = 0; self.text.x = 0; self.addChild(self.text); // Restart dancing after value change if (self.startDancing) self.startDancing(); }; // Animate merge self.animateMerge = function () { tween(self, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 100, easing: tween.easeIn, onFinish: function onFinish() { if (self.startDancing) self.startDancing(); } }); } }); }; // Animate spawn self.animateSpawn = function () { self.scaleX = 0.2; self.scaleY = 0.2; tween(self, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { if (self.startDancing) self.startDancing(); } }); }; // Set position on grid self.setGridPos = function (row, col, cellSize, gridX, gridY) { self.row = row; self.col = col; self.x = gridX + col * cellSize + cellSize / 2; self.y = gridY + row * cellSize + cellSize / 2; }; // Add dancing animation (continuous, unique per value) self.startDancing = function () { // Cancel any previous tweens on scale tween.stop(self, { scaleX: true, scaleY: true }); // Unique dance parameters per value var dances = { 2: { sx1: 1.10, sy1: 0.90, sx2: 0.90, sy2: 1.10, dur1: 220, dur2: 220, ease1: tween.easeInOut, ease2: tween.easeInOut }, 4: { sx1: 1.15, sy1: 0.85, sx2: 0.85, sy2: 1.15, dur1: 180, dur2: 180, ease1: tween.bounceIn, ease2: tween.bounceOut }, 8: { sx1: 1.08, sy1: 1.08, sx2: 0.92, sy2: 0.92, dur1: 260, dur2: 260, ease1: tween.elasticIn, ease2: tween.elasticOut }, 16: { sx1: 1.20, sy1: 0.80, sx2: 0.80, sy2: 1.20, dur1: 150, dur2: 150, ease1: tween.cubicOut, ease2: tween.cubicIn }, 32: { sx1: 1.05, sy1: 1.15, sx2: 1.15, sy2: 1.05, dur1: 200, dur2: 200, ease1: tween.quarticInOut, ease2: tween.quarticInOut }, 64: { sx1: 1.18, sy1: 0.95, sx2: 0.95, sy2: 1.18, dur1: 170, dur2: 170, ease1: tween.sineIn, ease2: tween.sineOut }, 128: { sx1: 1.12, sy1: 1.12, sx2: 0.88, sy2: 0.88, dur1: 250, dur2: 250, ease1: tween.elasticInOut, ease2: tween.elasticInOut }, 256: { sx1: 1.25, sy1: 0.75, sx2: 0.75, sy2: 1.25, dur1: 140, dur2: 140, ease1: tween.bounceOut, ease2: tween.bounceIn }, 512: { sx1: 1.10, sy1: 1.05, sx2: 1.05, sy2: 1.10, dur1: 210, dur2: 210, ease1: tween.cubicIn, ease2: tween.cubicOut }, 1024: { sx1: 1.22, sy1: 0.92, sx2: 0.92, sy2: 1.22, dur1: 160, dur2: 160, ease1: tween.quinticIn, ease2: tween.quinticOut }, 2048: { sx1: 1.30, sy1: 0.70, sx2: 0.70, sy2: 1.30, dur1: 120, dur2: 120, ease1: tween.expoIn, ease2: tween.expoOut } }; var dance = dances[self.value] || dances[2]; function danceUp() { tween(self, { scaleX: dance.sx1, scaleY: dance.sy1 }, { duration: dance.dur1, easing: dance.ease1, onFinish: function onFinish() { danceDown(); } }); } function danceDown() { tween(self, { scaleX: dance.sx2, scaleY: dance.sy2 }, { duration: dance.dur2, easing: dance.ease2, onFinish: function onFinish() { danceUp(); } }); } // Start the loop danceUp(); }; // Stop dancing animation self.stopDancing = function () { tween.stop(self, { scaleX: true, scaleY: true }); self.scaleX = 1; self.scaleY = 1; }; // Start dancing by default self.startDancing(); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xffffff }); /**** * Game Code ****/ // Optionally, a subtle grid background // 2: Asteroid (gray), 4: Moon (light blue), 8: Planet (blue), 16: Star (yellow), 32: Red Giant (red), 64: Neutron Star (purple), 128: Black Hole (black), 256: Nebula (pink), 512: Galaxy (cyan), 1024: Quasar (orange), 2048: Universe (white) // Space tile assets: We'll use colored ellipses for different tile values // --- Grid and Layout --- var gridSize = 4; var cellSize = 370; // 370*4=1480, bigger board var gridPadding = 28; var gridWidth = cellSize * gridSize + gridPadding * (gridSize + 1); var gridHeight = cellSize * gridSize + gridPadding * (gridSize + 1); var gridX = Math.floor((2048 - gridWidth) / 2); var gridY = Math.floor((2732 - gridHeight) / 2); // --- Game State --- var grid = []; // 2D array of tiles or null var tiles = []; // All tile objects for easy management var score = 0; var scoreTxt = null; var isMoving = false; // Prevent input during animation // --- GUI: Score --- scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // --- Draw grid background --- var gridBg = LK.getAsset('gridBg', { anchorX: 0, anchorY: 0, x: gridX, y: gridY, width: gridWidth, height: gridHeight }); game.addChild(gridBg); // --- Draw transparent gray grill overlay --- var grillContainer = new Container(); var grillLineColor = 0x000000; var grillAlpha = 0.25; var grillLineWidth = 24; // The grill lines should not cover the outer grid edges, so lines are drawn only between cells // The vertical lines go from the top inner padding to the bottom inner padding var grillInnerHeight = cellSize * gridSize + gridPadding * (gridSize - 1); var grillInnerWidth = cellSize * gridSize + gridPadding * (gridSize - 1); // Vertical lines (between columns, not at the very left/right) for (var c = 1; c < gridSize; c++) { var x = gridX + gridPadding + c * cellSize + (c - 0.5) * gridPadding; var lineAsset = LK.getAsset('shape', { width: grillLineWidth, height: grillInnerHeight, color: grillLineColor, shape: 'box', anchorX: 0.5, anchorY: 0, alpha: grillAlpha, x: x, y: gridY + gridPadding }); grillContainer.addChild(lineAsset); } // Horizontal lines (between rows, not at the very top/bottom) for (var r = 1; r < gridSize; r++) { var y = gridY + gridPadding + r * cellSize + (r - 0.5) * gridPadding; var lineAsset = LK.getAsset('shape', { width: grillInnerWidth, height: grillLineWidth, color: grillLineColor, shape: 'box', anchorX: 0, anchorY: 0.5, alpha: grillAlpha, x: gridX + gridPadding, y: y }); grillContainer.addChild(lineAsset); } game.addChild(grillContainer); // --- Draw grid cell backgrounds --- // (Removed transparent fruit tiles on the back) // --- Initialize grid state --- function resetGrid() { grid = []; for (var r = 0; r < gridSize; r++) { var row = []; for (var c = 0; c < gridSize; c++) { row.push(null); } grid.push(row); } // Remove all tiles from game for (var i = 0; i < tiles.length; i++) { tiles[i].destroy(); } tiles = []; score = 0; scoreTxt.setText(score); } // --- Add a new tile (2 or 4) at random empty position --- function addRandomTile() { var empties = []; for (var r = 0; r < gridSize; r++) { for (var c = 0; c < gridSize; c++) { if (!grid[r][c]) empties.push({ r: r, c: c }); } } if (empties.length === 0) return false; var idx = Math.floor(Math.random() * empties.length); var pos = empties[idx]; var val = Math.random() < 0.9 ? 2 : 4; var tile = new Tile(); tile.setValue(val); tile.setGridPos(pos.r, pos.c, cellSize, gridX + gridPadding, gridY + gridPadding); tile.animateSpawn(); grid[pos.r][pos.c] = tile; tiles.push(tile); game.addChild(tile); return true; } // --- Move/merge logic --- function canMove() { // Check for any empty cell for (var r = 0; r < gridSize; r++) { for (var c = 0; c < gridSize; c++) { if (!grid[r][c]) return true; } } // Check for any mergeable neighbor for (var r = 0; r < gridSize; r++) { for (var c = 0; c < gridSize; c++) { var tile = grid[r][c]; if (!tile) continue; // Right if (c + 1 < gridSize && grid[r][c + 1] && grid[r][c + 1].value === tile.value) return true; // Down if (r + 1 < gridSize && grid[r + 1][c] && grid[r + 1][c].value === tile.value) return true; } } return false; } // Returns true if any tile reached 2048 function has2048() { for (var r = 0; r < gridSize; r++) { for (var c = 0; c < gridSize; c++) { if (grid[r][c] && grid[r][c].value === 2048) return true; } } return false; } // Move tiles in a direction: 'up', 'down', 'left', 'right' function moveTiles(dir) { if (isMoving) return; isMoving = true; var moved = false; var mergedThisMove = []; // Helper to reset merged flags function resetMerged() { for (var i = 0; i < tiles.length; i++) { tiles[i].merged = false; } } resetMerged(); // For each direction, set up iteration order var startR = 0, endR = gridSize, stepR = 1; var startC = 0, endC = gridSize, stepC = 1; if (dir === 'up') { startR = 1; endR = gridSize; stepR = 1; } if (dir === 'down') { startR = gridSize - 2; endR = -1; stepR = -1; } if (dir === 'left') { startC = 1; endC = gridSize; stepC = 1; } if (dir === 'right') { startC = gridSize - 2; endC = -1; stepC = -1; } // Move/merge logic var movedTiles = []; if (dir === 'up' || dir === 'down') { for (var c = 0; c < gridSize; c++) { for (var r = startR; r != endR; r += stepR) { var tile = grid[r][c]; if (!tile) continue; var tr = r; while (true) { var nr = tr + (dir === 'up' ? -1 : 1); if (nr < 0 || nr >= gridSize) break; if (!grid[nr][c]) { // Move grid[nr][c] = tile; grid[tr][c] = null; tr = nr; moved = true; } else if (grid[nr][c].value === tile.value && !grid[nr][c].merged && !tile.merged) { // Merge grid[nr][c].setValue(tile.value * 2); grid[nr][c].animateMerge(); grid[nr][c].merged = true; score += tile.value * 2; scoreTxt.setText(score); // Explode animation for merged tile tile.stopDancing && tile.stopDancing(); // Show explosion effect // Unique explosion animation per value var explosionAnims = { 2: { scale: 1.6, alpha: 0, duration: 180, easing: tween.bounceOut, tint: 0xE0E0E0 }, 4: { scale: 2.0, alpha: 0, duration: 220, easing: tween.elasticOut, tint: 0xB0E0FF }, 8: { scale: 1.7, alpha: 0, duration: 200, easing: tween.cubicOut, tint: 0x6CA0FF }, 16: { scale: 2.2, alpha: 0, duration: 240, easing: tween.quarticInOut, tint: 0xFFF700 }, 32: { scale: 1.9, alpha: 0, duration: 210, easing: tween.sineOut, tint: 0xFF3A3A }, 64: { scale: 2.3, alpha: 0, duration: 250, easing: tween.elasticInOut, tint: 0xB000FF }, 128: { scale: 2.0, alpha: 0, duration: 230, easing: tween.bounceIn, tint: 0x000000 }, 256: { scale: 2.5, alpha: 0, duration: 260, easing: tween.cubicIn, tint: 0xFF7EB9 }, 512: { scale: 2.1, alpha: 0, duration: 200, easing: tween.quinticOut, tint: 0x00FFF7 }, 1024: { scale: 2.4, alpha: 0, duration: 270, easing: tween.expoIn, tint: 0xFFB84C }, 2048: { scale: 2.8, alpha: 0, duration: 300, easing: tween.expoOut, tint: 0xFFFFFF } }; var exp = explosionAnims[tile.value] || explosionAnims[2]; LK.effects.flashObject(tile, exp.tint, Math.min(180, exp.duration)); tween(tile, { scaleX: exp.scale, scaleY: exp.scale, alpha: exp.alpha }, { duration: exp.duration, easing: exp.easing, onFinish: function onFinish() { if (tile && typeof tile.destroy === "function") { tile.destroy(); } } }); tiles.splice(tiles.indexOf(tile), 1); grid[tr][c] = null; moved = true; break; } else { break; } } // Animate move if (tile && tr !== r) { tile.setGridPos(tr, c, cellSize, gridX + gridPadding, gridY + gridPadding); movedTiles.push(tile); } } } } else { for (var r = 0; r < gridSize; r++) { for (var c = startC; c != endC; c += stepC) { var tile = grid[r][c]; if (!tile) continue; var tc = c; while (true) { var nc = tc + (dir === 'left' ? -1 : 1); if (nc < 0 || nc >= gridSize) break; if (!grid[r][nc]) { // Move grid[r][nc] = tile; grid[r][tc] = null; tc = nc; moved = true; } else if (grid[r][nc].value === tile.value && !grid[r][nc].merged && !tile.merged) { // Merge grid[r][nc].setValue(tile.value * 2); grid[r][nc].animateMerge(); grid[r][nc].merged = true; score += tile.value * 2; scoreTxt.setText(score); // Explode animation for merged tile tile.stopDancing && tile.stopDancing(); // Show explosion effect // Unique explosion animation per value var explosionAnims = { 2: { scale: 1.6, alpha: 0, duration: 180, easing: tween.bounceOut, tint: 0xE0E0E0 }, 4: { scale: 2.0, alpha: 0, duration: 220, easing: tween.elasticOut, tint: 0xB0E0FF }, 8: { scale: 1.7, alpha: 0, duration: 200, easing: tween.cubicOut, tint: 0x6CA0FF }, 16: { scale: 2.2, alpha: 0, duration: 240, easing: tween.quarticInOut, tint: 0xFFF700 }, 32: { scale: 1.9, alpha: 0, duration: 210, easing: tween.sineOut, tint: 0xFF3A3A }, 64: { scale: 2.3, alpha: 0, duration: 250, easing: tween.elasticInOut, tint: 0xB000FF }, 128: { scale: 2.0, alpha: 0, duration: 230, easing: tween.bounceIn, tint: 0x000000 }, 256: { scale: 2.5, alpha: 0, duration: 260, easing: tween.cubicIn, tint: 0xFF7EB9 }, 512: { scale: 2.1, alpha: 0, duration: 200, easing: tween.quinticOut, tint: 0x00FFF7 }, 1024: { scale: 2.4, alpha: 0, duration: 270, easing: tween.expoIn, tint: 0xFFB84C }, 2048: { scale: 2.8, alpha: 0, duration: 300, easing: tween.expoOut, tint: 0xFFFFFF } }; var exp = explosionAnims[tile.value] || explosionAnims[2]; LK.effects.flashObject(tile, exp.tint, Math.min(180, exp.duration)); tween(tile, { scaleX: exp.scale, scaleY: exp.scale, alpha: exp.alpha }, { duration: exp.duration, easing: exp.easing, onFinish: function onFinish() { if (tile && typeof tile.destroy === "function") { tile.destroy(); } } }); tiles.splice(tiles.indexOf(tile), 1); grid[r][tc] = null; moved = true; break; } else { break; } } // Animate move if (tile && tc !== c) { tile.setGridPos(r, tc, cellSize, gridX + gridPadding, gridY + gridPadding); movedTiles.push(tile); } } } } // Animate all moved tiles var animCount = movedTiles.length; if (animCount === 0) { isMoving = false; afterMove(moved); } else { for (var i = 0; i < movedTiles.length; i++) { var t = movedTiles[i]; tween(t, { x: t.x, y: t.y }, { duration: 100, easing: tween.easeInOut, onFinish: function onFinish() { animCount--; if (animCount === 0) { isMoving = false; afterMove(moved); } } }); } } if (animCount === 0) { // No animation, call afterMove immediately afterMove(moved); } } // After move: add new tile, check win/lose function afterMove(moved) { if (moved) { addRandomTile(); } if (has2048()) { LK.showYouWin(); return; } if (!canMove()) { LK.showGameOver(); return; } } // --- Input: Swipe detection --- // We'll use game.down, game.move, game.up to detect swipe direction var touchStartX = 0, touchStartY = 0, touchMoved = false; game.down = function (x, y, obj) { if (isMoving) return; touchStartX = x; touchStartY = y; touchMoved = false; }; game.move = function (x, y, obj) { if (isMoving) return; if (touchStartX === undefined) return; var dx = x - touchStartX; var dy = y - touchStartY; if (!touchMoved && (Math.abs(dx) > 40 || Math.abs(dy) > 40)) { touchMoved = true; // Determine direction if (Math.abs(dx) > Math.abs(dy)) { if (dx > 0) moveTiles('right');else moveTiles('left'); } else { if (dy > 0) moveTiles('down');else moveTiles('up'); } // Reset to prevent multiple moves per swipe touchStartX = undefined; touchStartY = undefined; } }; game.up = function (x, y, obj) { touchStartX = undefined; touchStartY = undefined; touchMoved = false; }; // --- Game update (not used, but required for tick-based games) --- game.update = function () { // No per-frame logic needed }; // --- Start game --- function startGame() { resetGrid(); addRandomTile(); addRandomTile(); scoreTxt.setText(score); } startGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Tile class for each grid cell
var Tile = Container.expand(function () {
var self = Container.call(this);
// Properties
self.value = 2; // Default value
self.row = 0;
self.col = 0;
self.asset = null;
self.text = null;
// Set tile value and update appearance
self.setValue = function (val) {
self.value = val;
// Remove old asset if exists
if (self.asset) {
self.removeChild(self.asset);
}
// Choose asset by value
var assetId = 'tile' + val;
var supportedAssets = {
'tile2': true,
'tile4': true,
'tile8': true,
'tile16': true,
'tile32': true,
'tile64': true,
'tile128': true,
'tile256': true,
'tile512': true,
'tile1024': true,
'tile2048': true
};
if (!supportedAssets[assetId]) assetId = 'tile2048'; // fallback
self.asset = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Remove old text if exists
if (self.text) {
self.removeChild(self.text);
}
// Add value text (empty label, no fruit names)
var label = '';
self.text = new Text2(label, {
size: 60,
fill: 0xFFFFFF,
align: "center"
});
self.text.anchor.set(0.5, 0.5);
self.text.y = 0;
self.text.x = 0;
self.addChild(self.text);
// Restart dancing after value change
if (self.startDancing) self.startDancing();
};
// Animate merge
self.animateMerge = function () {
tween(self, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.easeIn,
onFinish: function onFinish() {
if (self.startDancing) self.startDancing();
}
});
}
});
};
// Animate spawn
self.animateSpawn = function () {
self.scaleX = 0.2;
self.scaleY = 0.2;
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
if (self.startDancing) self.startDancing();
}
});
};
// Set position on grid
self.setGridPos = function (row, col, cellSize, gridX, gridY) {
self.row = row;
self.col = col;
self.x = gridX + col * cellSize + cellSize / 2;
self.y = gridY + row * cellSize + cellSize / 2;
};
// Add dancing animation (continuous, unique per value)
self.startDancing = function () {
// Cancel any previous tweens on scale
tween.stop(self, {
scaleX: true,
scaleY: true
});
// Unique dance parameters per value
var dances = {
2: {
sx1: 1.10,
sy1: 0.90,
sx2: 0.90,
sy2: 1.10,
dur1: 220,
dur2: 220,
ease1: tween.easeInOut,
ease2: tween.easeInOut
},
4: {
sx1: 1.15,
sy1: 0.85,
sx2: 0.85,
sy2: 1.15,
dur1: 180,
dur2: 180,
ease1: tween.bounceIn,
ease2: tween.bounceOut
},
8: {
sx1: 1.08,
sy1: 1.08,
sx2: 0.92,
sy2: 0.92,
dur1: 260,
dur2: 260,
ease1: tween.elasticIn,
ease2: tween.elasticOut
},
16: {
sx1: 1.20,
sy1: 0.80,
sx2: 0.80,
sy2: 1.20,
dur1: 150,
dur2: 150,
ease1: tween.cubicOut,
ease2: tween.cubicIn
},
32: {
sx1: 1.05,
sy1: 1.15,
sx2: 1.15,
sy2: 1.05,
dur1: 200,
dur2: 200,
ease1: tween.quarticInOut,
ease2: tween.quarticInOut
},
64: {
sx1: 1.18,
sy1: 0.95,
sx2: 0.95,
sy2: 1.18,
dur1: 170,
dur2: 170,
ease1: tween.sineIn,
ease2: tween.sineOut
},
128: {
sx1: 1.12,
sy1: 1.12,
sx2: 0.88,
sy2: 0.88,
dur1: 250,
dur2: 250,
ease1: tween.elasticInOut,
ease2: tween.elasticInOut
},
256: {
sx1: 1.25,
sy1: 0.75,
sx2: 0.75,
sy2: 1.25,
dur1: 140,
dur2: 140,
ease1: tween.bounceOut,
ease2: tween.bounceIn
},
512: {
sx1: 1.10,
sy1: 1.05,
sx2: 1.05,
sy2: 1.10,
dur1: 210,
dur2: 210,
ease1: tween.cubicIn,
ease2: tween.cubicOut
},
1024: {
sx1: 1.22,
sy1: 0.92,
sx2: 0.92,
sy2: 1.22,
dur1: 160,
dur2: 160,
ease1: tween.quinticIn,
ease2: tween.quinticOut
},
2048: {
sx1: 1.30,
sy1: 0.70,
sx2: 0.70,
sy2: 1.30,
dur1: 120,
dur2: 120,
ease1: tween.expoIn,
ease2: tween.expoOut
}
};
var dance = dances[self.value] || dances[2];
function danceUp() {
tween(self, {
scaleX: dance.sx1,
scaleY: dance.sy1
}, {
duration: dance.dur1,
easing: dance.ease1,
onFinish: function onFinish() {
danceDown();
}
});
}
function danceDown() {
tween(self, {
scaleX: dance.sx2,
scaleY: dance.sy2
}, {
duration: dance.dur2,
easing: dance.ease2,
onFinish: function onFinish() {
danceUp();
}
});
}
// Start the loop
danceUp();
};
// Stop dancing animation
self.stopDancing = function () {
tween.stop(self, {
scaleX: true,
scaleY: true
});
self.scaleX = 1;
self.scaleY = 1;
};
// Start dancing by default
self.startDancing();
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xffffff
});
/****
* Game Code
****/
// Optionally, a subtle grid background
// 2: Asteroid (gray), 4: Moon (light blue), 8: Planet (blue), 16: Star (yellow), 32: Red Giant (red), 64: Neutron Star (purple), 128: Black Hole (black), 256: Nebula (pink), 512: Galaxy (cyan), 1024: Quasar (orange), 2048: Universe (white)
// Space tile assets: We'll use colored ellipses for different tile values
// --- Grid and Layout ---
var gridSize = 4;
var cellSize = 370; // 370*4=1480, bigger board
var gridPadding = 28;
var gridWidth = cellSize * gridSize + gridPadding * (gridSize + 1);
var gridHeight = cellSize * gridSize + gridPadding * (gridSize + 1);
var gridX = Math.floor((2048 - gridWidth) / 2);
var gridY = Math.floor((2732 - gridHeight) / 2);
// --- Game State ---
var grid = []; // 2D array of tiles or null
var tiles = []; // All tile objects for easy management
var score = 0;
var scoreTxt = null;
var isMoving = false; // Prevent input during animation
// --- GUI: Score ---
scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Draw grid background ---
var gridBg = LK.getAsset('gridBg', {
anchorX: 0,
anchorY: 0,
x: gridX,
y: gridY,
width: gridWidth,
height: gridHeight
});
game.addChild(gridBg);
// --- Draw transparent gray grill overlay ---
var grillContainer = new Container();
var grillLineColor = 0x000000;
var grillAlpha = 0.25;
var grillLineWidth = 24;
// The grill lines should not cover the outer grid edges, so lines are drawn only between cells
// The vertical lines go from the top inner padding to the bottom inner padding
var grillInnerHeight = cellSize * gridSize + gridPadding * (gridSize - 1);
var grillInnerWidth = cellSize * gridSize + gridPadding * (gridSize - 1);
// Vertical lines (between columns, not at the very left/right)
for (var c = 1; c < gridSize; c++) {
var x = gridX + gridPadding + c * cellSize + (c - 0.5) * gridPadding;
var lineAsset = LK.getAsset('shape', {
width: grillLineWidth,
height: grillInnerHeight,
color: grillLineColor,
shape: 'box',
anchorX: 0.5,
anchorY: 0,
alpha: grillAlpha,
x: x,
y: gridY + gridPadding
});
grillContainer.addChild(lineAsset);
}
// Horizontal lines (between rows, not at the very top/bottom)
for (var r = 1; r < gridSize; r++) {
var y = gridY + gridPadding + r * cellSize + (r - 0.5) * gridPadding;
var lineAsset = LK.getAsset('shape', {
width: grillInnerWidth,
height: grillLineWidth,
color: grillLineColor,
shape: 'box',
anchorX: 0,
anchorY: 0.5,
alpha: grillAlpha,
x: gridX + gridPadding,
y: y
});
grillContainer.addChild(lineAsset);
}
game.addChild(grillContainer);
// --- Draw grid cell backgrounds ---
// (Removed transparent fruit tiles on the back)
// --- Initialize grid state ---
function resetGrid() {
grid = [];
for (var r = 0; r < gridSize; r++) {
var row = [];
for (var c = 0; c < gridSize; c++) {
row.push(null);
}
grid.push(row);
}
// Remove all tiles from game
for (var i = 0; i < tiles.length; i++) {
tiles[i].destroy();
}
tiles = [];
score = 0;
scoreTxt.setText(score);
}
// --- Add a new tile (2 or 4) at random empty position ---
function addRandomTile() {
var empties = [];
for (var r = 0; r < gridSize; r++) {
for (var c = 0; c < gridSize; c++) {
if (!grid[r][c]) empties.push({
r: r,
c: c
});
}
}
if (empties.length === 0) return false;
var idx = Math.floor(Math.random() * empties.length);
var pos = empties[idx];
var val = Math.random() < 0.9 ? 2 : 4;
var tile = new Tile();
tile.setValue(val);
tile.setGridPos(pos.r, pos.c, cellSize, gridX + gridPadding, gridY + gridPadding);
tile.animateSpawn();
grid[pos.r][pos.c] = tile;
tiles.push(tile);
game.addChild(tile);
return true;
}
// --- Move/merge logic ---
function canMove() {
// Check for any empty cell
for (var r = 0; r < gridSize; r++) {
for (var c = 0; c < gridSize; c++) {
if (!grid[r][c]) return true;
}
}
// Check for any mergeable neighbor
for (var r = 0; r < gridSize; r++) {
for (var c = 0; c < gridSize; c++) {
var tile = grid[r][c];
if (!tile) continue;
// Right
if (c + 1 < gridSize && grid[r][c + 1] && grid[r][c + 1].value === tile.value) return true;
// Down
if (r + 1 < gridSize && grid[r + 1][c] && grid[r + 1][c].value === tile.value) return true;
}
}
return false;
}
// Returns true if any tile reached 2048
function has2048() {
for (var r = 0; r < gridSize; r++) {
for (var c = 0; c < gridSize; c++) {
if (grid[r][c] && grid[r][c].value === 2048) return true;
}
}
return false;
}
// Move tiles in a direction: 'up', 'down', 'left', 'right'
function moveTiles(dir) {
if (isMoving) return;
isMoving = true;
var moved = false;
var mergedThisMove = [];
// Helper to reset merged flags
function resetMerged() {
for (var i = 0; i < tiles.length; i++) {
tiles[i].merged = false;
}
}
resetMerged();
// For each direction, set up iteration order
var startR = 0,
endR = gridSize,
stepR = 1;
var startC = 0,
endC = gridSize,
stepC = 1;
if (dir === 'up') {
startR = 1;
endR = gridSize;
stepR = 1;
}
if (dir === 'down') {
startR = gridSize - 2;
endR = -1;
stepR = -1;
}
if (dir === 'left') {
startC = 1;
endC = gridSize;
stepC = 1;
}
if (dir === 'right') {
startC = gridSize - 2;
endC = -1;
stepC = -1;
}
// Move/merge logic
var movedTiles = [];
if (dir === 'up' || dir === 'down') {
for (var c = 0; c < gridSize; c++) {
for (var r = startR; r != endR; r += stepR) {
var tile = grid[r][c];
if (!tile) continue;
var tr = r;
while (true) {
var nr = tr + (dir === 'up' ? -1 : 1);
if (nr < 0 || nr >= gridSize) break;
if (!grid[nr][c]) {
// Move
grid[nr][c] = tile;
grid[tr][c] = null;
tr = nr;
moved = true;
} else if (grid[nr][c].value === tile.value && !grid[nr][c].merged && !tile.merged) {
// Merge
grid[nr][c].setValue(tile.value * 2);
grid[nr][c].animateMerge();
grid[nr][c].merged = true;
score += tile.value * 2;
scoreTxt.setText(score);
// Explode animation for merged tile
tile.stopDancing && tile.stopDancing();
// Show explosion effect
// Unique explosion animation per value
var explosionAnims = {
2: {
scale: 1.6,
alpha: 0,
duration: 180,
easing: tween.bounceOut,
tint: 0xE0E0E0
},
4: {
scale: 2.0,
alpha: 0,
duration: 220,
easing: tween.elasticOut,
tint: 0xB0E0FF
},
8: {
scale: 1.7,
alpha: 0,
duration: 200,
easing: tween.cubicOut,
tint: 0x6CA0FF
},
16: {
scale: 2.2,
alpha: 0,
duration: 240,
easing: tween.quarticInOut,
tint: 0xFFF700
},
32: {
scale: 1.9,
alpha: 0,
duration: 210,
easing: tween.sineOut,
tint: 0xFF3A3A
},
64: {
scale: 2.3,
alpha: 0,
duration: 250,
easing: tween.elasticInOut,
tint: 0xB000FF
},
128: {
scale: 2.0,
alpha: 0,
duration: 230,
easing: tween.bounceIn,
tint: 0x000000
},
256: {
scale: 2.5,
alpha: 0,
duration: 260,
easing: tween.cubicIn,
tint: 0xFF7EB9
},
512: {
scale: 2.1,
alpha: 0,
duration: 200,
easing: tween.quinticOut,
tint: 0x00FFF7
},
1024: {
scale: 2.4,
alpha: 0,
duration: 270,
easing: tween.expoIn,
tint: 0xFFB84C
},
2048: {
scale: 2.8,
alpha: 0,
duration: 300,
easing: tween.expoOut,
tint: 0xFFFFFF
}
};
var exp = explosionAnims[tile.value] || explosionAnims[2];
LK.effects.flashObject(tile, exp.tint, Math.min(180, exp.duration));
tween(tile, {
scaleX: exp.scale,
scaleY: exp.scale,
alpha: exp.alpha
}, {
duration: exp.duration,
easing: exp.easing,
onFinish: function onFinish() {
if (tile && typeof tile.destroy === "function") {
tile.destroy();
}
}
});
tiles.splice(tiles.indexOf(tile), 1);
grid[tr][c] = null;
moved = true;
break;
} else {
break;
}
}
// Animate move
if (tile && tr !== r) {
tile.setGridPos(tr, c, cellSize, gridX + gridPadding, gridY + gridPadding);
movedTiles.push(tile);
}
}
}
} else {
for (var r = 0; r < gridSize; r++) {
for (var c = startC; c != endC; c += stepC) {
var tile = grid[r][c];
if (!tile) continue;
var tc = c;
while (true) {
var nc = tc + (dir === 'left' ? -1 : 1);
if (nc < 0 || nc >= gridSize) break;
if (!grid[r][nc]) {
// Move
grid[r][nc] = tile;
grid[r][tc] = null;
tc = nc;
moved = true;
} else if (grid[r][nc].value === tile.value && !grid[r][nc].merged && !tile.merged) {
// Merge
grid[r][nc].setValue(tile.value * 2);
grid[r][nc].animateMerge();
grid[r][nc].merged = true;
score += tile.value * 2;
scoreTxt.setText(score);
// Explode animation for merged tile
tile.stopDancing && tile.stopDancing();
// Show explosion effect
// Unique explosion animation per value
var explosionAnims = {
2: {
scale: 1.6,
alpha: 0,
duration: 180,
easing: tween.bounceOut,
tint: 0xE0E0E0
},
4: {
scale: 2.0,
alpha: 0,
duration: 220,
easing: tween.elasticOut,
tint: 0xB0E0FF
},
8: {
scale: 1.7,
alpha: 0,
duration: 200,
easing: tween.cubicOut,
tint: 0x6CA0FF
},
16: {
scale: 2.2,
alpha: 0,
duration: 240,
easing: tween.quarticInOut,
tint: 0xFFF700
},
32: {
scale: 1.9,
alpha: 0,
duration: 210,
easing: tween.sineOut,
tint: 0xFF3A3A
},
64: {
scale: 2.3,
alpha: 0,
duration: 250,
easing: tween.elasticInOut,
tint: 0xB000FF
},
128: {
scale: 2.0,
alpha: 0,
duration: 230,
easing: tween.bounceIn,
tint: 0x000000
},
256: {
scale: 2.5,
alpha: 0,
duration: 260,
easing: tween.cubicIn,
tint: 0xFF7EB9
},
512: {
scale: 2.1,
alpha: 0,
duration: 200,
easing: tween.quinticOut,
tint: 0x00FFF7
},
1024: {
scale: 2.4,
alpha: 0,
duration: 270,
easing: tween.expoIn,
tint: 0xFFB84C
},
2048: {
scale: 2.8,
alpha: 0,
duration: 300,
easing: tween.expoOut,
tint: 0xFFFFFF
}
};
var exp = explosionAnims[tile.value] || explosionAnims[2];
LK.effects.flashObject(tile, exp.tint, Math.min(180, exp.duration));
tween(tile, {
scaleX: exp.scale,
scaleY: exp.scale,
alpha: exp.alpha
}, {
duration: exp.duration,
easing: exp.easing,
onFinish: function onFinish() {
if (tile && typeof tile.destroy === "function") {
tile.destroy();
}
}
});
tiles.splice(tiles.indexOf(tile), 1);
grid[r][tc] = null;
moved = true;
break;
} else {
break;
}
}
// Animate move
if (tile && tc !== c) {
tile.setGridPos(r, tc, cellSize, gridX + gridPadding, gridY + gridPadding);
movedTiles.push(tile);
}
}
}
}
// Animate all moved tiles
var animCount = movedTiles.length;
if (animCount === 0) {
isMoving = false;
afterMove(moved);
} else {
for (var i = 0; i < movedTiles.length; i++) {
var t = movedTiles[i];
tween(t, {
x: t.x,
y: t.y
}, {
duration: 100,
easing: tween.easeInOut,
onFinish: function onFinish() {
animCount--;
if (animCount === 0) {
isMoving = false;
afterMove(moved);
}
}
});
}
}
if (animCount === 0) {
// No animation, call afterMove immediately
afterMove(moved);
}
}
// After move: add new tile, check win/lose
function afterMove(moved) {
if (moved) {
addRandomTile();
}
if (has2048()) {
LK.showYouWin();
return;
}
if (!canMove()) {
LK.showGameOver();
return;
}
}
// --- Input: Swipe detection ---
// We'll use game.down, game.move, game.up to detect swipe direction
var touchStartX = 0,
touchStartY = 0,
touchMoved = false;
game.down = function (x, y, obj) {
if (isMoving) return;
touchStartX = x;
touchStartY = y;
touchMoved = false;
};
game.move = function (x, y, obj) {
if (isMoving) return;
if (touchStartX === undefined) return;
var dx = x - touchStartX;
var dy = y - touchStartY;
if (!touchMoved && (Math.abs(dx) > 40 || Math.abs(dy) > 40)) {
touchMoved = true;
// Determine direction
if (Math.abs(dx) > Math.abs(dy)) {
if (dx > 0) moveTiles('right');else moveTiles('left');
} else {
if (dy > 0) moveTiles('down');else moveTiles('up');
}
// Reset to prevent multiple moves per swipe
touchStartX = undefined;
touchStartY = undefined;
}
};
game.up = function (x, y, obj) {
touchStartX = undefined;
touchStartY = undefined;
touchMoved = false;
};
// --- Game update (not used, but required for tick-based games) ---
game.update = function () {
// No per-frame logic needed
};
// --- Start game ---
function startGame() {
resetGrid();
addRandomTile();
addRandomTile();
scoreTxt.setText(score);
}
startGame();
Furit King!. In-Game asset. 2d. High contrast. No shadows
Orange with face. In-Game asset. 2d. High contrast. No shadows
Cherry with face. In-Game asset. 2d. High contrast. No shadows
Grape with face. In-Game asset. 2d. High contrast. No shadows
Lemon with face. In-Game asset. 2d. High contrast. No shadows
Pear with face. In-Game asset. 2d. High contrast. No shadows
Mango with face. In-Game asset. 2d. High contrast. No shadows
Apple with face. In-Game asset. 2d. High contrast. No shadows
Watermelon with face. In-Game asset. 2d. High contrast. No shadows
Strawberry with face. In-Game asset. 2d. High contrast. No shadows
Banana with face. In-Game asset. 2d. High contrast. No shadows
Let the clouds in the back move. In-Game asset. 2d. High contrast. No shadows