User prompt
platforms should start from the bottom
Code edit (1 edits merged)
Please save this source code
User prompt
Color Bounce
Initial prompt
Build a complete, mobile-friendly rhythm-reaction game called **Color Bounce** with the following specifications: 🎮 Core Mechanics: 1. A ball automatically bounces downward onto colored platforms. 2. Each platform has one of 4 colors: red, blue, yellow and green. 3. The player must tap to **cycle the ball's color** in the order: red → blue → yellow → green → red... 4. When the ball lands on a platform: - ✅ If the color matches: it bounces again and the game continues. - ❌ If the color doesn't match: the game ends. 5. The ball falls faster as the game progresses — **increase tempo over time.** 🎯 Controls: - Single-tap input anywhere on screen = cycle ball color to next one. 🧠 Difficulty Progression: - Start with 4 colored platforms, slow tempo. - Every 10 bounces, increase fall speed slightly. - After 20 bounces, introduce *moving* platforms (horizontal slide). - After 40 bounces, introduce random gaps (platform missing). - Cap difficulty at 80 bounces for now. 🌈 Visual & Audio Feedback: - Each color has a **distinct glow and trail**. - Add a **particle burst** on successful bounce (small confetti or glow). - Add a **shake + screen flash** on failure. - Add SFX: - Bounce success = pitch-matched ping - Color switch = soft click - Fail = low thud + screen flash 🏆 Score & Game Flow: - Show **score as total successful bounces**. - At game over, show: - Final score - Best score - Retry button ✨ Cosmetic System: - Add **coin pickup items** that appear randomly on platforms (10% chance). - Player earns coins to unlock: - Ball skins (change ball appearance) - Trail styles (fire, smoke, sparkles) - Background themes (night, neon, pixel) - Add a basic **shop UI** with 6 total unlockable items (2 per category).
/**** * 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 () { if (self.vx !== 0) { self.x += self.vx; // Bounce off edges if (self.x < self.asset.width / 2) { self.x = self.asset.width / 2; self.vx *= -1; } if (self.x > 2048 - self.asset.width / 2) { self.x = 2048 - self.asset.width / 2; self.vx *= -1; } } }; // Set initial color self.setColor(0); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181c20 }); /**** * Game Code ****/ // Music (stub, not played in MVP) // SFX // Ball colors (platforms and ball) // --- Game variables --- var ball = null; var platforms = []; var coins = []; var score = 0; var coinCount = 0; var platformSpacing = 340; // vertical distance between platforms var platformSpeed = 12; // initial fall speed var gravity = 1.1; // increases as score increases var bounceVelocity = -38; // initial bounce velocity 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); // --- 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; // MVP: no moving platforms 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 = 12; gravity = 1.1; bounceVelocity = -38; 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 = []; // Remove ball if (ball) { ball.destroy(); ball = null; } } // --- Game start --- function startGame() { resetGameVars(); // Ball ball = new Ball(); ball.x = 2048 / 2; ball.y = 600; game.addChild(ball); // Platforms: fill screen var y = 1000; for (var i = 0; i < 8; ++i) { var colorIdx = getRandomColorIdx(); var plat = spawnPlatform(y, colorIdx); // 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 var moveUp = platformSpeed; 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/coins that go off top for (var i = platforms.length - 1; i >= 0; --i) { if (platforms[i].y < -100) { platforms[i].destroy(); platforms.splice(i, 1); } } for (var i = coins.length - 1; i >= 0; --i) { if (coins[i].y < -100) { coins[i].destroy(); coins.splice(i, 1); } } // Spawn new platforms as needed var lastPlat = platforms[platforms.length - 1]; if (lastPlat && lastPlat.y < 2732 - platformSpacing) { var colorIdx = getRandomColorIdx(); var plat = spawnPlatform(lastPlat.y + platformSpacing, colorIdx); // Place a coin on every 3rd platform if ((score + platforms.length) % 3 === 0) { spawnCoin(plat.x, plat.y); } } // 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(); // Increase difficulty if (score % 10 === 0) { platformSpeed += 1.5; gravity += 0.12; } } 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();
===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,359 @@
-/****
+/****
+* 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 () {
+ if (self.vx !== 0) {
+ self.x += self.vx;
+ // Bounce off edges
+ if (self.x < self.asset.width / 2) {
+ self.x = self.asset.width / 2;
+ self.vx *= -1;
+ }
+ if (self.x > 2048 - self.asset.width / 2) {
+ self.x = 2048 - self.asset.width / 2;
+ self.vx *= -1;
+ }
+ }
+ };
+ // Set initial color
+ self.setColor(0);
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0x181c20
+});
+
+/****
+* Game Code
+****/
+// Music (stub, not played in MVP)
+// SFX
+// Ball colors (platforms and ball)
+// --- Game variables ---
+var ball = null;
+var platforms = [];
+var coins = [];
+var score = 0;
+var coinCount = 0;
+var platformSpacing = 340; // vertical distance between platforms
+var platformSpeed = 12; // initial fall speed
+var gravity = 1.1; // increases as score increases
+var bounceVelocity = -38; // initial bounce velocity
+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);
+// --- 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;
+ // MVP: no moving platforms
+ 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 = 12;
+ gravity = 1.1;
+ bounceVelocity = -38;
+ 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 = [];
+ // Remove ball
+ if (ball) {
+ ball.destroy();
+ ball = null;
+ }
+}
+// --- Game start ---
+function startGame() {
+ resetGameVars();
+ // Ball
+ ball = new Ball();
+ ball.x = 2048 / 2;
+ ball.y = 600;
+ game.addChild(ball);
+ // Platforms: fill screen
+ var y = 1000;
+ for (var i = 0; i < 8; ++i) {
+ var colorIdx = getRandomColorIdx();
+ var plat = spawnPlatform(y, colorIdx);
+ // 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
+ var moveUp = platformSpeed;
+ 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/coins that go off top
+ for (var i = platforms.length - 1; i >= 0; --i) {
+ if (platforms[i].y < -100) {
+ platforms[i].destroy();
+ platforms.splice(i, 1);
+ }
+ }
+ for (var i = coins.length - 1; i >= 0; --i) {
+ if (coins[i].y < -100) {
+ coins[i].destroy();
+ coins.splice(i, 1);
+ }
+ }
+ // Spawn new platforms as needed
+ var lastPlat = platforms[platforms.length - 1];
+ if (lastPlat && lastPlat.y < 2732 - platformSpacing) {
+ var colorIdx = getRandomColorIdx();
+ var plat = spawnPlatform(lastPlat.y + platformSpacing, colorIdx);
+ // Place a coin on every 3rd platform
+ if ((score + platforms.length) % 3 === 0) {
+ spawnCoin(plat.x, plat.y);
+ }
+ }
+ // 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();
+ // Increase difficulty
+ if (score % 10 === 0) {
+ platformSpeed += 1.5;
+ gravity += 0.12;
+ }
+ } 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();
\ No newline at end of file
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