User prompt
change flash color to hit object color
User prompt
convert corners to edges for flash effect
User prompt
flash all of the same time
User prompt
add a flash effect to corner when hit
User prompt
remove top particle
User prompt
eac hit drop a particles from top
User prompt
change fonts
User prompt
change game fonts to lilita
User prompt
make hit particle smaller
User prompt
when hit create a particle
User prompt
add another image behind of background image
User prompt
create a background image and give me reference
User prompt
change time bar color
User prompt
change time bar fill color to orange
User prompt
move time bottom
User prompt
time bar need a count down with song coming
User prompt
time bar wont work
User prompt
add a time bar for song coming part
User prompt
add a particle when hitted
User prompt
add hit effect
User prompt
change song duration to 2 min
User prompt
change game time with duration of song. and create hit circles randomly.
User prompt
start music before bar
User prompt
wait for 10 second and start create hit circles and create a "song coming" texted loading bar in center
User prompt
reset song each time
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // FollowCircle: For "follow the path" notes (future extension, not used in MVP) var FollowCircle = Container.expand(function () { var self = Container.call(this); var circle = self.attachAsset('followCircle', { anchorX: 0.5, anchorY: 0.5 }); circle.alpha = 0.8; return self; }); // HitCircle: Tap or Hold circle var HitCircle = Container.expand(function () { var self = Container.call(this); // Attach approach circle (shrinks towards hit time) var approach = self.attachAsset('approachCircle', { anchorX: 0.5, anchorY: 0.5 }); approach.alpha = 0.5; // Generate a random color for this hit circle self.circleColor = Math.floor(Math.random() * 0xFFFFFF); // Attach main hit circle with random color var circle = self.attachAsset('hitCircle', { anchorX: 0.5, anchorY: 0.5, color: self.circleColor }); circle.alpha = 1; // Text for number or timing var label = new Text2('', { size: 80, fill: 0x222222, font: "Lilita One" }); label.anchor.set(0.5, 0.5); self.addChild(label); self.hitTime = 0; // ms self.type = 'tap'; // 'tap' | 'hold' self.holdDuration = 0; // ms, for hold notes self.hit = false; self.held = false; self.completed = false; self.index = 0; // for combo display self.setType = function (type, holdDuration) { self.type = type; if (type === 'hold') { self.holdDuration = holdDuration; } }; self.setLabel = function (txt) { label.setText(txt); }; self.setApproachScale = function (scale) { approach.scaleX = scale; approach.scaleY = scale; }; self.setCircleAlpha = function (a) { circle.alpha = a; }; self.flash = function (color, duration) { LK.effects.flashObject(circle, color, duration); }; self.hideApproach = function () { approach.visible = false; }; self.hideHoldRing = function () { // No-op: hold ring removed }; self.destroySelf = function () { self.destroy(); }; return self; }); // Particle: Simple expanding/fading circle for hit effect var Particle = Container.expand(function () { var self = Container.call(this); var circle = self.attachAsset('hitCircle', { anchorX: 0.5, anchorY: 0.5, color: 0xffffff }); circle.alpha = 0.7; self.life = 350; // ms self.elapsed = 0; self.startScale = 0.5; self.endScale = 1.1; self.startAlpha = 0.7; self.endAlpha = 0.0; self.update = function () { self.elapsed += 1000 / 60; var t = Math.min(1, self.elapsed / self.life); circle.scaleX = circle.scaleY = self.startScale + (self.endScale - self.startScale) * t; circle.alpha = self.startAlpha + (self.endAlpha - self.startAlpha) * t; if (self.elapsed >= self.life) { self.destroy(); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181c24 }); /**** * Game Code ****/ // Display new background image behind the current background image (centered, covers full play area) // Background image: 2048x2732, id is a placeholder, replace with your actual image asset id if needed var bgImageBehind = LK.getAsset('bgImageBehind', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); game.addChild(bgImageBehind); var bgImage = LK.getAsset('bgImage', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); game.addChild(bgImage); // Music (placeholder, actual music asset will be loaded by LK) // Sounds (placeholders, actual sound assets will be loaded by LK) // Circles: main hit objects // --- Rhythm Map Data: Generate random hit circles for full song duration --- // Set songDuration to actual song length (LK asset 'song1' end - start in ms) var songDuration = 120000; // 2 minutes in ms var rhythmMap = []; var rpm = 80; var interval = 60000 / rpm; // ms per beat var numCircles = Math.floor(songDuration / interval); for (var i = 0; i < numCircles; i++) { // Random position, but keep inside safe play area (avoid edges) var margin = 250; var x = Math.round(margin + Math.random() * (2048 - 2 * margin)); var y = Math.round(600 + Math.random() * (2000 - 2 * margin)); rhythmMap.push({ time: Math.round(1200 + i * interval), x: x, y: y, type: 'tap' }); } // --- Game State --- var hitObjects = []; // All active hit circles var currentIndex = 0; // Next object to spawn var startTime = 0; // ms, when song started var playing = false; var score = 0; var combo = 0; var maxCombo = 0; var lastTapTime = 0; var lastTapObj = null; var holdActive = null; // Currently held hold note var holdStartTime = 0; var holdBroken = false; // var songDuration = 10000; // ms, for MVP (removed, now set to 2 min above) var approachTime = 900; // ms, time for approach circle to shrink var hitWindow = 320; // ms, allowed timing error for "perfect" (increased) var goodWindow = 500; // ms, allowed for "good" (increased) var missWindow = 700; // ms, after this it's a miss (increased) // --- UI Elements --- var scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF, font: "Lilita One" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var comboBg = LK.getAsset('comboTextBg', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 300 }); comboBg.alpha = 0.7; game.addChild(comboBg); var comboTxt = new Text2('', { size: 90, fill: 0x00FF99, font: "Lilita One" }); comboTxt.anchor.set(0.5, 0.5); comboTxt.x = 1024; comboTxt.y = 300; game.addChild(comboTxt); // --- Time Bar UI --- // Bar background var timeBarBg = LK.getAsset('comboTextBg', { anchorX: 0, anchorY: 0.5, x: 120, y: 2732 - 100 }); timeBarBg.width = 1800; timeBarBg.height = 44; timeBarBg.alpha = 0.5; game.addChild(timeBarBg); // Foreground progress bar var timeBarFg = LK.getAsset('comboTextBg', { anchorX: 0, anchorY: 0.5, x: 120, y: 2732 - 100, color: 0x00bfff // blue (change this to your desired color) }); timeBarFg.width = 0; timeBarFg.height = 32; timeBarFg.alpha = 1; game.addChild(timeBarFg); // Countdown text for time bar var timeBarCountdown = new Text2('2:00', { size: 60, fill: 0xffffff, font: "Lilita One" }); timeBarCountdown.anchor.set(1, 0.5); timeBarCountdown.x = timeBarBg.x + timeBarBg.width - 10; timeBarCountdown.y = timeBarBg.y; game.addChild(timeBarCountdown); // --- Start Game --- var loadingBar = null; var loadingText = null; var loadingTween = null; function showLoadingBar(duration, onFinish) { // Remove previous if any if (loadingBar) loadingBar.destroy(); if (loadingText) loadingText.destroy(); // Bar background loadingBar = LK.getAsset('comboTextBg', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366 }); loadingBar.width = 800; loadingBar.height = 120; loadingBar.alpha = 0.85; game.addChild(loadingBar); // Foreground bar (progress) var barFg = LK.getAsset('comboTextBg', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, color: 0x00ff99 }); barFg.width = 0; barFg.height = 80; barFg.alpha = 1; loadingBar.addChild(barFg); // Text loadingText = new Text2('Song coming...', { size: 80, fill: 0xffffff, font: "Lilita One" }); loadingText.anchor.set(0.5, 0.5); loadingText.x = 1024; loadingText.y = 1366; game.addChild(loadingText); // Animate bar var start = Date.now(); function updateBar() { var now = Date.now(); var elapsed = now - start; var frac = Math.min(1, elapsed / duration); barFg.width = 760 * frac; if (frac < 1) { loadingTween = LK.setTimeout(updateBar, 16); } else { if (loadingBar) loadingBar.destroy(); if (loadingText) loadingText.destroy(); loadingBar = null; loadingText = null; if (onFinish) onFinish(); } } updateBar(); } function startGame() { // Reset state for (var i = 0; i < hitObjects.length; i++) hitObjects[i].destroySelf(); hitObjects = []; currentIndex = 0; score = 0; combo = 0; maxCombo = 0; holdActive = null; holdBroken = false; scoreTxt.setText('0'); comboTxt.setText(''); comboBg.visible = false; playing = false; LK.setScore(0); LK.stopMusic(); // Start music before loading bar, so music plays during the loading bar wait LK.playMusic('song1'); // Show loading bar for 10s, then start hit circles showLoadingBar(10000, function () { startTime = Date.now(); playing = true; }); } startGame(); // --- Spawning Hit Circles --- function spawnHitObject(obj, idx) { var hc = new HitCircle(); hc.x = obj.x; hc.y = obj.y; hc.hitTime = obj.time; hc.index = idx + 1; // Set label to (idx mod 8) + 1, so it loops 1-8 var circleNum = idx % 8 + 1; hc.setLabel(circleNum + ''); // Define a palette of 8 distinct colors (looped) var palette = [0xff5555, // red 0xffc300, // yellow 0x4dd0e1, // cyan 0x81c784, // green 0xba68c8, // purple 0xff8a65, // orange 0x90caf9, // blue 0xf06292 // pink ]; var colorIdx = (circleNum - 1) % palette.length; var color = palette[colorIdx]; // Set the hit circle's color to match its number if (hc.children && hc.children.length > 1) { hc.children[1].tint = color; } hc.circleColor = color; if (obj.type === 'hold') { hc.setType('hold', obj.hold); } // Start approach circle at a larger scale for more dramatic appearance hc.setApproachScale(1.5); game.addChild(hc); hitObjects.push(hc); } // --- Scoring --- function addScore(val) { score += val; LK.setScore(score); scoreTxt.setText(score); } function setCombo(val) { combo = val; if (combo > maxCombo) maxCombo = combo; if (combo > 1) { comboTxt.setText(combo + 'x'); comboBg.visible = true; } else { comboTxt.setText(''); comboBg.visible = false; } } // --- Judgement Feedback --- function showJudgement(hc, type) { var txt = ''; var color = 0xffffff; if (type === 'perfect') { txt = 'Perfect!'; color = 0x00ff99; } else if (type === 'good') { txt = 'Good'; color = 0xffe066; } else if (type === 'miss') { txt = 'Miss'; color = 0xff3333; } var judge = new Text2(txt, { size: 90, fill: 0xFFFFFF, font: "Lilita One" }); judge.anchor.set(0.5, 0.5); judge.x = hc.x; judge.y = hc.y - 120; game.addChild(judge); tween(judge, { alpha: 0, y: hc.y - 220 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { judge.destroy(); } }); // Hit effect: flash the main circle (not the container) for more visual feedback if (hc.children && hc.children.length > 1) { LK.effects.flashObject(hc.children[1], color, 200); } else { LK.effects.flashObject(hc, color, 200); } // Flash all four screen edges for hit feedback var edges = [ // Top edge { x: 0, y: 0, anchorX: 0, anchorY: 0, width: 2048, height: 80 }, // Bottom edge { x: 0, y: 2732, anchorX: 0, anchorY: 1, width: 2048, height: 80 }, // Left edge { x: 0, y: 0, anchorX: 0, anchorY: 0, width: 80, height: 2732 }, // Right edge { x: 2048, y: 0, anchorX: 1, anchorY: 0, width: 80, height: 2732 }]; var edgeFlashColor = hc && hc.circleColor ? hc.circleColor : color; for (var i = 0; i < edges.length; i++) { var e = edges[i]; var flashRect = LK.getAsset('comboTextBg', { anchorX: e.anchorX, anchorY: e.anchorY, x: e.x, y: e.y }); flashRect.width = e.width; flashRect.height = e.height; flashRect.alpha = 0.7; flashRect.tint = edgeFlashColor; game.addChild(flashRect); tween(flashRect, { alpha: 0 }, { duration: 220, onFinish: function (rect) { return function () { rect.destroy(); }; }(flashRect) }); } // Particle effect at hit location var particle = new Particle(); particle.x = hc.x; particle.y = hc.y; if (hc.circleColor) { if (particle.children && particle.children.length > 0) { particle.children[0].tint = hc.circleColor; } } game.addChild(particle); // Extra: spawn a burst of small particles for more impact for (var i = 0; i < 6; i++) { var p = new Particle(); p.x = hc.x; p.y = hc.y; if (hc.circleColor && p.children && p.children.length > 0) { p.children[0].tint = hc.circleColor; } // Give each particle a random direction and speed var angle = Math.random() * Math.PI * 2; var speed = 8 + Math.random() * 10; p.vx = Math.cos(angle) * speed; p.vy = Math.sin(angle) * speed; // Override update to move outward and fade (function (p) { var baseUpdate = p.update; p.update = function () { p.x += p.vx; p.y += p.vy; // Slow down p.vx *= 0.92; p.vy *= 0.92; baseUpdate.call(p); }; })(p); game.addChild(p); } } // --- Input Handling --- game.down = function (x, y, obj) { if (!playing) return; var now = Date.now(); var found = false; for (var i = 0; i < hitObjects.length; i++) { var hc = hitObjects[i]; if (hc.hit) continue; // Only allow tap if within approach window var dt = now - startTime - hc.hitTime; if (Math.abs(dt) > missWindow + 200) continue; // Check if tap is inside circle var dx = x - hc.x; var dy = y - hc.y; var r = hc.width / 2; if (dx * dx + dy * dy < r * r) { found = true; if (hc.type === 'tap') { judgeTap(hc, dt); } else if (hc.type === 'hold') { // Start hold if (Math.abs(dt) <= hitWindow) { holdActive = hc; holdStartTime = now; holdBroken = false; hc.held = true; hc.hideApproach(); LK.getSound('hold').play(); } else { judgeTap(hc, dt); // treat as tap if too early/late } } break; } } if (!found) { // Miss: tap not on any circle setCombo(0); LK.getSound('miss').play(); } }; game.up = function (x, y, obj) { if (holdActive && holdActive.held && !holdActive.completed) { var now = Date.now(); var heldTime = now - holdStartTime; if (heldTime >= holdActive.holdDuration - 120) { // Success judgeHold(holdActive, heldTime); } else { // Released too early holdBroken = true; judgeHold(holdActive, heldTime); } holdActive.held = false; holdActive = null; } }; // --- Judgement Logic --- function judgeTap(hc, dt) { if (hc.hit) return; var absdt = Math.abs(dt); if (absdt <= hitWindow) { // Perfect addScore(300); setCombo(combo + 1); showJudgement(hc, 'perfect'); LK.getSound('hit').play(); } else if (absdt <= goodWindow) { addScore(100); setCombo(combo + 1); showJudgement(hc, 'good'); LK.getSound('hit').play(); } else if (absdt <= missWindow) { addScore(0); setCombo(0); showJudgement(hc, 'miss'); LK.getSound('miss').play(); } else { return; // too early/late, ignore } hc.hit = true; hc.setCircleAlpha(0.3); hc.hideApproach(); tween(hc, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { hc.destroySelf(); } }); } function judgeHold(hc, heldTime) { if (hc.hit) return; if (holdBroken || heldTime < hc.holdDuration - 120) { // Miss addScore(0); setCombo(0); showJudgement(hc, 'miss'); LK.getSound('miss').play(); } else { addScore(400); setCombo(combo + 1); showJudgement(hc, 'perfect'); LK.getSound('hit').play(); } hc.hit = true; hc.completed = true; hc.setCircleAlpha(0.3); hc.hideHoldRing(); tween(hc, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { hc.destroySelf(); } }); } // --- Main Update Loop --- game.update = function () { if (!playing) return; var now = Date.now(); var songPos = now - startTime; // Spawn new hit objects while (currentIndex < rhythmMap.length && rhythmMap[currentIndex].time - approachTime <= songPos) { spawnHitObject(rhythmMap[currentIndex], currentIndex); currentIndex++; } // Update hit objects for (var i = hitObjects.length - 1; i >= 0; i--) { var hc = hitObjects[i]; if (hc.hit) continue; var dt = songPos - hc.hitTime; // Approach circle shrinks var approachScale = Math.max(0.2, 1.0 - (dt + approachTime) / approachTime); hc.setApproachScale(approachScale); // Missed? if (dt > missWindow && !hc.hit) { // Miss addScore(0); setCombo(0); showJudgement(hc, 'miss'); LK.getSound('miss').play(); hc.hit = true; hc.setCircleAlpha(0.2); hc.hideApproach(); tween(hc, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { hc.destroySelf(); } }); } // For hold notes: if held, show progress if (hc.type === 'hold' && hc.held && !hc.completed) { var heldTime = now - holdStartTime; if (heldTime >= hc.holdDuration) { judgeHold(hc, heldTime); holdActive = null; } } } // Update time bar if (typeof timeBarFg !== "undefined" && typeof timeBarBg !== "undefined") { // Always set the left edge and width, so the bar grows from left to right var frac = Math.max(0, Math.min(1, songPos / songDuration)); timeBarFg.x = timeBarBg.x; // align left edge timeBarFg.y = timeBarBg.y; timeBarFg.width = timeBarBg.width * frac; timeBarFg.height = timeBarBg.height - 12; // Update countdown text if (typeof timeBarCountdown !== "undefined") { var remaining = Math.max(0, songDuration - songPos); var sec = Math.floor(remaining / 1000); var min = Math.floor(sec / 60); sec = sec % 60; var timeStr = min + ":" + (sec < 10 ? "0" : "") + sec; timeBarCountdown.setText(timeStr); } } // End of song if (songPos > songDuration + 1000) { playing = false; LK.showYouWin(); } }; // --- Music Start --- // (Music is started in startGame) // --- Game Over/Win Handling (handled by LK) --- // --- Touchscreen: No keyboard controls needed --- // --- Prevent elements in top left 100x100 px --- comboBg.x = 1024; comboBg.y = 300; comboTxt.x = 1024; comboTxt.y = 300; // --- (Optional) Restart on game over/win --- /* LK.on('gameover', function(){ startGame(); }); LK.on('youwin', function(){ startGame(); }); */
===================================================================
--- original.js
+++ change.js
@@ -451,8 +451,9 @@
anchorY: 0,
width: 80,
height: 2732
}];
+ var edgeFlashColor = hc && hc.circleColor ? hc.circleColor : color;
for (var i = 0; i < edges.length; i++) {
var e = edges[i];
var flashRect = LK.getAsset('comboTextBg', {
anchorX: e.anchorX,
@@ -462,9 +463,9 @@
});
flashRect.width = e.width;
flashRect.height = e.height;
flashRect.alpha = 0.7;
- flashRect.tint = color;
+ flashRect.tint = edgeFlashColor;
game.addChild(flashRect);
tween(flashRect, {
alpha: 0
}, {