User prompt
for one block make 10 points
Code edit (1 edits merged)
Please save this source code
User prompt
Stack the Blocks
Initial prompt
"Stack the Blocks" is a simple yet addictive casual game where players aim to build the tallest tower by stacking moving blocks on top of each other. Each block moves horizontally across the screen, and the player must tap at the right moment to drop it onto the previous one. If the block is not perfectly aligned, the overhanging part gets trimmed off, making the next block smaller and increasing the challenge. The game continues until a block completely misses the stack. With smooth animations, responsive tap controls, and progressively increasing speed, this game tests the player's timing and precision. Ideal for FRVR, it offers an endless gameplay loop perfect for mobile and web platforms.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Block = Container.expand(function () { var self = Container.call(this); // Properties self.blockWidth = 400; self.blockHeight = 100; self.movingDirection = 1; // 1 for right, -1 for left self.speed = 10; self.moving = true; self.trimmedAmount = 0; // Create block visual var blockGraphics = self.attachAsset('block', { anchorX: 0.5, anchorY: 0.5, width: self.blockWidth, height: self.blockHeight }); // Method to update block position self.update = function () { if (self.moving) { self.x += self.speed * self.movingDirection; // Bounce off the edges of the screen if (self.x + self.blockWidth / 2 > 2048) { self.x = 2048 - self.blockWidth / 2; self.movingDirection = -1; } else if (self.x - self.blockWidth / 2 < 0) { self.x = self.blockWidth / 2; self.movingDirection = 1; } } }; // Method to stop the block from moving self.stopMoving = function () { self.moving = false; }; // Method to trim the block based on the block below self.trimToFit = function (prevBlockX, prevBlockWidth) { // Calculate the trim amount var leftEdge = self.x - self.blockWidth / 2; var rightEdge = self.x + self.blockWidth / 2; var prevLeftEdge = prevBlockX - prevBlockWidth / 2; var prevRightEdge = prevBlockX + prevBlockWidth / 2; // The block is completely off the stack if (rightEdge < prevLeftEdge || leftEdge > prevRightEdge) { self.blockWidth = 0; self.trimmedAmount = self.blockWidth; return false; } // Calculate new dimensions after trimming var newLeftEdge = Math.max(leftEdge, prevLeftEdge); var newRightEdge = Math.min(rightEdge, prevRightEdge); var newWidth = newRightEdge - newLeftEdge; // Store the trimmed amount self.trimmedAmount = self.blockWidth - newWidth; // Update the block width self.blockWidth = newWidth; // Update the visual representation blockGraphics.width = self.blockWidth; // Center the block on its visible portion self.x = newLeftEdge + newWidth / 2; return true; }; // Method to highlight block when it's a miss self.highlightMiss = function () { // Replace current graphics with red block blockGraphics.destroy(); blockGraphics = self.attachAsset('gameOverBlock', { anchorX: 0.5, anchorY: 0.5, width: self.blockWidth, height: self.blockHeight }); }; return self; }); var StackedBlock = Container.expand(function () { var self = Container.call(this); // Properties self.blockWidth = 400; self.blockHeight = 100; // Create block visual var blockGraphics = self.attachAsset('stackBlock', { anchorX: 0.5, anchorY: 0.5, width: self.blockWidth, height: self.blockHeight }); // Method to set the width self.setWidth = function (width) { self.blockWidth = width; blockGraphics.width = width; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB }); /**** * Game Code ****/ // Game state variables var currentBlock; var stackedBlocks = []; var baseY = 2400; var score = 0; var gameActive = false; var difficultyLevel = 1; var lastBlockWidth = 400; var lastBlockX = 2048 / 2; // Create base platform var base = game.addChild(LK.getAsset('base', { anchorX: 0.5, anchorY: 0.5 })); base.x = 2048 / 2; base.y = baseY; base.width = 480; // UI Elements var scoreTxt = new Text2('0', { size: 100, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var instructionTxt = new Text2('Tap to drop blocks and build your tower!', { size: 60, fill: 0xFFFFFF }); instructionTxt.anchor.set(0.5, 0.5); instructionTxt.x = 0; instructionTxt.y = 300; LK.gui.center.addChild(instructionTxt); // Game logic methods function startGame() { // Reset game state for (var i = 0; i < stackedBlocks.length; i++) { stackedBlocks[i].destroy(); } stackedBlocks = []; score = 0; difficultyLevel = 1; baseY = 2400; lastBlockWidth = 400; lastBlockX = 2048 / 2; gameActive = true; // Update score display scoreTxt.setText('0'); // Hide instruction text instructionTxt.visible = false; // Create first block spawnNewBlock(); // Start background music LK.playMusic('bgMusic'); } function spawnNewBlock() { currentBlock = new Block(); currentBlock.blockWidth = lastBlockWidth; var blockGraphics = currentBlock.getChildAt(0); blockGraphics.width = currentBlock.blockWidth; // Randomly start from left or right currentBlock.x = Math.random() > 0.5 ? 0 + currentBlock.blockWidth / 2 : 2048 - currentBlock.blockWidth / 2; currentBlock.y = baseY - (stackedBlocks.length + 1) * 110; currentBlock.movingDirection = currentBlock.x < 1024 ? 1 : -1; // Increase speed based on difficulty level currentBlock.speed = 5 + difficultyLevel; game.addChild(currentBlock); } function dropBlock() { if (!currentBlock || !gameActive) { return; } // Stop the block from moving currentBlock.stopMoving(); // Check if the block fits on the previous block var successfulPlacement = true; if (stackedBlocks.length > 0) { successfulPlacement = currentBlock.trimToFit(lastBlockX, lastBlockWidth); } if (successfulPlacement && currentBlock.blockWidth > 0) { // Play placement sound LK.getSound('place').play(); // Update variables for next block lastBlockWidth = currentBlock.blockWidth; lastBlockX = currentBlock.x; // Create a stacked version of the block var stackedBlock = new StackedBlock(); stackedBlock.setWidth(lastBlockWidth); stackedBlock.x = lastBlockX; stackedBlock.y = currentBlock.y; game.addChild(stackedBlock); stackedBlocks.push(stackedBlock); // Remove the moving block currentBlock.destroy(); currentBlock = null; // Update score score += 10; scoreTxt.setText(score.toString()); // Increase difficulty every 5 points if (score % 5 === 0) { difficultyLevel++; } // After a short delay, spawn a new block LK.setTimeout(function () { spawnNewBlock(); }, 300); } else { // Game over gameActive = false; // Play miss sound LK.getSound('miss').play(); // Highlight the missed block in red currentBlock.highlightMiss(); // Make the block fall off screen tween(currentBlock, { y: 3000, rotation: Math.PI * 2 }, { duration: 1500, easing: tween.easeIn, onFinish: function onFinish() { // Check if this is a new high score var highScore = storage.highScore || 0; if (score > highScore) { storage.highScore = score; } // Show game over screen after a short delay LK.setTimeout(function () { LK.showGameOver(); }, 500); } }); } } function moveCamera() { // Only move camera if we have enough stacked blocks if (stackedBlocks.length > 8) { // Target Y for the topmost block var targetY = 2732 / 2; // Calculate how much to move everything down var moveAmount = baseY - (stackedBlocks.length + 1) * 110 - targetY; // Move everything down base.y -= moveAmount; for (var i = 0; i < stackedBlocks.length; i++) { stackedBlocks[i].y -= moveAmount; } if (currentBlock) { currentBlock.y -= moveAmount; } baseY -= moveAmount; } } // Input handlers game.down = function (x, y, obj) { if (!gameActive) { startGame(); } else { dropBlock(); } }; // Update method game.update = function () { if (currentBlock && gameActive) { currentBlock.update(); } // Move camera to follow the tower moveCamera(); }; // Show instruction text at start instructionTxt.visible = true;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Block = Container.expand(function () {
var self = Container.call(this);
// Properties
self.blockWidth = 400;
self.blockHeight = 100;
self.movingDirection = 1; // 1 for right, -1 for left
self.speed = 10;
self.moving = true;
self.trimmedAmount = 0;
// Create block visual
var blockGraphics = self.attachAsset('block', {
anchorX: 0.5,
anchorY: 0.5,
width: self.blockWidth,
height: self.blockHeight
});
// Method to update block position
self.update = function () {
if (self.moving) {
self.x += self.speed * self.movingDirection;
// Bounce off the edges of the screen
if (self.x + self.blockWidth / 2 > 2048) {
self.x = 2048 - self.blockWidth / 2;
self.movingDirection = -1;
} else if (self.x - self.blockWidth / 2 < 0) {
self.x = self.blockWidth / 2;
self.movingDirection = 1;
}
}
};
// Method to stop the block from moving
self.stopMoving = function () {
self.moving = false;
};
// Method to trim the block based on the block below
self.trimToFit = function (prevBlockX, prevBlockWidth) {
// Calculate the trim amount
var leftEdge = self.x - self.blockWidth / 2;
var rightEdge = self.x + self.blockWidth / 2;
var prevLeftEdge = prevBlockX - prevBlockWidth / 2;
var prevRightEdge = prevBlockX + prevBlockWidth / 2;
// The block is completely off the stack
if (rightEdge < prevLeftEdge || leftEdge > prevRightEdge) {
self.blockWidth = 0;
self.trimmedAmount = self.blockWidth;
return false;
}
// Calculate new dimensions after trimming
var newLeftEdge = Math.max(leftEdge, prevLeftEdge);
var newRightEdge = Math.min(rightEdge, prevRightEdge);
var newWidth = newRightEdge - newLeftEdge;
// Store the trimmed amount
self.trimmedAmount = self.blockWidth - newWidth;
// Update the block width
self.blockWidth = newWidth;
// Update the visual representation
blockGraphics.width = self.blockWidth;
// Center the block on its visible portion
self.x = newLeftEdge + newWidth / 2;
return true;
};
// Method to highlight block when it's a miss
self.highlightMiss = function () {
// Replace current graphics with red block
blockGraphics.destroy();
blockGraphics = self.attachAsset('gameOverBlock', {
anchorX: 0.5,
anchorY: 0.5,
width: self.blockWidth,
height: self.blockHeight
});
};
return self;
});
var StackedBlock = Container.expand(function () {
var self = Container.call(this);
// Properties
self.blockWidth = 400;
self.blockHeight = 100;
// Create block visual
var blockGraphics = self.attachAsset('stackBlock', {
anchorX: 0.5,
anchorY: 0.5,
width: self.blockWidth,
height: self.blockHeight
});
// Method to set the width
self.setWidth = function (width) {
self.blockWidth = width;
blockGraphics.width = width;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game state variables
var currentBlock;
var stackedBlocks = [];
var baseY = 2400;
var score = 0;
var gameActive = false;
var difficultyLevel = 1;
var lastBlockWidth = 400;
var lastBlockX = 2048 / 2;
// Create base platform
var base = game.addChild(LK.getAsset('base', {
anchorX: 0.5,
anchorY: 0.5
}));
base.x = 2048 / 2;
base.y = baseY;
base.width = 480;
// UI Elements
var scoreTxt = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var instructionTxt = new Text2('Tap to drop blocks and build your tower!', {
size: 60,
fill: 0xFFFFFF
});
instructionTxt.anchor.set(0.5, 0.5);
instructionTxt.x = 0;
instructionTxt.y = 300;
LK.gui.center.addChild(instructionTxt);
// Game logic methods
function startGame() {
// Reset game state
for (var i = 0; i < stackedBlocks.length; i++) {
stackedBlocks[i].destroy();
}
stackedBlocks = [];
score = 0;
difficultyLevel = 1;
baseY = 2400;
lastBlockWidth = 400;
lastBlockX = 2048 / 2;
gameActive = true;
// Update score display
scoreTxt.setText('0');
// Hide instruction text
instructionTxt.visible = false;
// Create first block
spawnNewBlock();
// Start background music
LK.playMusic('bgMusic');
}
function spawnNewBlock() {
currentBlock = new Block();
currentBlock.blockWidth = lastBlockWidth;
var blockGraphics = currentBlock.getChildAt(0);
blockGraphics.width = currentBlock.blockWidth;
// Randomly start from left or right
currentBlock.x = Math.random() > 0.5 ? 0 + currentBlock.blockWidth / 2 : 2048 - currentBlock.blockWidth / 2;
currentBlock.y = baseY - (stackedBlocks.length + 1) * 110;
currentBlock.movingDirection = currentBlock.x < 1024 ? 1 : -1;
// Increase speed based on difficulty level
currentBlock.speed = 5 + difficultyLevel;
game.addChild(currentBlock);
}
function dropBlock() {
if (!currentBlock || !gameActive) {
return;
}
// Stop the block from moving
currentBlock.stopMoving();
// Check if the block fits on the previous block
var successfulPlacement = true;
if (stackedBlocks.length > 0) {
successfulPlacement = currentBlock.trimToFit(lastBlockX, lastBlockWidth);
}
if (successfulPlacement && currentBlock.blockWidth > 0) {
// Play placement sound
LK.getSound('place').play();
// Update variables for next block
lastBlockWidth = currentBlock.blockWidth;
lastBlockX = currentBlock.x;
// Create a stacked version of the block
var stackedBlock = new StackedBlock();
stackedBlock.setWidth(lastBlockWidth);
stackedBlock.x = lastBlockX;
stackedBlock.y = currentBlock.y;
game.addChild(stackedBlock);
stackedBlocks.push(stackedBlock);
// Remove the moving block
currentBlock.destroy();
currentBlock = null;
// Update score
score += 10;
scoreTxt.setText(score.toString());
// Increase difficulty every 5 points
if (score % 5 === 0) {
difficultyLevel++;
}
// After a short delay, spawn a new block
LK.setTimeout(function () {
spawnNewBlock();
}, 300);
} else {
// Game over
gameActive = false;
// Play miss sound
LK.getSound('miss').play();
// Highlight the missed block in red
currentBlock.highlightMiss();
// Make the block fall off screen
tween(currentBlock, {
y: 3000,
rotation: Math.PI * 2
}, {
duration: 1500,
easing: tween.easeIn,
onFinish: function onFinish() {
// Check if this is a new high score
var highScore = storage.highScore || 0;
if (score > highScore) {
storage.highScore = score;
}
// Show game over screen after a short delay
LK.setTimeout(function () {
LK.showGameOver();
}, 500);
}
});
}
}
function moveCamera() {
// Only move camera if we have enough stacked blocks
if (stackedBlocks.length > 8) {
// Target Y for the topmost block
var targetY = 2732 / 2;
// Calculate how much to move everything down
var moveAmount = baseY - (stackedBlocks.length + 1) * 110 - targetY;
// Move everything down
base.y -= moveAmount;
for (var i = 0; i < stackedBlocks.length; i++) {
stackedBlocks[i].y -= moveAmount;
}
if (currentBlock) {
currentBlock.y -= moveAmount;
}
baseY -= moveAmount;
}
}
// Input handlers
game.down = function (x, y, obj) {
if (!gameActive) {
startGame();
} else {
dropBlock();
}
};
// Update method
game.update = function () {
if (currentBlock && gameActive) {
currentBlock.update();
}
// Move camera to follow the tower
moveCamera();
};
// Show instruction text at start
instructionTxt.visible = true;