/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { coins: 0, unlockedSkins: [], selectedSkin: 0 }); /**** * Classes ****/ // Ball class var Ball = Container.expand(function () { var self = Container.call(this); // Ball color index: 0=red, 1=blue, 2=green, 3=yellow self.colorIndex = 0; self.colors = ['red', 'blue', 'green', 'yellow']; self.asset = null; // Attach initial asset self.setColor = function (idx) { if (self.asset) { self.removeChild(self.asset); } self.colorIndex = idx; var colorName = self.colors[self.colorIndex]; self.asset = self.attachAsset('ball_' + colorName, { anchorX: 0.5, anchorY: 0.5 }); }; self.cycleColor = function () { var next = (self.colorIndex + 1) % self.colors.length; self.setColor(next); }; // For simple trail effect (MVP: just a quick flash) self.flash = function () { tween(self.asset, { alpha: 0.5 }, { duration: 60, easing: tween.linear, onFinish: function onFinish() { tween(self.asset, { alpha: 1 }, { duration: 80 }); } }); }; // Set initial color self.setColor(0); return self; }); // Coin class var Coin = Container.expand(function () { var self = Container.call(this); self.asset = self.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { // MVP: no animation }; return self; }); // Platform class var Platform = Container.expand(function () { var self = Container.call(this); // Color index: 0=red, 1=blue, 2=green, 3=yellow self.colorIndex = 0; self.colors = ['red', 'blue', 'green', 'yellow']; self.asset = null; // For moving platforms (future) self.vx = 0; self.setColor = function (idx) { if (self.asset) { self.removeChild(self.asset); } self.colorIndex = idx; var colorName = self.colors[self.colorIndex]; self.asset = self.attachAsset('platform_' + colorName, { anchorX: 0.5, anchorY: 0.5 }); }; // For MVP, platforms are static self.update = function () { // No horizontal movement; platforms remain centered }; // Set initial color self.setColor(0); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181c20 }); /**** * Game Code ****/ // --- Game variables --- // Ball colors (platforms and ball) // SFX // Music (stub, not played in MVP) var ball = null; var platforms = []; var coins = []; var score = 0; var coinCount = 0; var platformSpacing = 400; // vertical distance between platforms // platformSpeed is now calculated dynamically based on score var gravity = 1; // increases as score increases (slightly higher) var bounceVelocity = -31; // initial bounce velocity (slightly higher magnitude) var ballVy = 0; var isFalling = true; var currentPlatformIdx = 0; var lastTapTick = 0; var gameStarted = false; var dragNode = null; // not used, but required by guidelines var lastIntersecting = false; var canTap = true; // prevent double tap in one frame // GUI var scoreTxt = new Text2('0', { size: 120, fill: '#fff' }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var coinTxt = new Text2('0', { size: 80, fill: '#ffd700' }); coinTxt.anchor.set(1, 0); coinTxt.x = -40; coinTxt.y = 20; LK.gui.topRight.addChild(coinTxt); // --- Music Play/Pause and Volume Switch (Top Left) --- var musicPlaying = true; var musicVolume = 1; // 1 = max, 0 = muted // Play music at start LK.playMusic('fallingdown', { loop: true, fade: { start: 0, end: musicVolume, duration: 600 } }); // Play/Pause Button var playPauseBtn = new Text2('⏸', { size: 90, fill: '#fff' }); playPauseBtn.anchor.set(0, 0); playPauseBtn.x = 110; playPauseBtn.y = 10; // Volume Button var volumeBtn = new Text2('🔊', { size: 90, fill: '#fff' }); volumeBtn.anchor.set(0, 0); volumeBtn.x = 220; volumeBtn.y = 10; // Add to top left GUI (leave 100x100 px clear for menu) LK.gui.topLeft.addChild(playPauseBtn); LK.gui.topLeft.addChild(volumeBtn); // Play/Pause toggle playPauseBtn.down = function (x, y, obj) { if (musicPlaying) { LK.stopMusic(); playPauseBtn.setText('▶️'); musicPlaying = false; } else { LK.playMusic('fallingdown', { loop: true, fade: { start: 0, end: musicVolume, duration: 400 } }); playPauseBtn.setText('⏸'); musicPlaying = true; } }; // Volume toggle volumeBtn.down = function (x, y, obj) { if (musicVolume === 1) { musicVolume = 0; volumeBtn.setText('🔇'); if (musicPlaying) { LK.playMusic('fallingdown', { loop: true, fade: { start: 1, end: 0, duration: 300 } }); } } else { musicVolume = 1; volumeBtn.setText('🔊'); if (musicPlaying) { LK.playMusic('fallingdown', { loop: true, fade: { start: 0, end: 1, duration: 300 } }); } } }; // --- Helper functions --- function getRandomColorIdx() { // 0=red, 1=blue, 2=green, 3=yellow return Math.floor(Math.random() * 4); } function spawnPlatform(y, colorIdx) { var plat = new Platform(); plat.setColor(colorIdx); plat.x = 2048 / 2; plat.y = y; // No horizontal movement; platforms remain centered plat.vx = 0; platforms.push(plat); game.addChild(plat); return plat; } function spawnCoin(x, y) { var c = new Coin(); c.x = x; c.y = y - 60; coins.push(c); game.addChild(c); return c; } function resetGameVars() { score = 0; coinCount = 0; // platformSpeed is now calculated dynamically based on score gravity = 1.23; // increased initial gravity for even faster ball bounceVelocity = -43; // increased initial bounce velocity for even faster ball ballVy = 0; isFalling = true; currentPlatformIdx = 0; lastTapTick = 0; canTap = true; gameStarted = false; lastIntersecting = false; // Remove old platforms and coins for (var i = 0; i < platforms.length; ++i) { platforms[i].destroy(); } for (var i = 0; i < coins.length; ++i) { coins[i].destroy(); } platforms = []; coins = []; // Reset lastPlatformY to initial spawn value lastPlatformY = 1600; // Remove ball if (ball) { ball.destroy(); ball = null; } } // --- Game start --- function startGame() { resetGameVars(); // Add background image behind all gameplay elements var bg = LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732 }); game.addChild(bg); // Ball and Platforms: fill screen // Place first platform lower to give player time to react var y = 1600; // Lower on screen for first platform var firstPlatformColorIdx = getRandomColorIdx(); ball = new Ball(); ball.setColor(firstPlatformColorIdx); ball.x = 2048 / 2; ball.y = 600; game.addChild(ball); for (var i = 0; i < 8; ++i) { var colorIdx = i === 0 ? firstPlatformColorIdx : getRandomColorIdx(); var plat = spawnPlatform(y, colorIdx); lastPlatformY = plat.y; // Place a coin on every 3rd platform if (i > 0 && i % 3 === 0) { spawnCoin(plat.x, plat.y); } y += platformSpacing; } currentPlatformIdx = 0; isFalling = true; ballVy = 0; score = 0; coinCount = 0; scoreTxt.setText('0'); coinTxt.setText(storage.coins || 0); gameStarted = true; } // --- Input: tap to cycle color --- game.down = function (x, y, obj) { if (!gameStarted) { startGame(); return; } if (!canTap) { return; } canTap = false; if (ball) { ball.cycleColor(); ball.flash(); } lastTapTick = LK.ticks; }; game.up = function (x, y, obj) { canTap = true; }; // --- Main game loop --- game.update = function () { if (!gameStarted) { return; } // Ball physics if (isFalling) { ballVy += gravity; ball.y += ballVy; } // Move platforms upward to simulate ball falling // Platform upward speed: increases slightly at each difficulty level for smooth progression var moveUp = 0; if (score >= 0) { // Base speed (increased even more at first interval) moveUp = 6.2; // For every 7 points, increase speed by 0.27, up to a max var level = Math.floor(score / 7); moveUp += Math.min(level * 0.27, 4.5); // max speed increase is 4.5 (so max moveUp = 10.7) // Prevent platforms from moving up so fast that the ball can exit the top // If the ball is within 200px of the top, pause upward movement if (ball && ball.y < 200) { moveUp = 0; } } for (var i = 0; i < platforms.length; ++i) { platforms[i].y -= moveUp; platforms[i].update(); } for (var i = 0; i < coins.length; ++i) { coins[i].y -= moveUp; coins[i].update(); } // Remove platforms that go off top and immediately recycle/spawn new ones at the bottom to maintain infinite flow if (typeof lastPlatformY === "undefined") { var lastPlatformY = 1600; } // Remove platforms that go off top and robustly recycle to always maintain at least 3 platforms below the ball for (var i = platforms.length - 1; i >= 0; --i) { if (platforms[i].y < -100) { // Remove the platform from the game and array platforms[i].destroy(); platforms.splice(i, 1); } } // After removals, ensure at least 3 platforms exist, all spaced consistently below the lowest // Find the current lowest platform Y var lowestY = -Infinity; for (var j = 0; j < platforms.length; ++j) { if (platforms[j].y > lowestY) { lowestY = platforms[j].y; } } // If there are no platforms left, use the last known platform Y if (lowestY === -Infinity) { lowestY = lastPlatformY; } // Always keep at least 3 platforms while (platforms.length < 3) { var colorIdx = getRandomColorIdx(); var plat = spawnPlatform(lowestY + platformSpacing, colorIdx); lastPlatformY = plat.y; lowestY = plat.y; // Place a coin on every 3rd platform if ((score + platforms.length) % 3 === 0) { spawnCoin(plat.x, plat.y); } } for (var i = coins.length - 1; i >= 0; --i) { if (coins[i].y < -100) { coins[i].destroy(); coins.splice(i, 1); } } // (Platform count is now managed by recycling above. No need for extra always-ensure loop.) // Ball collision with platform (only check nearest platform) var hit = false; for (var i = 0; i < platforms.length; ++i) { var plat = platforms[i]; // Only check platforms below ball if (plat.y > ball.y && plat.y - ball.y < 120) { // Check horizontal overlap var dx = Math.abs(ball.x - plat.x); if (dx < plat.asset.width / 2 - 30) { // Check vertical overlap var dy = Math.abs(ball.y - plat.y); if (dy < 90) { // Ball is landing on platform hit = true; // Only trigger bounce if falling if (ballVy > 0) { // Color match? if (ball.colorIndex === plat.colorIndex) { // Success! ballVy = bounceVelocity; isFalling = true; score += 1; scoreTxt.setText(score); LK.setScore(score); LK.getSound('bounce').play(); // Flash ball ball.flash(); // Remove platform after bounce plat.destroy(); platforms.splice(i, 1); // Increase difficulty if (score === 3) { // After first 3 bounces, increase ball speed (slightly more) gravity += 0.18; bounceVelocity -= 3.5; } // Difficulty increases every 7 points, with smooth transitions and more levels if (score % 7 === 0) { // Level up: increase gravity and platform speed smoothly // Add more levels with different increments for smoothness if (score < 21) { gravity += 0.09; } else if (score < 35) { gravity += 0.08; } else if (score < 56) { gravity += 0.07; } else if (score < 84) { gravity += 0.06; } else { gravity += 0.05; } } } else { // Fail: wrong color LK.getSound('fail').play(); LK.effects.flashScreen(0xff0000, 600); LK.showGameOver(); return; } } } } } } // Ball falls off bottom if (ball.y > 2732 + 100) { LK.getSound('fail').play(); LK.effects.flashScreen(0xff0000, 600); LK.showGameOver(); return; } // Coin collection for (var i = coins.length - 1; i >= 0; --i) { var c = coins[i]; var dx = Math.abs(ball.x - c.x); var dy = Math.abs(ball.y - c.y); if (dx < 90 && dy < 90) { // Collect coin LK.getSound('coin').play(); c.destroy(); coins.splice(i, 1); coinCount += 1; storage.coins = (storage.coins || 0) + 1; coinTxt.setText(storage.coins); // Simple coin pop // (future: animate) } } }; // --- Game over / win handling is automatic by LK --- // --- Start screen: tap to start --- startGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
coins: 0,
unlockedSkins: [],
selectedSkin: 0
});
/****
* Classes
****/
// Ball class
var Ball = Container.expand(function () {
var self = Container.call(this);
// Ball color index: 0=red, 1=blue, 2=green, 3=yellow
self.colorIndex = 0;
self.colors = ['red', 'blue', 'green', 'yellow'];
self.asset = null;
// Attach initial asset
self.setColor = function (idx) {
if (self.asset) {
self.removeChild(self.asset);
}
self.colorIndex = idx;
var colorName = self.colors[self.colorIndex];
self.asset = self.attachAsset('ball_' + colorName, {
anchorX: 0.5,
anchorY: 0.5
});
};
self.cycleColor = function () {
var next = (self.colorIndex + 1) % self.colors.length;
self.setColor(next);
};
// For simple trail effect (MVP: just a quick flash)
self.flash = function () {
tween(self.asset, {
alpha: 0.5
}, {
duration: 60,
easing: tween.linear,
onFinish: function onFinish() {
tween(self.asset, {
alpha: 1
}, {
duration: 80
});
}
});
};
// Set initial color
self.setColor(0);
return self;
});
// Coin class
var Coin = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
// MVP: no animation
};
return self;
});
// Platform class
var Platform = Container.expand(function () {
var self = Container.call(this);
// Color index: 0=red, 1=blue, 2=green, 3=yellow
self.colorIndex = 0;
self.colors = ['red', 'blue', 'green', 'yellow'];
self.asset = null;
// For moving platforms (future)
self.vx = 0;
self.setColor = function (idx) {
if (self.asset) {
self.removeChild(self.asset);
}
self.colorIndex = idx;
var colorName = self.colors[self.colorIndex];
self.asset = self.attachAsset('platform_' + colorName, {
anchorX: 0.5,
anchorY: 0.5
});
};
// For MVP, platforms are static
self.update = function () {
// No horizontal movement; platforms remain centered
};
// Set initial color
self.setColor(0);
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181c20
});
/****
* Game Code
****/
// --- Game variables ---
// Ball colors (platforms and ball)
// SFX
// Music (stub, not played in MVP)
var ball = null;
var platforms = [];
var coins = [];
var score = 0;
var coinCount = 0;
var platformSpacing = 400; // vertical distance between platforms
// platformSpeed is now calculated dynamically based on score
var gravity = 1; // increases as score increases (slightly higher)
var bounceVelocity = -31; // initial bounce velocity (slightly higher magnitude)
var ballVy = 0;
var isFalling = true;
var currentPlatformIdx = 0;
var lastTapTick = 0;
var gameStarted = false;
var dragNode = null; // not used, but required by guidelines
var lastIntersecting = false;
var canTap = true; // prevent double tap in one frame
// GUI
var scoreTxt = new Text2('0', {
size: 120,
fill: '#fff'
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var coinTxt = new Text2('0', {
size: 80,
fill: '#ffd700'
});
coinTxt.anchor.set(1, 0);
coinTxt.x = -40;
coinTxt.y = 20;
LK.gui.topRight.addChild(coinTxt);
// --- Music Play/Pause and Volume Switch (Top Left) ---
var musicPlaying = true;
var musicVolume = 1; // 1 = max, 0 = muted
// Play music at start
LK.playMusic('fallingdown', {
loop: true,
fade: {
start: 0,
end: musicVolume,
duration: 600
}
});
// Play/Pause Button
var playPauseBtn = new Text2('⏸', {
size: 90,
fill: '#fff'
});
playPauseBtn.anchor.set(0, 0);
playPauseBtn.x = 110;
playPauseBtn.y = 10;
// Volume Button
var volumeBtn = new Text2('🔊', {
size: 90,
fill: '#fff'
});
volumeBtn.anchor.set(0, 0);
volumeBtn.x = 220;
volumeBtn.y = 10;
// Add to top left GUI (leave 100x100 px clear for menu)
LK.gui.topLeft.addChild(playPauseBtn);
LK.gui.topLeft.addChild(volumeBtn);
// Play/Pause toggle
playPauseBtn.down = function (x, y, obj) {
if (musicPlaying) {
LK.stopMusic();
playPauseBtn.setText('▶️');
musicPlaying = false;
} else {
LK.playMusic('fallingdown', {
loop: true,
fade: {
start: 0,
end: musicVolume,
duration: 400
}
});
playPauseBtn.setText('⏸');
musicPlaying = true;
}
};
// Volume toggle
volumeBtn.down = function (x, y, obj) {
if (musicVolume === 1) {
musicVolume = 0;
volumeBtn.setText('🔇');
if (musicPlaying) {
LK.playMusic('fallingdown', {
loop: true,
fade: {
start: 1,
end: 0,
duration: 300
}
});
}
} else {
musicVolume = 1;
volumeBtn.setText('🔊');
if (musicPlaying) {
LK.playMusic('fallingdown', {
loop: true,
fade: {
start: 0,
end: 1,
duration: 300
}
});
}
}
};
// --- Helper functions ---
function getRandomColorIdx() {
// 0=red, 1=blue, 2=green, 3=yellow
return Math.floor(Math.random() * 4);
}
function spawnPlatform(y, colorIdx) {
var plat = new Platform();
plat.setColor(colorIdx);
plat.x = 2048 / 2;
plat.y = y;
// No horizontal movement; platforms remain centered
plat.vx = 0;
platforms.push(plat);
game.addChild(plat);
return plat;
}
function spawnCoin(x, y) {
var c = new Coin();
c.x = x;
c.y = y - 60;
coins.push(c);
game.addChild(c);
return c;
}
function resetGameVars() {
score = 0;
coinCount = 0;
// platformSpeed is now calculated dynamically based on score
gravity = 1.23; // increased initial gravity for even faster ball
bounceVelocity = -43; // increased initial bounce velocity for even faster ball
ballVy = 0;
isFalling = true;
currentPlatformIdx = 0;
lastTapTick = 0;
canTap = true;
gameStarted = false;
lastIntersecting = false;
// Remove old platforms and coins
for (var i = 0; i < platforms.length; ++i) {
platforms[i].destroy();
}
for (var i = 0; i < coins.length; ++i) {
coins[i].destroy();
}
platforms = [];
coins = [];
// Reset lastPlatformY to initial spawn value
lastPlatformY = 1600;
// Remove ball
if (ball) {
ball.destroy();
ball = null;
}
}
// --- Game start ---
function startGame() {
resetGameVars();
// Add background image behind all gameplay elements
var bg = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
game.addChild(bg);
// Ball and Platforms: fill screen
// Place first platform lower to give player time to react
var y = 1600; // Lower on screen for first platform
var firstPlatformColorIdx = getRandomColorIdx();
ball = new Ball();
ball.setColor(firstPlatformColorIdx);
ball.x = 2048 / 2;
ball.y = 600;
game.addChild(ball);
for (var i = 0; i < 8; ++i) {
var colorIdx = i === 0 ? firstPlatformColorIdx : getRandomColorIdx();
var plat = spawnPlatform(y, colorIdx);
lastPlatformY = plat.y;
// Place a coin on every 3rd platform
if (i > 0 && i % 3 === 0) {
spawnCoin(plat.x, plat.y);
}
y += platformSpacing;
}
currentPlatformIdx = 0;
isFalling = true;
ballVy = 0;
score = 0;
coinCount = 0;
scoreTxt.setText('0');
coinTxt.setText(storage.coins || 0);
gameStarted = true;
}
// --- Input: tap to cycle color ---
game.down = function (x, y, obj) {
if (!gameStarted) {
startGame();
return;
}
if (!canTap) {
return;
}
canTap = false;
if (ball) {
ball.cycleColor();
ball.flash();
}
lastTapTick = LK.ticks;
};
game.up = function (x, y, obj) {
canTap = true;
};
// --- Main game loop ---
game.update = function () {
if (!gameStarted) {
return;
}
// Ball physics
if (isFalling) {
ballVy += gravity;
ball.y += ballVy;
}
// Move platforms upward to simulate ball falling
// Platform upward speed: increases slightly at each difficulty level for smooth progression
var moveUp = 0;
if (score >= 0) {
// Base speed (increased even more at first interval)
moveUp = 6.2;
// For every 7 points, increase speed by 0.27, up to a max
var level = Math.floor(score / 7);
moveUp += Math.min(level * 0.27, 4.5); // max speed increase is 4.5 (so max moveUp = 10.7)
// Prevent platforms from moving up so fast that the ball can exit the top
// If the ball is within 200px of the top, pause upward movement
if (ball && ball.y < 200) {
moveUp = 0;
}
}
for (var i = 0; i < platforms.length; ++i) {
platforms[i].y -= moveUp;
platforms[i].update();
}
for (var i = 0; i < coins.length; ++i) {
coins[i].y -= moveUp;
coins[i].update();
}
// Remove platforms that go off top and immediately recycle/spawn new ones at the bottom to maintain infinite flow
if (typeof lastPlatformY === "undefined") {
var lastPlatformY = 1600;
}
// Remove platforms that go off top and robustly recycle to always maintain at least 3 platforms below the ball
for (var i = platforms.length - 1; i >= 0; --i) {
if (platforms[i].y < -100) {
// Remove the platform from the game and array
platforms[i].destroy();
platforms.splice(i, 1);
}
}
// After removals, ensure at least 3 platforms exist, all spaced consistently below the lowest
// Find the current lowest platform Y
var lowestY = -Infinity;
for (var j = 0; j < platforms.length; ++j) {
if (platforms[j].y > lowestY) {
lowestY = platforms[j].y;
}
}
// If there are no platforms left, use the last known platform Y
if (lowestY === -Infinity) {
lowestY = lastPlatformY;
}
// Always keep at least 3 platforms
while (platforms.length < 3) {
var colorIdx = getRandomColorIdx();
var plat = spawnPlatform(lowestY + platformSpacing, colorIdx);
lastPlatformY = plat.y;
lowestY = plat.y;
// Place a coin on every 3rd platform
if ((score + platforms.length) % 3 === 0) {
spawnCoin(plat.x, plat.y);
}
}
for (var i = coins.length - 1; i >= 0; --i) {
if (coins[i].y < -100) {
coins[i].destroy();
coins.splice(i, 1);
}
}
// (Platform count is now managed by recycling above. No need for extra always-ensure loop.)
// Ball collision with platform (only check nearest platform)
var hit = false;
for (var i = 0; i < platforms.length; ++i) {
var plat = platforms[i];
// Only check platforms below ball
if (plat.y > ball.y && plat.y - ball.y < 120) {
// Check horizontal overlap
var dx = Math.abs(ball.x - plat.x);
if (dx < plat.asset.width / 2 - 30) {
// Check vertical overlap
var dy = Math.abs(ball.y - plat.y);
if (dy < 90) {
// Ball is landing on platform
hit = true;
// Only trigger bounce if falling
if (ballVy > 0) {
// Color match?
if (ball.colorIndex === plat.colorIndex) {
// Success!
ballVy = bounceVelocity;
isFalling = true;
score += 1;
scoreTxt.setText(score);
LK.setScore(score);
LK.getSound('bounce').play();
// Flash ball
ball.flash();
// Remove platform after bounce
plat.destroy();
platforms.splice(i, 1);
// Increase difficulty
if (score === 3) {
// After first 3 bounces, increase ball speed (slightly more)
gravity += 0.18;
bounceVelocity -= 3.5;
}
// Difficulty increases every 7 points, with smooth transitions and more levels
if (score % 7 === 0) {
// Level up: increase gravity and platform speed smoothly
// Add more levels with different increments for smoothness
if (score < 21) {
gravity += 0.09;
} else if (score < 35) {
gravity += 0.08;
} else if (score < 56) {
gravity += 0.07;
} else if (score < 84) {
gravity += 0.06;
} else {
gravity += 0.05;
}
}
} else {
// Fail: wrong color
LK.getSound('fail').play();
LK.effects.flashScreen(0xff0000, 600);
LK.showGameOver();
return;
}
}
}
}
}
}
// Ball falls off bottom
if (ball.y > 2732 + 100) {
LK.getSound('fail').play();
LK.effects.flashScreen(0xff0000, 600);
LK.showGameOver();
return;
}
// Coin collection
for (var i = coins.length - 1; i >= 0; --i) {
var c = coins[i];
var dx = Math.abs(ball.x - c.x);
var dy = Math.abs(ball.y - c.y);
if (dx < 90 && dy < 90) {
// Collect coin
LK.getSound('coin').play();
c.destroy();
coins.splice(i, 1);
coinCount += 1;
storage.coins = (storage.coins || 0) + 1;
coinTxt.setText(storage.coins);
// Simple coin pop
// (future: animate)
}
}
};
// --- Game over / win handling is automatic by LK ---
// --- Start screen: tap to start ---
startGame();
blue ball. In-Game asset. 2d. High contrast. No shadows
green ball. In-Game asset. 2d. High contrast. No shadows
red ball. In-Game asset. 2d. High contrast. No shadows
yellow ball. In-Game asset. 2d. High contrast. No shadows
coin. In-Game asset. 2d. High contrast. No shadows
blue platform. In-Game asset. 2d. High contrast. No shadows
green platform. In-Game asset. 2d. High contrast. No shadows
red platform. In-Game asset. 2d. High contrast. No shadows
yellow platform. In-Game asset. 2d. High contrast. No shadows
Create a vertical-scrolling, futuristic game background for a rhythm-based mobile game. The style should be vibrant and glowing, with a deep gradient from dark purple to electric blue. Include subtle abstract shapes like floating geometric particles, soft energy lines, and a faint digital grid. The mood should feel like you're inside a pulsing rhythm tunnel or neon cyber tower. No text, no characters — just atmospheric depth and motion-friendly layers. Must loop seamlessly for vertical scrolling. Style: sleek, minimal, energetic.. In-Game asset. 2d. High contrast. No shadows