User prompt
Prevent note generation when key animation is not playing.
User prompt
Prevent note spawning in the top 4 square when animation is not playing.
User prompt
Notes spawn only when the animation plays in the top 4 square. Instantly spawn notes when the animation plays.
User prompt
Make all difficulty levels a little easier
User prompt
Spawn notes when animation plays in top 4 frames. Limit the maximum number of notes that can be on screen at the same time.
User prompt
Remove all note effects.
User prompt
Move the top 4 squares down a little. When an animation is played in these frames, a note will appear. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Move the bottom 4 squares very high.
User prompt
İ want to create a key animation that is synchronized with the beats of the background music. Every time there's a beat in the music, a key (either a virtual or on-secreen key) should visually animate – as if it's being pressed. The animation should follow the BPM (beats per minute) of the music. At each beat, a visual effect (like a scale change, color flash or glow) should be triggered. Remove all note animations. Then recreate button and note animations accordingly. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: setTimeout is not a function' in or related to this line: 'setTimeout(function () {' Line Number: 1453
User prompt
Please fix the bug: 'ReferenceError: notes is not defined' in or related to this line: 'for (var t = 0; t < notes.length; t++) {' Line Number: 1662
User prompt
İ want to create a key animation that is synchronized with the beats of the background music. Every time there's a beat in the music, a key (either a virtual or on-secreen key) should visually animate – as if it's being pressed. The animation should follow the BPM (beats per minute) of the music. At each beat, a visual effect (like a scale change, color flash or glow) should be triggered. Remove all note animations. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
İ want to create a key animation that is synchronized with the beats of the background music. Every time there's a beat in the music, a key (either a virtual or on-secreen key) should visually animate – as if it's being pressed. The animation should follow the BPM (beats per minute) of the music. At each beat, a visual effect (like a scale change, color flash or glow) should be triggered. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Remix started
Copy Rhythm Rush
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BackgroundParticle = Container.expand(function () {
var self = Container.call(this);
// Create particle using small note asset
self.particleGraphics = self.attachAsset('chordNote2', {
anchorX: 0.5,
anchorY: 0.5
});
// Random properties
self.speed = 1 + Math.random() * 3;
self.rotationSpeed = (Math.random() - 0.5) * 0.05;
self.floatAmplitude = 20 + Math.random() * 30;
self.floatSpeed = 0.02 + Math.random() * 0.03;
self.initialY = self.y;
// Set random position
self.x = Math.random() * 2048;
self.y = Math.random() * 2732;
self.initialY = self.y;
// Set random size and transparency
var scale = 0.1 + Math.random() * 0.3;
self.particleGraphics.scaleX = scale;
self.particleGraphics.scaleY = scale;
self.particleGraphics.alpha = 0.1 + Math.random() * 0.3;
// Random color tint
var colors = [0x8800ff, 0xff00ff, 0x00ffff, 0xffffff];
self.particleGraphics.tint = colors[Math.floor(Math.random() * colors.length)];
self.update = function () {
// Slow upward movement
self.y -= self.speed;
// Floating motion
self.x += Math.sin(LK.ticks * self.floatSpeed) * 0.5;
// Gentle rotation
self.particleGraphics.rotation += self.rotationSpeed;
// Music-reactive pulsing during gameplay
var basePulse = 1 + Math.sin(LK.ticks * 0.05) * 0.1;
var musicPulse = 1;
if (gameState === 'playing' && beatIntensity > 0) {
musicPulse = 1 + beatIntensity * 0.3;
beatIntensity *= 0.95; // Decay beat intensity
}
var finalPulse = basePulse * musicPulse;
self.particleGraphics.scaleX = scale * finalPulse;
self.particleGraphics.scaleY = scale * finalPulse;
// Music-reactive color shifting
if (gameState === 'playing') {
var beatColor = Math.sin(musicBeat * 0.1) * 0.5 + 0.5;
var musicColorShift = Math.floor(beatColor * 100);
self.particleGraphics.tint = self.particleGraphics.tint & 0xFF00FF | musicColorShift << 8;
}
// Reset particle when it goes off screen
if (self.y < -100) {
self.y = 2732 + 100;
self.x = Math.random() * 2048;
}
};
return self;
});
var BackgroundWave = Container.expand(function () {
var self = Container.call(this);
// Create wave using lane asset
self.waveGraphics = self.attachAsset('lane', {
anchorX: 0.5,
anchorY: 0.5
});
// Wave properties
self.amplitude = 50 + Math.random() * 100;
self.frequency = 0.01 + Math.random() * 0.02;
self.speed = 0.5 + Math.random() * 1.5;
self.initialX = Math.random() * 2048;
self.direction = Math.random() > 0.5 ? 1 : -1;
// Set position and appearance
self.x = self.initialX;
self.y = Math.random() * 2732;
self.waveGraphics.alpha = 0.05 + Math.random() * 0.1;
self.waveGraphics.scaleX = 0.5 + Math.random() * 1.5;
self.waveGraphics.scaleY = 0.1 + Math.random() * 0.3;
// Random color tint
var colors = [0x8800ff, 0xff00ff, 0x00ffff];
self.waveGraphics.tint = colors[Math.floor(Math.random() * colors.length)];
self.update = function () {
// Wave movement
self.x += self.speed * self.direction;
// Sine wave motion
self.y += Math.sin(LK.ticks * self.frequency) * 0.5;
// Gentle rotation
self.waveGraphics.rotation = Math.sin(LK.ticks * 0.02) * 0.2;
// Reset wave when it goes off screen
if (self.x < -200 || self.x > 2248) {
self.x = self.direction > 0 ? -200 : 2248;
self.y = Math.random() * 2732;
}
};
return self;
});
var Button = Container.expand(function (lane) {
var self = Container.call(this);
self.lane = lane;
self.isPressed = false;
self.buttonGraphics = self.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
// Position at bottom of lane
self.x = lane * (2048 / 3) + 2048 / 6;
self.y = 2732 - 200;
self.down = function (x, y, obj) {
self.press();
};
self.press = function () {
if (self.isPressed) return;
self.isPressed = true;
// Create ripple wave effect
var ripple = game.addChild(LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5
}));
ripple.x = self.x;
ripple.y = self.y;
ripple.alpha = 0.8;
ripple.scaleX = 0.8;
ripple.scaleY = 0.8;
ripple.tint = 0x00FFFF;
// Animate ripple expanding outward
tween(ripple, {
scaleX: 2.5,
scaleY: 2.5,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
if (ripple && ripple.parent) {
ripple.destroy();
}
}
});
// Enhanced press animation with bounce
tween(self.buttonGraphics, {
alpha: 0.6,
scaleX: 0.85,
scaleY: 0.85,
rotation: Math.PI * 0.1
}, {
duration: 80,
easing: tween.easeOut,
onFinish: function onFinish() {
// Quick bounce back
tween(self.buttonGraphics, {
scaleX: 1.1,
scaleY: 1.1,
rotation: -Math.PI * 0.05
}, {
duration: 60,
easing: tween.bounceOut
});
}
});
// Check for beat timing accuracy when button is pressed
var currentTime = LK.ticks;
var timeSinceLastBeat = currentTime - lastBeatTime;
var beatWindow = beatInterval * 0.3; // 30% of beat interval for timing window
if (gameState === 'playing') {
var points = 0;
var feedbackText = '';
// Check if button press is close to a beat
if (timeSinceLastBeat <= beatWindow) {
// Perfect timing - very close to beat
points = 100;
feedbackText = 'PERFECT BEAT';
LK.getSound('hit').play();
} else if (timeSinceLastBeat <= beatWindow * 2) {
// Good timing - somewhat close to beat
points = 50;
feedbackText = 'GOOD BEAT';
LK.getSound('hit').play();
} else {
// Off beat
feedbackText = 'OFF BEAT';
LK.getSound('miss').play();
}
if (points > 0) {
LK.setScore(LK.getScore() + points);
scoreTxt.setText(LK.getScore());
updateCombo(true);
} else {
updateCombo(false);
}
// Show feedback text
feedbackTxt.setText(feedbackText);
feedbackTxt.alpha = 1;
tween(feedbackTxt, {
alpha: 0
}, {
duration: 800
});
// Create score animation if points were earned
if (points > 0) {
var scoreText = new Text2('+' + points, {
size: 50,
fill: points === 100 ? 0xFFD700 : 0x00FFFF
});
scoreText.anchor.set(0.5, 0.5);
scoreText.x = self.x;
scoreText.y = self.y - 100;
scoreText.alpha = 1;
game.addChild(scoreText);
tween(scoreText, {
y: scoreText.y - 150,
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
if (scoreText && scoreText.parent) {
scoreText.destroy();
}
}
});
}
}
// Reset button after short delay with smooth animation
LK.setTimeout(function () {
self.isPressed = false;
// Smooth release animation
tween(self.buttonGraphics, {
alpha: 1.0,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.elasticOut
});
}, 100);
};
return self;
});
var Key = Container.expand(function () {
var self = Container.call(this);
self.keyGraphics = self.attachAsset('key', {
anchorX: 0.5,
anchorY: 0.5
});
// Position key in center of screen
self.x = 2048 / 2;
self.y = 2732 / 2;
// Key animation properties
self.baseScale = 1.0;
self.isAnimating = false;
// Beat animation method
self.animateOnBeat = function (intensity) {
if (self.isAnimating) return;
self.isAnimating = true;
// Scale and color effect based on beat intensity
var targetScale = self.baseScale + intensity * 0.5;
var beatColor = intensity > 0.7 ? 0xFFD700 : intensity > 0.4 ? 0x00FFFF : 0xFFFFFF;
// Store original values
var originalScale = self.keyGraphics.scaleX;
var originalTint = self.keyGraphics.tint;
// Quick press animation
tween(self.keyGraphics, {
scaleX: targetScale * 0.8,
scaleY: targetScale * 0.8,
tint: beatColor
}, {
duration: 50,
easing: tween.easeOut,
onFinish: function onFinish() {
// Release animation
tween(self.keyGraphics, {
scaleX: targetScale,
scaleY: targetScale
}, {
duration: 100,
easing: tween.bounceOut,
onFinish: function onFinish() {
// Return to normal
tween(self.keyGraphics, {
scaleX: originalScale,
scaleY: originalScale,
tint: originalTint
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isAnimating = false;
}
});
}
});
}
});
// Add glow effect for strong beats
if (intensity > 0.7) {
var glowRing = game.addChild(LK.getAsset('key', {
anchorX: 0.5,
anchorY: 0.5
}));
glowRing.x = self.x;
glowRing.y = self.y;
glowRing.keyGraphics = glowRing.children[0];
glowRing.keyGraphics.alpha = 0.6;
glowRing.keyGraphics.tint = 0xFFD700;
glowRing.keyGraphics.scaleX = 0.8;
glowRing.keyGraphics.scaleY = 0.8;
tween(glowRing.keyGraphics, {
scaleX: 2.0,
scaleY: 2.0,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
if (glowRing && glowRing.parent) {
glowRing.destroy();
}
}
});
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game state management
var gameState = 'menu'; // 'menu', 'playing', or 'gameOver'
// Background effects
var backgroundParticles = [];
var backgroundWaves = [];
var startButton;
var gameOverButton;
var difficultyButtons = [];
var currentDifficulty = -1; // -1=None selected, 0=Easy, 1=Medium, 2=Hard, 3=Expert
var difficultySelected = false;
var musicButtons = [];
var currentMusic = -1; // -1=None selected, 0=Music1, 1=Music2, 2=Music3
var musicTracks = [{
name: '1',
id: 'GameMusic1',
color: 0x8800ff
}, {
name: '2',
id: 'GameMusic2',
color: 0xff8800
}, {
name: '3',
id: 'GameMusic3',
color: 0x0088ff
}, {
name: '4',
id: 'GameMusic4',
color: 0x00ff88
}, {
name: '5',
id: 'GameMusic5',
color: 0xff0088
}, {
name: '6',
id: 'GameMusic6',
color: 0x88ff00
}];
var difficulties = [{
name: 'EASY',
spawnInterval: 120,
maxNotes: 4,
timeLimit: 60,
noteSpeedMultiplier: 0.8,
color: 0x00FF00
}, {
name: 'MEDIUM',
spawnInterval: 80,
maxNotes: 6,
timeLimit: 60,
noteSpeedMultiplier: 1.2,
color: 0xFFFF00
}, {
name: 'HARD',
spawnInterval: 50,
maxNotes: 8,
timeLimit: 60,
noteSpeedMultiplier: 1.6,
color: 0xFF8800
}, {
name: 'EXPERT',
spawnInterval: 30,
maxNotes: 12,
timeLimit: 60,
noteSpeedMultiplier: 2.0,
color: 0xFF0000
}];
// Game variables
var buttons = [];
var timingLineY = 2732 - 500;
var bottomLineY = 2732 - 2; // Position miss line at bottom edge of screen
// Key and beat synchronization variables
var visualKey = null;
var musicBeat = 0;
var beatInterval = 30; // frames per beat (adjustable based on music tempo)
var beatIntensity = 0;
var lastBeatTime = 0;
var bpmSettings = {
'GameMusic1': 120,
// 120 BPM
'GameMusic2': 128,
// 128 BPM
'GameMusic3': 110,
// 110 BPM
'GameMusic4': 140,
// 140 BPM
'GameMusic5': 100,
// 100 BPM
'GameMusic6': 150 // 150 BPM
};
var currentBPM = 120;
// Create lane separators
var lane1 = game.addChild(LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0
}));
lane1.x = 2048 / 3;
lane1.y = 0;
lane1.visible = false; // Hide initially
var lane2 = game.addChild(LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0
}));
lane2.x = 2048 / 3 * 2;
lane2.y = 0;
lane2.visible = false; // Hide initially
// Create timing line
var timingLine = game.addChild(LK.getAsset('timingLine', {
anchorX: 0,
anchorY: 0.5
}));
timingLine.x = 0;
timingLine.y = timingLineY;
timingLine.visible = false; // Hide initially
// Create bottom line
var bottomLine = game.addChild(LK.getAsset('bottomLine', {
anchorX: 0,
anchorY: 0.5
}));
bottomLine.x = 0;
bottomLine.y = bottomLineY;
bottomLine.visible = false; // Hide initially
// Create start menu elements
var titleTxt = new Text2('RHYTHM GAME', {
size: 120,
fill: 0xFFFFFF
});
titleTxt.anchor.set(0.5, 0.5);
titleTxt.x = 2048 / 2;
titleTxt.y = 2732 / 2 - 200;
titleTxt.alpha = 0;
titleTxt.scaleX = 0.5;
titleTxt.scaleY = 0.5;
game.addChild(titleTxt);
// Animate title appearing
tween(titleTxt, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 1000,
easing: tween.elasticOut
});
// Add continuous floating animation
function animateTitleFloat() {
tween(titleTxt, {
y: titleTxt.y - 20
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(titleTxt, {
y: titleTxt.y + 20
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: animateTitleFloat
});
}
});
}
LK.setTimeout(animateTitleFloat, 1500);
// Add breathing effect to background elements
function createBreathingEffect() {
// Animate lane separators with subtle breathing
tween(lane1, {
alpha: 0.8,
scaleY: 1.05
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(lane1, {
alpha: 0.5,
scaleY: 1.0
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: createBreathingEffect
});
}
});
// Offset timing for lane2
LK.setTimeout(function () {
tween(lane2, {
alpha: 0.8,
scaleY: 1.05
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(lane2, {
alpha: 0.5,
scaleY: 1.0
}, {
duration: 3000,
easing: tween.easeInOut
});
}
});
}, 1500);
}
// Start breathing effect when game elements are visible
var breathingEffectStarted = false;
var startButtonShape = game.addChild(LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5
}));
startButtonShape.x = 2048 / 2;
startButtonShape.y = 2732 / 2 + 100;
startButtonShape.tint = 0x00AA00;
startButtonShape.alpha = 0;
startButtonShape.scaleX = 0;
startButtonShape.scaleY = 0;
// Animate start button appearing
tween(startButtonShape, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.bounceOut,
delay: 1200
});
// Add pulsing glow effect to start button
function animateStartButtonPulse() {
tween(startButtonShape, {
scaleX: 1.1,
scaleY: 1.1,
tint: 0x00FF00
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(startButtonShape, {
scaleX: 1,
scaleY: 1,
tint: 0x00AA00
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: animateStartButtonPulse
});
}
});
}
LK.setTimeout(animateStartButtonPulse, 2000);
var startButtonTxt = new Text2('START', {
size: 60,
fill: 0xFFFFFF
});
startButtonTxt.anchor.set(0.5, 0.5);
startButtonTxt.x = 2048 / 2 + 300;
startButtonTxt.y = 2732 / 2 + 100;
startButtonTxt.alpha = 0;
game.addChild(startButtonTxt);
// Animate start button text sliding in
tween(startButtonTxt, {
x: 2048 / 2,
alpha: 1
}, {
duration: 600,
easing: tween.easeOut,
delay: 1400
});
// Create difficulty selection text
var difficultyTitleTxt = new Text2('SELECT DIFFICULTY', {
size: 80,
fill: 0xFFFFFF
});
difficultyTitleTxt.anchor.set(0.5, 0.5);
difficultyTitleTxt.x = 2048 / 2;
difficultyTitleTxt.y = 2732 / 2 + 300;
game.addChild(difficultyTitleTxt);
// Create difficulty selection indicator text
var difficultyIndicatorTxt = new Text2('None selected', {
size: 60,
fill: 0xFF0000
});
difficultyIndicatorTxt.anchor.set(0.5, 0.5);
difficultyIndicatorTxt.x = 2048 / 2;
difficultyIndicatorTxt.y = 2732 / 2 + 500;
difficultyIndicatorTxt.alpha = 0;
game.addChild(difficultyIndicatorTxt);
// Create music selection text
var musicTitleTxt = new Text2('SELECT MUSIC', {
size: 70,
fill: 0xFFFFFF
});
musicTitleTxt.anchor.set(0.5, 0.5);
musicTitleTxt.x = 2048 / 2;
musicTitleTxt.y = 2732 / 2 + 650;
game.addChild(musicTitleTxt);
// Create music selection indicator text
var musicIndicatorTxt = new Text2('None selected', {
size: 50,
fill: 0xFF0000
});
musicIndicatorTxt.anchor.set(0.5, 0.5);
musicIndicatorTxt.x = 2048 / 2;
musicIndicatorTxt.y = 2732 / 2 + 1130;
musicIndicatorTxt.alpha = 0;
game.addChild(musicIndicatorTxt);
// Create difficulty buttons
for (var d = 0; d < difficulties.length; d++) {
var diffButton = {
index: d,
text: new Text2(difficulties[d].name, {
size: 50,
fill: difficulties[d].color
})
};
diffButton.text.anchor.set(0.5, 0.5);
diffButton.text.x = d * 400 + 424; // Spread across screen
diffButton.text.y = 2732 / 2 + 400; // Below the "select difficulty" text
diffButton.text.scaleX = 0;
diffButton.text.scaleY = 0;
diffButton.text.alpha = 0;
game.addChild(diffButton.text);
difficultyButtons.push(diffButton);
// Animate text appearing with staggered timing
tween(diffButton.text, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 600,
easing: tween.elasticOut,
delay: d * 150
});
}
// Animate difficulty title appearing
difficultyTitleTxt.alpha = 0;
difficultyTitleTxt.scaleY = 0;
tween(difficultyTitleTxt, {
alpha: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.bounceOut,
delay: 200
});
// Animate difficulty indicator appearing
tween(difficultyIndicatorTxt, {
alpha: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.bounceOut,
delay: 800
});
// Create music selection buttons
for (var m = 0; m < musicTracks.length; m++) {
var musicButton = {
index: m,
shape: game.addChild(LK.getAsset('musicButton', {
anchorX: 0.5,
anchorY: 0.5
})),
text: new Text2(musicTracks[m].name, {
size: 40,
fill: 0xFFFFFF
})
};
// Position buttons in 2 rows of 3
var row = Math.floor(m / 3);
var col = m % 3;
musicButton.shape.x = col * 300 + 724; // Spread across screen
musicButton.shape.y = 2732 / 2 + 800 + row * 200;
musicButton.shape.tint = musicTracks[m].color;
musicButton.shape.scaleX = 0;
musicButton.shape.scaleY = 0;
musicButton.shape.alpha = 0;
musicButton.text.anchor.set(0.5, 0.5);
musicButton.text.x = musicButton.shape.x;
musicButton.text.y = musicButton.shape.y;
musicButton.text.scaleX = 0;
musicButton.text.scaleY = 0;
musicButton.text.alpha = 0;
game.addChild(musicButton.text);
musicButtons.push(musicButton);
// Animate buttons appearing with staggered timing
tween(musicButton.shape, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 600,
easing: tween.elasticOut,
delay: 1000 + m * 100
});
tween(musicButton.text, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 600,
easing: tween.elasticOut,
delay: 1200 + m * 100
});
}
// Animate music title appearing
musicTitleTxt.alpha = 0;
musicTitleTxt.scaleY = 0;
tween(musicTitleTxt, {
alpha: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.bounceOut,
delay: 400
});
// Animate music indicator appearing
tween(musicIndicatorTxt, {
alpha: 1
}, {
duration: 800,
easing: tween.bounceOut,
delay: 1000
});
// No initial music selection - all buttons start at normal size
// Initialize background effects
function createBackgroundEffects() {
// Create floating particles
for (var p = 0; p < 15; p++) {
var particle = new BackgroundParticle();
backgroundParticles.push(particle);
game.addChildAt(particle, 0); // Add behind other elements
}
// Create background waves
for (var w = 0; w < 5; w++) {
var wave = new BackgroundWave();
backgroundWaves.push(wave);
game.addChildAt(wave, 0); // Add behind other elements
}
}
// Start background effects
createBackgroundEffects();
// Add dynamic background color transitions
var backgroundColors = [0x001122, 0x220011, 0x112200, 0x001100];
var currentBgColorIndex = 0;
var bgColorTransitionTimer = 0;
function updateBackgroundColor() {
bgColorTransitionTimer++;
// Music-reactive beat pulsing
if (gameState === 'playing') {
musicBeat++;
if (musicBeat >= beatInterval) {
musicBeat = 0;
beatIntensity = 1.0;
// Flash background on beat
var currentBgColor = backgroundColors[currentBgColorIndex];
var brightColor = {
r: Math.min(255, (currentBgColor >> 16 & 0xFF) + 30),
g: Math.min(255, (currentBgColor >> 8 & 0xFF) + 30),
b: Math.min(255, (currentBgColor & 0xFF) + 30)
};
var flashColor = brightColor.r << 16 | brightColor.g << 8 | brightColor.b;
game.setBackgroundColor(flashColor);
// Fade back to normal
tween({
intensity: 1
}, {
intensity: 0
}, {
duration: beatInterval * 16,
easing: tween.easeOut,
onUpdate: function onUpdate() {
var fade = this.intensity;
var newR = Math.floor((currentBgColor >> 16 & 0xFF) + (brightColor.r - (currentBgColor >> 16 & 0xFF)) * fade);
var newG = Math.floor((currentBgColor >> 8 & 0xFF) + (brightColor.g - (currentBgColor >> 8 & 0xFF)) * fade);
var newB = Math.floor((currentBgColor & 0xFF) + (brightColor.b - (currentBgColor & 0xFF)) * fade);
game.setBackgroundColor(newR << 16 | newG << 8 | newB);
}
});
}
}
if (bgColorTransitionTimer >= 600) {
// Change every 10 seconds
bgColorTransitionTimer = 0;
var nextColorIndex = (currentBgColorIndex + 1) % backgroundColors.length;
// Smooth color transition using tween
var currentColor = backgroundColors[currentBgColorIndex];
var nextColor = backgroundColors[nextColorIndex];
// Create temporary object to animate color values
var colorTransition = {
r: currentColor >> 16 & 0xFF,
g: currentColor >> 8 & 0xFF,
b: currentColor & 0xFF
};
var targetR = nextColor >> 16 & 0xFF;
var targetG = nextColor >> 8 & 0xFF;
var targetB = nextColor & 0xFF;
tween(colorTransition, {
r: targetR,
g: targetG,
b: targetB
}, {
duration: 3000,
easing: tween.easeInOut,
onUpdate: function onUpdate() {
var newColor = Math.floor(colorTransition.r) << 16 | Math.floor(colorTransition.g) << 8 | Math.floor(colorTransition.b);
game.setBackgroundColor(newColor);
},
onFinish: function onFinish() {
currentBgColorIndex = nextColorIndex;
}
});
}
}
// No initial difficulty highlighting since none is selected
// Create start button handler
startButton = {
shape: startButtonShape,
text: startButtonTxt,
down: function down(x, y, obj) {
this.startGame();
},
startGame: function startGame() {
// Check if difficulty is selected
if (currentDifficulty === -1) {
// Flash difficulty title to indicate selection is required
tween(difficultyTitleTxt, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFF0000
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(difficultyTitleTxt, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
return; // Don't start the game
}
// Check if music is selected
if (currentMusic === -1) {
// Flash music title to indicate selection is required
tween(musicTitleTxt, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFF0000
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(musicTitleTxt, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
return; // Don't start the game
}
// Stop all music for 3 seconds and play start sound
LK.stopMusic();
LK.getSound('Gameisstart').play();
// Create enhanced zoom animation sequence with pulsing and glow effects
// Initial button press feedback - quick scale down
tween(this.shape, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
// Phase 1: Zoom out with rotation and color change
tween(startButton.shape, {
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.7,
rotation: Math.PI * 2,
tint: 0x00FFFF
}, {
duration: 700,
easing: tween.easeOut,
onFinish: function onFinish() {
// Phase 2: Explosive zoom in with multiple pulses
tween(startButton.shape, {
scaleX: 4.0,
scaleY: 4.0,
alpha: 0,
rotation: Math.PI * 4,
tint: 0xFFFFFF
}, {
duration: 2000,
easing: tween.elasticOut
});
// Create multiple expanding rings for more dramatic effect
for (var r = 0; r < 3; r++) {
var expandingRing = game.addChild(LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5
}));
expandingRing.x = startButton.shape.x;
expandingRing.y = startButton.shape.y;
expandingRing.tint = r === 0 ? 0x00FF00 : r === 1 ? 0x00FFFF : 0xFFFFFF;
expandingRing.alpha = 0.6;
expandingRing.scaleX = 0.5;
expandingRing.scaleY = 0.5;
// Animate each ring with staggered timing
tween(expandingRing, {
scaleX: 6.0 + r * 2,
scaleY: 6.0 + r * 2,
alpha: 0,
rotation: Math.PI * (2 + r)
}, {
duration: 2500 + r * 200,
easing: tween.easeOut,
delay: r * 150,
onFinish: function onFinish() {
if (expandingRing && expandingRing.parent) {
expandingRing.destroy();
}
}
});
}
}
});
}
});
// Text animation with bounce and glow effect
tween(this.text, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
// Text zoom out with glow
tween(startButton.text, {
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.8,
tint: 0xFFD700
}, {
duration: 700,
easing: tween.easeOut,
onFinish: function onFinish() {
// Text explosive zoom with sparkle effect
tween(startButton.text, {
scaleX: 4.0,
scaleY: 4.0,
alpha: 0,
tint: 0xFFFFFF
}, {
duration: 2000,
easing: tween.bounceOut
});
}
});
}
});
// Hide menu elements immediately
titleTxt.visible = false;
// Hide difficulty selection
difficultyTitleTxt.visible = false;
difficultyIndicatorTxt.visible = false;
for (var d = 0; d < difficultyButtons.length; d++) {
difficultyButtons[d].text.visible = false;
}
// Hide music selection
musicTitleTxt.visible = false;
musicIndicatorTxt.visible = false;
for (var m = 0; m < musicButtons.length; m++) {
musicButtons[m].shape.visible = false;
musicButtons[m].text.visible = false;
}
// Start the actual game after 3 seconds
LK.setTimeout(function () {
// Hide start button elements after animation
startButton.shape.visible = false;
startButton.text.visible = false;
// Show game elements
timerTxt.visible = true;
scoreTxt.visible = true;
timingLine.visible = true;
bottomLine.visible = true;
lane1.visible = true;
lane2.visible = true;
// Create game buttons
for (var i = 0; i < 3; i++) {
var button = new Button(i);
buttons.push(button);
game.addChild(button);
}
// Create visual key for beat synchronization
visualKey = new Key();
game.addChild(visualKey);
// Play selected music and set BPM
var selectedMusicId = musicTracks[currentMusic].id;
LK.playMusic(selectedMusicId);
currentBPM = bpmSettings[selectedMusicId] || 120;
beatInterval = Math.floor(3600 / currentBPM); // Convert BPM to frames (60fps)
// Apply difficulty settings
var difficulty = difficulties[currentDifficulty];
timeRemaining = difficulty.timeLimit;
// Start the game
gameState = 'playing';
LK.setScore(0);
scoreTxt.setText('0');
timerTxt.setText(timeRemaining.toString());
}, 3000);
}
};
// Initially hide game elements
var gameElements = [];
// Create countdown timer
var timeRemaining = 60;
// Create timer display above score
var timerTxt = new Text2('60', {
size: 80,
fill: 0xFFFFFF
});
timerTxt.anchor.set(0.5, 0.5);
timerTxt.x = 2048 / 2;
timerTxt.y = 2732 / 2 - 300;
timerTxt.visible = false; // Hide initially
game.addChild(timerTxt);
// Create score display in center of game area
var scoreTxt = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0.5);
scoreTxt.x = 2048 / 2;
scoreTxt.y = 2732 / 2 - 200;
scoreTxt.visible = false; // Hide initially
game.addChild(scoreTxt);
// Create feedback text display
var feedbackTxt = new Text2('', {
size: 80,
fill: 0xFFFFFF
});
feedbackTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(feedbackTxt);
feedbackTxt.alpha = 0;
// Create game over menu elements
var gameOverTxt = new Text2('GAME OVER', {
size: 120,
fill: 0xFF0000
});
gameOverTxt.anchor.set(0.5, 0.5);
gameOverTxt.x = 2048 / 2;
gameOverTxt.y = 2732 / 2 - 300;
gameOverTxt.visible = false;
game.addChild(gameOverTxt);
var finalScoreTxt = new Text2('', {
size: 80,
fill: 0xFFFFFF
});
finalScoreTxt.anchor.set(0.5, 0.5);
finalScoreTxt.x = 2048 / 2;
finalScoreTxt.y = 2732 / 2 - 150;
finalScoreTxt.visible = false;
game.addChild(finalScoreTxt);
var restartButtonShape = game.addChild(LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5
}));
restartButtonShape.x = 2048 / 2;
restartButtonShape.y = 2732 / 2 + 100;
restartButtonShape.tint = 0x0066CC;
restartButtonShape.visible = false;
var restartButtonTxt = new Text2('RESTART', {
size: 60,
fill: 0xFFFFFF
});
restartButtonTxt.anchor.set(0.5, 0.5);
restartButtonTxt.x = 2048 / 2;
restartButtonTxt.y = 2732 / 2 + 100;
restartButtonTxt.visible = false;
game.addChild(restartButtonTxt);
// Create restart button handler
gameOverButton = {
shape: restartButtonShape,
text: restartButtonTxt,
down: function down(x, y, obj) {
this.restartGame();
},
restartGame: function restartGame() {
// Stop all music when restart is pressed
LK.stopMusic();
// Hide game over elements
gameOverTxt.visible = false;
finalScoreTxt.visible = false;
this.shape.visible = false;
this.text.visible = false;
// Clear existing buttons and key
for (var i = buttons.length - 1; i >= 0; i--) {
buttons[i].destroy();
buttons.splice(i, 1);
}
// Clean up visual key if it exists
if (visualKey && visualKey.parent) {
visualKey.destroy();
visualKey = null;
}
// Show menu elements with smooth animations
titleTxt.visible = true;
titleTxt.alpha = 0;
titleTxt.scaleX = 0.8;
titleTxt.scaleY = 0.8;
startButton.shape.visible = true;
startButton.shape.alpha = 0;
startButton.shape.scaleX = 0;
startButton.shape.scaleY = 0;
startButton.text.visible = true;
startButton.text.alpha = 0;
startButton.text.x = startButton.text.x + 200;
difficultyTitleTxt.visible = true;
difficultyTitleTxt.alpha = 0;
difficultyTitleTxt.scaleY = 0;
difficultyIndicatorTxt.visible = true;
difficultyIndicatorTxt.alpha = 0;
musicTitleTxt.visible = true;
musicTitleTxt.alpha = 0;
musicTitleTxt.scaleY = 0;
musicIndicatorTxt.visible = true;
musicIndicatorTxt.alpha = 0;
// Animate title appearing
tween(titleTxt, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.elasticOut
});
// Animate start button
tween(startButton.shape, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 600,
easing: tween.bounceOut,
delay: 400
});
// Animate start button text sliding in
tween(startButton.text, {
alpha: 1,
x: startButton.text.x - 200
}, {
duration: 500,
easing: tween.easeOut,
delay: 600
});
// Animate difficulty title
tween(difficultyTitleTxt, {
alpha: 1,
scaleY: 1
}, {
duration: 600,
easing: tween.bounceOut,
delay: 200
});
// Animate difficulty indicator
tween(difficultyIndicatorTxt, {
alpha: 1
}, {
duration: 400,
delay: 800
});
// Animate difficulty buttons
for (var d = 0; d < difficultyButtons.length; d++) {
difficultyButtons[d].text.visible = true;
difficultyButtons[d].text.alpha = 0;
difficultyButtons[d].text.scaleX = 0;
difficultyButtons[d].text.scaleY = 0;
// Staggered animation for each button
tween(difficultyButtons[d].text, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.elasticOut,
delay: 600 + d * 100
});
}
// Animate music title
tween(musicTitleTxt, {
alpha: 1,
scaleY: 1
}, {
duration: 600,
easing: tween.bounceOut,
delay: 400
});
// Animate music indicator
tween(musicIndicatorTxt, {
alpha: 1
}, {
duration: 400,
delay: 1000
});
// Animate music buttons
for (var m = 0; m < musicButtons.length; m++) {
musicButtons[m].shape.visible = true;
musicButtons[m].shape.alpha = 0;
musicButtons[m].shape.scaleX = currentMusic === m && currentMusic !== -1 ? 1.2 : 0;
musicButtons[m].shape.scaleY = currentMusic === m && currentMusic !== -1 ? 1.2 : 0;
musicButtons[m].text.visible = true;
musicButtons[m].text.alpha = 0;
musicButtons[m].text.scaleX = 0;
musicButtons[m].text.scaleY = 0;
// Staggered animation for each button
tween(musicButtons[m].shape, {
alpha: 1,
scaleX: currentMusic === m && currentMusic !== -1 ? 1.2 : 1,
scaleY: currentMusic === m && currentMusic !== -1 ? 1.2 : 1
}, {
duration: 500,
easing: tween.elasticOut,
delay: 800 + m * 100
});
tween(musicButtons[m].text, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.elasticOut,
delay: 1000 + m * 100
});
}
// Hide game elements
timerTxt.visible = false;
scoreTxt.visible = false;
timingLine.visible = false;
bottomLine.visible = false;
lane1.visible = false;
lane2.visible = false;
// Reset game state to menu
gameState = 'menu';
spawnTimer = 0;
LK.setScore(0);
}
};
// Create explosion animation function
function createExplosion(x, y, color) {
var particleCount = 8;
var particles = [];
for (var i = 0; i < particleCount; i++) {
// Create particle using small ellipse shape
var particle = game.addChild(LK.getAsset('fastNote', {
anchorX: 0.5,
anchorY: 0.5
}));
particle.x = x;
particle.y = y;
particle.tint = color || 0xFFFFFF;
particle.scaleX = 0.3;
particle.scaleY = 0.3;
particles.push(particle);
// Calculate random direction for each particle
var angle = i / particleCount * Math.PI * 2 + (Math.random() - 0.5) * 0.5;
var distance = 100 + Math.random() * 50;
var targetX = x + Math.cos(angle) * distance;
var targetY = y + Math.sin(angle) * distance;
// Animate particle outward and fade out
tween(particle, {
x: targetX,
y: targetY,
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 600 + Math.random() * 400,
easing: tween.easeOut,
onFinish: function onFinish() {
if (particle && particle.parent) {
particle.destroy();
}
}
});
}
}
// Create screen shake effect
function shakeScreen(intensity, duration) {
var originalX = game.x;
var originalY = game.y;
var shakeStartTime = LK.ticks;
var shakeDuration = duration || 300;
var shakeIntensity = intensity || 20;
// Create shake animation
function shakeUpdate() {
var elapsed = LK.ticks - shakeStartTime;
if (elapsed < shakeDuration) {
var progress = elapsed / shakeDuration;
var currentIntensity = shakeIntensity * (1 - progress);
game.x = originalX + (Math.random() - 0.5) * currentIntensity * 2;
game.y = originalY + (Math.random() - 0.5) * currentIntensity * 2;
LK.setTimeout(shakeUpdate, 16); // ~60fps
} else {
game.x = originalX;
game.y = originalY;
}
}
shakeUpdate();
}
// Create combo streak effects
var comboCount = 0;
var lastHitTime = 0;
function updateCombo(isHit) {
if (isHit) {
var currentTime = LK.ticks;
if (currentTime - lastHitTime < 180) {
// Within 3 seconds
comboCount++;
} else {
comboCount = 1;
}
lastHitTime = currentTime;
// Show combo effects for streaks
if (comboCount >= 5 && comboCount % 5 === 0) {
// Create combo text
var comboText = new Text2('COMBO x' + comboCount, {
size: 60,
fill: 0xFFD700
});
comboText.anchor.set(0.5, 0.5);
comboText.x = 2048 / 2;
comboText.y = 2732 / 2 + 100;
comboText.alpha = 0;
game.addChild(comboText);
// Animate combo text
tween(comboText, {
alpha: 1,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(comboText, {
alpha: 0,
y: comboText.y - 100
}, {
duration: 500,
onFinish: function onFinish() {
if (comboText && comboText.parent) {
comboText.destroy();
}
}
});
}
});
}
} else {
comboCount = 0;
}
}
// Beat detection and key animation trigger
function detectBeat() {
musicBeat++;
if (musicBeat >= beatInterval) {
musicBeat = 0;
lastBeatTime = LK.ticks;
// Calculate beat intensity based on game progression
beatIntensity = 0.8 + Math.random() * 0.4; // Random intensity between 0.8-1.2
// Trigger key animation if visual key exists
if (visualKey && gameState === 'playing') {
visualKey.animateOnBeat(beatIntensity);
}
// Create beat effects around the key
if (beatIntensity > 0.7) {
createExplosion(2048 / 2, 2732 / 2, 0x00FFFF);
}
// Beat intensity decay over time
setTimeout(function () {
beatIntensity *= 0.95;
}, 100);
}
}
// Handle clicks for start button
game.down = function (x, y, obj) {
if (gameState === 'menu') {
// Check if click is on start button
var dx = x - startButton.shape.x;
var dy = y - startButton.shape.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 150) {
// Button radius
startButton.down(x, y, obj);
}
// Check if click is on difficulty buttons
for (var d = 0; d < difficultyButtons.length; d++) {
var diffButton = difficultyButtons[d];
var ddx = x - diffButton.text.x;
var ddy = y - diffButton.text.y;
var ddistance = Math.sqrt(ddx * ddx + ddy * ddy);
if (ddistance <= 100) {
// Text button hit area
// Reset previous selection if any
if (currentDifficulty !== -1) {
difficultyButtons[currentDifficulty].text.scaleX = 1.0;
difficultyButtons[currentDifficulty].text.scaleY = 1.0;
}
// Set new selection
currentDifficulty = d;
difficultySelected = true;
diffButton.text.scaleX = 1.0;
diffButton.text.scaleY = 1.0;
// Update indicator text to show selected difficulty
if (d === 0) {
// Easy mode - show in red
difficultyIndicatorTxt.setText('Easy mode selected');
difficultyIndicatorTxt.fill = 0xFF0000;
difficultyIndicatorTxt.tint = 0xFF0000;
} else {
difficultyIndicatorTxt.setText(difficulties[d].name.toLowerCase() + ' mode selected');
difficultyIndicatorTxt.fill = difficulties[d].color;
difficultyIndicatorTxt.tint = difficulties[d].color;
}
// Add selection effect
tween(diffButton.text, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(diffButton.text, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
}
});
// Animate the indicator text to show selection
tween(difficultyIndicatorTxt, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(difficultyIndicatorTxt, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
}
});
break;
}
}
// Check if click is on music buttons
for (var m = 0; m < musicButtons.length; m++) {
var musicButton = musicButtons[m];
var mdx = x - musicButton.shape.x;
var mdy = y - musicButton.shape.y;
var mdistance = Math.sqrt(mdx * mdx + mdy * mdy);
if (mdistance <= 90) {
// Button radius
// Reset previous selection
if (currentMusic !== -1) {
musicButtons[currentMusic].shape.scaleX = 1.0;
musicButtons[currentMusic].shape.scaleY = 1.0;
}
// Set new selection
currentMusic = m;
musicButton.shape.scaleX = 1.0;
musicButton.shape.scaleY = 1.0;
// Update indicator text
if (m === 2) {
musicIndicatorTxt.setText('3 selected');
} else if (m === 3) {
musicIndicatorTxt.setText('4 selected');
} else {
musicIndicatorTxt.setText(musicTracks[m].name + ' selected');
}
musicIndicatorTxt.fill = musicTracks[m].color;
musicIndicatorTxt.tint = musicTracks[m].color;
// Add selection effect
tween(musicButton.shape, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.bounceOut
});
// Animate the indicator text
tween(musicIndicatorTxt, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(musicIndicatorTxt, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
}
});
// Play the selected music
LK.stopMusic();
LK.playMusic(musicTracks[m].id);
break;
}
}
} else if (gameState === 'gameOver') {
// Check if click is on restart button
var dx = x - gameOverButton.shape.x;
var dy = y - gameOverButton.shape.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 150) {
// Button radius
gameOverButton.down(x, y, obj);
}
}
};
game.update = function () {
// Update background effects regardless of game state
updateBackgroundColor();
// Update background particles
for (var p = 0; p < backgroundParticles.length; p++) {
if (backgroundParticles[p] && backgroundParticles[p].update) {
backgroundParticles[p].update();
}
}
// Update background waves
for (var w = 0; w < backgroundWaves.length; w++) {
if (backgroundWaves[w] && backgroundWaves[w].update) {
backgroundWaves[w].update();
}
}
// Start breathing effect when appropriate
if (!breathingEffectStarted && gameState === 'playing') {
breathingEffectStarted = true;
createBreathingEffect();
}
// Add music-synchronized button effects during menu
if (gameState === 'menu' && currentMusic !== -1) {
// Create gentle music-reactive effects for menu buttons
var menuMusicBeat = Math.sin(LK.ticks * 0.1) * 0.5 + 0.5;
// Animate start button with music
if (startButton && startButton.shape && startButton.shape.visible) {
var startPulse = 1 + menuMusicBeat * 0.05;
startButton.shape.scaleX = startPulse;
startButton.shape.scaleY = startPulse;
// Subtle color shift
var startColorShift = Math.floor(menuMusicBeat * 50);
startButton.shape.tint = 0x00AA00 + (startColorShift << 8);
}
// Animate music selection buttons with staggered timing
for (var mb = 0; mb < musicButtons.length; mb++) {
if (musicButtons[mb] && musicButtons[mb].shape && musicButtons[mb].shape.visible) {
var staggeredBeat = Math.sin(LK.ticks * 0.08 + mb * 0.8) * 0.5 + 0.5;
var buttonPulse = 1 + staggeredBeat * 0.03;
// Scale animation
if (currentMusic !== mb) {
musicButtons[mb].shape.scaleX = buttonPulse;
musicButtons[mb].shape.scaleY = buttonPulse;
}
// Color breathing effect
var colorIntensity = staggeredBeat * 0.3 + 0.7;
var originalColor = musicTracks[mb].color;
var r = Math.floor((originalColor >> 16 & 0xFF) * colorIntensity);
var g = Math.floor((originalColor >> 8 & 0xFF) * colorIntensity);
var b = Math.floor((originalColor & 0xFF) * colorIntensity);
musicButtons[mb].shape.tint = r << 16 | g << 8 | b;
}
}
}
// Only update game logic when playing
if (gameState !== 'playing') {
return;
}
// Add fluid animation to timing line with music synchronization
if (timingLine.visible) {
// Pulsing effect based on notes approaching
var nearbyNotes = 0;
for (var t = 0; t < notes.length; t++) {
var distanceToLine = Math.abs(notes[t].y - timingLineY);
if (distanceToLine < 200) {
nearbyNotes++;
}
}
var pulseIntensity = 1 + nearbyNotes * 0.1;
var basePulse = 1 + Math.sin(LK.ticks * 0.2) * 0.1 * pulseIntensity;
// Add music beat synchronization
var musicPulse = 1;
if (beatIntensity > 0) {
musicPulse = 1 + beatIntensity * 0.4;
// Create beat wave along timing line
timingLine.scaleX = 1 + beatIntensity * 0.1;
}
timingLine.scaleY = basePulse * musicPulse;
timingLine.alpha = 0.8 + Math.sin(LK.ticks * 0.15) * 0.2;
// Music-reactive color cycling
var musicColorCycle = Math.sin(musicBeat * 0.05) * 0.5 + 0.5;
var baseColor = 0xFFFFFF;
if (nearbyNotes > 0) {
var colorShift = Math.sin(LK.ticks * 0.3) * 0.5 + 0.5;
baseColor = 0xFFFFFF + (Math.floor(colorShift * 50) << 16);
}
// Add music-reactive blue/cyan tint
var musicTint = Math.floor(musicColorCycle * 255);
timingLine.tint = baseColor & 0xFF0000 | musicTint << 8 | 0xFF;
}
// Add music-synchronized glow effect to buttons
for (var b = 0; b < buttons.length; b++) {
if (buttons[b] && buttons[b].buttonGraphics && buttons[b].buttonGraphics.parent) {
var baseGlow = Math.sin(LK.ticks * 0.08 + b) * 0.1 + 0.9;
var musicGlow = 1;
// Music beat detection for synchronized effects
var isOnBeat = beatIntensity > 0.7;
var isMediumBeat = beatIntensity > 0.4;
if (beatIntensity > 0) {
musicGlow = 1 + beatIntensity * 0.3;
// Enhanced beat-reactive scaling with smooth transitions
var targetScaleX = 1 + beatIntensity * 0.15;
var targetScaleY = 1 + beatIntensity * 0.15;
// Smooth scaling animation to target with additional null check
if (buttons[b] && buttons[b].buttonGraphics && buttons[b].buttonGraphics.parent) {
tween(buttons[b].buttonGraphics, {
scaleX: targetScaleX,
scaleY: targetScaleY
}, {
duration: 100,
easing: tween.easeOut
});
}
// Strong beat effects
if (isOnBeat && buttons[b] && buttons[b].buttonGraphics && buttons[b].buttonGraphics.parent) {
// Pulse ring effect around button
var beatRing = game.addChild(LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5
}));
beatRing.x = buttons[b].x;
beatRing.y = buttons[b].y;
beatRing.alpha = 0.6;
beatRing.scaleX = 0.8;
beatRing.scaleY = 0.8;
beatRing.tint = 0x00FFFF;
// Animate ring expanding and fading
tween(beatRing, {
scaleX: 2.0,
scaleY: 2.0,
alpha: 0
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
if (beatRing && beatRing.parent) {
beatRing.destroy();
}
}
});
// Color flash effect on strong beats
if (buttons[b] && buttons[b].buttonGraphics && buttons[b].buttonGraphics.parent) {
var originalTint = buttons[b].buttonGraphics.tint;
buttons[b].buttonGraphics.tint = 0xFFFFFF;
tween(buttons[b].buttonGraphics, {
tint: originalTint
}, {
duration: 200,
easing: tween.easeOut
});
}
}
// Medium beat effects
if (isMediumBeat && !isOnBeat && buttons[b] && buttons[b].buttonGraphics && buttons[b].buttonGraphics.parent) {
// Subtle glow pulse
var originalAlpha = buttons[b].buttonGraphics.alpha;
tween(buttons[b].buttonGraphics, {
alpha: Math.min(1.0, originalAlpha * 1.3)
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
if (buttons[b] && buttons[b].buttonGraphics && buttons[b].buttonGraphics.parent) {
tween(buttons[b].buttonGraphics, {
alpha: originalAlpha
}, {
duration: 150,
easing: tween.easeOut
});
}
}
});
}
}
// Continuous music-reactive color cycling
var musicColorCycle = Math.sin(musicBeat * 0.1 + b * 1.2) * 0.5 + 0.5;
var baseColor = 0x00FF00; // Default green
// Apply music-reactive color shifts
if (beatIntensity > 0.2) {
var beatColorR = Math.floor((musicColorCycle + beatIntensity * 0.5) * 150) % 255;
var beatColorG = Math.floor(musicColorCycle * 255);
var beatColorB = Math.floor((1 - musicColorCycle + beatIntensity * 0.3) * 200) % 255;
baseColor = beatColorR << 16 | beatColorG << 8 | beatColorB;
}
// Apply subtle rotation on beats
if (beatIntensity > 0.5 && buttons[b] && buttons[b].buttonGraphics && buttons[b].buttonGraphics.parent) {
var rotationAmount = beatIntensity * 0.1 * Math.sin(LK.ticks * 0.2 + b);
buttons[b].buttonGraphics.rotation = rotationAmount;
} else if (buttons[b] && buttons[b].buttonGraphics && buttons[b].buttonGraphics.parent) {
// Return to neutral rotation smoothly
if (Math.abs(buttons[b].buttonGraphics.rotation) > 0.01) {
tween(buttons[b].buttonGraphics, {
rotation: 0
}, {
duration: 300,
easing: tween.easeOut
});
}
}
// Apply final alpha and color
if (buttons[b] && buttons[b].buttonGraphics && buttons[b].buttonGraphics.parent) {
buttons[b].buttonGraphics.alpha = buttons[b].isPressed ? 0.7 : baseGlow * musicGlow;
if (!buttons[b].isPressed) {
buttons[b].buttonGraphics.tint = baseColor;
}
}
}
}
// Add music-synchronized flowing effects to lane separators
if (lane1.visible && lane2.visible) {
// Flowing light effect down the lanes with music sync
var baseFlow = Math.sin(LK.ticks * 0.1) * 0.3 + 0.7;
var musicFlow = 1;
if (beatIntensity > 0) {
musicFlow = 1 + beatIntensity * 0.5;
}
lane1.alpha = baseFlow * musicFlow;
lane2.alpha = baseFlow * musicFlow;
// Music-reactive width pulsing
var baseWidthPulse = 1 + Math.sin(LK.ticks * 0.05) * 0.2;
var musicWidthPulse = 1;
if (beatIntensity > 0) {
musicWidthPulse = 1 + beatIntensity * 0.3;
}
lane1.scaleX = baseWidthPulse * musicWidthPulse;
lane2.scaleX = baseWidthPulse * musicWidthPulse;
// Music-reactive color shifting
var tempo = Math.sin(LK.ticks * 0.2) * 0.5 + 0.5;
var musicTempo = Math.sin(musicBeat * 0.1) * 0.5 + 0.5;
var colorR = Math.floor((tempo + musicTempo) * 100) % 255;
var colorG = Math.floor(musicTempo * 150) % 255;
var colorTint = 0xFF0000 | colorG << 8 | 0xFF;
lane1.tint = colorTint;
lane2.tint = colorTint;
}
// Update timer - countdown every second (60 frames)
if (LK.ticks % 60 === 0 && timeRemaining > 0) {
timeRemaining--;
timerTxt.setText(timeRemaining.toString());
}
// Check for game over when timer reaches 0
if (timeRemaining <= 0 && gameState === 'playing') {
gameState = 'gameOver';
// Clean up visual key
if (visualKey && visualKey.parent) {
visualKey.destroy();
visualKey = null;
}
// Update final score display
scoreTxt.setText(LK.getScore());
// Hide game elements
timerTxt.visible = false;
scoreTxt.visible = false;
timingLine.visible = false;
bottomLine.visible = false;
lane1.visible = false;
lane2.visible = false;
// Hide buttons
for (var i = 0; i < buttons.length; i++) {
buttons[i].visible = false;
}
// Stop all music when game over screen appears
LK.stopMusic();
// Play game ending music after a brief pause
LK.setTimeout(function () {
LK.playMusic('End', {
loop: true
});
}, 500);
// Show game over menu with smooth animations
gameOverTxt.visible = true;
gameOverTxt.alpha = 0;
gameOverTxt.scaleX = 0.5;
gameOverTxt.scaleY = 0.5;
finalScoreTxt.setText('Final Score: ' + LK.getScore() + '\nDifficulty: ' + difficulties[currentDifficulty].name);
finalScoreTxt.visible = true;
finalScoreTxt.alpha = 0;
finalScoreTxt.y = finalScoreTxt.y + 50;
gameOverButton.shape.visible = true;
gameOverButton.shape.alpha = 0;
gameOverButton.shape.scaleX = 0;
gameOverButton.shape.scaleY = 0;
gameOverButton.text.visible = true;
gameOverButton.text.alpha = 0;
// Animate game over text
tween(gameOverTxt, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.bounceOut
});
// Animate final score text sliding up
tween(finalScoreTxt, {
alpha: 1,
y: finalScoreTxt.y - 50
}, {
duration: 600,
easing: tween.easeOut,
delay: 400
});
// Animate restart button
tween(gameOverButton.shape, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 600,
easing: tween.elasticOut,
delay: 800
});
tween(gameOverButton.text, {
alpha: 1
}, {
duration: 400,
delay: 1000
});
}
// Detect beats and trigger key animations
if (gameState === 'playing') {
detectBeat();
}
// Update visual key if it exists
if (visualKey && visualKey.update && gameState === 'playing') {
visualKey.update();
}
}; ===================================================================
--- original.js
+++ change.js
@@ -161,65 +161,57 @@
easing: tween.bounceOut
});
}
});
- // Check for notes in this lane
- var hitNote = null;
- var bestDistance = Infinity;
- for (var i = 0; i < notes.length; i++) {
- var note = notes[i];
- if (note.lane === self.lane && !note.hasBeenHit && note.y >= timingLineY) {
- var distance = Math.abs(note.y - self.y);
- if (distance < bestDistance) {
- bestDistance = distance;
- hitNote = note;
- }
- }
- }
- if (hitNote) {
- hitNote.hasBeenHit = true;
- // Calculate points based on timing
+ // Check for beat timing accuracy when button is pressed
+ var currentTime = LK.ticks;
+ var timeSinceLastBeat = currentTime - lastBeatTime;
+ var beatWindow = beatInterval * 0.3; // 30% of beat interval for timing window
+ if (gameState === 'playing') {
var points = 0;
var feedbackText = '';
- if (bestDistance <= 150) {
- // Perfect hit - note is on button
+ // Check if button press is close to a beat
+ if (timeSinceLastBeat <= beatWindow) {
+ // Perfect timing - very close to beat
points = 100;
- feedbackText = 'PERFECT';
- } else if (bestDistance <= 250) {
- // Great hit - note is between timing line and button
+ feedbackText = 'PERFECT BEAT';
+ LK.getSound('hit').play();
+ } else if (timeSinceLastBeat <= beatWindow * 2) {
+ // Good timing - somewhat close to beat
points = 50;
- feedbackText = 'GREAT';
+ feedbackText = 'GOOD BEAT';
+ LK.getSound('hit').play();
+ } else {
+ // Off beat
+ feedbackText = 'OFF BEAT';
+ LK.getSound('miss').play();
}
- if (points > 0 && gameState === 'playing') {
+ if (points > 0) {
LK.setScore(LK.getScore() + points);
scoreTxt.setText(LK.getScore());
- LK.getSound('hit').play();
- // Update combo (hit)
updateCombo(true);
- // Visual feedback
- LK.effects.flashObject(hitNote, 0xffffff, 200);
- // Create explosion effect at note position
- var explosionColor = feedbackText === 'PERFECT' ? 0xFFD700 : 0x00FFFF;
- createExplosion(hitNote.x, hitNote.y, explosionColor);
- // Show feedback text
- feedbackTxt.setText(feedbackText);
- feedbackTxt.alpha = 1;
- tween(feedbackTxt, {
- alpha: 0
- }, {
- duration: 800
- });
- // Create floating score animation
+ } else {
+ updateCombo(false);
+ }
+ // Show feedback text
+ feedbackTxt.setText(feedbackText);
+ feedbackTxt.alpha = 1;
+ tween(feedbackTxt, {
+ alpha: 0
+ }, {
+ duration: 800
+ });
+ // Create score animation if points were earned
+ if (points > 0) {
var scoreText = new Text2('+' + points, {
size: 50,
- fill: explosionColor
+ fill: points === 100 ? 0xFFD700 : 0x00FFFF
});
scoreText.anchor.set(0.5, 0.5);
- scoreText.x = hitNote.x;
- scoreText.y = hitNote.y;
+ scoreText.x = self.x;
+ scoreText.y = self.y - 100;
scoreText.alpha = 1;
game.addChild(scoreText);
- // Animate floating score
tween(scoreText, {
y: scoreText.y - 150,
alpha: 0,
scaleX: 1.5,
@@ -232,152 +224,8 @@
scoreText.destroy();
}
}
});
- // Special animations for GREAT and PERFECT
- if (feedbackText === 'PERFECT') {
- // Create sparkle particles around button
- for (var s = 0; s < 12; s++) {
- var sparkle = game.addChild(LK.getAsset('chordNote2', {
- anchorX: 0.5,
- anchorY: 0.5
- }));
- sparkle.x = self.x + (Math.random() - 0.5) * 200;
- sparkle.y = self.y + (Math.random() - 0.5) * 200;
- sparkle.scaleX = 0.2 + Math.random() * 0.3;
- sparkle.scaleY = sparkle.scaleX;
- sparkle.alpha = 0.8;
- sparkle.tint = 0xFFD700;
- // Animate sparkles floating upward and fading
- tween(sparkle, {
- y: sparkle.y - 150 - Math.random() * 100,
- x: sparkle.x + (Math.random() - 0.5) * 100,
- alpha: 0,
- rotation: Math.PI * 2 * (Math.random() > 0.5 ? 1 : -1),
- scaleX: sparkle.scaleX * 0.5,
- scaleY: sparkle.scaleY * 0.5
- }, {
- duration: 800 + Math.random() * 400,
- easing: tween.easeOut,
- delay: Math.random() * 200,
- onFinish: function onFinish() {
- if (sparkle && sparkle.parent) {
- sparkle.destroy();
- }
- }
- });
- }
- // Perfect animation: scale up and spin with color change
- var originalScale = self.buttonGraphics.scaleX;
- var originalTint = self.buttonGraphics.tint;
- self.buttonGraphics.tint = 0xFFD700; // Gold color
- tween(self.buttonGraphics, {
- scaleX: originalScale * 1.5,
- scaleY: originalScale * 1.5,
- rotation: Math.PI * 2
- }, {
- duration: 600,
- easing: tween.elasticOut,
- onFinish: function onFinish() {
- tween(self.buttonGraphics, {
- scaleX: originalScale,
- scaleY: originalScale,
- rotation: 0,
- tint: originalTint
- }, {
- duration: 200
- });
- }
- });
- // Create perfect ring animation around button
- var perfectRing = game.addChild(LK.getAsset('button', {
- anchorX: 0.5,
- anchorY: 0.5
- }));
- perfectRing.x = self.x;
- perfectRing.y = self.y;
- perfectRing.tint = 0xFFD700;
- perfectRing.alpha = 0.8;
- perfectRing.scaleX = 0.5;
- perfectRing.scaleY = 0.5;
- // Animate ring expanding outward while fading
- tween(perfectRing, {
- scaleX: 3.0,
- scaleY: 3.0,
- alpha: 0
- }, {
- duration: 800,
- easing: tween.easeOut,
- onFinish: function onFinish() {
- if (perfectRing && perfectRing.parent) {
- perfectRing.destroy();
- }
- }
- });
- } else if (feedbackText === 'GREAT') {
- // Create energy trail effect
- for (var t = 0; t < 6; t++) {
- var trail = game.addChild(LK.getAsset('chordNote3', {
- anchorX: 0.5,
- anchorY: 0.5
- }));
- trail.x = self.x;
- trail.y = self.y;
- trail.scaleX = 0.4;
- trail.scaleY = 0.4;
- trail.alpha = 0.7;
- trail.tint = 0x00FFFF;
- // Animate trails in different directions
- var angle = t / 6 * Math.PI * 2;
- var distance = 120 + Math.random() * 80;
- var targetX = self.x + Math.cos(angle) * distance;
- var targetY = self.y + Math.sin(angle) * distance;
- tween(trail, {
- x: targetX,
- y: targetY,
- alpha: 0,
- scaleX: 0.1,
- scaleY: 0.1,
- rotation: Math.PI
- }, {
- duration: 400 + Math.random() * 200,
- easing: tween.easeOut,
- delay: t * 50,
- onFinish: function onFinish() {
- if (trail && trail.parent) {
- trail.destroy();
- }
- }
- });
- }
- // Great animation: pulse and glow effect
- var originalScale = self.buttonGraphics.scaleX;
- var originalTint = self.buttonGraphics.tint;
- self.buttonGraphics.tint = 0x00FFFF; // Cyan color
- tween(self.buttonGraphics, {
- scaleX: originalScale * 1.3,
- scaleY: originalScale * 1.3
- }, {
- duration: 300,
- easing: tween.bounceOut,
- onFinish: function onFinish() {
- tween(self.buttonGraphics, {
- scaleX: originalScale,
- scaleY: originalScale,
- tint: originalTint
- }, {
- duration: 300,
- easing: tween.easeOut
- });
- }
- });
- }
- // Remove the note immediately
- hitNote.destroy();
- var noteIndex = notes.indexOf(hitNote);
- if (noteIndex !== -1) {
- notes.splice(noteIndex, 1);
- }
}
}
// Reset button after short delay with smooth animation
LK.setTimeout(function () {
@@ -394,42 +242,90 @@
}, 100);
};
return self;
});
-var Note = Container.expand(function (lane, type) {
+var Key = Container.expand(function () {
var self = Container.call(this);
- self.lane = lane;
- self.type = type;
- self.hasBeenHit = false;
- // Get speed multiplier from current difficulty
- var speedMultiplier = currentDifficulty !== -1 ? difficulties[currentDifficulty].noteSpeedMultiplier : 1.0;
- // Set speed and asset based on type
- if (type === 'slow') {
- self.speed = 8 * speedMultiplier;
- self.noteGraphics = self.attachAsset('slowNote', {
- anchorX: 0.5,
- anchorY: 0.5
+ self.keyGraphics = self.attachAsset('key', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Position key in center of screen
+ self.x = 2048 / 2;
+ self.y = 2732 / 2;
+ // Key animation properties
+ self.baseScale = 1.0;
+ self.isAnimating = false;
+ // Beat animation method
+ self.animateOnBeat = function (intensity) {
+ if (self.isAnimating) return;
+ self.isAnimating = true;
+ // Scale and color effect based on beat intensity
+ var targetScale = self.baseScale + intensity * 0.5;
+ var beatColor = intensity > 0.7 ? 0xFFD700 : intensity > 0.4 ? 0x00FFFF : 0xFFFFFF;
+ // Store original values
+ var originalScale = self.keyGraphics.scaleX;
+ var originalTint = self.keyGraphics.tint;
+ // Quick press animation
+ tween(self.keyGraphics, {
+ scaleX: targetScale * 0.8,
+ scaleY: targetScale * 0.8,
+ tint: beatColor
+ }, {
+ duration: 50,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ // Release animation
+ tween(self.keyGraphics, {
+ scaleX: targetScale,
+ scaleY: targetScale
+ }, {
+ duration: 100,
+ easing: tween.bounceOut,
+ onFinish: function onFinish() {
+ // Return to normal
+ tween(self.keyGraphics, {
+ scaleX: originalScale,
+ scaleY: originalScale,
+ tint: originalTint
+ }, {
+ duration: 200,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ self.isAnimating = false;
+ }
+ });
+ }
+ });
+ }
});
- } else if (type === 'medium') {
- self.speed = 12 * speedMultiplier;
- self.noteGraphics = self.attachAsset('mediumNote', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- } else if (type === 'fast') {
- self.speed = 16 * speedMultiplier;
- self.noteGraphics = self.attachAsset('fastNote', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- }
- // Position in lane
- self.x = lane * (2048 / 3) + 2048 / 6;
- self.y = 0;
- // Add idle breathing animation to buttons
- self.update = function () {
- // Move note down the screen
- self.y += self.speed;
+ // Add glow effect for strong beats
+ if (intensity > 0.7) {
+ var glowRing = game.addChild(LK.getAsset('key', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ }));
+ glowRing.x = self.x;
+ glowRing.y = self.y;
+ glowRing.keyGraphics = glowRing.children[0];
+ glowRing.keyGraphics.alpha = 0.6;
+ glowRing.keyGraphics.tint = 0xFFD700;
+ glowRing.keyGraphics.scaleX = 0.8;
+ glowRing.keyGraphics.scaleY = 0.8;
+ tween(glowRing.keyGraphics, {
+ scaleX: 2.0,
+ scaleY: 2.0,
+ alpha: 0
+ }, {
+ duration: 400,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ if (glowRing && glowRing.parent) {
+ glowRing.destroy();
+ }
+ }
+ });
+ }
};
return self;
});
@@ -457,35 +353,29 @@
var currentMusic = -1; // -1=None selected, 0=Music1, 1=Music2, 2=Music3
var musicTracks = [{
name: '1',
id: 'GameMusic1',
- color: 0x8800ff,
- bpm: 120
+ color: 0x8800ff
}, {
name: '2',
id: 'GameMusic2',
- color: 0xff8800,
- bpm: 140
+ color: 0xff8800
}, {
name: '3',
id: 'GameMusic3',
- color: 0x0088ff,
- bpm: 128
+ color: 0x0088ff
}, {
name: '4',
id: 'GameMusic4',
- color: 0x00ff88,
- bpm: 110
+ color: 0x00ff88
}, {
name: '5',
id: 'GameMusic5',
- color: 0xff0088,
- bpm: 135
+ color: 0xff0088
}, {
name: '6',
id: 'GameMusic6',
- color: 0x88ff00,
- bpm: 125
+ color: 0x88ff00
}];
var difficulties = [{
name: 'EASY',
spawnInterval: 120,
@@ -515,30 +405,31 @@
noteSpeedMultiplier: 2.0,
color: 0xFF0000
}];
// Game variables
-var notes = [];
var buttons = [];
-var maxNotes = 5;
-var spawnTimer = 0;
-var spawnInterval = 120; // frames between spawns
var timingLineY = 2732 - 500;
var bottomLineY = 2732 - 2; // Position miss line at bottom edge of screen
-// Music synchronization variables
+// Key and beat synchronization variables
+var visualKey = null;
var musicBeat = 0;
var beatInterval = 30; // frames per beat (adjustable based on music tempo)
var beatIntensity = 0;
-var musicPulseElements = [];
-// BPM-based beat detection and key animation variables
-var bpm = 120; // Default BPM - will be set based on selected music
-var beatsPerSecond = bpm / 60;
-var framesPerBeat = Math.floor(60 / beatsPerSecond); // 60 FPS assumed
-var beatTimer = 0;
var lastBeatTime = 0;
-var beatAccuracy = 0.1; // Timing accuracy for beat detection
-// Virtual key animation variables
-var virtualKey = null;
-var keyAnimationActive = false;
+var bpmSettings = {
+ 'GameMusic1': 120,
+ // 120 BPM
+ 'GameMusic2': 128,
+ // 128 BPM
+ 'GameMusic3': 110,
+ // 110 BPM
+ 'GameMusic4': 140,
+ // 140 BPM
+ 'GameMusic5': 100,
+ // 100 BPM
+ 'GameMusic6': 150 // 150 BPM
+};
+var currentBPM = 120;
// Create lane separators
var lane1 = game.addChild(LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0
@@ -1141,23 +1032,18 @@
var button = new Button(i);
buttons.push(button);
game.addChild(button);
}
- // Play selected music
- LK.playMusic(musicTracks[currentMusic].id);
- // Set BPM for beat detection based on selected music
- bpm = musicTracks[currentMusic].bpm;
- beatsPerSecond = bpm / 60;
- framesPerBeat = Math.floor(60 / beatsPerSecond);
- beatTimer = 0;
- lastBeatTime = 0;
- // Show virtual key during gameplay
- virtualKey.visible = true;
- if (keyLabel) keyLabel.visible = true;
+ // Create visual key for beat synchronization
+ visualKey = new Key();
+ game.addChild(visualKey);
+ // Play selected music and set BPM
+ var selectedMusicId = musicTracks[currentMusic].id;
+ LK.playMusic(selectedMusicId);
+ currentBPM = bpmSettings[selectedMusicId] || 120;
+ beatInterval = Math.floor(3600 / currentBPM); // Convert BPM to frames (60fps)
// Apply difficulty settings
var difficulty = difficulties[currentDifficulty];
- maxNotes = difficulty.maxNotes;
- spawnInterval = difficulty.spawnInterval;
timeRemaining = difficulty.timeLimit;
// Start the game
gameState = 'playing';
LK.setScore(0);
@@ -1189,30 +1075,8 @@
scoreTxt.x = 2048 / 2;
scoreTxt.y = 2732 / 2 - 200;
scoreTxt.visible = false; // Hide initially
game.addChild(scoreTxt);
-// Create virtual key for beat synchronization
-virtualKey = game.addChild(LK.getAsset('button', {
- anchorX: 0.5,
- anchorY: 0.5
-}));
-virtualKey.x = 2048 / 2;
-virtualKey.y = 150; // Top center of screen
-virtualKey.scaleX = 0.8;
-virtualKey.scaleY = 0.8;
-virtualKey.tint = 0xFFD700; // Gold color
-virtualKey.alpha = 0.7;
-virtualKey.visible = false; // Hide initially
-// Create key label
-var keyLabel = new Text2('♪ BEAT KEY ♪', {
- size: 40,
- fill: 0xFFFFFF
-});
-keyLabel.anchor.set(0.5, 0.5);
-keyLabel.x = virtualKey.x;
-keyLabel.y = virtualKey.y + 80;
-keyLabel.visible = false;
-game.addChild(keyLabel);
// Create feedback text display
var feedbackTxt = new Text2('', {
size: 80,
fill: 0xFFFFFF
@@ -1270,17 +1134,18 @@
gameOverTxt.visible = false;
finalScoreTxt.visible = false;
this.shape.visible = false;
this.text.visible = false;
- // Clear existing notes and buttons
- for (var i = notes.length - 1; i >= 0; i--) {
- notes[i].destroy();
- notes.splice(i, 1);
- }
+ // Clear existing buttons and key
for (var i = buttons.length - 1; i >= 0; i--) {
buttons[i].destroy();
buttons.splice(i, 1);
}
+ // Clean up visual key if it exists
+ if (visualKey && visualKey.parent) {
+ visualKey.destroy();
+ visualKey = null;
+ }
// Show menu elements with smooth animations
titleTxt.visible = true;
titleTxt.alpha = 0;
titleTxt.scaleX = 0.8;
@@ -1415,106 +1280,14 @@
timingLine.visible = false;
bottomLine.visible = false;
lane1.visible = false;
lane2.visible = false;
- // Hide virtual key
- virtualKey.visible = false;
- if (keyLabel) keyLabel.visible = false;
- keyAnimationActive = false;
// Reset game state to menu
gameState = 'menu';
spawnTimer = 0;
LK.setScore(0);
}
};
-// Create beat-synchronized key animation
-function triggerBeatKeyAnimation() {
- if (!virtualKey || !virtualKey.visible || keyAnimationActive) return;
- keyAnimationActive = true;
- // Key press animation - scale down quickly
- tween(virtualKey, {
- scaleX: 0.6,
- scaleY: 0.6,
- alpha: 1.0,
- tint: 0xFFFFFF
- }, {
- duration: 100,
- easing: tween.easeOut,
- onFinish: function onFinish() {
- // Key release animation - scale back up with glow
- tween(virtualKey, {
- scaleX: 1.0,
- scaleY: 1.0,
- alpha: 0.7,
- tint: 0xFFD700
- }, {
- duration: 300,
- easing: tween.elasticOut,
- onFinish: function onFinish() {
- keyAnimationActive = false;
- }
- });
- }
- });
- // Create ripple effect around key
- var ripple = game.addChild(LK.getAsset('button', {
- anchorX: 0.5,
- anchorY: 0.5
- }));
- ripple.x = virtualKey.x;
- ripple.y = virtualKey.y;
- ripple.alpha = 0.5;
- ripple.scaleX = 0.8;
- ripple.scaleY = 0.8;
- ripple.tint = 0x00FFFF;
- // Animate ripple expanding
- tween(ripple, {
- scaleX: 2.5,
- scaleY: 2.5,
- alpha: 0
- }, {
- duration: 600,
- easing: tween.easeOut,
- onFinish: function onFinish() {
- if (ripple && ripple.parent) {
- ripple.destroy();
- }
- }
- });
- // Create sparkle particles around key
- for (var s = 0; s < 6; s++) {
- var sparkle = game.addChild(LK.getAsset('chordNote2', {
- anchorX: 0.5,
- anchorY: 0.5
- }));
- var angle = s / 6 * Math.PI * 2;
- var distance = 60 + Math.random() * 40;
- sparkle.x = virtualKey.x + Math.cos(angle) * distance;
- sparkle.y = virtualKey.y + Math.sin(angle) * distance;
- sparkle.scaleX = 0.3;
- sparkle.scaleY = 0.3;
- sparkle.alpha = 0.8;
- sparkle.tint = 0xFFD700;
- // Animate sparkles
- tween(sparkle, {
- y: sparkle.y - 50 - Math.random() * 50,
- x: sparkle.x + (Math.random() - 0.5) * 80,
- alpha: 0,
- rotation: Math.PI * 2 * (Math.random() > 0.5 ? 1 : -1),
- scaleX: 0.1,
- scaleY: 0.1
- }, {
- duration: 400 + Math.random() * 300,
- easing: tween.easeOut,
- delay: Math.random() * 100,
- onFinish: function onFinish() {
- if (sparkle && sparkle.parent) {
- sparkle.destroy();
- }
- }
- });
- }
-}
// Create explosion animation function
function createExplosion(x, y, color) {
var particleCount = 8;
var particles = [];
@@ -1627,56 +1400,29 @@
} else {
comboCount = 0;
}
}
-// Spawn note function with music synchronization
-function spawnNote() {
- if (notes.length >= maxNotes) return;
- var lane = Math.floor(Math.random() * 3);
- var noteTypes = ['slow', 'medium', 'fast'];
- var type = noteTypes[Math.floor(Math.random() * noteTypes.length)];
- var note = new Note(lane, type);
- // Music-reactive entrance animation
- var musicSpawnScale = 1;
- var musicSpawnColor = 0xFFFFFF;
- if (beatIntensity > 0.7) {
- // Spawn with extra flair on strong beats
- musicSpawnScale = 1.3;
- musicSpawnColor = 0xFFD700; // Gold color for beat spawns
- note.noteGraphics.tint = musicSpawnColor;
- }
- // Add entrance animation - start small and grow with music sync
- note.noteGraphics.scaleX = 0;
- note.noteGraphics.scaleY = 0;
- note.noteGraphics.alpha = 0;
- tween(note.noteGraphics, {
- scaleX: musicSpawnScale,
- scaleY: musicSpawnScale,
- alpha: 1
- }, {
- duration: 300,
- easing: tween.elasticOut,
- onFinish: function onFinish() {
- // Return to normal scale after spawn
- if (musicSpawnScale > 1) {
- tween(note.noteGraphics, {
- scaleX: 1,
- scaleY: 1,
- tint: 0xFFFFFF
- }, {
- duration: 200,
- easing: tween.easeOut
- });
- }
+// Beat detection and key animation trigger
+function detectBeat() {
+ musicBeat++;
+ if (musicBeat >= beatInterval) {
+ musicBeat = 0;
+ lastBeatTime = LK.ticks;
+ // Calculate beat intensity based on game progression
+ beatIntensity = 0.8 + Math.random() * 0.4; // Random intensity between 0.8-1.2
+ // Trigger key animation if visual key exists
+ if (visualKey && gameState === 'playing') {
+ visualKey.animateOnBeat(beatIntensity);
}
- });
- // Add music-reactive spawn effects
- if (beatIntensity > 0.5) {
- // Create spawn burst effect
- createExplosion(note.x, note.y - 100, 0x00FFFF);
+ // Create beat effects around the key
+ if (beatIntensity > 0.7) {
+ createExplosion(2048 / 2, 2732 / 2, 0x00FFFF);
+ }
+ // Beat intensity decay over time
+ setTimeout(function () {
+ beatIntensity *= 0.95;
+ }, 100);
}
- notes.push(note);
- game.addChild(note);
}
// Handle clicks for start button
game.down = function (x, y, obj) {
if (gameState === 'menu') {
@@ -1876,37 +1622,8 @@
// Only update game logic when playing
if (gameState !== 'playing') {
return;
}
- // BPM-based beat detection and key animation
- if (virtualKey && virtualKey.visible) {
- beatTimer++;
- // Check if it's time for a beat based on BPM
- if (beatTimer >= framesPerBeat) {
- beatTimer = 0;
- lastBeatTime = LK.ticks;
- // Trigger beat key animation
- triggerBeatKeyAnimation();
- // Update beat intensity for other systems
- beatIntensity = 1.0;
- musicBeat = 0;
- }
- // Add continuous subtle breathing animation to virtual key
- if (!keyAnimationActive) {
- var breathingScale = 0.8 + Math.sin(LK.ticks * 0.05) * 0.1;
- virtualKey.scaleX = breathingScale;
- virtualKey.scaleY = breathingScale;
- // Subtle color shifting
- var colorCycle = Math.sin(LK.ticks * 0.03) * 0.5 + 0.5;
- var colorShift = Math.floor(colorCycle * 50);
- virtualKey.tint = 0xFFD700 + (colorShift << 8);
- }
- // Update key label position to follow virtual key
- if (keyLabel) {
- keyLabel.x = virtualKey.x;
- keyLabel.y = virtualKey.y + 80;
- }
- }
// Add fluid animation to timing line with music synchronization
if (timingLine.visible) {
// Pulsing effect based on notes approaching
var nearbyNotes = 0;
@@ -2090,20 +1807,12 @@
}
// Check for game over when timer reaches 0
if (timeRemaining <= 0 && gameState === 'playing') {
gameState = 'gameOver';
- // Clean up all remaining notes without awarding points
- for (var n = notes.length - 1; n >= 0; n--) {
- var note = notes[n];
- if (!note.hasBeenHit) {
- note.hasBeenHit = true;
- // Create visual effects for remaining notes without scoring
- LK.effects.flashObject(note, 0xFFD700, 200);
- createExplosion(note.x, note.y, 0xFFD700);
- // Remove the note
- note.destroy();
- notes.splice(n, 1);
- }
+ // Clean up visual key
+ if (visualKey && visualKey.parent) {
+ visualKey.destroy();
+ visualKey = null;
}
// Update final score display
scoreTxt.setText(LK.getScore());
// Hide game elements
@@ -2112,12 +1821,8 @@
timingLine.visible = false;
bottomLine.visible = false;
lane1.visible = false;
lane2.visible = false;
- // Hide virtual key
- virtualKey.visible = false;
- if (keyLabel) keyLabel.visible = false;
- keyAnimationActive = false;
// Hide buttons
for (var i = 0; i < buttons.length; i++) {
buttons[i].visible = false;
}
@@ -2178,79 +1883,13 @@
duration: 400,
delay: 1000
});
}
- // Spawn notes
- spawnTimer++;
- if (spawnTimer >= spawnInterval) {
- spawnNote();
- spawnTimer = 0;
- // Gradually increase spawn rate
- if (spawnInterval > 60) {
- spawnInterval = Math.max(60, spawnInterval - 1);
- }
+ // Detect beats and trigger key animations
+ if (gameState === 'playing') {
+ detectBeat();
}
- // Update and check notes with music synchronization
- for (var i = notes.length - 1; i >= 0; i--) {
- var note = notes[i];
- // Add music-reactive pulsing effect to notes as they approach timing line
- var distanceToTimingLine = Math.abs(note.y - timingLineY);
- if (distanceToTimingLine < 300) {
- var proximityPulse = 1 + (300 - distanceToTimingLine) / 300 * 0.3;
- var musicNotePulse = 1;
- if (beatIntensity > 0) {
- musicNotePulse = 1 + beatIntensity * 0.2;
- // Add music-reactive glow
- note.noteGraphics.alpha = 1 + beatIntensity * 0.3;
- }
- note.noteGraphics.scaleX = proximityPulse * musicNotePulse;
- note.noteGraphics.scaleY = proximityPulse * musicNotePulse;
- // Music-reactive color shifting for approaching notes
- if (distanceToTimingLine < 150) {
- var beatColor = Math.sin(musicBeat * 0.2) * 0.5 + 0.5;
- var colorShift = Math.floor(beatColor * 100);
- note.noteGraphics.tint = 0xFFFFFF + (colorShift << 16) + (colorShift << 8);
- }
- }
- // Check if note reached bottom line (missed)
- if (note.y >= bottomLineY && !note.hasBeenHit) {
- LK.getSound('miss').play();
- // Create red explosion effect for missed notes
- createExplosion(note.x, note.y, 0xFF0000);
- // Add screen shake for missed notes
- shakeScreen(5, 100);
- // Update combo (miss)
- updateCombo(false);
- // Show missed feedback text
- feedbackTxt.setText('MISSED');
- feedbackTxt.alpha = 1;
- tween(feedbackTxt, {
- alpha: 0
- }, {
- duration: 800
- });
- note.destroy();
- notes.splice(i, 1);
- continue;
- }
- // Remove notes that went off screen or were hit
- if (note.y > 2732 + 100 || note.hasBeenHit) {
- if (note.hasBeenHit) {
- // Small delay before removing hit notes for visual feedback
- LK.setTimeout(function (noteToRemove, index) {
- return function () {
- if (noteToRemove && noteToRemove.parent) {
- noteToRemove.destroy();
- }
- var noteIndex = notes.indexOf(noteToRemove);
- if (noteIndex !== -1) {
- notes.splice(noteIndex, 1);
- }
- };
- }(note, i), 200);
- } else {
- note.destroy();
- notes.splice(i, 1);
- }
- }
+ // Update visual key if it exists
+ if (visualKey && visualKey.update && gameState === 'playing') {
+ visualKey.update();
}
};
\ No newline at end of file