/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// CakeLayer: Represents a single cake layer
var CakeLayer = Container.expand(function () {
var self = Container.call(this);
// Attach a box shape as the cake layer (color: light pink)
var cakeAsset = self.attachAsset('cakeLayer', {
anchorX: 0.5,
anchorY: 0.5
});
// Store reference for resizing
self.cakeAsset = cakeAsset;
// Layer width and height (set on creation)
self.layerWidth = 600;
self.layerHeight = 80;
// Set initial size
cakeAsset.width = self.layerWidth;
cakeAsset.height = self.layerHeight;
// Set color (light pink)
cakeAsset.color = 0xffb6c1;
// Set position helpers
self.setSize = function (w, h) {
self.layerWidth = w;
self.layerHeight = h;
cakeAsset.width = w;
cakeAsset.height = h;
};
self.setColor = function (color) {
cakeAsset.color = color;
};
// Animate drop to target y
self.animateDrop = function (targetY, onFinish) {
tween(self, {
y: targetY
}, {
duration: 180,
easing: tween.cubicOut,
onFinish: onFinish
});
};
// Animate slice (shrink width and move x to align)
self.animateSlice = function (newWidth, newX, onFinish) {
tween(self.cakeAsset, {
width: newWidth
}, {
duration: 120,
easing: tween.cubicIn
});
tween(self, {
x: newX
}, {
duration: 120,
easing: tween.cubicIn,
onFinish: onFinish
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xf7e7e1 // Soft cake background
});
/****
* Game Code
****/
// Tween plugin for smooth layer drop and slice animations
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var BASE_LAYER_WIDTH = 600;
var BASE_LAYER_HEIGHT = 80;
var LAYER_GAP = 8; // Gap between layers
var MOVE_SPEED = 10; // px per frame
var MIN_LAYER_WIDTH = 80; // Minimum width before game over
// Colors for cake layers (cycle through for fun)
var CAKE_COLORS = [0xffb6c1, 0xfad6a5, 0xf7cac9, 0xf9e79f, 0xb5ead7, 0xa0c4ff, 0xffd6e0];
// Game state
var layers = []; // All stacked layers
var movingLayer = null; // The currently moving layer
var movingDir = 1; // 1: right, -1: left
var canDrop = true; // Prevent double tap
var score = 0;
var scoreTxt = null;
var lastColorIdx = 0;
// Center X for stack
var stackCenterX = GAME_WIDTH / 2;
// Y position for the base layer (bottom of the screen, with margin)
var baseY = GAME_HEIGHT - 300;
// GUI: Score display
scoreTxt = new Text2('0', {
size: 120,
fill: 0xD83318
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Helper: Get next color
function getNextColor() {
lastColorIdx = (lastColorIdx + 1) % CAKE_COLORS.length;
return CAKE_COLORS[lastColorIdx];
}
// Helper: Add a new layer to the stack (at y)
function addLayer(width, height, y, color) {
var layer = new CakeLayer();
layer.setSize(width, height);
layer.setColor(color);
layer.x = stackCenterX;
layer.y = y;
game.addChild(layer);
layers.push(layer);
return layer;
}
// Helper: Start a new moving layer above the stack
function spawnMovingLayer() {
var prevLayer = layers[layers.length - 1];
var newWidth = prevLayer.layerWidth;
var newHeight = prevLayer.layerHeight;
var color = getNextColor();
var layer = new CakeLayer();
layer.setSize(newWidth, newHeight);
layer.setColor(color);
// Start off-screen (left or right)
var startX = movingDir === 1 ? -newWidth / 2 : GAME_WIDTH + newWidth / 2;
layer.x = startX;
layer.y = prevLayer.y - newHeight - LAYER_GAP;
game.addChild(layer);
movingLayer = layer;
}
// Helper: End game
function endGame() {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
}
// Helper: Update score
function updateScore(val) {
score = val;
scoreTxt.setText(score);
LK.setScore(score);
}
// Initialize first (base) layer
function startGame() {
// Clear previous layers
for (var i = 0; i < layers.length; i++) {
layers[i].destroy();
}
layers = [];
movingLayer = null;
movingDir = 1;
canDrop = true;
score = 0;
updateScore(0);
lastColorIdx = 0;
// Add base layer
var baseLayer = addLayer(BASE_LAYER_WIDTH, BASE_LAYER_HEIGHT, baseY, CAKE_COLORS[0]);
// Spawn first moving layer
spawnMovingLayer();
}
// Drop the moving layer and handle alignment/slicing
function dropLayer() {
if (!movingLayer || !canDrop) return;
canDrop = false;
// Find previous layer
var prevLayer = layers[layers.length - 1];
// Target y for drop
var targetY = prevLayer.y - movingLayer.layerHeight - LAYER_GAP;
// Animate drop
movingLayer.animateDrop(targetY, function () {
// Calculate overlap
var leftA = movingLayer.x - movingLayer.layerWidth / 2;
var rightA = movingLayer.x + movingLayer.layerWidth / 2;
var leftB = prevLayer.x - prevLayer.layerWidth / 2;
var rightB = prevLayer.x + prevLayer.layerWidth / 2;
var overlapLeft = Math.max(leftA, leftB);
var overlapRight = Math.min(rightA, rightB);
var overlapWidth = overlapRight - overlapLeft;
if (overlapWidth <= 0 || overlapWidth < MIN_LAYER_WIDTH) {
// No overlap or too small: Game over
tween(movingLayer, {
alpha: 0
}, {
duration: 200
});
LK.setTimeout(endGame, 350);
return;
}
// Slice off misaligned parts
var newX = (overlapLeft + overlapRight) / 2;
movingLayer.animateSlice(overlapWidth, newX, function () {
// Finalize layer
movingLayer.setSize(overlapWidth, movingLayer.layerHeight);
movingLayer.x = newX;
layers.push(movingLayer);
movingLayer = null;
// Update score
updateScore(score + 1);
// Next layer moves in opposite direction
movingDir *= -1;
// Spawn next moving layer
spawnMovingLayer();
canDrop = true;
});
});
}
// Handle tap/click to drop
game.down = function (x, y, obj) {
if (!canDrop) return;
dropLayer();
};
// Main update loop
game.update = function () {
// Move the moving layer
if (movingLayer) {
// 0..1 between two colors
// Helper to interpolate between two colors
var lerpColor = function lerpColor(a, b, t) {
var ar = a >> 16 & 0xff,
ag = a >> 8 & 0xff,
ab = a & 0xff;
var br = b >> 16 & 0xff,
bg = b >> 8 & 0xff,
bb = b & 0xff;
var rr = Math.round(ar + (br - ar) * t);
var rg = Math.round(ag + (bg - ag) * t);
var rb = Math.round(ab + (bb - ab) * t);
return rr << 16 | rg << 8 | rb;
};
// Speed increases as score increases, capped at 32px/frame
var speedMultiplier = 1 + score * 0.07;
if (speedMultiplier > 3.2) speedMultiplier = 3.2;
var speed = MOVE_SPEED * speedMultiplier * movingDir;
movingLayer.x += speed;
// Bounce off screen edges
var halfW = movingLayer.layerWidth / 2;
if (movingDir === 1 && movingLayer.x + halfW >= GAME_WIDTH) {
movingLayer.x = GAME_WIDTH - halfW;
movingDir = -1;
} else if (movingDir === -1 && movingLayer.x - halfW <= 0) {
movingLayer.x = halfW;
movingDir = 1;
}
// --- Continuously change the color of the moving cake layer ---
// We'll cycle through the CAKE_COLORS array smoothly using time
// Use LK.ticks to get a time-based value
var colorIdx = Math.floor(LK.ticks / 10 % CAKE_COLORS.length);
var nextColorIdx = (colorIdx + 1) % CAKE_COLORS.length;
var t = LK.ticks / 10 % 1;
var colorA = CAKE_COLORS[colorIdx];
var colorB = CAKE_COLORS[nextColorIdx];
var lerpedColor = lerpColor(colorA, colorB, t);
movingLayer.setColor(lerpedColor);
}
};
// Start the game
startGame(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// CakeLayer: Represents a single cake layer
var CakeLayer = Container.expand(function () {
var self = Container.call(this);
// Attach a box shape as the cake layer (color: light pink)
var cakeAsset = self.attachAsset('cakeLayer', {
anchorX: 0.5,
anchorY: 0.5
});
// Store reference for resizing
self.cakeAsset = cakeAsset;
// Layer width and height (set on creation)
self.layerWidth = 600;
self.layerHeight = 80;
// Set initial size
cakeAsset.width = self.layerWidth;
cakeAsset.height = self.layerHeight;
// Set color (light pink)
cakeAsset.color = 0xffb6c1;
// Set position helpers
self.setSize = function (w, h) {
self.layerWidth = w;
self.layerHeight = h;
cakeAsset.width = w;
cakeAsset.height = h;
};
self.setColor = function (color) {
cakeAsset.color = color;
};
// Animate drop to target y
self.animateDrop = function (targetY, onFinish) {
tween(self, {
y: targetY
}, {
duration: 180,
easing: tween.cubicOut,
onFinish: onFinish
});
};
// Animate slice (shrink width and move x to align)
self.animateSlice = function (newWidth, newX, onFinish) {
tween(self.cakeAsset, {
width: newWidth
}, {
duration: 120,
easing: tween.cubicIn
});
tween(self, {
x: newX
}, {
duration: 120,
easing: tween.cubicIn,
onFinish: onFinish
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xf7e7e1 // Soft cake background
});
/****
* Game Code
****/
// Tween plugin for smooth layer drop and slice animations
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var BASE_LAYER_WIDTH = 600;
var BASE_LAYER_HEIGHT = 80;
var LAYER_GAP = 8; // Gap between layers
var MOVE_SPEED = 10; // px per frame
var MIN_LAYER_WIDTH = 80; // Minimum width before game over
// Colors for cake layers (cycle through for fun)
var CAKE_COLORS = [0xffb6c1, 0xfad6a5, 0xf7cac9, 0xf9e79f, 0xb5ead7, 0xa0c4ff, 0xffd6e0];
// Game state
var layers = []; // All stacked layers
var movingLayer = null; // The currently moving layer
var movingDir = 1; // 1: right, -1: left
var canDrop = true; // Prevent double tap
var score = 0;
var scoreTxt = null;
var lastColorIdx = 0;
// Center X for stack
var stackCenterX = GAME_WIDTH / 2;
// Y position for the base layer (bottom of the screen, with margin)
var baseY = GAME_HEIGHT - 300;
// GUI: Score display
scoreTxt = new Text2('0', {
size: 120,
fill: 0xD83318
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Helper: Get next color
function getNextColor() {
lastColorIdx = (lastColorIdx + 1) % CAKE_COLORS.length;
return CAKE_COLORS[lastColorIdx];
}
// Helper: Add a new layer to the stack (at y)
function addLayer(width, height, y, color) {
var layer = new CakeLayer();
layer.setSize(width, height);
layer.setColor(color);
layer.x = stackCenterX;
layer.y = y;
game.addChild(layer);
layers.push(layer);
return layer;
}
// Helper: Start a new moving layer above the stack
function spawnMovingLayer() {
var prevLayer = layers[layers.length - 1];
var newWidth = prevLayer.layerWidth;
var newHeight = prevLayer.layerHeight;
var color = getNextColor();
var layer = new CakeLayer();
layer.setSize(newWidth, newHeight);
layer.setColor(color);
// Start off-screen (left or right)
var startX = movingDir === 1 ? -newWidth / 2 : GAME_WIDTH + newWidth / 2;
layer.x = startX;
layer.y = prevLayer.y - newHeight - LAYER_GAP;
game.addChild(layer);
movingLayer = layer;
}
// Helper: End game
function endGame() {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
}
// Helper: Update score
function updateScore(val) {
score = val;
scoreTxt.setText(score);
LK.setScore(score);
}
// Initialize first (base) layer
function startGame() {
// Clear previous layers
for (var i = 0; i < layers.length; i++) {
layers[i].destroy();
}
layers = [];
movingLayer = null;
movingDir = 1;
canDrop = true;
score = 0;
updateScore(0);
lastColorIdx = 0;
// Add base layer
var baseLayer = addLayer(BASE_LAYER_WIDTH, BASE_LAYER_HEIGHT, baseY, CAKE_COLORS[0]);
// Spawn first moving layer
spawnMovingLayer();
}
// Drop the moving layer and handle alignment/slicing
function dropLayer() {
if (!movingLayer || !canDrop) return;
canDrop = false;
// Find previous layer
var prevLayer = layers[layers.length - 1];
// Target y for drop
var targetY = prevLayer.y - movingLayer.layerHeight - LAYER_GAP;
// Animate drop
movingLayer.animateDrop(targetY, function () {
// Calculate overlap
var leftA = movingLayer.x - movingLayer.layerWidth / 2;
var rightA = movingLayer.x + movingLayer.layerWidth / 2;
var leftB = prevLayer.x - prevLayer.layerWidth / 2;
var rightB = prevLayer.x + prevLayer.layerWidth / 2;
var overlapLeft = Math.max(leftA, leftB);
var overlapRight = Math.min(rightA, rightB);
var overlapWidth = overlapRight - overlapLeft;
if (overlapWidth <= 0 || overlapWidth < MIN_LAYER_WIDTH) {
// No overlap or too small: Game over
tween(movingLayer, {
alpha: 0
}, {
duration: 200
});
LK.setTimeout(endGame, 350);
return;
}
// Slice off misaligned parts
var newX = (overlapLeft + overlapRight) / 2;
movingLayer.animateSlice(overlapWidth, newX, function () {
// Finalize layer
movingLayer.setSize(overlapWidth, movingLayer.layerHeight);
movingLayer.x = newX;
layers.push(movingLayer);
movingLayer = null;
// Update score
updateScore(score + 1);
// Next layer moves in opposite direction
movingDir *= -1;
// Spawn next moving layer
spawnMovingLayer();
canDrop = true;
});
});
}
// Handle tap/click to drop
game.down = function (x, y, obj) {
if (!canDrop) return;
dropLayer();
};
// Main update loop
game.update = function () {
// Move the moving layer
if (movingLayer) {
// 0..1 between two colors
// Helper to interpolate between two colors
var lerpColor = function lerpColor(a, b, t) {
var ar = a >> 16 & 0xff,
ag = a >> 8 & 0xff,
ab = a & 0xff;
var br = b >> 16 & 0xff,
bg = b >> 8 & 0xff,
bb = b & 0xff;
var rr = Math.round(ar + (br - ar) * t);
var rg = Math.round(ag + (bg - ag) * t);
var rb = Math.round(ab + (bb - ab) * t);
return rr << 16 | rg << 8 | rb;
};
// Speed increases as score increases, capped at 32px/frame
var speedMultiplier = 1 + score * 0.07;
if (speedMultiplier > 3.2) speedMultiplier = 3.2;
var speed = MOVE_SPEED * speedMultiplier * movingDir;
movingLayer.x += speed;
// Bounce off screen edges
var halfW = movingLayer.layerWidth / 2;
if (movingDir === 1 && movingLayer.x + halfW >= GAME_WIDTH) {
movingLayer.x = GAME_WIDTH - halfW;
movingDir = -1;
} else if (movingDir === -1 && movingLayer.x - halfW <= 0) {
movingLayer.x = halfW;
movingDir = 1;
}
// --- Continuously change the color of the moving cake layer ---
// We'll cycle through the CAKE_COLORS array smoothly using time
// Use LK.ticks to get a time-based value
var colorIdx = Math.floor(LK.ticks / 10 % CAKE_COLORS.length);
var nextColorIdx = (colorIdx + 1) % CAKE_COLORS.length;
var t = LK.ticks / 10 % 1;
var colorA = CAKE_COLORS[colorIdx];
var colorB = CAKE_COLORS[nextColorIdx];
var lerpedColor = lerpColor(colorA, colorB, t);
movingLayer.setColor(lerpedColor);
}
};
// Start the game
startGame();