/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BeatCircle = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('beatCircle', {
anchorX: 0.5,
anchorY: 0.5
});
self.startRadius = 450;
self.targetRadius = 100;
self.speed = 2.2;
self.isActive = true;
self.hasBeenTapped = false;
self.update = function () {
if (!self.isActive) return;
var centerX = 2048 / 2;
var centerY = 2732 / 2;
// Calculate current distance from center
var dx = self.x - centerX;
var dy = self.y - centerY;
var currentRadius = Math.sqrt(dx * dx + dy * dy);
// Move inward
currentRadius -= self.speed;
if (currentRadius <= 98) {
// Beat missed - too close to center
if (!self.hasBeenTapped) {
missedBeat();
}
self.isActive = false;
return;
}
// Update position
var angle = Math.atan2(dy, dx);
self.x = centerX + Math.cos(angle) * currentRadius;
self.y = centerY + Math.sin(angle) * currentRadius;
// Scale based on distance for visual feedback
var scale = 0.5 + currentRadius / self.startRadius * 0.5;
graphics.scaleX = scale;
graphics.scaleY = scale;
};
self.checkHit = function () {
if (!self.isActive || self.hasBeenTapped) return false;
var centerX = 2048 / 2;
var centerY = 2732 / 2;
var dx = self.x - centerX;
var dy = self.y - centerY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 235 && distance >= 157) {
// Perfect hit
self.hasBeenTapped = true;
self.isActive = false;
return 'perfect';
} else if (distance <= 294 && distance >= 118) {
// Good hit
self.hasBeenTapped = true;
self.isActive = false;
return 'good';
}
return false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
var gameScore = 0;
var combo = 0;
var beats = [];
var spawnTimer = 0;
var spawnInterval = 50; // Spawn every 50 ticks (about 0.83 seconds at 60fps)
var currentLevel = 1;
var missCount = 0;
var maxMisses = 10;
var levelTimer = 0;
var levelDuration = 1800; // 30 seconds at 60fps
var showingIntro = true;
var gameStarted = false;
// Create lane background
var lane = game.addChild(LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
}));
lane.x = 2048 / 2;
lane.y = 2732 / 2;
// Lane animation variables
var laneWaveTimer = 0;
var laneBaseScale = 1.0;
var laneWaveIntensity = 0.08;
var laneWaveSpeed = 0.15;
// Create center target
var centerTarget = game.addChild(LK.getAsset('centerTarget', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.96,
scaleY: 1.96
}));
centerTarget.x = 2048 / 2;
centerTarget.y = 2732 / 2;
// Create intro screen elements
var introContainer = game.addChild(new Container());
// Intro background
var introBackground = introContainer.addChild(LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.0,
scaleY: 3.0,
alpha: 0.8
}));
introBackground.x = 2048 / 2;
introBackground.y = 2732 / 2;
introBackground.tint = 0x1a1a2e;
// Intro title text
var introTitle = new Text2('TAP THE BEAT', {
size: 150,
fill: 0xFFFFFF
});
introTitle.anchor.set(0.5, 0.5);
introContainer.addChild(introTitle);
introTitle.x = 2048 / 2;
introTitle.y = 2732 / 2 - 400;
// Instruction text lines
var instruction1 = new Text2('Tap when the beats reach the center circle!', {
size: 72,
fill: 0xCCCCCC
});
instruction1.anchor.set(0.5, 0.5);
introContainer.addChild(instruction1);
instruction1.x = 2048 / 2;
instruction1.y = 2732 / 2 - 200;
var instruction2 = new Text2('Time your taps to the rhythm.', {
size: 72,
fill: 0xCCCCCC
});
instruction2.anchor.set(0.5, 0.5);
introContainer.addChild(instruction2);
instruction2.x = 2048 / 2;
instruction2.y = 2732 / 2 - 100;
var instruction3 = new Text2('Earn combos and level up!', {
size: 72,
fill: 0xCCCCCC
});
instruction3.anchor.set(0.5, 0.5);
introContainer.addChild(instruction3);
instruction3.x = 2048 / 2;
instruction3.y = 2732 / 2;
// Start button
var startButton = introContainer.addChild(LK.getAsset('centerTarget', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5
}));
startButton.x = 2048 / 2;
startButton.y = 2732 / 2 + 300;
startButton.tint = 0x00FF00;
var startButtonText = new Text2('TAP TO START', {
size: 96,
fill: 0xFFFFFF
});
startButtonText.anchor.set(0.5, 0.5);
introContainer.addChild(startButtonText);
startButtonText.x = 2048 / 2;
startButtonText.y = 2732 / 2 + 300;
// Score display
var scoreText = new Text2('Score: 0', {
size: 96,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
scoreText.y = 100;
// Combo display
var comboText = new Text2('Combo: 0', {
size: 72,
fill: 0xFFFF00
});
comboText.anchor.set(0.5, 0);
LK.gui.top.addChild(comboText);
comboText.y = 200;
// Level display
var levelText = new Text2('Level: 1', {
size: 84,
fill: 0x00FFFF
});
levelText.anchor.set(0, 0);
LK.gui.topLeft.addChild(levelText);
levelText.x = 120;
levelText.y = 50;
// Miss count display
var missText = new Text2('Misses: 0/10', {
size: 60,
fill: 0xFF6666
});
missText.anchor.set(1, 0);
LK.gui.topRight.addChild(missText);
missText.x = -20;
missText.y = 50;
// Feedback text
var feedbackText = new Text2('', {
size: 120,
fill: 0x00FF00
});
feedbackText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(feedbackText);
feedbackText.alpha = 0;
function startGame() {
showingIntro = false;
gameStarted = true;
introContainer.visible = false;
}
function spawnBeat() {
var beat = new BeatCircle();
// Apply level-based speed increase
beat.speed = 2.2 + (currentLevel - 1) * 0.2;
// Random angle for spawn position
var angle = Math.random() * Math.PI * 2;
var centerX = 2048 / 2;
var centerY = 2732 / 2;
beat.x = centerX + Math.cos(angle) * beat.startRadius;
beat.y = centerY + Math.sin(angle) * beat.startRadius;
beats.push(beat);
game.addChild(beat);
}
function levelUp() {
currentLevel++;
levelTimer = 0;
// Show level up feedback
feedbackText.setText('LEVEL UP!');
feedbackText.tint = "#00FFFF";
feedbackText.alpha = 1;
tween(feedbackText, {
alpha: 0
}, {
duration: 1000
});
// Increase difficulty
spawnInterval = Math.max(25, spawnInterval - 3); // Faster spawning
var speedIncrease = 0.2;
for (var i = 0; i < beats.length; i++) {
beats[i].speed += speedIncrease;
}
// Update level display
levelText.setText('Level: ' + currentLevel);
// Flash screen for level up
LK.effects.flashScreen(0x00FFFF, 500);
}
function hitBeat(accuracy) {
var points = 0;
var feedbackColor = "#ffffff";
var feedbackMsg = "";
if (accuracy === 'perfect') {
points = 100 + combo * 10;
feedbackColor = "#00ff00";
feedbackMsg = "PERFECT!";
} else if (accuracy === 'good') {
points = 50 + combo * 5;
feedbackColor = "#ffff00";
feedbackMsg = "GOOD!";
}
gameScore += points;
combo++;
// Update displays
scoreText.setText('Score: ' + gameScore);
comboText.setText('Combo: ' + combo);
// Show feedback
feedbackText.setText(feedbackMsg);
feedbackText.tint = feedbackColor;
feedbackText.alpha = 1;
tween(feedbackText, {
alpha: 0
}, {
duration: 500
});
// Visual effects for combos
if (combo > 0 && combo % 5 === 0) {
LK.effects.flashScreen(0x00ff00, 200);
}
// Increase lane wave intensity with combo
laneWaveIntensity = 0.08 + combo * 0.002;
laneWaveSpeed = 0.15 + combo * 0.005;
// Add combo pulse effect to lane
tween(lane, {
scaleX: laneBaseScale + 0.15,
scaleY: laneBaseScale + 0.15
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(lane, {
scaleX: laneBaseScale,
scaleY: laneBaseScale
}, {
duration: 200,
easing: tween.easeOut
});
}
});
// Play hit sound
LK.getSound('hit').play();
// Update score for LK system
LK.setScore(gameScore);
}
function missedBeat() {
combo = 0;
missCount++;
comboText.setText('Combo: 0');
missText.setText('Misses: ' + missCount + '/' + maxMisses);
// Check for game over condition
if (missCount >= maxMisses) {
LK.showGameOver();
return;
}
// Reset lane wave intensity
laneWaveIntensity = 0.08;
laneWaveSpeed = 0.15;
// Show miss feedback
feedbackText.setText('MISS!');
feedbackText.tint = "#ff0000";
feedbackText.alpha = 1;
tween(feedbackText, {
alpha: 0
}, {
duration: 500
});
// Flash effect
LK.effects.flashScreen(0xff0000, 300);
// Play miss sound
LK.getSound('miss').play();
}
// Touch handler
game.down = function (x, y, obj) {
// Handle intro screen tap
if (showingIntro) {
// Check if tap is on start button area
var buttonX = 2048 / 2;
var buttonY = 2732 / 2 + 300;
var buttonRadius = 200; // Approximate button radius
var dx = x - buttonX;
var dy = y - buttonY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= buttonRadius) {
startGame();
}
return;
}
if (!gameStarted) return;
var hitDetected = false;
// Check all active beats for hits
for (var i = beats.length - 1; i >= 0; i--) {
var beat = beats[i];
if (!beat.isActive) continue;
var accuracy = beat.checkHit();
if (accuracy) {
hitBeat(accuracy);
hitDetected = true;
break; // Only hit one beat per tap
}
}
// If no beat was hit, it's a miss (but don't reset combo for tapping empty space)
};
game.update = function () {
// Only run game logic if the game has started
if (!gameStarted) return;
// Animate lane wave effect
laneWaveTimer += laneWaveSpeed;
var waveScale = laneBaseScale + Math.sin(laneWaveTimer) * laneWaveIntensity;
lane.scaleX = waveScale;
lane.scaleY = waveScale;
// Add subtle rotation wave
lane.rotation = Math.sin(laneWaveTimer * 0.7) * 0.02;
// Spawn new beats
spawnTimer++;
if (spawnTimer >= spawnInterval) {
spawnBeat();
spawnTimer = 0;
// Gradually increase difficulty by spawning beats more frequently
if (spawnInterval > 25) {
spawnInterval -= 0.1;
}
}
// Clean up inactive beats
for (var i = beats.length - 1; i >= 0; i--) {
var beat = beats[i];
if (!beat.isActive) {
beat.destroy();
beats.splice(i, 1);
}
}
// Level progression timer
levelTimer++;
if (levelTimer >= levelDuration) {
if (currentLevel >= 10) {
LK.showYouWin();
return;
}
levelUp();
}
// Win condition - reach level 10
if (currentLevel > 10) {
LK.showYouWin();
}
};
// Start background music
LK.playMusic('bgmusic'); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BeatCircle = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('beatCircle', {
anchorX: 0.5,
anchorY: 0.5
});
self.startRadius = 450;
self.targetRadius = 100;
self.speed = 2.2;
self.isActive = true;
self.hasBeenTapped = false;
self.update = function () {
if (!self.isActive) return;
var centerX = 2048 / 2;
var centerY = 2732 / 2;
// Calculate current distance from center
var dx = self.x - centerX;
var dy = self.y - centerY;
var currentRadius = Math.sqrt(dx * dx + dy * dy);
// Move inward
currentRadius -= self.speed;
if (currentRadius <= 98) {
// Beat missed - too close to center
if (!self.hasBeenTapped) {
missedBeat();
}
self.isActive = false;
return;
}
// Update position
var angle = Math.atan2(dy, dx);
self.x = centerX + Math.cos(angle) * currentRadius;
self.y = centerY + Math.sin(angle) * currentRadius;
// Scale based on distance for visual feedback
var scale = 0.5 + currentRadius / self.startRadius * 0.5;
graphics.scaleX = scale;
graphics.scaleY = scale;
};
self.checkHit = function () {
if (!self.isActive || self.hasBeenTapped) return false;
var centerX = 2048 / 2;
var centerY = 2732 / 2;
var dx = self.x - centerX;
var dy = self.y - centerY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 235 && distance >= 157) {
// Perfect hit
self.hasBeenTapped = true;
self.isActive = false;
return 'perfect';
} else if (distance <= 294 && distance >= 118) {
// Good hit
self.hasBeenTapped = true;
self.isActive = false;
return 'good';
}
return false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
var gameScore = 0;
var combo = 0;
var beats = [];
var spawnTimer = 0;
var spawnInterval = 50; // Spawn every 50 ticks (about 0.83 seconds at 60fps)
var currentLevel = 1;
var missCount = 0;
var maxMisses = 10;
var levelTimer = 0;
var levelDuration = 1800; // 30 seconds at 60fps
var showingIntro = true;
var gameStarted = false;
// Create lane background
var lane = game.addChild(LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
}));
lane.x = 2048 / 2;
lane.y = 2732 / 2;
// Lane animation variables
var laneWaveTimer = 0;
var laneBaseScale = 1.0;
var laneWaveIntensity = 0.08;
var laneWaveSpeed = 0.15;
// Create center target
var centerTarget = game.addChild(LK.getAsset('centerTarget', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.96,
scaleY: 1.96
}));
centerTarget.x = 2048 / 2;
centerTarget.y = 2732 / 2;
// Create intro screen elements
var introContainer = game.addChild(new Container());
// Intro background
var introBackground = introContainer.addChild(LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.0,
scaleY: 3.0,
alpha: 0.8
}));
introBackground.x = 2048 / 2;
introBackground.y = 2732 / 2;
introBackground.tint = 0x1a1a2e;
// Intro title text
var introTitle = new Text2('TAP THE BEAT', {
size: 150,
fill: 0xFFFFFF
});
introTitle.anchor.set(0.5, 0.5);
introContainer.addChild(introTitle);
introTitle.x = 2048 / 2;
introTitle.y = 2732 / 2 - 400;
// Instruction text lines
var instruction1 = new Text2('Tap when the beats reach the center circle!', {
size: 72,
fill: 0xCCCCCC
});
instruction1.anchor.set(0.5, 0.5);
introContainer.addChild(instruction1);
instruction1.x = 2048 / 2;
instruction1.y = 2732 / 2 - 200;
var instruction2 = new Text2('Time your taps to the rhythm.', {
size: 72,
fill: 0xCCCCCC
});
instruction2.anchor.set(0.5, 0.5);
introContainer.addChild(instruction2);
instruction2.x = 2048 / 2;
instruction2.y = 2732 / 2 - 100;
var instruction3 = new Text2('Earn combos and level up!', {
size: 72,
fill: 0xCCCCCC
});
instruction3.anchor.set(0.5, 0.5);
introContainer.addChild(instruction3);
instruction3.x = 2048 / 2;
instruction3.y = 2732 / 2;
// Start button
var startButton = introContainer.addChild(LK.getAsset('centerTarget', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5
}));
startButton.x = 2048 / 2;
startButton.y = 2732 / 2 + 300;
startButton.tint = 0x00FF00;
var startButtonText = new Text2('TAP TO START', {
size: 96,
fill: 0xFFFFFF
});
startButtonText.anchor.set(0.5, 0.5);
introContainer.addChild(startButtonText);
startButtonText.x = 2048 / 2;
startButtonText.y = 2732 / 2 + 300;
// Score display
var scoreText = new Text2('Score: 0', {
size: 96,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
scoreText.y = 100;
// Combo display
var comboText = new Text2('Combo: 0', {
size: 72,
fill: 0xFFFF00
});
comboText.anchor.set(0.5, 0);
LK.gui.top.addChild(comboText);
comboText.y = 200;
// Level display
var levelText = new Text2('Level: 1', {
size: 84,
fill: 0x00FFFF
});
levelText.anchor.set(0, 0);
LK.gui.topLeft.addChild(levelText);
levelText.x = 120;
levelText.y = 50;
// Miss count display
var missText = new Text2('Misses: 0/10', {
size: 60,
fill: 0xFF6666
});
missText.anchor.set(1, 0);
LK.gui.topRight.addChild(missText);
missText.x = -20;
missText.y = 50;
// Feedback text
var feedbackText = new Text2('', {
size: 120,
fill: 0x00FF00
});
feedbackText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(feedbackText);
feedbackText.alpha = 0;
function startGame() {
showingIntro = false;
gameStarted = true;
introContainer.visible = false;
}
function spawnBeat() {
var beat = new BeatCircle();
// Apply level-based speed increase
beat.speed = 2.2 + (currentLevel - 1) * 0.2;
// Random angle for spawn position
var angle = Math.random() * Math.PI * 2;
var centerX = 2048 / 2;
var centerY = 2732 / 2;
beat.x = centerX + Math.cos(angle) * beat.startRadius;
beat.y = centerY + Math.sin(angle) * beat.startRadius;
beats.push(beat);
game.addChild(beat);
}
function levelUp() {
currentLevel++;
levelTimer = 0;
// Show level up feedback
feedbackText.setText('LEVEL UP!');
feedbackText.tint = "#00FFFF";
feedbackText.alpha = 1;
tween(feedbackText, {
alpha: 0
}, {
duration: 1000
});
// Increase difficulty
spawnInterval = Math.max(25, spawnInterval - 3); // Faster spawning
var speedIncrease = 0.2;
for (var i = 0; i < beats.length; i++) {
beats[i].speed += speedIncrease;
}
// Update level display
levelText.setText('Level: ' + currentLevel);
// Flash screen for level up
LK.effects.flashScreen(0x00FFFF, 500);
}
function hitBeat(accuracy) {
var points = 0;
var feedbackColor = "#ffffff";
var feedbackMsg = "";
if (accuracy === 'perfect') {
points = 100 + combo * 10;
feedbackColor = "#00ff00";
feedbackMsg = "PERFECT!";
} else if (accuracy === 'good') {
points = 50 + combo * 5;
feedbackColor = "#ffff00";
feedbackMsg = "GOOD!";
}
gameScore += points;
combo++;
// Update displays
scoreText.setText('Score: ' + gameScore);
comboText.setText('Combo: ' + combo);
// Show feedback
feedbackText.setText(feedbackMsg);
feedbackText.tint = feedbackColor;
feedbackText.alpha = 1;
tween(feedbackText, {
alpha: 0
}, {
duration: 500
});
// Visual effects for combos
if (combo > 0 && combo % 5 === 0) {
LK.effects.flashScreen(0x00ff00, 200);
}
// Increase lane wave intensity with combo
laneWaveIntensity = 0.08 + combo * 0.002;
laneWaveSpeed = 0.15 + combo * 0.005;
// Add combo pulse effect to lane
tween(lane, {
scaleX: laneBaseScale + 0.15,
scaleY: laneBaseScale + 0.15
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(lane, {
scaleX: laneBaseScale,
scaleY: laneBaseScale
}, {
duration: 200,
easing: tween.easeOut
});
}
});
// Play hit sound
LK.getSound('hit').play();
// Update score for LK system
LK.setScore(gameScore);
}
function missedBeat() {
combo = 0;
missCount++;
comboText.setText('Combo: 0');
missText.setText('Misses: ' + missCount + '/' + maxMisses);
// Check for game over condition
if (missCount >= maxMisses) {
LK.showGameOver();
return;
}
// Reset lane wave intensity
laneWaveIntensity = 0.08;
laneWaveSpeed = 0.15;
// Show miss feedback
feedbackText.setText('MISS!');
feedbackText.tint = "#ff0000";
feedbackText.alpha = 1;
tween(feedbackText, {
alpha: 0
}, {
duration: 500
});
// Flash effect
LK.effects.flashScreen(0xff0000, 300);
// Play miss sound
LK.getSound('miss').play();
}
// Touch handler
game.down = function (x, y, obj) {
// Handle intro screen tap
if (showingIntro) {
// Check if tap is on start button area
var buttonX = 2048 / 2;
var buttonY = 2732 / 2 + 300;
var buttonRadius = 200; // Approximate button radius
var dx = x - buttonX;
var dy = y - buttonY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= buttonRadius) {
startGame();
}
return;
}
if (!gameStarted) return;
var hitDetected = false;
// Check all active beats for hits
for (var i = beats.length - 1; i >= 0; i--) {
var beat = beats[i];
if (!beat.isActive) continue;
var accuracy = beat.checkHit();
if (accuracy) {
hitBeat(accuracy);
hitDetected = true;
break; // Only hit one beat per tap
}
}
// If no beat was hit, it's a miss (but don't reset combo for tapping empty space)
};
game.update = function () {
// Only run game logic if the game has started
if (!gameStarted) return;
// Animate lane wave effect
laneWaveTimer += laneWaveSpeed;
var waveScale = laneBaseScale + Math.sin(laneWaveTimer) * laneWaveIntensity;
lane.scaleX = waveScale;
lane.scaleY = waveScale;
// Add subtle rotation wave
lane.rotation = Math.sin(laneWaveTimer * 0.7) * 0.02;
// Spawn new beats
spawnTimer++;
if (spawnTimer >= spawnInterval) {
spawnBeat();
spawnTimer = 0;
// Gradually increase difficulty by spawning beats more frequently
if (spawnInterval > 25) {
spawnInterval -= 0.1;
}
}
// Clean up inactive beats
for (var i = beats.length - 1; i >= 0; i--) {
var beat = beats[i];
if (!beat.isActive) {
beat.destroy();
beats.splice(i, 1);
}
}
// Level progression timer
levelTimer++;
if (levelTimer >= levelDuration) {
if (currentLevel >= 10) {
LK.showYouWin();
return;
}
levelUp();
}
// Win condition - reach level 10
if (currentLevel > 10) {
LK.showYouWin();
}
};
// Start background music
LK.playMusic('bgmusic');