User prompt
show life with hearts start with 5 life and each time remove one image
User prompt
remove last change
User prompt
instead of showing life with text show with music note shape images vertical order
User prompt
make win state 50 score
User prompt
add a tutorial to start say and show left tilt and right tilt
User prompt
size up particle effect 3x
User prompt
add particle effect to center when i hit note โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
add color to that ui
User prompt
add a nice ui to score and combo
User prompt
add 5 more bgmusic and each level start choose random
User prompt
add 5 more music not image for each side and choose random image each time
Code edit (1 edits merged)
Please save this source code
User prompt
remove rightpathline convert left to center
Code edit (2 edits merged)
Please save this source code
User prompt
Please fix the bug: 'rightPathLine is not defined' in or related to this line: 'rightPathLine.rotation = Math.PI; // horizontal, flipped' Line Number: 170
Code edit (1 edits merged)
Please save this source code
User prompt
add a reference slot to note lines
User prompt
add asset reference to that line
User prompt
add a transparent line for notes way
User prompt
when miss note stop music when hit resume it
User prompt
remove last change
User prompt
if miss rewind the note and play again
User prompt
Please fix the bug: 'TypeError: LK.setMusicVolume is not a function' in or related to this line: 'LK.setMusicVolume(0.1, 100);' Line Number: 340
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var facekit = LK.import("@upit/facekit.v1"); /**** * Classes ****/ // Note class var Note = Container.expand(function () { var self = Container.call(this); self.direction = 'up'; // will be set after creation self.speed = 12; // pixels per frame, reduced for easier gameplay self.hit = false; self.active = true; // Attach asset self.noteAsset = null; // will be set after direction is set // For hit animation self.flashTween = null; // Called every tick self.update = function () { if (!self.active) { return; } var vec = getNoteTravelVec(self.direction); self.x += vec.x * self.speed; self.y += vec.y * self.speed; }; // Animate on hit self.animateHit = function () { self.active = false; if (self.flashTween) { tween.stop(self, { alpha: true, scaleX: true, scaleY: true }); } tween(self, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 180, easing: tween.cubicOut, onFinish: function onFinish() { self.destroy(); } }); }; // Animate on miss self.animateMiss = function () { self.active = false; if (self.flashTween) { tween.stop(self, { alpha: true, scaleX: true, scaleY: true }); } tween(self, { alpha: 0.2 }, { duration: 180, easing: tween.cubicOut, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181c20 }); /**** * Game Code ****/ // Note directions // Music (placeholder, actual music asset id will be auto-loaded) // Sounds for hit and miss // Four note shapes for each direction // Center of play area var NOTE_DIRECTIONS = ['left', 'right']; var NOTE_ASSET = { left: 'noteLeft', right: 'noteRight' }; // Note spawn positions (offscreen, moving toward center) function getNoteSpawnPos(direction) { var centerX = 2048 / 2, centerY = 2732 / 2; var offset = 900; // How far from center to spawn if (direction === 'left') { return { x: centerX - offset, y: centerY }; } if (direction === 'right') { return { x: centerX + offset, y: centerY }; } return { x: centerX, y: centerY }; } // Note travel vector (unit vector toward center) function getNoteTravelVec(direction) { if (direction === 'left') { return { x: 1, y: 0 }; } if (direction === 'right') { return { x: -1, y: 0 }; } return { x: 0, y: 0 }; } var centerX = 2048 / 2, centerY = 2732 / 2; // Add center target var centerTarget = LK.getAsset('centerTarget', { anchorX: 0.5, anchorY: 0.5, x: centerX, y: centerY, scaleX: 1, scaleY: 1 }); game.addChild(centerTarget); // Score display var score = 0; var scoreTxt = new Text2('0', { size: 120, fill: '#fff' }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Combo display var combo = 0; var comboTxt = new Text2('', { size: 70, fill: '#ffe082' }); comboTxt.anchor.set(0.5, 0); LK.gui.top.addChild(comboTxt); comboTxt.y = 130; // Remaining life display (top right) var lifeTxt = new Text2('', { size: 90, fill: '#ff8a65' }); lifeTxt.anchor.set(1, 0); // right-top LK.gui.topRight.addChild(lifeTxt); lifeTxt.x = 0; lifeTxt.y = 20; // Miss feedback var missTxt = new Text2('', { size: 100, fill: '#e57373' }); missTxt.anchor.set(0.5, 0.5); LK.gui.center.addChild(missTxt); // Notes array var notes = []; // Timing var noteInterval = 38; // frames between notes (about 1.5 notes/sec at 60fps) var noteTimer = 0; // Game state var isGameOver = false; var isYouWin = false; var maxMisses = 8; var misses = 0; var targetScore = 15; // Face direction detection function getFaceDirection() { // Use noseTip and chin to estimate tilt // If facekit is not ready, return null if (!facekit.noseTip || !facekit.chin || !facekit.leftEye || !facekit.rightEye) { return null; } var dx = facekit.rightEye.x - facekit.leftEye.x; var dy = facekit.rightEye.y - facekit.leftEye.y; var angle = Math.atan2(dy, dx) * 180 / Math.PI; // horizontal head tilt // Left/right: compare noseTip.x to center var horizontal = facekit.noseTip.x - centerX; // Heuristics: prioritize strong tilts if (horizontal < -80) { return 'left'; } if (horizontal > 80) { return 'right'; } // If head is turned, use angle if (angle < -25) { return 'right'; } if (angle > 25) { return 'left'; } return null; } // Show combo function showCombo() { if (combo > 1) { comboTxt.setText(combo + 'x Combo!'); } else { comboTxt.setText(''); } } // Show miss function showMiss() { missTxt.setText('Miss!'); tween(missTxt, { alpha: 0 }, { duration: 600, onFinish: function onFinish() { missTxt.setText(''); missTxt.alpha = 1; } }); } // Spawn a note function spawnNote() { var dir = NOTE_DIRECTIONS[Math.floor(Math.random() * NOTE_DIRECTIONS.length)]; var note = new Note(); note.direction = dir; var spawn = getNoteSpawnPos(dir); note.x = spawn.x; note.y = spawn.y; note.noteAsset = note.attachAsset(NOTE_ASSET[dir], { anchorX: 0.5, anchorY: 0.5 }); // Rotate asset to point toward center if (dir === 'left') { note.noteAsset.rotation = -Math.PI / 2; } if (dir === 'right') { note.noteAsset.rotation = Math.PI / 2; } notes.push(note); game.addChild(note); } // Check if note is in hit window (distance to center) function isNoteHittable(note) { var dx = note.x - centerX; var dy = note.y - centerY; var dist = Math.sqrt(dx * dx + dy * dy); return dist < 180; // hit window radius (increased for easier hits) } // Check if note is missed (passed center) function isNoteMissed(note) { var dx = note.x - centerX; var dy = note.y - centerY; var dist = Math.sqrt(dx * dx + dy * dy); return dist < 20; // too close, missed (smaller, so player has more time to hit) } // Main update loop game.update = function () { if (isGameOver || isYouWin) { return; } // Spawn notes noteTimer++; if (noteTimer >= noteInterval) { spawnNote(); noteTimer = 0; } // Get current face direction var faceDir = getFaceDirection(); // For each note: move, check for hit/miss for (var i = notes.length - 1; i >= 0; i--) { var note = notes[i]; note.update(); if (!note.active) { notes.splice(i, 1); continue; } // If note is in hit window and faceDir matches, hit! if (!note.hit && isNoteHittable(note) && faceDir === note.direction) { note.hit = true; note.animateHit(); // On hit, set music volume to 1 using fade LK.playMusic('bgmusic', { fade: { start: 0.1, end: 1, duration: 100 } }); score++; combo++; showCombo(); scoreTxt.setText(score); if (score >= targetScore) { isYouWin = true; LK.showYouWin(); return; } continue; } // If note passes center and not hit, miss // Only trigger miss the moment the note crosses the miss threshold (not repeatedly) if (!note.hit && note.lastMissed !== true && isNoteMissed(note)) { note.hit = true; note.lastMissed = true; note.animateMiss(); // On miss, set music volume to 0.1 using fade LK.playMusic('bgmusic', { fade: { start: 1, end: 0.1, duration: 100 } }); misses++; combo = 0; showCombo(); showMiss(); lifeTxt.setText('Life: ' + (maxMisses - misses)); if (misses >= maxMisses) { isGameOver = true; LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } continue; } else if (!note.hit && note.lastMissed !== true) { // Track that note is not yet missed note.lastMissed = false; } } }; LK.playMusic('bgmusic', { fade: { start: 0.1, end: 1, duration: 100 } }); // Reset state on game restart game.on('reset', function () { score = 0; combo = 0; misses = 0; isGameOver = false; isYouWin = false; notes = []; scoreTxt.setText('0'); comboTxt.setText(''); missTxt.setText(''); noteTimer = 0; lifeTxt.setText('Life: ' + (maxMisses - misses)); }); // No touch controls needed; game is face-only // Make sure all elements are visible and not in top left 100x100 centerTarget.x = centerX; centerTarget.y = centerY; scoreTxt.x = LK.gui.top.width / 2; scoreTxt.y = 10; comboTxt.x = LK.gui.top.width / 2; comboTxt.y = 130; missTxt.x = LK.gui.center.width / 2; missTxt.y = LK.gui.center.height / 2; ; // Set initial value for life display lifeTxt.setText('Life: ' + (maxMisses - misses));
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
// Note class
var Note = Container.expand(function () {
var self = Container.call(this);
self.direction = 'up'; // will be set after creation
self.speed = 12; // pixels per frame, reduced for easier gameplay
self.hit = false;
self.active = true;
// Attach asset
self.noteAsset = null; // will be set after direction is set
// For hit animation
self.flashTween = null;
// Called every tick
self.update = function () {
if (!self.active) {
return;
}
var vec = getNoteTravelVec(self.direction);
self.x += vec.x * self.speed;
self.y += vec.y * self.speed;
};
// Animate on hit
self.animateHit = function () {
self.active = false;
if (self.flashTween) {
tween.stop(self, {
alpha: true,
scaleX: true,
scaleY: true
});
}
tween(self, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 180,
easing: tween.cubicOut,
onFinish: function onFinish() {
self.destroy();
}
});
};
// Animate on miss
self.animateMiss = function () {
self.active = false;
if (self.flashTween) {
tween.stop(self, {
alpha: true,
scaleX: true,
scaleY: true
});
}
tween(self, {
alpha: 0.2
}, {
duration: 180,
easing: tween.cubicOut,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181c20
});
/****
* Game Code
****/
// Note directions
// Music (placeholder, actual music asset id will be auto-loaded)
// Sounds for hit and miss
// Four note shapes for each direction
// Center of play area
var NOTE_DIRECTIONS = ['left', 'right'];
var NOTE_ASSET = {
left: 'noteLeft',
right: 'noteRight'
};
// Note spawn positions (offscreen, moving toward center)
function getNoteSpawnPos(direction) {
var centerX = 2048 / 2,
centerY = 2732 / 2;
var offset = 900; // How far from center to spawn
if (direction === 'left') {
return {
x: centerX - offset,
y: centerY
};
}
if (direction === 'right') {
return {
x: centerX + offset,
y: centerY
};
}
return {
x: centerX,
y: centerY
};
}
// Note travel vector (unit vector toward center)
function getNoteTravelVec(direction) {
if (direction === 'left') {
return {
x: 1,
y: 0
};
}
if (direction === 'right') {
return {
x: -1,
y: 0
};
}
return {
x: 0,
y: 0
};
}
var centerX = 2048 / 2,
centerY = 2732 / 2;
// Add center target
var centerTarget = LK.getAsset('centerTarget', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX,
y: centerY,
scaleX: 1,
scaleY: 1
});
game.addChild(centerTarget);
// Score display
var score = 0;
var scoreTxt = new Text2('0', {
size: 120,
fill: '#fff'
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Combo display
var combo = 0;
var comboTxt = new Text2('', {
size: 70,
fill: '#ffe082'
});
comboTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(comboTxt);
comboTxt.y = 130;
// Remaining life display (top right)
var lifeTxt = new Text2('', {
size: 90,
fill: '#ff8a65'
});
lifeTxt.anchor.set(1, 0); // right-top
LK.gui.topRight.addChild(lifeTxt);
lifeTxt.x = 0;
lifeTxt.y = 20;
// Miss feedback
var missTxt = new Text2('', {
size: 100,
fill: '#e57373'
});
missTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(missTxt);
// Notes array
var notes = [];
// Timing
var noteInterval = 38; // frames between notes (about 1.5 notes/sec at 60fps)
var noteTimer = 0;
// Game state
var isGameOver = false;
var isYouWin = false;
var maxMisses = 8;
var misses = 0;
var targetScore = 15;
// Face direction detection
function getFaceDirection() {
// Use noseTip and chin to estimate tilt
// If facekit is not ready, return null
if (!facekit.noseTip || !facekit.chin || !facekit.leftEye || !facekit.rightEye) {
return null;
}
var dx = facekit.rightEye.x - facekit.leftEye.x;
var dy = facekit.rightEye.y - facekit.leftEye.y;
var angle = Math.atan2(dy, dx) * 180 / Math.PI; // horizontal head tilt
// Left/right: compare noseTip.x to center
var horizontal = facekit.noseTip.x - centerX;
// Heuristics: prioritize strong tilts
if (horizontal < -80) {
return 'left';
}
if (horizontal > 80) {
return 'right';
}
// If head is turned, use angle
if (angle < -25) {
return 'right';
}
if (angle > 25) {
return 'left';
}
return null;
}
// Show combo
function showCombo() {
if (combo > 1) {
comboTxt.setText(combo + 'x Combo!');
} else {
comboTxt.setText('');
}
}
// Show miss
function showMiss() {
missTxt.setText('Miss!');
tween(missTxt, {
alpha: 0
}, {
duration: 600,
onFinish: function onFinish() {
missTxt.setText('');
missTxt.alpha = 1;
}
});
}
// Spawn a note
function spawnNote() {
var dir = NOTE_DIRECTIONS[Math.floor(Math.random() * NOTE_DIRECTIONS.length)];
var note = new Note();
note.direction = dir;
var spawn = getNoteSpawnPos(dir);
note.x = spawn.x;
note.y = spawn.y;
note.noteAsset = note.attachAsset(NOTE_ASSET[dir], {
anchorX: 0.5,
anchorY: 0.5
});
// Rotate asset to point toward center
if (dir === 'left') {
note.noteAsset.rotation = -Math.PI / 2;
}
if (dir === 'right') {
note.noteAsset.rotation = Math.PI / 2;
}
notes.push(note);
game.addChild(note);
}
// Check if note is in hit window (distance to center)
function isNoteHittable(note) {
var dx = note.x - centerX;
var dy = note.y - centerY;
var dist = Math.sqrt(dx * dx + dy * dy);
return dist < 180; // hit window radius (increased for easier hits)
}
// Check if note is missed (passed center)
function isNoteMissed(note) {
var dx = note.x - centerX;
var dy = note.y - centerY;
var dist = Math.sqrt(dx * dx + dy * dy);
return dist < 20; // too close, missed (smaller, so player has more time to hit)
}
// Main update loop
game.update = function () {
if (isGameOver || isYouWin) {
return;
}
// Spawn notes
noteTimer++;
if (noteTimer >= noteInterval) {
spawnNote();
noteTimer = 0;
}
// Get current face direction
var faceDir = getFaceDirection();
// For each note: move, check for hit/miss
for (var i = notes.length - 1; i >= 0; i--) {
var note = notes[i];
note.update();
if (!note.active) {
notes.splice(i, 1);
continue;
}
// If note is in hit window and faceDir matches, hit!
if (!note.hit && isNoteHittable(note) && faceDir === note.direction) {
note.hit = true;
note.animateHit();
// On hit, set music volume to 1 using fade
LK.playMusic('bgmusic', {
fade: {
start: 0.1,
end: 1,
duration: 100
}
});
score++;
combo++;
showCombo();
scoreTxt.setText(score);
if (score >= targetScore) {
isYouWin = true;
LK.showYouWin();
return;
}
continue;
}
// If note passes center and not hit, miss
// Only trigger miss the moment the note crosses the miss threshold (not repeatedly)
if (!note.hit && note.lastMissed !== true && isNoteMissed(note)) {
note.hit = true;
note.lastMissed = true;
note.animateMiss();
// On miss, set music volume to 0.1 using fade
LK.playMusic('bgmusic', {
fade: {
start: 1,
end: 0.1,
duration: 100
}
});
misses++;
combo = 0;
showCombo();
showMiss();
lifeTxt.setText('Life: ' + (maxMisses - misses));
if (misses >= maxMisses) {
isGameOver = true;
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
continue;
} else if (!note.hit && note.lastMissed !== true) {
// Track that note is not yet missed
note.lastMissed = false;
}
}
};
LK.playMusic('bgmusic', {
fade: {
start: 0.1,
end: 1,
duration: 100
}
});
// Reset state on game restart
game.on('reset', function () {
score = 0;
combo = 0;
misses = 0;
isGameOver = false;
isYouWin = false;
notes = [];
scoreTxt.setText('0');
comboTxt.setText('');
missTxt.setText('');
noteTimer = 0;
lifeTxt.setText('Life: ' + (maxMisses - misses));
});
// No touch controls needed; game is face-only
// Make sure all elements are visible and not in top left 100x100
centerTarget.x = centerX;
centerTarget.y = centerY;
scoreTxt.x = LK.gui.top.width / 2;
scoreTxt.y = 10;
comboTxt.x = LK.gui.top.width / 2;
comboTxt.y = 130;
missTxt.x = LK.gui.center.width / 2;
missTxt.y = LK.gui.center.height / 2;
;
// Set initial value for life display
lifeTxt.setText('Life: ' + (maxMisses - misses));