/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var HitEffect = Container.expand(function () { var self = Container.call(this); var effectGraphics = self.attachAsset('perfectHit', { anchorX: 0.5, anchorY: 0.5 }); self.init = function (x, y) { self.x = x; self.y = y; effectGraphics.alpha = 1; effectGraphics.scale.x = 0.5; effectGraphics.scale.y = 0.5; tween(effectGraphics.scale, { x: 1.5, y: 1.5 }, { duration: 300, easing: tween.easeOut }); tween(effectGraphics, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); // MusicNote class with trail var MusicNote = Container.expand(function () { var self = Container.call(this); var noteGraphics = self.attachAsset('musicNote', { anchorX: 0.5, anchorY: 0.5 }); // Add a soft blur effect to the note by overlaying a faint, larger, low-alpha version behind the main note var blurOverlay = self.attachAsset('musicNote', { anchorX: 0.5, anchorY: 0.5, alpha: 0.22, scaleX: 1.18, scaleY: 1.18 }); blurOverlay.zIndex = -1; self.addChild(blurOverlay); self.speed = 8; self.lane = 0; self.isPerfect = false; self.lastY = 0; // Add trail self.trail = self.addChild(new NoteTrail()); self.init = function (laneIndex) { self.lane = laneIndex; self.x = 256 + laneIndex * 512; self.y = -100; self.lastY = self.y; // Assign a unique asset for each note type (laneIndex 0-5) var noteAssets = ['musicNote', 'musicNote2', 'musicNote3', 'musicNote4', 'musicNote5', 'musicNote6']; var assetId = noteAssets[self.lane % noteAssets.length]; // Remove previous graphics if any if (self.children && self.children.length > 0) { // Remove both main note and blur overlay if present if (self.children[0] === blurOverlay) { self.removeChild(self.children[0]); if (self.children.length > 0) self.removeChild(self.children[0]); } else { self.removeChild(self.children[0]); if (self.children.length > 0 && self.children[0] === blurOverlay) self.removeChild(self.children[0]); } } // Attach the correct asset for this note var newBlurOverlay = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5, alpha: 0.22, scaleX: 1.18, scaleY: 1.18 }); newBlurOverlay.zIndex = -1; self.addChild(newBlurOverlay); var newNoteGraphics = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); noteGraphics = newNoteGraphics; self.addChild(noteGraphics); // Set a fixed color and asset for the trail for each note type var trailColors = [0xffa500, // Orange 0x9400d3, // Purple 0xff69b4, // Pink 0x1e90ff, // Blue 0xffd700 // Yellow ]; var trailAssets = ['noteTrail1', 'noteTrail2', 'noteTrail3', 'noteTrail4', 'noteTrail5']; self.trail.setAsset(trailAssets[self.lane % trailAssets.length]); self.trail.clearTrail(); }; self.update = function () { self.lastY = self.y; self.y += self.speed; self.trail.updateTrail(self.x, self.y); }; return self; }); // NoteTrail class: renders a long, thick, colorful, blurred trail behind the note var NoteTrail = Container.expand(function () { var self = Container.call(this); self.trailColor = 0xffffff; self.trailAlpha = 0.7; self.trailLength = 340; // Even longer trail self.trailPoints = []; // We'll use multiple line assets for a blurred, colorful effect var lineNodes = []; // Make the trail much thicker, longer, and softer for a blurrier look var blurWidths = [140, 110, 85, 65, 48, 32, 20, 12]; // More layers, thicker var blurAlphas = [0.08, 0.11, 0.15, 0.22, 0.32, 0.45, 0.62, 0.8]; // Softer, more gradual var blurColorOffsets = [0, 0.06, 0.12, 0.18, 0.24, 0.30, 0.36, 0.42]; // More rainbow steps // Helper to shift color hue function shiftHue(color, offset) { // Convert hex to r,g,b var r = color >> 16 & 0xff; var g = color >> 8 & 0xff; var b = color & 0xff; // Convert to HSV var max = Math.max(r, g, b), min = Math.min(r, g, b); var h, s, v = max; var d = max - min; s = max === 0 ? 0 : d / max; if (max === min) { h = 0; } else { switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; } h /= 6; } // Shift hue h = (h + offset) % 1; // Convert back to RGB var i = Math.floor(h * 6); var f = h * 6 - i; var p = v * (1 - s); var q = v * (1 - f * s); var t = v * (1 - (1 - f) * s); switch (i % 6) { case 0: r = v, g = t, b = p; break; case 1: r = q, g = v, b = p; break; case 2: r = p, g = v, b = t; break; case 3: r = p, g = q, b = v; break; case 4: r = t, g = p, b = v; break; case 5: r = v, g = p, b = q; break; } // Convert hex to r,g,b var r = color >> 16 & 0xff; var g = color >> 8 & 0xff; var b = color & 0xff; // Convert to HSV var max = Math.max(r, g, b), min = Math.min(r, g, b); var h, s, v = max; var d = max - min; s = max === 0 ? 0 : d / max; if (max === min) { h = 0; } else { switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; } h /= 6; } // Shift hue h = (h + offset) % 1; // Convert back to RGB var i = Math.floor(h * 6); var f = h * 6 - i; var p = v * (1 - s); var q = v * (1 - f * s); var t = v * (1 - (1 - f) * s); switch (i % 6) { case 0: r = v, g = t, b = p; break; case 1: r = q, g = v, b = p; break; case 2: r = p, g = v, b = t; break; case 3: r = p, g = q, b = v; break; case 4: r = t, g = p, b = v; break; case 5: r = v, g = p, b = q; break; } return Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b); } // Default trail asset self.trailAssetId = 'bullet'; // Create several line assets for the blur effect function createTrailLines(assetId) { // Remove old nodes if any for (var i = 0; i < lineNodes.length; i++) { if (lineNodes[i].parent) { self.removeChild(lineNodes[i]); } } lineNodes = []; for (var i = 0; i < blurWidths.length; i++) { var node = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 1.0, width: blurWidths[i], height: 1, color: self.trailColor, alpha: blurAlphas[i] }); node.visible = false; lineNodes.push(node); } } createTrailLines(self.trailAssetId); // Add setAsset method to change trail asset self.setAsset = function (assetId) { if (assetId && assetId !== self.trailAssetId) { self.trailAssetId = assetId; createTrailLines(assetId); // Re-apply color to new nodes self.setColor(self.trailColor); } }; self.setColor = function (color) { self.trailColor = color; for (var i = 0; i < lineNodes.length; i++) { // Each line gets a slightly shifted hue for rainbow effect lineNodes[i].tint = shiftHue(color, blurColorOffsets[i]); } }; self.updateTrail = function (x, y) { // Add new point to the front self.trailPoints.unshift({ x: x, y: y }); if (self.trailPoints.length > self.trailLength) { self.trailPoints.pop(); } // Only draw if we have enough points if (self.trailPoints.length > 2) { var from = self.trailPoints[0]; var to = self.trailPoints[self.trailPoints.length - 1]; var dx = to.x - from.x; var dy = to.y - from.y; var dist = Math.sqrt(dx * dx + dy * dy); for (var i = 0; i < lineNodes.length; i++) { var node = lineNodes[i]; node.visible = true; node.x = 0; node.y = 0; node.width = blurWidths[i]; node.height = dist * 1.18; // Make the trail visually taller node.rotation = Math.atan2(dy, dx) + Math.PI / 2; // Animate alpha for a soft blur pulse node.alpha = blurAlphas[i] * (0.8 + 0.2 * Math.abs(Math.sin(LK.ticks / (6 + i * 2)))); } } else { for (var i = 0; i < lineNodes.length; i++) { lineNodes[i].visible = false; } } }; self.clearTrail = function () { self.trailPoints = []; for (var i = 0; i < lineNodes.length; i++) { lineNodes[i].visible = false; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Yellow // Blue // Pink // Purple // Orange game.setBackgroundColor(0x1a1a2e); // Animated background color effect var bgColorBase = [26, 26, 46]; // 0x1a1a2e var bgColorPulse = [40, 40, 80]; var bgColorTick = 0; var bgColorNode = null; // Create a full screen rectangle as background bgColorNode = game.addChild(LK.getAsset('hitZone', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, alpha: 0.18 })); bgColorNode.width = 2048; bgColorNode.height = 2732; bgColorNode.zIndex = -1000; // Ensure it's always at the back var notes = []; var score = 0; var combo = 0; var maxCombo = 0; var hitZoneY = 2200; var noteSpawnTimer = 0; var gameStarted = false; // Fever mode variables var feverActive = false; var feverGauge = 0; var feverGaugeMax = 1000; var feverTimer = 0; var feverBar = null; var feverBarBg = null; // Create hit zone var hitZone = game.addChild(LK.getAsset('hitZone', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: hitZoneY, alpha: 0.3 })); // Score display var scoreTxt = new Text2('Score: 0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Combo display var comboTxt = new Text2('Combo: 0', { size: 60, fill: 0xFFFF00 }); comboTxt.anchor.set(0.5, 0); comboTxt.y = 100; LK.gui.top.addChild(comboTxt); // Instructions var instructionTxt = new Text2('Tap the notes when they reach the blue zone!', { size: 50, fill: 0xFFFFFF }); instructionTxt.anchor.set(0.5, 0.5); LK.gui.center.addChild(instructionTxt); // Fever Bar UI feverBarBg = new Text2('', { size: 1, fill: 0x000000 }); feverBarBg.width = 600; feverBarBg.height = 40; feverBarBg.x = 1024 - 300; feverBarBg.y = 180; feverBarBg.alpha = 0.4; LK.gui.top.addChild(feverBarBg); feverBar = new Text2('', { size: 1, fill: 0xffd700 }); feverBar.width = 0; feverBar.height = 40; feverBar.x = 1024 - 300; feverBar.y = 180; feverBar.alpha = 0.8; LK.gui.top.addChild(feverBar); var feverTxt = new Text2('FEVER', { size: 40, fill: 0xffd700 }); feverTxt.anchor.set(0.5, 0.5); feverTxt.x = 1024; feverTxt.y = 200; LK.gui.top.addChild(feverTxt); // Start game after 2 seconds LK.setTimeout(function () { instructionTxt.visible = false; gameStarted = true; LK.playMusic('gameMusic'); }, 2000); // Touch handler game.down = function (x, y, obj) { if (!gameStarted) { return; } // Determine which lane was tapped var lane = Math.floor(x / 512); if (lane > 3) { lane = 3; } // Check for hit var hitNote = false; for (var i = notes.length - 1; i >= 0; i--) { var note = notes[i]; if (note.lane === lane && Math.abs(note.y - hitZoneY) < 180) { // Hit! hitNote = true; // Hit zone pulse VFX tween(hitZone.scale, { x: 1.15, y: 1.15 }, { duration: 60, onFinish: function onFinish() { tween(hitZone.scale, { x: 1, y: 1 }, { duration: 80 }); } }); var baseScore = 100; combo++; if (combo > maxCombo) { maxCombo = combo; } // Perfect hit bonus if (Math.abs(note.y - hitZoneY) < 30) { baseScore += 50; var effect = game.addChild(new HitEffect()); effect.init(note.x, note.y); // Increase fever gauge on perfect feverGauge += 120; if (feverGauge > feverGaugeMax) { feverGauge = feverGaugeMax; } } else { feverGauge += 40; if (feverGauge > feverGaugeMax) { feverGauge = feverGaugeMax; } } // Fast note bonus: if the note is a fast note (speed > 8), give bonus points but no visual/speed effect if (note.speed > 8) { baseScore += 100; // Bonus for fast note } // Long note if (note.isLong && Math.abs(note.y - hitZoneY) < 30) { baseScore += 100; // Visual feedback for long note perfect hit LK.effects.flashObject(note, 0x00ffff, 200); } // Fever mode bonus if (feverActive) { baseScore *= 2; } score += baseScore; LK.getSound('hitSound').play(); // Flash the note before destroying LK.effects.flashObject(note, 0xffffff, 200); LK.setTimeout(function () { if (note.trail && typeof note.trail.clearTrail === "function") { note.trail.clearTrail(); } note.destroy(); }, 200); notes.splice(i, 1); break; } } if (!hitNote) { // Miss! combo = 0; LK.getSound('missSound').play(); LK.effects.flashScreen(0xff0000, 200); // Combo break feedback LK.effects.flashScreen(0x000000, 100); } // Update UI scoreTxt.setText('Score: ' + score); comboTxt.setText('Combo: ' + combo); // Combo text pop VFX tween(comboTxt.scale, { x: 1.3, y: 1.3 }, { duration: 80, onFinish: function onFinish() { tween(comboTxt.scale, { x: 1, y: 1 }, { duration: 100 }); } }); // Win condition if (score >= 10000) { feverActive = false; feverGauge = 0; feverTimer = 0; feverBar.width = 0; hitZone.alpha = 0.3; LK.showYouWin(); } // Side brightness effect when near end if (score >= 9000 && score < 10000) { // Pretend to increase the brightness by flashing the screen with a white overlay, pulsing var brightness = 0.18 + 0.12 * Math.abs(Math.sin(LK.ticks / 10)); LK.effects.flashScreen(0xffffff, 60, brightness); } }; // Game update loop game.update = function () { if (!gameStarted) { return; } // Animate background color bgColorTick++; var t = 0.5 + 0.5 * Math.sin(bgColorTick / 60); var r = Math.floor(bgColorBase[0] * (1 - t) + bgColorPulse[0] * t); var g = Math.floor(bgColorBase[1] * (1 - t) + bgColorPulse[1] * t); var b = Math.floor(bgColorBase[2] * (1 - t) + bgColorPulse[2] * t); var bgColor = r << 16 | g << 8 | b; if (bgColorNode) { bgColorNode.tint = bgColor; // Animate alpha for a more dynamic effect bgColorNode.alpha = 0.14 + 0.06 * Math.abs(Math.sin(bgColorTick / 80)); } // Spawn notes noteSpawnTimer++; if (noteSpawnTimer > 45) { noteSpawnTimer = 0; var laneIdx = Math.floor(Math.random() * 4); var note = game.addChild(new MusicNote()); note.init(laneIdx); // Note spawn VFX: pop-in scale and alpha animation if (note.children && note.children.length > 0 && note.children[0]) { var g = note.children[0]; g.scale.x = 0.2; g.scale.y = 0.2; g.alpha = 0.2; tween(g.scale, { x: 1, y: 1 }, { duration: 200, easing: tween.easeOut }); tween(g, { alpha: 1 }, { duration: 200 }); } // Fast note bonus: randomly make some notes fast (no visual effect, just for scoring) if (Math.random() < 0.1) { note.speed = 12; // Fast note } // 10% chance to spawn a long note (slower, worth more if hit perfectly) if (Math.random() < 0.1) { note.isLong = true; note.speed = 4; } notes.push(note); } // Check for missed notes for (var i = notes.length - 1; i >= 0; i--) { var note = notes[i]; // Always set trail asset to match note's lane (for non-fever mode) if (!feverActive && note.trail && typeof note.trail.setAsset === "function") { var trailAssets = ['noteTrail1', 'noteTrail2', 'noteTrail3', 'noteTrail4', 'noteTrail5']; note.trail.setAsset(trailAssets[note.lane % trailAssets.length]); } // Check if note passed the hit zone if (note.lastY <= hitZoneY + 180 && note.y > hitZoneY + 180) { // Missed! combo = 0; LK.getSound('missSound').play(); comboTxt.setText('Combo: ' + combo); // Visual feedback for miss LK.effects.flashObject(note, 0xff0000, 200); } // Remove notes that are off screen if (note.y > 2832) { if (note.trail && typeof note.trail.clearTrail === "function") { note.trail.clearTrail(); } note.destroy(); notes.splice(i, 1); } } // Fever mode logic if (feverActive) { feverTimer--; feverGauge -= 2; if (feverGauge < 0) { feverGauge = 0; } // Add a pulsing effect to the hit zone during fever hitZone.alpha = 0.5 + 0.2 * Math.sin(LK.ticks / 5); // Rainbow color cycling for all notes during fever for (var n = 0; n < notes.length; n++) { var noteObj = notes[n]; // Cycle hue based on time and note index for variety var hue = (LK.ticks * 4 + n * 40) % 360 / 360; // Convert hue to RGB (simple HSV to RGB) var i = Math.floor(hue * 6); var f = hue * 6 - i; var q = 1 - f; var r, g, b; switch (i % 6) { case 0: r = 1; g = f; b = 0; break; case 1: r = q; g = 1; b = 0; break; case 2: r = 0; g = 1; b = f; break; case 3: r = 0; g = q; b = 1; break; case 4: r = f; g = 0; b = 1; break; case 5: r = 1; g = 0; b = q; break; } var rgb = Math.floor(r * 255) << 16 | Math.floor(g * 255) << 8 | Math.floor(b * 255); // Set tint for the note's main graphics if (noteObj.children && noteObj.children.length > 0 && noteObj.children[0]) { noteObj.children[0].tint = rgb; } // Set trail asset to match the note's lane (even in fever mode) if (noteObj.trail && typeof noteObj.trail.setAsset === "function") { var trailAssets = ['noteTrail1', 'noteTrail2', 'noteTrail3', 'noteTrail4', 'noteTrail5']; noteObj.trail.setAsset(trailAssets[noteObj.lane % trailAssets.length]); } } if (feverTimer <= 0 || feverGauge <= 0) { feverActive = false; feverTimer = 0; hitZone.alpha = 0.3; // End fever visual LK.effects.flashScreen(0x1a1a2e, 400); } } else if (feverGauge >= feverGaugeMax) { feverActive = true; feverTimer = 360; // 6 seconds at 60fps // Fever visual LK.effects.flashScreen(0xffd700, 400); hitZone.alpha = 0.7; // Visual feedback: all notes flash gold for (var f = 0; f < notes.length; f++) { LK.effects.flashObject(notes[f], 0xffd700, 300); } } // Update fever bar UI feverBar.width = Math.floor(feverGauge / feverGaugeMax * 600); // Fever bar glow VFX if (feverActive) { feverBar.alpha = 0.8 + 0.2 * Math.abs(Math.sin(LK.ticks / 4)); } else { feverBar.alpha = 0.8; } // Game over if too many misses if (notes.length > 10) { feverActive = false; feverGauge = 0; feverTimer = 0; feverBar.width = 0; hitZone.alpha = 0.3; LK.showGameOver(); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var HitEffect = Container.expand(function () {
var self = Container.call(this);
var effectGraphics = self.attachAsset('perfectHit', {
anchorX: 0.5,
anchorY: 0.5
});
self.init = function (x, y) {
self.x = x;
self.y = y;
effectGraphics.alpha = 1;
effectGraphics.scale.x = 0.5;
effectGraphics.scale.y = 0.5;
tween(effectGraphics.scale, {
x: 1.5,
y: 1.5
}, {
duration: 300,
easing: tween.easeOut
});
tween(effectGraphics, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
// MusicNote class with trail
var MusicNote = Container.expand(function () {
var self = Container.call(this);
var noteGraphics = self.attachAsset('musicNote', {
anchorX: 0.5,
anchorY: 0.5
});
// Add a soft blur effect to the note by overlaying a faint, larger, low-alpha version behind the main note
var blurOverlay = self.attachAsset('musicNote', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.22,
scaleX: 1.18,
scaleY: 1.18
});
blurOverlay.zIndex = -1;
self.addChild(blurOverlay);
self.speed = 8;
self.lane = 0;
self.isPerfect = false;
self.lastY = 0;
// Add trail
self.trail = self.addChild(new NoteTrail());
self.init = function (laneIndex) {
self.lane = laneIndex;
self.x = 256 + laneIndex * 512;
self.y = -100;
self.lastY = self.y;
// Assign a unique asset for each note type (laneIndex 0-5)
var noteAssets = ['musicNote', 'musicNote2', 'musicNote3', 'musicNote4', 'musicNote5', 'musicNote6'];
var assetId = noteAssets[self.lane % noteAssets.length];
// Remove previous graphics if any
if (self.children && self.children.length > 0) {
// Remove both main note and blur overlay if present
if (self.children[0] === blurOverlay) {
self.removeChild(self.children[0]);
if (self.children.length > 0) self.removeChild(self.children[0]);
} else {
self.removeChild(self.children[0]);
if (self.children.length > 0 && self.children[0] === blurOverlay) self.removeChild(self.children[0]);
}
}
// Attach the correct asset for this note
var newBlurOverlay = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.22,
scaleX: 1.18,
scaleY: 1.18
});
newBlurOverlay.zIndex = -1;
self.addChild(newBlurOverlay);
var newNoteGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
noteGraphics = newNoteGraphics;
self.addChild(noteGraphics);
// Set a fixed color and asset for the trail for each note type
var trailColors = [0xffa500,
// Orange
0x9400d3,
// Purple
0xff69b4,
// Pink
0x1e90ff,
// Blue
0xffd700 // Yellow
];
var trailAssets = ['noteTrail1', 'noteTrail2', 'noteTrail3', 'noteTrail4', 'noteTrail5'];
self.trail.setAsset(trailAssets[self.lane % trailAssets.length]);
self.trail.clearTrail();
};
self.update = function () {
self.lastY = self.y;
self.y += self.speed;
self.trail.updateTrail(self.x, self.y);
};
return self;
});
// NoteTrail class: renders a long, thick, colorful, blurred trail behind the note
var NoteTrail = Container.expand(function () {
var self = Container.call(this);
self.trailColor = 0xffffff;
self.trailAlpha = 0.7;
self.trailLength = 340; // Even longer trail
self.trailPoints = [];
// We'll use multiple line assets for a blurred, colorful effect
var lineNodes = [];
// Make the trail much thicker, longer, and softer for a blurrier look
var blurWidths = [140, 110, 85, 65, 48, 32, 20, 12]; // More layers, thicker
var blurAlphas = [0.08, 0.11, 0.15, 0.22, 0.32, 0.45, 0.62, 0.8]; // Softer, more gradual
var blurColorOffsets = [0, 0.06, 0.12, 0.18, 0.24, 0.30, 0.36, 0.42]; // More rainbow steps
// Helper to shift color hue
function shiftHue(color, offset) {
// Convert hex to r,g,b
var r = color >> 16 & 0xff;
var g = color >> 8 & 0xff;
var b = color & 0xff;
// Convert to HSV
var max = Math.max(r, g, b),
min = Math.min(r, g, b);
var h,
s,
v = max;
var d = max - min;
s = max === 0 ? 0 : d / max;
if (max === min) {
h = 0;
} else {
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
// Shift hue
h = (h + offset) % 1;
// Convert back to RGB
var i = Math.floor(h * 6);
var f = h * 6 - i;
var p = v * (1 - s);
var q = v * (1 - f * s);
var t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0:
r = v, g = t, b = p;
break;
case 1:
r = q, g = v, b = p;
break;
case 2:
r = p, g = v, b = t;
break;
case 3:
r = p, g = q, b = v;
break;
case 4:
r = t, g = p, b = v;
break;
case 5:
r = v, g = p, b = q;
break;
}
// Convert hex to r,g,b
var r = color >> 16 & 0xff;
var g = color >> 8 & 0xff;
var b = color & 0xff;
// Convert to HSV
var max = Math.max(r, g, b),
min = Math.min(r, g, b);
var h,
s,
v = max;
var d = max - min;
s = max === 0 ? 0 : d / max;
if (max === min) {
h = 0;
} else {
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
// Shift hue
h = (h + offset) % 1;
// Convert back to RGB
var i = Math.floor(h * 6);
var f = h * 6 - i;
var p = v * (1 - s);
var q = v * (1 - f * s);
var t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0:
r = v, g = t, b = p;
break;
case 1:
r = q, g = v, b = p;
break;
case 2:
r = p, g = v, b = t;
break;
case 3:
r = p, g = q, b = v;
break;
case 4:
r = t, g = p, b = v;
break;
case 5:
r = v, g = p, b = q;
break;
}
return Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
}
// Default trail asset
self.trailAssetId = 'bullet';
// Create several line assets for the blur effect
function createTrailLines(assetId) {
// Remove old nodes if any
for (var i = 0; i < lineNodes.length; i++) {
if (lineNodes[i].parent) {
self.removeChild(lineNodes[i]);
}
}
lineNodes = [];
for (var i = 0; i < blurWidths.length; i++) {
var node = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 1.0,
width: blurWidths[i],
height: 1,
color: self.trailColor,
alpha: blurAlphas[i]
});
node.visible = false;
lineNodes.push(node);
}
}
createTrailLines(self.trailAssetId);
// Add setAsset method to change trail asset
self.setAsset = function (assetId) {
if (assetId && assetId !== self.trailAssetId) {
self.trailAssetId = assetId;
createTrailLines(assetId);
// Re-apply color to new nodes
self.setColor(self.trailColor);
}
};
self.setColor = function (color) {
self.trailColor = color;
for (var i = 0; i < lineNodes.length; i++) {
// Each line gets a slightly shifted hue for rainbow effect
lineNodes[i].tint = shiftHue(color, blurColorOffsets[i]);
}
};
self.updateTrail = function (x, y) {
// Add new point to the front
self.trailPoints.unshift({
x: x,
y: y
});
if (self.trailPoints.length > self.trailLength) {
self.trailPoints.pop();
}
// Only draw if we have enough points
if (self.trailPoints.length > 2) {
var from = self.trailPoints[0];
var to = self.trailPoints[self.trailPoints.length - 1];
var dx = to.x - from.x;
var dy = to.y - from.y;
var dist = Math.sqrt(dx * dx + dy * dy);
for (var i = 0; i < lineNodes.length; i++) {
var node = lineNodes[i];
node.visible = true;
node.x = 0;
node.y = 0;
node.width = blurWidths[i];
node.height = dist * 1.18; // Make the trail visually taller
node.rotation = Math.atan2(dy, dx) + Math.PI / 2;
// Animate alpha for a soft blur pulse
node.alpha = blurAlphas[i] * (0.8 + 0.2 * Math.abs(Math.sin(LK.ticks / (6 + i * 2))));
}
} else {
for (var i = 0; i < lineNodes.length; i++) {
lineNodes[i].visible = false;
}
}
};
self.clearTrail = function () {
self.trailPoints = [];
for (var i = 0; i < lineNodes.length; i++) {
lineNodes[i].visible = false;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Yellow
// Blue
// Pink
// Purple
// Orange
game.setBackgroundColor(0x1a1a2e);
// Animated background color effect
var bgColorBase = [26, 26, 46]; // 0x1a1a2e
var bgColorPulse = [40, 40, 80];
var bgColorTick = 0;
var bgColorNode = null;
// Create a full screen rectangle as background
bgColorNode = game.addChild(LK.getAsset('hitZone', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
alpha: 0.18
}));
bgColorNode.width = 2048;
bgColorNode.height = 2732;
bgColorNode.zIndex = -1000; // Ensure it's always at the back
var notes = [];
var score = 0;
var combo = 0;
var maxCombo = 0;
var hitZoneY = 2200;
var noteSpawnTimer = 0;
var gameStarted = false;
// Fever mode variables
var feverActive = false;
var feverGauge = 0;
var feverGaugeMax = 1000;
var feverTimer = 0;
var feverBar = null;
var feverBarBg = null;
// Create hit zone
var hitZone = game.addChild(LK.getAsset('hitZone', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: hitZoneY,
alpha: 0.3
}));
// Score display
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Combo display
var comboTxt = new Text2('Combo: 0', {
size: 60,
fill: 0xFFFF00
});
comboTxt.anchor.set(0.5, 0);
comboTxt.y = 100;
LK.gui.top.addChild(comboTxt);
// Instructions
var instructionTxt = new Text2('Tap the notes when they reach the blue zone!', {
size: 50,
fill: 0xFFFFFF
});
instructionTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionTxt);
// Fever Bar UI
feverBarBg = new Text2('', {
size: 1,
fill: 0x000000
});
feverBarBg.width = 600;
feverBarBg.height = 40;
feverBarBg.x = 1024 - 300;
feverBarBg.y = 180;
feverBarBg.alpha = 0.4;
LK.gui.top.addChild(feverBarBg);
feverBar = new Text2('', {
size: 1,
fill: 0xffd700
});
feverBar.width = 0;
feverBar.height = 40;
feverBar.x = 1024 - 300;
feverBar.y = 180;
feverBar.alpha = 0.8;
LK.gui.top.addChild(feverBar);
var feverTxt = new Text2('FEVER', {
size: 40,
fill: 0xffd700
});
feverTxt.anchor.set(0.5, 0.5);
feverTxt.x = 1024;
feverTxt.y = 200;
LK.gui.top.addChild(feverTxt);
// Start game after 2 seconds
LK.setTimeout(function () {
instructionTxt.visible = false;
gameStarted = true;
LK.playMusic('gameMusic');
}, 2000);
// Touch handler
game.down = function (x, y, obj) {
if (!gameStarted) {
return;
}
// Determine which lane was tapped
var lane = Math.floor(x / 512);
if (lane > 3) {
lane = 3;
}
// Check for hit
var hitNote = false;
for (var i = notes.length - 1; i >= 0; i--) {
var note = notes[i];
if (note.lane === lane && Math.abs(note.y - hitZoneY) < 180) {
// Hit!
hitNote = true;
// Hit zone pulse VFX
tween(hitZone.scale, {
x: 1.15,
y: 1.15
}, {
duration: 60,
onFinish: function onFinish() {
tween(hitZone.scale, {
x: 1,
y: 1
}, {
duration: 80
});
}
});
var baseScore = 100;
combo++;
if (combo > maxCombo) {
maxCombo = combo;
}
// Perfect hit bonus
if (Math.abs(note.y - hitZoneY) < 30) {
baseScore += 50;
var effect = game.addChild(new HitEffect());
effect.init(note.x, note.y);
// Increase fever gauge on perfect
feverGauge += 120;
if (feverGauge > feverGaugeMax) {
feverGauge = feverGaugeMax;
}
} else {
feverGauge += 40;
if (feverGauge > feverGaugeMax) {
feverGauge = feverGaugeMax;
}
}
// Fast note bonus: if the note is a fast note (speed > 8), give bonus points but no visual/speed effect
if (note.speed > 8) {
baseScore += 100; // Bonus for fast note
}
// Long note
if (note.isLong && Math.abs(note.y - hitZoneY) < 30) {
baseScore += 100;
// Visual feedback for long note perfect hit
LK.effects.flashObject(note, 0x00ffff, 200);
}
// Fever mode bonus
if (feverActive) {
baseScore *= 2;
}
score += baseScore;
LK.getSound('hitSound').play();
// Flash the note before destroying
LK.effects.flashObject(note, 0xffffff, 200);
LK.setTimeout(function () {
if (note.trail && typeof note.trail.clearTrail === "function") {
note.trail.clearTrail();
}
note.destroy();
}, 200);
notes.splice(i, 1);
break;
}
}
if (!hitNote) {
// Miss!
combo = 0;
LK.getSound('missSound').play();
LK.effects.flashScreen(0xff0000, 200);
// Combo break feedback
LK.effects.flashScreen(0x000000, 100);
}
// Update UI
scoreTxt.setText('Score: ' + score);
comboTxt.setText('Combo: ' + combo);
// Combo text pop VFX
tween(comboTxt.scale, {
x: 1.3,
y: 1.3
}, {
duration: 80,
onFinish: function onFinish() {
tween(comboTxt.scale, {
x: 1,
y: 1
}, {
duration: 100
});
}
});
// Win condition
if (score >= 10000) {
feverActive = false;
feverGauge = 0;
feverTimer = 0;
feverBar.width = 0;
hitZone.alpha = 0.3;
LK.showYouWin();
}
// Side brightness effect when near end
if (score >= 9000 && score < 10000) {
// Pretend to increase the brightness by flashing the screen with a white overlay, pulsing
var brightness = 0.18 + 0.12 * Math.abs(Math.sin(LK.ticks / 10));
LK.effects.flashScreen(0xffffff, 60, brightness);
}
};
// Game update loop
game.update = function () {
if (!gameStarted) {
return;
}
// Animate background color
bgColorTick++;
var t = 0.5 + 0.5 * Math.sin(bgColorTick / 60);
var r = Math.floor(bgColorBase[0] * (1 - t) + bgColorPulse[0] * t);
var g = Math.floor(bgColorBase[1] * (1 - t) + bgColorPulse[1] * t);
var b = Math.floor(bgColorBase[2] * (1 - t) + bgColorPulse[2] * t);
var bgColor = r << 16 | g << 8 | b;
if (bgColorNode) {
bgColorNode.tint = bgColor;
// Animate alpha for a more dynamic effect
bgColorNode.alpha = 0.14 + 0.06 * Math.abs(Math.sin(bgColorTick / 80));
}
// Spawn notes
noteSpawnTimer++;
if (noteSpawnTimer > 45) {
noteSpawnTimer = 0;
var laneIdx = Math.floor(Math.random() * 4);
var note = game.addChild(new MusicNote());
note.init(laneIdx);
// Note spawn VFX: pop-in scale and alpha animation
if (note.children && note.children.length > 0 && note.children[0]) {
var g = note.children[0];
g.scale.x = 0.2;
g.scale.y = 0.2;
g.alpha = 0.2;
tween(g.scale, {
x: 1,
y: 1
}, {
duration: 200,
easing: tween.easeOut
});
tween(g, {
alpha: 1
}, {
duration: 200
});
}
// Fast note bonus: randomly make some notes fast (no visual effect, just for scoring)
if (Math.random() < 0.1) {
note.speed = 12; // Fast note
}
// 10% chance to spawn a long note (slower, worth more if hit perfectly)
if (Math.random() < 0.1) {
note.isLong = true;
note.speed = 4;
}
notes.push(note);
}
// Check for missed notes
for (var i = notes.length - 1; i >= 0; i--) {
var note = notes[i];
// Always set trail asset to match note's lane (for non-fever mode)
if (!feverActive && note.trail && typeof note.trail.setAsset === "function") {
var trailAssets = ['noteTrail1', 'noteTrail2', 'noteTrail3', 'noteTrail4', 'noteTrail5'];
note.trail.setAsset(trailAssets[note.lane % trailAssets.length]);
}
// Check if note passed the hit zone
if (note.lastY <= hitZoneY + 180 && note.y > hitZoneY + 180) {
// Missed!
combo = 0;
LK.getSound('missSound').play();
comboTxt.setText('Combo: ' + combo);
// Visual feedback for miss
LK.effects.flashObject(note, 0xff0000, 200);
}
// Remove notes that are off screen
if (note.y > 2832) {
if (note.trail && typeof note.trail.clearTrail === "function") {
note.trail.clearTrail();
}
note.destroy();
notes.splice(i, 1);
}
}
// Fever mode logic
if (feverActive) {
feverTimer--;
feverGauge -= 2;
if (feverGauge < 0) {
feverGauge = 0;
}
// Add a pulsing effect to the hit zone during fever
hitZone.alpha = 0.5 + 0.2 * Math.sin(LK.ticks / 5);
// Rainbow color cycling for all notes during fever
for (var n = 0; n < notes.length; n++) {
var noteObj = notes[n];
// Cycle hue based on time and note index for variety
var hue = (LK.ticks * 4 + n * 40) % 360 / 360;
// Convert hue to RGB (simple HSV to RGB)
var i = Math.floor(hue * 6);
var f = hue * 6 - i;
var q = 1 - f;
var r, g, b;
switch (i % 6) {
case 0:
r = 1;
g = f;
b = 0;
break;
case 1:
r = q;
g = 1;
b = 0;
break;
case 2:
r = 0;
g = 1;
b = f;
break;
case 3:
r = 0;
g = q;
b = 1;
break;
case 4:
r = f;
g = 0;
b = 1;
break;
case 5:
r = 1;
g = 0;
b = q;
break;
}
var rgb = Math.floor(r * 255) << 16 | Math.floor(g * 255) << 8 | Math.floor(b * 255);
// Set tint for the note's main graphics
if (noteObj.children && noteObj.children.length > 0 && noteObj.children[0]) {
noteObj.children[0].tint = rgb;
}
// Set trail asset to match the note's lane (even in fever mode)
if (noteObj.trail && typeof noteObj.trail.setAsset === "function") {
var trailAssets = ['noteTrail1', 'noteTrail2', 'noteTrail3', 'noteTrail4', 'noteTrail5'];
noteObj.trail.setAsset(trailAssets[noteObj.lane % trailAssets.length]);
}
}
if (feverTimer <= 0 || feverGauge <= 0) {
feverActive = false;
feverTimer = 0;
hitZone.alpha = 0.3;
// End fever visual
LK.effects.flashScreen(0x1a1a2e, 400);
}
} else if (feverGauge >= feverGaugeMax) {
feverActive = true;
feverTimer = 360; // 6 seconds at 60fps
// Fever visual
LK.effects.flashScreen(0xffd700, 400);
hitZone.alpha = 0.7;
// Visual feedback: all notes flash gold
for (var f = 0; f < notes.length; f++) {
LK.effects.flashObject(notes[f], 0xffd700, 300);
}
}
// Update fever bar UI
feverBar.width = Math.floor(feverGauge / feverGaugeMax * 600);
// Fever bar glow VFX
if (feverActive) {
feverBar.alpha = 0.8 + 0.2 * Math.abs(Math.sin(LK.ticks / 4));
} else {
feverBar.alpha = 0.8;
}
// Game over if too many misses
if (notes.length > 10) {
feverActive = false;
feverGauge = 0;
feverTimer = 0;
feverBar.width = 0;
hitZone.alpha = 0.3;
LK.showGameOver();
}
};
Only the note no background
Delete the background only the note
The same note without the background only change is it will be green
Same note no background only change is it will be pink
Same note no background but orange
Same note no background but it will be purple
Patlama efekti. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat