Code edit (2 edits merged)
Please save this source code
User prompt
move high score live's bottom, smaller and same colour
User prompt
add high score ↪💡 Consider importing and using the following plugins: @upit/storage.v1
Code edit (1 edits merged)
Please save this source code
User prompt
“The catching bar should not be colored but use the asset added. The speed should increase slightly each time the score reaches a multiple of 10.”
User prompt
"Lives will not decrease when bubbles of colors that should not be caught fall."
User prompt
"If a bubble of a color not mentioned in the text is caught or missed, lives will not be deducted."
User prompt
"Create a game feature where there is a target color shown on the screen. Only bubbles of this target color can be caught. If a player catches a bubble of a different color, the player loses one life. Catching the correct color allows the game to continue without penalty. Display this information prominently in the center of the screen as text. Update the target color and the on-screen message every 10 seconds." only allow catching the target color.
User prompt
"Create a game feature where there is a target color shown on the screen. Only bubbles of this target color can be caught. If a player catches a bubble of a different color, the player loses one life. Catching the correct color allows the game to continue without penalty. Display this information prominently in the center of the screen as text. Update the target color and the on-screen message every 10 seconds."
User prompt
"Display the word 'Catch' in the center of the screen. Update this word every 10 seconds to reflect the current target color."
User prompt
Please fix the bug: 'Timeout.tick error: Cannot set properties of undefined (setting 'fill')' in or related to this line: 'warningTxt.style.fill = colorMap[targetColor];' Line Number: 165
User prompt
"At the start of the game, display a warning message at the top indicating the target color to catch. Only bubbles of this target color can be catch. Over time, the target color changes, and the warning message updates accordingly."
Code edit (1 edits merged)
Please save this source code
User prompt
Color Catchers
Initial prompt
build a kids game
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // FallingShape class var FallingShape = Container.expand(function () { var self = Container.call(this); // Pick a random color/shape var colorList = ['shapeRed', 'shapeBlue', 'shapeGreen', 'shapeYellow', 'shapePurple']; var colorIdx = Math.floor(Math.random() * colorList.length); self.shapeId = colorList[colorIdx]; // Attach the shape asset var shape = self.attachAsset(self.shapeId, { anchorX: 0.5, anchorY: 0.5 }); // Set speed (randomize a bit for variety) self.speed = 12 + Math.floor(Math.random() * 6); // For collision detection self.radius = shape.width * 0.5; // For state tracking self.caught = false; self.missed = false; // Update method called every tick self.update = function () { self.y += self.speed; }; return self; }); // Player class var Player = Container.expand(function () { var self = Container.call(this); // Attach player asset var playerShape = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); // For collision detection self.width = playerShape.width; self.height = playerShape.height; // For touch feedback self.flash = function () { tween(self, { alpha: 0.5 }, { duration: 80, onFinish: function onFinish() { tween(self, { alpha: 1 }, { duration: 120 }); } }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xf0f8ff // Light blue background }); /**** * Game Code ****/ // Use a neutral (gray) color for the catching bar asset, not a color from the falling shapes // Game constants // Shapes for falling objects (different colors) // Player character // Sound effects // Music var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var PLAYER_Y = GAME_HEIGHT - 180; var SHAPE_SPAWN_INTERVAL = 48; // Ticks (0.8s at 60fps) var SHAPE_MIN_X = 120; var SHAPE_MAX_X = GAME_WIDTH - 120; var TARGET_SCORE = 20; var MAX_LIVES = 5; // Game state var player; var fallingShapes = []; var score = 0; var lives = MAX_LIVES; var dragNode = null; var lastTouchX = 0; // Track how many shapes to spawn per interval (difficulty) var shapesPerSpawn = 1; var lastShapeScore = 0; // Home screen overlay var homeOverlay = new Container(); // Add "confetti" shapes for extra playfulness for (var c = 0; c < 18; c++) { var confettiColors = [0xff4c4c, 0x4c7bff, 0x4cff7b, 0xfff94c, 0xb44cff]; var confettiId = ['shapeRed', 'shapeBlue', 'shapeGreen', 'shapeYellow', 'shapePurple'][Math.floor(Math.random() * 5)]; var confetti = LK.getAsset(confettiId, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.7 + Math.random() * 0.5, scaleY: 0.7 + Math.random() * 0.5, x: GAME_WIDTH / 2 + Math.cos(c / 18 * Math.PI * 2) * (700 + Math.random() * 200), y: GAME_HEIGHT / 2 + Math.sin(c / 18 * Math.PI * 2) * (700 + Math.random() * 200) }); confetti.alpha = 0.7 + Math.random() * 0.2; homeOverlay.addChild(confetti); } // Add playful falling shapes as icons around the title var iconY = GAME_HEIGHT / 2 - 370; var iconSpacing = 260; var iconIds = ['shapeRed', 'shapeBlue', 'shapeGreen', 'shapeYellow', 'shapePurple']; for (var i = 0; i < iconIds.length; i++) { var icon = LK.getAsset(iconIds[i], { anchorX: 0.5, anchorY: 0.5, scaleX: 1.1, scaleY: 1.1, x: GAME_WIDTH / 2 - iconSpacing * 2 + iconSpacing * i, y: iconY }); icon.alpha = 0.93; homeOverlay.addChild(icon); } // Main title with a friendlier, rounded font and a subtle shadow var homeTitleShadow = new Text2('Catch the Color!', { size: 190, fill: 0x4c7bff, font: "'Comic Sans MS', 'Comic Sans', 'Arial Rounded MT Bold', 'GillSans', 'Arial', sans-serif" }); homeTitleShadow.anchor.set(0.5, 0.5); homeTitleShadow.x = GAME_WIDTH / 2 + 12; homeTitleShadow.y = GAME_HEIGHT / 2 - 160; homeTitleShadow.alpha = 0.22; homeOverlay.addChild(homeTitleShadow); var homeTitle = new Text2('Catch the Color!', { size: 190, fill: 0x333399, font: "'Comic Sans MS', 'Comic Sans', 'Arial Rounded MT Bold', 'GillSans', 'Arial', sans-serif" }); homeTitle.anchor.set(0.5, 0.5); homeTitle.x = GAME_WIDTH / 2; homeTitle.y = GAME_HEIGHT / 2 - 170; homeOverlay.addChild(homeTitle); // Playful subtitle var homeSubtitle = new Text2('A colorful game for everyone!', { size: 74, fill: 0x4c7bff, font: "'Comic Sans MS', 'Comic Sans', 'Arial Rounded MT Bold', 'GillSans', 'Arial', sans-serif" }); homeSubtitle.anchor.set(0.5, 0.5); homeSubtitle.x = GAME_WIDTH / 2 + 10; homeSubtitle.y = GAME_HEIGHT / 2 - 20; homeOverlay.addChild(homeSubtitle); // Description with a friendlier font and more spacing var homeDesc = new Text2('Catch only the target color!\nMiss or catch wrong color = lose a life.', { size: 66, fill: 0x333333, font: "'Comic Sans MS', 'Comic Sans', 'Arial Rounded MT Bold', 'GillSans', 'Arial', sans-serif" }); homeDesc.anchor.set(0.5, 0.5); homeDesc.x = GAME_WIDTH / 2; homeDesc.y = GAME_HEIGHT / 2 + 130; homeOverlay.addChild(homeDesc); // Play button with a soft colored shadow and rounded font var playBtnShadow = new Text2('PLAY', { size: 190, fill: 0x4c7bff, font: "'Comic Sans MS', 'Comic Sans', 'Arial Rounded MT Bold', 'GillSans', 'Arial', sans-serif" }); playBtnShadow.anchor.set(0.5, 0.5); playBtnShadow.x = GAME_WIDTH / 2 + 16; playBtnShadow.y = GAME_HEIGHT / 2 + 220 + 118; playBtnShadow.alpha = 0.22; homeOverlay.addChild(playBtnShadow); var playBtn = new Text2('PLAY', { size: 190, fill: 0xff4c4c, font: "'Comic Sans MS', 'Comic Sans', 'Arial Rounded MT Bold', 'GillSans', 'Arial', sans-serif" }); playBtn.anchor.set(0.5, 0.5); playBtn.x = GAME_WIDTH / 2; playBtn.y = GAME_HEIGHT / 2 + 220 + 128; playBtn.interactive = true; playBtn.buttonMode = true; homeOverlay.addChild(playBtn); homeOverlay.visible = true; game.addChild(homeOverlay); // Block game input until play var gameStarted = false; // Play button handler playBtn.down = function (x, y, obj) { homeOverlay.visible = false; gameStarted = true; // Show target color text when game starts targetColorTxt.visible = true; // Show player, score, lives, and high score when game starts player.visible = true; scoreTxt.visible = true; livesTxt.visible = true; highScoreTxt.visible = true; // Start music when game starts LK.playMusic('bgmusic'); }; // Target color logic var colorList = [{ id: 'shapeRed', name: 'Red' }, { id: 'shapeBlue', name: 'Blue' }, { id: 'shapeGreen', name: 'Green' }, { id: 'shapeYellow', name: 'Yellow' }, { id: 'shapePurple', name: 'Purple' }]; var targetColorIdx = Math.floor(Math.random() * colorList.length); var targetColor = colorList[targetColorIdx].id; var targetColorName = colorList[targetColorIdx].name; var targetColorTimer = null; // Target color display (centered) var targetColorTxt = new Text2('Catch: ' + targetColorName, { size: 140, fill: 0x333333 }); targetColorTxt.anchor.set(0.5, 0.5); targetColorTxt.x = LK.gui.center.width / 2; targetColorTxt.y = LK.gui.center.height / 2 - 100; LK.gui.center.addChild(targetColorTxt); // Hide target color text on home screen targetColorTxt.visible = false; // Hide player, score, lives, and high score on home screen // Only set player.visible if player is already defined if (typeof player !== "undefined" && player) { player.visible = false; } if (typeof scoreTxt !== "undefined" && scoreTxt) { scoreTxt.visible = false; } if (typeof livesTxt !== "undefined" && livesTxt) { livesTxt.visible = false; } if (typeof highScoreTxt !== "undefined" && highScoreTxt) { highScoreTxt.visible = false; } // Score display var scoreTxt = new Text2('Score: 0', { size: 120, fill: 0x222222 }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // High score display var highScore = storage.highScore || 0; var highScoreTxt = new Text2('High Score: ' + highScore, { size: 50, fill: 0x888800 }); highScoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(highScoreTxt); // Lives display var livesTxt = new Text2('Lives: ' + MAX_LIVES, { size: 90, fill: 0x444444 }); livesTxt.anchor.set(0.5, 0); LK.gui.top.addChild(livesTxt); livesTxt.y = 130; // Center score, high score, and lives horizontally, but not in the top 100px scoreTxt.x = LK.gui.top.width / 2; scoreTxt.y = 20; highScoreTxt.x = LK.gui.top.width / 2; highScoreTxt.y = 220; livesTxt.x = LK.gui.top.width / 2; // Helper to update target color function updateTargetColor() { var prevIdx = targetColorIdx; // Pick a new color different from the last do { targetColorIdx = Math.floor(Math.random() * colorList.length); } while (targetColorIdx === prevIdx && colorList.length > 1); targetColor = colorList[targetColorIdx].id; targetColorName = colorList[targetColorIdx].name; targetColorTxt.setText('Catch: ' + targetColorName); } // Start/restart the 10s timer for target color function startTargetColorTimer() { if (targetColorTimer) { LK.clearInterval(targetColorTimer); } targetColorTimer = LK.setInterval(function () { updateTargetColor(); }, 10000); } startTargetColorTimer(); // Start music LK.playMusic('bgmusic'); // Create player player = new Player(); game.addChild(player); player.x = GAME_WIDTH / 2; player.y = PLAYER_Y; // Hide player, score, lives, and high score on home screen player.visible = false; scoreTxt.visible = false; livesTxt.visible = false; highScoreTxt.visible = false; // Helper: clamp player within screen function clampPlayerX(x) { var halfW = player.width * 0.5; if (x < halfW) { return halfW; } if (x > GAME_WIDTH - halfW) { return GAME_WIDTH - halfW; } return x; } // Touch/move handling function handleMove(x, y, obj) { if (!gameStarted || homeOverlay.visible) { return; } if (dragNode) { // Clamp to screen dragNode.x = clampPlayerX(x); lastTouchX = dragNode.x; } } game.move = handleMove; game.down = function (x, y, obj) { if (!gameStarted || homeOverlay.visible) { return; } // Only allow drag if touch is near player (within 200px vertically) if (y > player.y - 200) { dragNode = player; handleMove(x, y, obj); } }; game.up = function (x, y, obj) { if (!gameStarted || homeOverlay.visible) { return; } dragNode = null; }; // Spawn a new falling shape function spawnShape() { var shape = new FallingShape(); // Apply speed boost if any if (typeof game.shapeSpeedBoost !== "undefined") { shape.speed += game.shapeSpeedBoost; } // Random X within bounds shape.x = SHAPE_MIN_X + Math.random() * (SHAPE_MAX_X - SHAPE_MIN_X); shape.y = -100; fallingShapes.push(shape); game.addChild(shape); } // Animate positive feedback function animateCatch(shape) { tween(shape, { scaleX: 1.4, scaleY: 1.4, alpha: 0 }, { duration: 350, easing: tween.easeOut, onFinish: function onFinish() { shape.destroy(); } }); } // Animate negative feedback function animateMiss(shape) { tween(shape, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { shape.destroy(); } }); } // Update score/lives display function updateScoreLives() { scoreTxt.setText('Score: ' + score); livesTxt.setText('Lives: ' + lives); if (score > highScore) { highScore = score; storage.highScore = highScore; highScoreTxt.setText('High Score: ' + highScore); } } // Main game update loop game.update = function () { // Block all game logic if home screen is visible or game not started if (!gameStarted || homeOverlay.visible) { return; } // Increase speed every time score reaches a new multiple of 10 (but only once per threshold) if (typeof game.lastSpeedScore === "undefined") { game.lastSpeedScore = 0; } if (score >= 3 && score % 3 === 0 && game.lastSpeedScore !== score) { // Increase speed of new shapes if (typeof game.shapeSpeedBoost === "undefined") { game.shapeSpeedBoost = 0; } game.shapeSpeedBoost += 2; // Increase by 2 pixels per tick per 10 points game.lastSpeedScore = score; } // Increase number of falling shapes every time score increases by 3 if (score >= 3 && score % 3 === 0 && lastShapeScore !== score) { shapesPerSpawn += 1; lastShapeScore = score; } // Spawn shapes at interval if (LK.ticks % SHAPE_SPAWN_INTERVAL === 0) { for (var s = 0; s < shapesPerSpawn; s++) { spawnShape(); } } // Update all falling shapes for (var i = fallingShapes.length - 1; i >= 0; i--) { var shape = fallingShapes[i]; shape.update(); // Check for catch (simple AABB) if (!shape.caught && !shape.missed) { var dx = Math.abs(shape.x - player.x); var dy = Math.abs(shape.y - player.y); var catchW = player.width * 0.5 + shape.radius * 0.7; var catchH = player.height * 0.5 + shape.radius * 0.7; if (dx < catchW && dy < catchH) { // Caught! shape.caught = true; if (shape.shapeId === targetColor) { // Correct color score += 1; updateScoreLives(); LK.getSound('catch').play(); player.flash(); animateCatch(shape); fallingShapes.splice(i, 1); // Win condition if (score >= TARGET_SCORE) { LK.effects.flashScreen(0x00ff00, 800); LK.showYouWin(); return; } } else { // Wrong color // Only penalize if the shape's color is mentioned in the target color text var isMentioned = false; for (var c = 0; c < colorList.length; c++) { if (shape.shapeId === colorList[c].id) { isMentioned = true; break; } } if (isMentioned) { lives -= 1; updateScoreLives(); LK.getSound('miss').play(); player.flash(); animateMiss(shape); fallingShapes.splice(i, 1); // Lose condition if (lives <= 0) { LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } } else { // Not a mentioned color, just remove the shape with no penalty animateMiss(shape); fallingShapes.splice(i, 1); } } continue; } } // Check for miss (off bottom) if (!shape.caught && !shape.missed && shape.y > GAME_HEIGHT + 100) { shape.missed = true; // Only penalize if the shape's color is mentioned in the target color text AND is the current target color var isMentioned = false; for (var c = 0; c < colorList.length; c++) { if (shape.shapeId === colorList[c].id) { isMentioned = true; break; } } // Only decrease lives if the missed shape is the current target color if (isMentioned && shape.shapeId === targetColor) { lives -= 1; updateScoreLives(); LK.getSound('miss').play(); animateMiss(shape); fallingShapes.splice(i, 1); // Lose condition if (lives <= 0) { LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } } else { // Not a mentioned color or not the target color, just remove the shape with no penalty animateMiss(shape); fallingShapes.splice(i, 1); } } } }; // On game reset, clear state game.on('destroy', function () { fallingShapes = []; score = 0; lives = MAX_LIVES; dragNode = null; lastTouchX = 0; LK.stopMusic(); // Reset target color and timer if (targetColorTimer) { LK.clearInterval(targetColorTimer); } updateTargetColor(); startTargetColorTimer(); targetColorTxt.setText('Catch: ' + targetColorName); // Update high score display from storage in case it changed highScore = storage.highScore || 0; highScoreTxt.setText('High Score: ' + highScore); // Show home screen again homeOverlay.visible = true; gameStarted = false; // Hide target color text on home screen targetColorTxt.visible = false; });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// FallingShape class
var FallingShape = Container.expand(function () {
var self = Container.call(this);
// Pick a random color/shape
var colorList = ['shapeRed', 'shapeBlue', 'shapeGreen', 'shapeYellow', 'shapePurple'];
var colorIdx = Math.floor(Math.random() * colorList.length);
self.shapeId = colorList[colorIdx];
// Attach the shape asset
var shape = self.attachAsset(self.shapeId, {
anchorX: 0.5,
anchorY: 0.5
});
// Set speed (randomize a bit for variety)
self.speed = 12 + Math.floor(Math.random() * 6);
// For collision detection
self.radius = shape.width * 0.5;
// For state tracking
self.caught = false;
self.missed = false;
// Update method called every tick
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
// Attach player asset
var playerShape = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// For collision detection
self.width = playerShape.width;
self.height = playerShape.height;
// For touch feedback
self.flash = function () {
tween(self, {
alpha: 0.5
}, {
duration: 80,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 120
});
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xf0f8ff // Light blue background
});
/****
* Game Code
****/
// Use a neutral (gray) color for the catching bar asset, not a color from the falling shapes
// Game constants
// Shapes for falling objects (different colors)
// Player character
// Sound effects
// Music
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var PLAYER_Y = GAME_HEIGHT - 180;
var SHAPE_SPAWN_INTERVAL = 48; // Ticks (0.8s at 60fps)
var SHAPE_MIN_X = 120;
var SHAPE_MAX_X = GAME_WIDTH - 120;
var TARGET_SCORE = 20;
var MAX_LIVES = 5;
// Game state
var player;
var fallingShapes = [];
var score = 0;
var lives = MAX_LIVES;
var dragNode = null;
var lastTouchX = 0;
// Track how many shapes to spawn per interval (difficulty)
var shapesPerSpawn = 1;
var lastShapeScore = 0;
// Home screen overlay
var homeOverlay = new Container();
// Add "confetti" shapes for extra playfulness
for (var c = 0; c < 18; c++) {
var confettiColors = [0xff4c4c, 0x4c7bff, 0x4cff7b, 0xfff94c, 0xb44cff];
var confettiId = ['shapeRed', 'shapeBlue', 'shapeGreen', 'shapeYellow', 'shapePurple'][Math.floor(Math.random() * 5)];
var confetti = LK.getAsset(confettiId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7 + Math.random() * 0.5,
scaleY: 0.7 + Math.random() * 0.5,
x: GAME_WIDTH / 2 + Math.cos(c / 18 * Math.PI * 2) * (700 + Math.random() * 200),
y: GAME_HEIGHT / 2 + Math.sin(c / 18 * Math.PI * 2) * (700 + Math.random() * 200)
});
confetti.alpha = 0.7 + Math.random() * 0.2;
homeOverlay.addChild(confetti);
}
// Add playful falling shapes as icons around the title
var iconY = GAME_HEIGHT / 2 - 370;
var iconSpacing = 260;
var iconIds = ['shapeRed', 'shapeBlue', 'shapeGreen', 'shapeYellow', 'shapePurple'];
for (var i = 0; i < iconIds.length; i++) {
var icon = LK.getAsset(iconIds[i], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.1,
scaleY: 1.1,
x: GAME_WIDTH / 2 - iconSpacing * 2 + iconSpacing * i,
y: iconY
});
icon.alpha = 0.93;
homeOverlay.addChild(icon);
}
// Main title with a friendlier, rounded font and a subtle shadow
var homeTitleShadow = new Text2('Catch the Color!', {
size: 190,
fill: 0x4c7bff,
font: "'Comic Sans MS', 'Comic Sans', 'Arial Rounded MT Bold', 'GillSans', 'Arial', sans-serif"
});
homeTitleShadow.anchor.set(0.5, 0.5);
homeTitleShadow.x = GAME_WIDTH / 2 + 12;
homeTitleShadow.y = GAME_HEIGHT / 2 - 160;
homeTitleShadow.alpha = 0.22;
homeOverlay.addChild(homeTitleShadow);
var homeTitle = new Text2('Catch the Color!', {
size: 190,
fill: 0x333399,
font: "'Comic Sans MS', 'Comic Sans', 'Arial Rounded MT Bold', 'GillSans', 'Arial', sans-serif"
});
homeTitle.anchor.set(0.5, 0.5);
homeTitle.x = GAME_WIDTH / 2;
homeTitle.y = GAME_HEIGHT / 2 - 170;
homeOverlay.addChild(homeTitle);
// Playful subtitle
var homeSubtitle = new Text2('A colorful game for everyone!', {
size: 74,
fill: 0x4c7bff,
font: "'Comic Sans MS', 'Comic Sans', 'Arial Rounded MT Bold', 'GillSans', 'Arial', sans-serif"
});
homeSubtitle.anchor.set(0.5, 0.5);
homeSubtitle.x = GAME_WIDTH / 2 + 10;
homeSubtitle.y = GAME_HEIGHT / 2 - 20;
homeOverlay.addChild(homeSubtitle);
// Description with a friendlier font and more spacing
var homeDesc = new Text2('Catch only the target color!\nMiss or catch wrong color = lose a life.', {
size: 66,
fill: 0x333333,
font: "'Comic Sans MS', 'Comic Sans', 'Arial Rounded MT Bold', 'GillSans', 'Arial', sans-serif"
});
homeDesc.anchor.set(0.5, 0.5);
homeDesc.x = GAME_WIDTH / 2;
homeDesc.y = GAME_HEIGHT / 2 + 130;
homeOverlay.addChild(homeDesc);
// Play button with a soft colored shadow and rounded font
var playBtnShadow = new Text2('PLAY', {
size: 190,
fill: 0x4c7bff,
font: "'Comic Sans MS', 'Comic Sans', 'Arial Rounded MT Bold', 'GillSans', 'Arial', sans-serif"
});
playBtnShadow.anchor.set(0.5, 0.5);
playBtnShadow.x = GAME_WIDTH / 2 + 16;
playBtnShadow.y = GAME_HEIGHT / 2 + 220 + 118;
playBtnShadow.alpha = 0.22;
homeOverlay.addChild(playBtnShadow);
var playBtn = new Text2('PLAY', {
size: 190,
fill: 0xff4c4c,
font: "'Comic Sans MS', 'Comic Sans', 'Arial Rounded MT Bold', 'GillSans', 'Arial', sans-serif"
});
playBtn.anchor.set(0.5, 0.5);
playBtn.x = GAME_WIDTH / 2;
playBtn.y = GAME_HEIGHT / 2 + 220 + 128;
playBtn.interactive = true;
playBtn.buttonMode = true;
homeOverlay.addChild(playBtn);
homeOverlay.visible = true;
game.addChild(homeOverlay);
// Block game input until play
var gameStarted = false;
// Play button handler
playBtn.down = function (x, y, obj) {
homeOverlay.visible = false;
gameStarted = true;
// Show target color text when game starts
targetColorTxt.visible = true;
// Show player, score, lives, and high score when game starts
player.visible = true;
scoreTxt.visible = true;
livesTxt.visible = true;
highScoreTxt.visible = true;
// Start music when game starts
LK.playMusic('bgmusic');
};
// Target color logic
var colorList = [{
id: 'shapeRed',
name: 'Red'
}, {
id: 'shapeBlue',
name: 'Blue'
}, {
id: 'shapeGreen',
name: 'Green'
}, {
id: 'shapeYellow',
name: 'Yellow'
}, {
id: 'shapePurple',
name: 'Purple'
}];
var targetColorIdx = Math.floor(Math.random() * colorList.length);
var targetColor = colorList[targetColorIdx].id;
var targetColorName = colorList[targetColorIdx].name;
var targetColorTimer = null;
// Target color display (centered)
var targetColorTxt = new Text2('Catch: ' + targetColorName, {
size: 140,
fill: 0x333333
});
targetColorTxt.anchor.set(0.5, 0.5);
targetColorTxt.x = LK.gui.center.width / 2;
targetColorTxt.y = LK.gui.center.height / 2 - 100;
LK.gui.center.addChild(targetColorTxt);
// Hide target color text on home screen
targetColorTxt.visible = false;
// Hide player, score, lives, and high score on home screen
// Only set player.visible if player is already defined
if (typeof player !== "undefined" && player) {
player.visible = false;
}
if (typeof scoreTxt !== "undefined" && scoreTxt) {
scoreTxt.visible = false;
}
if (typeof livesTxt !== "undefined" && livesTxt) {
livesTxt.visible = false;
}
if (typeof highScoreTxt !== "undefined" && highScoreTxt) {
highScoreTxt.visible = false;
}
// Score display
var scoreTxt = new Text2('Score: 0', {
size: 120,
fill: 0x222222
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// High score display
var highScore = storage.highScore || 0;
var highScoreTxt = new Text2('High Score: ' + highScore, {
size: 50,
fill: 0x888800
});
highScoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(highScoreTxt);
// Lives display
var livesTxt = new Text2('Lives: ' + MAX_LIVES, {
size: 90,
fill: 0x444444
});
livesTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(livesTxt);
livesTxt.y = 130;
// Center score, high score, and lives horizontally, but not in the top 100px
scoreTxt.x = LK.gui.top.width / 2;
scoreTxt.y = 20;
highScoreTxt.x = LK.gui.top.width / 2;
highScoreTxt.y = 220;
livesTxt.x = LK.gui.top.width / 2;
// Helper to update target color
function updateTargetColor() {
var prevIdx = targetColorIdx;
// Pick a new color different from the last
do {
targetColorIdx = Math.floor(Math.random() * colorList.length);
} while (targetColorIdx === prevIdx && colorList.length > 1);
targetColor = colorList[targetColorIdx].id;
targetColorName = colorList[targetColorIdx].name;
targetColorTxt.setText('Catch: ' + targetColorName);
}
// Start/restart the 10s timer for target color
function startTargetColorTimer() {
if (targetColorTimer) {
LK.clearInterval(targetColorTimer);
}
targetColorTimer = LK.setInterval(function () {
updateTargetColor();
}, 10000);
}
startTargetColorTimer();
// Start music
LK.playMusic('bgmusic');
// Create player
player = new Player();
game.addChild(player);
player.x = GAME_WIDTH / 2;
player.y = PLAYER_Y;
// Hide player, score, lives, and high score on home screen
player.visible = false;
scoreTxt.visible = false;
livesTxt.visible = false;
highScoreTxt.visible = false;
// Helper: clamp player within screen
function clampPlayerX(x) {
var halfW = player.width * 0.5;
if (x < halfW) {
return halfW;
}
if (x > GAME_WIDTH - halfW) {
return GAME_WIDTH - halfW;
}
return x;
}
// Touch/move handling
function handleMove(x, y, obj) {
if (!gameStarted || homeOverlay.visible) {
return;
}
if (dragNode) {
// Clamp to screen
dragNode.x = clampPlayerX(x);
lastTouchX = dragNode.x;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
if (!gameStarted || homeOverlay.visible) {
return;
}
// Only allow drag if touch is near player (within 200px vertically)
if (y > player.y - 200) {
dragNode = player;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
if (!gameStarted || homeOverlay.visible) {
return;
}
dragNode = null;
};
// Spawn a new falling shape
function spawnShape() {
var shape = new FallingShape();
// Apply speed boost if any
if (typeof game.shapeSpeedBoost !== "undefined") {
shape.speed += game.shapeSpeedBoost;
}
// Random X within bounds
shape.x = SHAPE_MIN_X + Math.random() * (SHAPE_MAX_X - SHAPE_MIN_X);
shape.y = -100;
fallingShapes.push(shape);
game.addChild(shape);
}
// Animate positive feedback
function animateCatch(shape) {
tween(shape, {
scaleX: 1.4,
scaleY: 1.4,
alpha: 0
}, {
duration: 350,
easing: tween.easeOut,
onFinish: function onFinish() {
shape.destroy();
}
});
}
// Animate negative feedback
function animateMiss(shape) {
tween(shape, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
shape.destroy();
}
});
}
// Update score/lives display
function updateScoreLives() {
scoreTxt.setText('Score: ' + score);
livesTxt.setText('Lives: ' + lives);
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('High Score: ' + highScore);
}
}
// Main game update loop
game.update = function () {
// Block all game logic if home screen is visible or game not started
if (!gameStarted || homeOverlay.visible) {
return;
}
// Increase speed every time score reaches a new multiple of 10 (but only once per threshold)
if (typeof game.lastSpeedScore === "undefined") {
game.lastSpeedScore = 0;
}
if (score >= 3 && score % 3 === 0 && game.lastSpeedScore !== score) {
// Increase speed of new shapes
if (typeof game.shapeSpeedBoost === "undefined") {
game.shapeSpeedBoost = 0;
}
game.shapeSpeedBoost += 2; // Increase by 2 pixels per tick per 10 points
game.lastSpeedScore = score;
}
// Increase number of falling shapes every time score increases by 3
if (score >= 3 && score % 3 === 0 && lastShapeScore !== score) {
shapesPerSpawn += 1;
lastShapeScore = score;
}
// Spawn shapes at interval
if (LK.ticks % SHAPE_SPAWN_INTERVAL === 0) {
for (var s = 0; s < shapesPerSpawn; s++) {
spawnShape();
}
}
// Update all falling shapes
for (var i = fallingShapes.length - 1; i >= 0; i--) {
var shape = fallingShapes[i];
shape.update();
// Check for catch (simple AABB)
if (!shape.caught && !shape.missed) {
var dx = Math.abs(shape.x - player.x);
var dy = Math.abs(shape.y - player.y);
var catchW = player.width * 0.5 + shape.radius * 0.7;
var catchH = player.height * 0.5 + shape.radius * 0.7;
if (dx < catchW && dy < catchH) {
// Caught!
shape.caught = true;
if (shape.shapeId === targetColor) {
// Correct color
score += 1;
updateScoreLives();
LK.getSound('catch').play();
player.flash();
animateCatch(shape);
fallingShapes.splice(i, 1);
// Win condition
if (score >= TARGET_SCORE) {
LK.effects.flashScreen(0x00ff00, 800);
LK.showYouWin();
return;
}
} else {
// Wrong color
// Only penalize if the shape's color is mentioned in the target color text
var isMentioned = false;
for (var c = 0; c < colorList.length; c++) {
if (shape.shapeId === colorList[c].id) {
isMentioned = true;
break;
}
}
if (isMentioned) {
lives -= 1;
updateScoreLives();
LK.getSound('miss').play();
player.flash();
animateMiss(shape);
fallingShapes.splice(i, 1);
// Lose condition
if (lives <= 0) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
} else {
// Not a mentioned color, just remove the shape with no penalty
animateMiss(shape);
fallingShapes.splice(i, 1);
}
}
continue;
}
}
// Check for miss (off bottom)
if (!shape.caught && !shape.missed && shape.y > GAME_HEIGHT + 100) {
shape.missed = true;
// Only penalize if the shape's color is mentioned in the target color text AND is the current target color
var isMentioned = false;
for (var c = 0; c < colorList.length; c++) {
if (shape.shapeId === colorList[c].id) {
isMentioned = true;
break;
}
}
// Only decrease lives if the missed shape is the current target color
if (isMentioned && shape.shapeId === targetColor) {
lives -= 1;
updateScoreLives();
LK.getSound('miss').play();
animateMiss(shape);
fallingShapes.splice(i, 1);
// Lose condition
if (lives <= 0) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
} else {
// Not a mentioned color or not the target color, just remove the shape with no penalty
animateMiss(shape);
fallingShapes.splice(i, 1);
}
}
}
};
// On game reset, clear state
game.on('destroy', function () {
fallingShapes = [];
score = 0;
lives = MAX_LIVES;
dragNode = null;
lastTouchX = 0;
LK.stopMusic();
// Reset target color and timer
if (targetColorTimer) {
LK.clearInterval(targetColorTimer);
}
updateTargetColor();
startTargetColorTimer();
targetColorTxt.setText('Catch: ' + targetColorName);
// Update high score display from storage in case it changed
highScore = storage.highScore || 0;
highScoreTxt.setText('High Score: ' + highScore);
// Show home screen again
homeOverlay.visible = true;
gameStarted = false;
// Hide target color text on home screen
targetColorTxt.visible = false;
});