User prompt
fix the buttons problem, I can't touch the buttons and i can not get reaction
User prompt
I can't touch the buttons
User prompt
I can't press the buttons
User prompt
fix the buttons
User prompt
I can't press the buttons
User prompt
I can't press the buttons
User prompt
buttons should be pressable
User prompt
Please fix the bug: 'LK.now is not a function' in or related to this line: 'lastTapTime = LK.now();' Line Number: 266
Code edit (1 edits merged)
Please save this source code
User prompt
Color Ball
Initial prompt
I want to create a simple, engaging 2D mobile game called "Color Ball." The game should be easy to play, visually minimalistic but colorful, and optimized for both iOS and Android platforms. Below are the detailed requirements and features: 1. Core Gameplay: - The game features a single ball positioned in the center of the screen. - The ball continuously rotates or spins slowly. - The ball changes its color randomly among three colors: Red, Blue, and Green. - At the bottom of the screen, display three large buttons, each corresponding to one of the colors (Red, Blue, Green). - The player’s objective is to tap the button that matches the current color of the ball as quickly and accurately as possible. - Upon a correct tap: - Increase the player’s score by 1. - Slightly increase the rotation speed of the ball, making the game progressively harder. - Immediately change the ball's color randomly (not repeating the previous color consecutively). - Upon an incorrect tap or if the player fails to tap within a limited time window (e.g., 2 seconds): - End the game and display the final score. - Show options to restart or exit. 2. User Interface & Visual Design: - Use a clean, flat design style with smooth animations. - The background should be neutral (e.g., white or light gray) to keep focus on the colored ball and buttons. - The buttons should have clear labels or color fill matching the respective colors. - Display the current score prominently at the top-center of the screen. - Include a subtle sound effect on button tap and a different sound for correct/incorrect responses. 3. Gameplay Mechanics: - The ball’s rotation speed starts slow and increases incrementally by a small fixed amount after every successful tap. - Color changes must not repeat immediately the previous color to maintain unpredictability. - The tapping time window starts at 2 seconds and can optionally decrease as the score increases to add difficulty. - Include a simple countdown timer or visual indicator for the remaining tapping time. 4. Additional Features: - Implement a simple start menu with a “Play” button and instructions or tips. - Add a “Game Over” screen showing the player’s final score and high score. - Save the highest score locally on the device. - Include a mute/unmute button for sounds. - Support both portrait and landscape orientations with responsive UI elements. - Optimize for smooth performance on low to mid-range mobile devices. 5. Technical Notes: - The game should be implemented with a mobile-friendly 2D game engine like Unity or Godot. - Code should be modular and easy to maintain, allowing easy tweaks of gameplay parameters (speed increments, timing, colors). - Use touch input detection for button presses. - Include comments in the code for clarity.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0 }); /**** * Classes ****/ // Ball class: handles color, rotation, and animation var ColorBall = Container.expand(function () { var self = Container.call(this); // Color order: 0=Red, 1=Blue, 2=Green self.colors = ['red', 'blue', 'green']; self.colorIdx = 0; // --- Add shadow assets for each color, matching the ball shape and style --- self.shadowAssets = { red: self.attachAsset('ballRed', { anchorX: 0.5, anchorY: 0.5, x: 0, y: self.ballAssets && self.ballAssets.red ? self.ballAssets.red.height * 0.045 : 22, scaleX: 1.04, scaleY: 1.04, alpha: 0.32, tint: 0x000000 }), blue: self.attachAsset('ballBlue', { anchorX: 0.5, anchorY: 0.5, x: 0, y: self.ballAssets && self.ballAssets.blue ? self.ballAssets.blue.height * 0.045 : 22, scaleX: 1.04, scaleY: 1.04, alpha: 0.32, tint: 0x000000 }), green: self.attachAsset('ballGreen', { anchorX: 0.5, anchorY: 0.5, x: 0, y: self.ballAssets && self.ballAssets.green ? self.ballAssets.green.height * 0.045 : 22, scaleX: 1.04, scaleY: 1.04, alpha: 0.32, tint: 0x000000 }) }; // Attach all color assets, only one visible at a time self.ballAssets = { red: self.attachAsset('ballRed', { anchorX: 0.5, anchorY: 0.5 }), blue: self.attachAsset('ballBlue', { anchorX: 0.5, anchorY: 0.5 }), green: self.attachAsset('ballGreen', { anchorX: 0.5, anchorY: 0.5 }) }; // Place shadow behind the ball self.addChildAt(self.shadowAssets.red, 0); self.addChildAt(self.shadowAssets.blue, 0); self.addChildAt(self.shadowAssets.green, 0); self.setColor = function (color) { self.color = color; self.ballAssets.red.visible = color === 'red'; self.ballAssets.blue.visible = color === 'blue'; self.ballAssets.green.visible = color === 'green'; // Show only the shadow for the current color self.shadowAssets.red.visible = color === 'red'; self.shadowAssets.blue.visible = color === 'blue'; self.shadowAssets.green.visible = color === 'green'; }; self.setColor('red'); self.rotationSpeed = 0.02; // radians per tick self.update = function () { // Defensive: ensure rotationSpeed is always defined if (typeof self.rotationSpeed !== "number") self.rotationSpeed = 0.02; self.rotation += self.rotationSpeed; }; // Animate color change (optional: flash or scale) self.animateColorChange = function () { tween(self, { scaleX: 1.15, scaleY: 1.15 }, { duration: 80, easing: tween.cubicOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 100, easing: tween.cubicIn }); } }); }; return self; }); // Button class: handles color, label, and press feedback var ColorButton = Container.expand(function () { var self = Container.call(this); // color: 'red', 'blue', 'green' self.setColor = function (color) { self.color = color; if (self.asset) { self.removeChild(self.asset); } if (self.label) { self.removeChild(self.label); } if (self.hitbox) { self.removeChild(self.hitbox); } var assetId = color === 'red' ? 'btnRed' : color === 'blue' ? 'btnBlue' : 'btnGreen'; self.asset = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Add realistic shadow by duplicating the button asset, tinting and offsetting it if (self.shadow) { self.removeChild(self.shadow); } var shadowAssetId = assetId; self.shadow = self.attachAsset(shadowAssetId, { anchorX: 0.5, anchorY: 0.5, x: 0, y: self.asset.height * 0.06, // offset shadow downward (closer) scaleX: 1.03, scaleY: 1.03, alpha: 0.32, tint: 0x000000 }); self.shadow.zIndex = -2; self.addChild(self.shadow); self.addChild(self.asset); }; // Animate press feedback self.animatePress = function () { tween(self.asset, { scaleX: 0.93, scaleY: 0.93 }, { duration: 60, easing: tween.cubicOut, onFinish: function onFinish() { tween(self.asset, { scaleX: 1, scaleY: 1 }, { duration: 80, easing: tween.cubicIn }); } }); }; // For hit detection self.containsPoint = function (x, y) { var local = self.toLocal({ x: x, y: y }); return local.x > -self.hitbox.width / 2 && local.x < self.hitbox.width / 2 && local.y > -self.hitbox.height / 2 && local.y < self.hitbox.height / 2; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xffb638 }); /**** * Game Code ****/ // btnBlue image // btnGreen image // btnRed image // Health bar assets // --- Game State --- // Ball colors // Button colors // Sound // Music (optional, but initialized for future use) var ball; var buttons = []; var colorOrder = ['red', 'blue', 'green']; var currentColor = 'red'; var score = 0; var highScore = storage.highScore || 0; var timeLimit = 5000; // ms, initial time to respond (5 seconds) var minTimeLimit = 5000; // ms, minimum allowed (5 seconds) var timeDecrease = 80; // ms, decrease per correct var rotationSpeed = 0.02; // initial var maxRotationSpeed = 0.09; var rotationIncrease = 0.008; var waitingForInput = true; var responseTimer = null; var lastTapTime = 0; // --- UI Elements --- // Add shadow for score text (centered, slightly offset, behind main score) var scoreShadowTxt = new Text2('0', { size: 120, fill: "#000", alpha: 0.32 }); scoreShadowTxt.anchor.set(0.5, 0); scoreShadowTxt.y = 60 + 3; // offset shadow downward (closer) var scoreTxt = new Text2('0', { size: 120, fill: 0xFF4500 }); scoreTxt.anchor.set(0.5, 0); scoreTxt.y = 60; // Add scoreTxt after timer and hearts to ensure it is always on top LK.setTimeout(function () { LK.gui.top.addChild(scoreShadowTxt); LK.gui.top.addChild(scoreTxt); }, 0); var highScoreTxt = new Text2('BEST: ' + highScore, { size: 60, fill: 0xFF4500, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); highScoreTxt.anchor.set(0.5, 0); highScoreTxt.y = 190; LK.gui.top.addChild(highScoreTxt); // --- Timer Bar (under score) --- // --- Timer Bar (under score, in GUI) --- var timerBar = LK.getAsset('timerBar', { width: 900, height: 60, color: 0xffffff, anchorX: 0.5, anchorY: 0.0 }); timerBar.x = 0.5; // relative to gui width timerBar.y = scoreTxt.y + scoreTxt.height + 70; // 70px below score (moved further down) timerBar.alpha = 0.18; timerBar.isGUI = true; LK.gui.top.addChild(timerBar); var timerBarFill = LK.getAsset('timerBarFill', { width: 900, height: 60, color: 0x2ecc40, anchorX: 0.5, anchorY: 0.0 }); timerBarFill.x = 0.5; // relative to gui width timerBarFill.y = scoreTxt.y + scoreTxt.height + 70; // 70px below score (moved further down) timerBarFill.isGUI = true; LK.gui.top.addChild(timerBarFill); // --- Health Bars (under timer, in GUI) --- var health = 3; var maxHealth = 3; var healthBars = []; var healthBarSpacing = 130; var healthBarWidth = 120; var healthBarHeight = 120; var healthBarAssets = [{ id: 'healthBarRed', color: 0xd83318 }, // Red { id: 'healthBarBlue', color: 0x187ad8 }, // Blue { id: 'healthBarGreen', color: 0x2ecc40 } // Green ]; var healthBarY = timerBar.y + timerBar.height + 30; // 30px below timer bar // Calculate total width of all health bars and starting x for centering var totalHealthWidth = (maxHealth - 1) * healthBarSpacing; var healthStartX = 0.5 + -(totalHealthWidth / 2); // 0.5 is center in LK.gui coordinates for (var i = 0; i < maxHealth; i++) { var assetInfo = healthBarAssets[i]; var bar = LK.getAsset(assetInfo.id, { width: healthBarWidth, height: healthBarHeight, color: assetInfo.color, anchorX: 0.5, anchorY: 0.0 }); // Center health bars under the timer bar.x = healthStartX + i * healthBarSpacing; bar.y = healthBarY; bar.isGUI = true; LK.gui.top.addChild(bar); healthBars.push(bar); } // Helper to update health bar visuals function updateHealthBars() { for (var i = 0; i < maxHealth; i++) { // Each bar is independent: show full alpha if "alive", faded if "lost" healthBars[i].alpha = i < health ? 1 : 0.18; } } updateHealthBars(); // --- Setup Ball --- ball = new ColorBall(); ball.x = 2048 / 2; ball.y = 1100; game.addChild(ball); // --- Setup Buttons --- var btnSpacing = 480; var btnY = 2732 - 320; // Create btnRed var btnRed = new ColorButton(); btnRed.setColor('red'); btnRed.x = 2048 / 2 - btnSpacing; btnRed.y = btnY; game.addChild(btnRed); // Create btnBlue var btnBlue = new ColorButton(); btnBlue.setColor('blue'); btnBlue.x = 2048 / 2; btnBlue.y = btnY; game.addChild(btnBlue); // Create btnGreen var btnGreen = new ColorButton(); btnGreen.setColor('green'); btnGreen.x = 2048 / 2 + btnSpacing; btnGreen.y = btnY; game.addChild(btnGreen); // Place all buttons in the same positions as btnRed, btnBlue, btnGreen buttons = [btnRed, btnBlue, btnGreen]; // Ensure all color buttons are positioned exactly as btnRed, btnBlue, btnGreen buttons[0].x = btnRed.x; buttons[0].y = btnRed.y; buttons[1].x = btnBlue.x; buttons[1].y = btnBlue.y; buttons[2].x = btnGreen.x; buttons[2].y = btnGreen.y; // --- Helper Functions --- function setScore(val) { score = val; scoreTxt.setText(score); if (typeof scoreShadowTxt !== "undefined") { scoreShadowTxt.setText(score); } if (score > highScore) { highScore = score; storage.highScore = highScore; highScoreTxt.setText('BEST: ' + highScore); } } function randomColor() { // Never repeat the same color twice in a row var idx = colorOrder.indexOf(currentColor); var nextIdx = idx; while (nextIdx === idx) { nextIdx = Math.floor(Math.random() * 3); } return colorOrder[nextIdx]; } function startNewRound() { waitingForInput = true; // Pick new color currentColor = randomColor(); ball.setColor(currentColor); ball.animateColorChange(); // Timer always starts from 5 seconds (5000ms) for each round, and decreases by 0.1s for each correct answer timeLimit = 5000 - score * 100; if (timeLimit < 500) { timeLimit = 500; // minimum 0.5s to avoid negative/zero } // --- Smoothly increase rotation speed at every multiple of 10 points --- var baseSpeed = 0.02; var speedStep = 0.008; var maxSpeed = 0.09; var targetSpeed = baseSpeed + Math.floor(score / 10) * speedStep; if (targetSpeed > maxSpeed) targetSpeed = maxSpeed; // Only tween if the speed is changing if (Math.abs(ball.rotationSpeed - targetSpeed) > 0.0001) { tween.stop(ball, { rotationSpeed: true }); tween(ball, { rotationSpeed: targetSpeed }, { duration: 700, easing: tween.cubicInOut }); } rotationSpeed = targetSpeed; ball.targetRotationSpeed = targetSpeed; // Start timer timerBarFill.width = 900; if (responseTimer) { LK.clearTimeout(responseTimer); responseTimer = null; } responseTimer = LK.setTimeout(function () { // Time's up waitingForInput = false; LK.effects.flashScreen(0xff0000, 600); LK.getSound('wrong').play(); health--; updateHealthBars(); if (health <= 0) { endGame(); } else { // Give player another chance, start new round after short delay responseTimer = LK.setTimeout(function () { startNewRound(); }, 700); } }, timeLimit); lastTapTime = Date.now(); } function endGame() { if (responseTimer) { LK.clearTimeout(responseTimer); responseTimer = null; } waitingForInput = false; // Flash ball red LK.effects.flashObject(ball, 0xff0000, 600); // Show game over (handled by LK) LK.showGameOver(); } // --- Input Handling --- game.down = function (x, y, obj) { if (!waitingForInput) { return; } // Divide the screen into three vertical regions: left, middle, right var region; if (x < 2048 / 3) { region = 'left'; } else if (x > 2048 * 2 / 3) { region = 'right'; } else { region = 'middle'; } // Map region to color var color; if (region === 'left') { color = 'red'; } else if (region === 'right') { color = 'green'; } else { color = 'blue'; } // Find the button with this color (for animation/feedback) var btn = null; for (var i = 0; i < buttons.length; i++) { if (buttons[i].color === color) { btn = buttons[i]; break; } } if (btn) { handleButtonPress(btn); } }; function handleButtonPress(btn) { if (!waitingForInput) { return; } btn.animatePress(); LK.effects.flashObject(btn.asset, 0xffffff, 120); waitingForInput = false; if (btn.color === currentColor) { // Correct! setScore(score + 1); LK.getSound('correct').play(); // Animate ball tween(ball, { scaleX: 1.18, scaleY: 1.18 }, { duration: 80, easing: tween.cubicOut, onFinish: function onFinish() { tween(ball, { scaleX: 1, scaleY: 1 }, { duration: 100, easing: tween.cubicIn }); } }); // Decrease timer by 0.1 seconds (100ms) for each correct answer if (responseTimer) { LK.clearTimeout(responseTimer); responseTimer = null; } responseTimer = LK.setTimeout(function () { startNewRound(); }, 220); } else { // Wrong! LK.effects.flashScreen(0xff0000, 600); LK.getSound('wrong').play(); if (responseTimer) { LK.clearTimeout(responseTimer); responseTimer = null; } health--; updateHealthBars(); if (health <= 0) { endGame(); } else { // Decrease timer by 0.2 seconds (200ms) for each incorrect answer and reset to 3 seconds timeLimit = 3000 - score * 100 - (maxHealth - health) * 200; if (timeLimit < 500) { timeLimit = 500; // minimum 0.5s to avoid negative/zero } // Give player another chance, start new round after short delay responseTimer = LK.setTimeout(function () { startNewRound(); }, 700); } } } // --- Game Update Loop --- game.update = function () { // Animate timer bar if (waitingForInput) { var elapsed = Date.now() - lastTapTime; var frac = 1 - elapsed / timeLimit; if (frac < 0) { frac = 0; } timerBarFill.width = 900 * frac; // Do not change timerBarFill color or tint here to prevent fading } else { timerBarFill.width = 0; } }; // --- Start Game --- function startGame() { LK.playMusic('g'); setScore(0); timeLimit = 5000; rotationSpeed = 0.02; ball.rotationSpeed = rotationSpeed; ball.targetRotationSpeed = rotationSpeed; health = maxHealth; updateHealthBars(); startNewRound(); } startGame(); // --- Responsive UI (optional, handled by LK) --- // --- Music (optional, not started by default) --- // LK.playMusic('bgmusic', {fade: {start: 0, end: 1, duration: 1000}});
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0
});
/****
* Classes
****/
// Ball class: handles color, rotation, and animation
var ColorBall = Container.expand(function () {
var self = Container.call(this);
// Color order: 0=Red, 1=Blue, 2=Green
self.colors = ['red', 'blue', 'green'];
self.colorIdx = 0;
// --- Add shadow assets for each color, matching the ball shape and style ---
self.shadowAssets = {
red: self.attachAsset('ballRed', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: self.ballAssets && self.ballAssets.red ? self.ballAssets.red.height * 0.045 : 22,
scaleX: 1.04,
scaleY: 1.04,
alpha: 0.32,
tint: 0x000000
}),
blue: self.attachAsset('ballBlue', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: self.ballAssets && self.ballAssets.blue ? self.ballAssets.blue.height * 0.045 : 22,
scaleX: 1.04,
scaleY: 1.04,
alpha: 0.32,
tint: 0x000000
}),
green: self.attachAsset('ballGreen', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: self.ballAssets && self.ballAssets.green ? self.ballAssets.green.height * 0.045 : 22,
scaleX: 1.04,
scaleY: 1.04,
alpha: 0.32,
tint: 0x000000
})
};
// Attach all color assets, only one visible at a time
self.ballAssets = {
red: self.attachAsset('ballRed', {
anchorX: 0.5,
anchorY: 0.5
}),
blue: self.attachAsset('ballBlue', {
anchorX: 0.5,
anchorY: 0.5
}),
green: self.attachAsset('ballGreen', {
anchorX: 0.5,
anchorY: 0.5
})
};
// Place shadow behind the ball
self.addChildAt(self.shadowAssets.red, 0);
self.addChildAt(self.shadowAssets.blue, 0);
self.addChildAt(self.shadowAssets.green, 0);
self.setColor = function (color) {
self.color = color;
self.ballAssets.red.visible = color === 'red';
self.ballAssets.blue.visible = color === 'blue';
self.ballAssets.green.visible = color === 'green';
// Show only the shadow for the current color
self.shadowAssets.red.visible = color === 'red';
self.shadowAssets.blue.visible = color === 'blue';
self.shadowAssets.green.visible = color === 'green';
};
self.setColor('red');
self.rotationSpeed = 0.02; // radians per tick
self.update = function () {
// Defensive: ensure rotationSpeed is always defined
if (typeof self.rotationSpeed !== "number") self.rotationSpeed = 0.02;
self.rotation += self.rotationSpeed;
};
// Animate color change (optional: flash or scale)
self.animateColorChange = function () {
tween(self, {
scaleX: 1.15,
scaleY: 1.15
}, {
duration: 80,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.cubicIn
});
}
});
};
return self;
});
// Button class: handles color, label, and press feedback
var ColorButton = Container.expand(function () {
var self = Container.call(this);
// color: 'red', 'blue', 'green'
self.setColor = function (color) {
self.color = color;
if (self.asset) {
self.removeChild(self.asset);
}
if (self.label) {
self.removeChild(self.label);
}
if (self.hitbox) {
self.removeChild(self.hitbox);
}
var assetId = color === 'red' ? 'btnRed' : color === 'blue' ? 'btnBlue' : 'btnGreen';
self.asset = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Add realistic shadow by duplicating the button asset, tinting and offsetting it
if (self.shadow) {
self.removeChild(self.shadow);
}
var shadowAssetId = assetId;
self.shadow = self.attachAsset(shadowAssetId, {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: self.asset.height * 0.06,
// offset shadow downward (closer)
scaleX: 1.03,
scaleY: 1.03,
alpha: 0.32,
tint: 0x000000
});
self.shadow.zIndex = -2;
self.addChild(self.shadow);
self.addChild(self.asset);
};
// Animate press feedback
self.animatePress = function () {
tween(self.asset, {
scaleX: 0.93,
scaleY: 0.93
}, {
duration: 60,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(self.asset, {
scaleX: 1,
scaleY: 1
}, {
duration: 80,
easing: tween.cubicIn
});
}
});
};
// For hit detection
self.containsPoint = function (x, y) {
var local = self.toLocal({
x: x,
y: y
});
return local.x > -self.hitbox.width / 2 && local.x < self.hitbox.width / 2 && local.y > -self.hitbox.height / 2 && local.y < self.hitbox.height / 2;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xffb638
});
/****
* Game Code
****/
// btnBlue image
// btnGreen image
// btnRed image
// Health bar assets
// --- Game State ---
// Ball colors
// Button colors
// Sound
// Music (optional, but initialized for future use)
var ball;
var buttons = [];
var colorOrder = ['red', 'blue', 'green'];
var currentColor = 'red';
var score = 0;
var highScore = storage.highScore || 0;
var timeLimit = 5000; // ms, initial time to respond (5 seconds)
var minTimeLimit = 5000; // ms, minimum allowed (5 seconds)
var timeDecrease = 80; // ms, decrease per correct
var rotationSpeed = 0.02; // initial
var maxRotationSpeed = 0.09;
var rotationIncrease = 0.008;
var waitingForInput = true;
var responseTimer = null;
var lastTapTime = 0;
// --- UI Elements ---
// Add shadow for score text (centered, slightly offset, behind main score)
var scoreShadowTxt = new Text2('0', {
size: 120,
fill: "#000",
alpha: 0.32
});
scoreShadowTxt.anchor.set(0.5, 0);
scoreShadowTxt.y = 60 + 3; // offset shadow downward (closer)
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFF4500
});
scoreTxt.anchor.set(0.5, 0);
scoreTxt.y = 60;
// Add scoreTxt after timer and hearts to ensure it is always on top
LK.setTimeout(function () {
LK.gui.top.addChild(scoreShadowTxt);
LK.gui.top.addChild(scoreTxt);
}, 0);
var highScoreTxt = new Text2('BEST: ' + highScore, {
size: 60,
fill: 0xFF4500,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
highScoreTxt.anchor.set(0.5, 0);
highScoreTxt.y = 190;
LK.gui.top.addChild(highScoreTxt);
// --- Timer Bar (under score) ---
// --- Timer Bar (under score, in GUI) ---
var timerBar = LK.getAsset('timerBar', {
width: 900,
height: 60,
color: 0xffffff,
anchorX: 0.5,
anchorY: 0.0
});
timerBar.x = 0.5; // relative to gui width
timerBar.y = scoreTxt.y + scoreTxt.height + 70; // 70px below score (moved further down)
timerBar.alpha = 0.18;
timerBar.isGUI = true;
LK.gui.top.addChild(timerBar);
var timerBarFill = LK.getAsset('timerBarFill', {
width: 900,
height: 60,
color: 0x2ecc40,
anchorX: 0.5,
anchorY: 0.0
});
timerBarFill.x = 0.5; // relative to gui width
timerBarFill.y = scoreTxt.y + scoreTxt.height + 70; // 70px below score (moved further down)
timerBarFill.isGUI = true;
LK.gui.top.addChild(timerBarFill);
// --- Health Bars (under timer, in GUI) ---
var health = 3;
var maxHealth = 3;
var healthBars = [];
var healthBarSpacing = 130;
var healthBarWidth = 120;
var healthBarHeight = 120;
var healthBarAssets = [{
id: 'healthBarRed',
color: 0xd83318
},
// Red
{
id: 'healthBarBlue',
color: 0x187ad8
},
// Blue
{
id: 'healthBarGreen',
color: 0x2ecc40
} // Green
];
var healthBarY = timerBar.y + timerBar.height + 30; // 30px below timer bar
// Calculate total width of all health bars and starting x for centering
var totalHealthWidth = (maxHealth - 1) * healthBarSpacing;
var healthStartX = 0.5 + -(totalHealthWidth / 2); // 0.5 is center in LK.gui coordinates
for (var i = 0; i < maxHealth; i++) {
var assetInfo = healthBarAssets[i];
var bar = LK.getAsset(assetInfo.id, {
width: healthBarWidth,
height: healthBarHeight,
color: assetInfo.color,
anchorX: 0.5,
anchorY: 0.0
});
// Center health bars under the timer
bar.x = healthStartX + i * healthBarSpacing;
bar.y = healthBarY;
bar.isGUI = true;
LK.gui.top.addChild(bar);
healthBars.push(bar);
}
// Helper to update health bar visuals
function updateHealthBars() {
for (var i = 0; i < maxHealth; i++) {
// Each bar is independent: show full alpha if "alive", faded if "lost"
healthBars[i].alpha = i < health ? 1 : 0.18;
}
}
updateHealthBars();
// --- Setup Ball ---
ball = new ColorBall();
ball.x = 2048 / 2;
ball.y = 1100;
game.addChild(ball);
// --- Setup Buttons ---
var btnSpacing = 480;
var btnY = 2732 - 320;
// Create btnRed
var btnRed = new ColorButton();
btnRed.setColor('red');
btnRed.x = 2048 / 2 - btnSpacing;
btnRed.y = btnY;
game.addChild(btnRed);
// Create btnBlue
var btnBlue = new ColorButton();
btnBlue.setColor('blue');
btnBlue.x = 2048 / 2;
btnBlue.y = btnY;
game.addChild(btnBlue);
// Create btnGreen
var btnGreen = new ColorButton();
btnGreen.setColor('green');
btnGreen.x = 2048 / 2 + btnSpacing;
btnGreen.y = btnY;
game.addChild(btnGreen);
// Place all buttons in the same positions as btnRed, btnBlue, btnGreen
buttons = [btnRed, btnBlue, btnGreen];
// Ensure all color buttons are positioned exactly as btnRed, btnBlue, btnGreen
buttons[0].x = btnRed.x;
buttons[0].y = btnRed.y;
buttons[1].x = btnBlue.x;
buttons[1].y = btnBlue.y;
buttons[2].x = btnGreen.x;
buttons[2].y = btnGreen.y;
// --- Helper Functions ---
function setScore(val) {
score = val;
scoreTxt.setText(score);
if (typeof scoreShadowTxt !== "undefined") {
scoreShadowTxt.setText(score);
}
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('BEST: ' + highScore);
}
}
function randomColor() {
// Never repeat the same color twice in a row
var idx = colorOrder.indexOf(currentColor);
var nextIdx = idx;
while (nextIdx === idx) {
nextIdx = Math.floor(Math.random() * 3);
}
return colorOrder[nextIdx];
}
function startNewRound() {
waitingForInput = true;
// Pick new color
currentColor = randomColor();
ball.setColor(currentColor);
ball.animateColorChange();
// Timer always starts from 5 seconds (5000ms) for each round, and decreases by 0.1s for each correct answer
timeLimit = 5000 - score * 100;
if (timeLimit < 500) {
timeLimit = 500; // minimum 0.5s to avoid negative/zero
}
// --- Smoothly increase rotation speed at every multiple of 10 points ---
var baseSpeed = 0.02;
var speedStep = 0.008;
var maxSpeed = 0.09;
var targetSpeed = baseSpeed + Math.floor(score / 10) * speedStep;
if (targetSpeed > maxSpeed) targetSpeed = maxSpeed;
// Only tween if the speed is changing
if (Math.abs(ball.rotationSpeed - targetSpeed) > 0.0001) {
tween.stop(ball, {
rotationSpeed: true
});
tween(ball, {
rotationSpeed: targetSpeed
}, {
duration: 700,
easing: tween.cubicInOut
});
}
rotationSpeed = targetSpeed;
ball.targetRotationSpeed = targetSpeed;
// Start timer
timerBarFill.width = 900;
if (responseTimer) {
LK.clearTimeout(responseTimer);
responseTimer = null;
}
responseTimer = LK.setTimeout(function () {
// Time's up
waitingForInput = false;
LK.effects.flashScreen(0xff0000, 600);
LK.getSound('wrong').play();
health--;
updateHealthBars();
if (health <= 0) {
endGame();
} else {
// Give player another chance, start new round after short delay
responseTimer = LK.setTimeout(function () {
startNewRound();
}, 700);
}
}, timeLimit);
lastTapTime = Date.now();
}
function endGame() {
if (responseTimer) {
LK.clearTimeout(responseTimer);
responseTimer = null;
}
waitingForInput = false;
// Flash ball red
LK.effects.flashObject(ball, 0xff0000, 600);
// Show game over (handled by LK)
LK.showGameOver();
}
// --- Input Handling ---
game.down = function (x, y, obj) {
if (!waitingForInput) {
return;
}
// Divide the screen into three vertical regions: left, middle, right
var region;
if (x < 2048 / 3) {
region = 'left';
} else if (x > 2048 * 2 / 3) {
region = 'right';
} else {
region = 'middle';
}
// Map region to color
var color;
if (region === 'left') {
color = 'red';
} else if (region === 'right') {
color = 'green';
} else {
color = 'blue';
}
// Find the button with this color (for animation/feedback)
var btn = null;
for (var i = 0; i < buttons.length; i++) {
if (buttons[i].color === color) {
btn = buttons[i];
break;
}
}
if (btn) {
handleButtonPress(btn);
}
};
function handleButtonPress(btn) {
if (!waitingForInput) {
return;
}
btn.animatePress();
LK.effects.flashObject(btn.asset, 0xffffff, 120);
waitingForInput = false;
if (btn.color === currentColor) {
// Correct!
setScore(score + 1);
LK.getSound('correct').play();
// Animate ball
tween(ball, {
scaleX: 1.18,
scaleY: 1.18
}, {
duration: 80,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(ball, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.cubicIn
});
}
});
// Decrease timer by 0.1 seconds (100ms) for each correct answer
if (responseTimer) {
LK.clearTimeout(responseTimer);
responseTimer = null;
}
responseTimer = LK.setTimeout(function () {
startNewRound();
}, 220);
} else {
// Wrong!
LK.effects.flashScreen(0xff0000, 600);
LK.getSound('wrong').play();
if (responseTimer) {
LK.clearTimeout(responseTimer);
responseTimer = null;
}
health--;
updateHealthBars();
if (health <= 0) {
endGame();
} else {
// Decrease timer by 0.2 seconds (200ms) for each incorrect answer and reset to 3 seconds
timeLimit = 3000 - score * 100 - (maxHealth - health) * 200;
if (timeLimit < 500) {
timeLimit = 500; // minimum 0.5s to avoid negative/zero
}
// Give player another chance, start new round after short delay
responseTimer = LK.setTimeout(function () {
startNewRound();
}, 700);
}
}
}
// --- Game Update Loop ---
game.update = function () {
// Animate timer bar
if (waitingForInput) {
var elapsed = Date.now() - lastTapTime;
var frac = 1 - elapsed / timeLimit;
if (frac < 0) {
frac = 0;
}
timerBarFill.width = 900 * frac;
// Do not change timerBarFill color or tint here to prevent fading
} else {
timerBarFill.width = 0;
}
};
// --- Start Game ---
function startGame() {
LK.playMusic('g');
setScore(0);
timeLimit = 5000;
rotationSpeed = 0.02;
ball.rotationSpeed = rotationSpeed;
ball.targetRotationSpeed = rotationSpeed;
health = maxHealth;
updateHealthBars();
startNewRound();
}
startGame();
// --- Responsive UI (optional, handled by LK) ---
// --- Music (optional, not started by default) ---
// LK.playMusic('bgmusic', {fade: {start: 0, end: 1, duration: 1000}});