/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
totalPlays: 0
});
/****
* Classes
****/
var HitEffect = Container.expand(function (rating) {
var self = Container.call(this);
// Create effect circle
var effect = self.attachAsset('hitEffect', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
// Create rating text
var text = new Text2(rating, {
size: 60,
fill: 0xFFFFFF
});
text.anchor.set(0.5, 0.5);
self.addChild(text);
// Set initial scale
self.scale.set(0.8);
// Animate and destroy
tween(self, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
return self;
});
var MissEffect = Container.expand(function () {
var self = Container.call(this);
// Create miss effect
var effect = self.attachAsset('missEffect', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
// Create X text
var text = new Text2("MISS", {
size: 60,
fill: 0xFFFFFF
});
text.anchor.set(0.5, 0.5);
self.addChild(text);
// Set initial scale
self.scale.set(0.8);
// Animate and destroy
tween(self, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
return self;
});
var Note = Container.expand(function (timing, lane) {
var self = Container.call(this);
self.timing = timing;
self.lane = lane;
self.active = true;
self.hit = false;
self.approaching = false;
// Create outer ring
var ring = self.attachAsset('targetRing', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
// Create inner circle
var circle = self.attachAsset('targetCircle', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
// Set initial scale for approach animation
self.scale.set(0.1);
// Define tap/click interaction
self.down = function (x, y, obj) {
if (!self.active || !self.approaching) {
return;
}
// Check timing
var currentTime = gameTime;
var timeDiff = Math.abs(currentTime - self.timing);
if (timeDiff <= PERFECT_TIMING) {
hitNote(self, "PERFECT", 100);
} else if (timeDiff <= GREAT_TIMING) {
hitNote(self, "GREAT", 75);
} else if (timeDiff <= GOOD_TIMING) {
hitNote(self, "GOOD", 50);
} else if (timeDiff <= OK_TIMING) {
hitNote(self, "OK", 25);
} else {
// Too early or too late
missNote(self);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
// Constants
var PERFECT_TIMING = 100; // ms
var GREAT_TIMING = 150; // ms
var GOOD_TIMING = 200; // ms
var OK_TIMING = 250; // ms
var APPROACH_TIME = 1500; // ms
var START_DELAY = 3000; // ms
// Game state variables
var gameTime = 0;
var isPlaying = false;
var currentCombo = 0;
var maxCombo = 0;
var perfectCount = 0;
var greatCount = 0;
var goodCount = 0;
var okCount = 0;
var missCount = 0;
// Notes and patterns
var notes = [];
var noteLanes = [{
x: 2048 * 0.2,
y: 2732 * 0.5
}, {
x: 2048 * 0.4,
y: 2732 * 0.4
}, {
x: 2048 * 0.6,
y: 2732 * 0.4
}, {
x: 2048 * 0.8,
y: 2732 * 0.5
}];
// UI Elements
var comboText = new Text2("", {
size: 120,
fill: 0xFFFFFF
});
comboText.anchor.set(0.5, 0.5);
comboText.position.set(2048 / 2, 400);
game.addChild(comboText);
var scoreText = new Text2("SCORE: 0", {
size: 80,
fill: 0xFFFFFF
});
scoreText.anchor.set(1.0, 0);
scoreText.position.set(2048 - 50, 50);
game.addChild(scoreText);
var progressBarBackground = LK.getAsset('progressBar', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x34495e,
scaleX: 1,
scaleY: 1,
x: 2048 / 2,
y: 100
});
game.addChild(progressBarBackground);
var progressBarFill = LK.getAsset('progressBar', {
anchorX: 0,
anchorY: 0.5,
tint: 0x9b59b6,
scaleX: 0,
scaleY: 1,
x: (2048 - 1800) / 2,
y: 100
});
game.addChild(progressBarFill);
// Generate a sample beat pattern
function generateBeatPattern() {
var pattern = [];
var songLength = 30000; // 30 seconds
var beatInterval = 500; // 500ms between beats
for (var time = START_DELAY; time < songLength; time += beatInterval) {
// Simple pattern: alternate between lanes
var lane = Math.floor(Math.random() * noteLanes.length);
pattern.push({
timing: time,
lane: lane
});
// Occasionally add a double note
if (Math.random() < 0.2) {
var secondLane = (lane + 1 + Math.floor(Math.random() * (noteLanes.length - 1))) % noteLanes.length;
pattern.push({
timing: time,
lane: secondLane
});
}
}
return pattern;
}
// Initialize game
function initGame() {
gameTime = 0;
isPlaying = true;
currentCombo = 0;
maxCombo = 0;
perfectCount = 0;
greatCount = 0;
goodCount = 0;
okCount = 0;
missCount = 0;
// Clear previous notes
for (var i = 0; i < notes.length; i++) {
if (notes[i].parent) {
notes[i].parent.removeChild(notes[i]);
}
}
notes = [];
// Reset score
LK.setScore(0);
scoreText.setText("SCORE: 0");
comboText.setText("");
progressBarFill.scaleX = 0;
// Generate beat pattern
var pattern = generateBeatPattern();
for (var j = 0; j < pattern.length; j++) {
var noteInfo = pattern[j];
var note = new Note(noteInfo.timing, noteInfo.lane);
note.position.set(noteLanes[noteInfo.lane].x, noteLanes[noteInfo.lane].y);
notes.push(note);
}
// Start music after countdown
LK.setTimeout(function () {
LK.playMusic('gameMusic', {
loop: false
});
}, START_DELAY);
}
// Hit a note successfully
function hitNote(note, rating, points) {
if (!note.active || note.hit) {
return;
}
note.active = false;
note.hit = true;
// Update combo
currentCombo++;
if (currentCombo > maxCombo) {
maxCombo = currentCombo;
}
// Update stats
switch (rating) {
case "PERFECT":
perfectCount++;
break;
case "GREAT":
greatCount++;
break;
case "GOOD":
goodCount++;
break;
case "OK":
okCount++;
break;
}
// Calculate score with combo multiplier
var comboMultiplier = Math.min(4, 1 + Math.floor(currentCombo / 10) * 0.1);
var finalPoints = Math.floor(points * comboMultiplier);
// Update score
LK.setScore(LK.getScore() + finalPoints);
scoreText.setText("SCORE: " + LK.getScore());
// Update combo display
comboText.setText("COMBO " + currentCombo);
tween(comboText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(comboText, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeIn
});
}
});
// Play hit sound
LK.getSound('hitSound').play();
// Special effects for higher combos
if (currentCombo % 10 === 0) {
LK.getSound('comboSound').play();
LK.effects.flashScreen(0x9b59b6, 300);
}
// Create hit effect
var effect = new HitEffect(rating);
effect.position.set(note.x, note.y);
game.addChild(effect);
// Remove the note
if (note.parent) {
note.parent.removeChild(note);
}
}
// Miss a note
function missNote(note) {
if (!note.active || note.hit) {
return;
}
note.active = false;
note.hit = true;
// Reset combo
currentCombo = 0;
missCount++;
// Play miss sound
LK.getSound('missSound').play();
// Update combo display
comboText.setText("");
// Create miss effect
var effect = new MissEffect();
effect.position.set(note.x, note.y);
game.addChild(effect);
// Remove the note
if (note.parent) {
note.parent.removeChild(note);
}
}
// Check for auto-miss (when a note passes without being tapped)
function checkMissedNotes() {
for (var i = 0; i < notes.length; i++) {
var note = notes[i];
if (note.active && note.approaching && gameTime > note.timing + OK_TIMING) {
missNote(note);
}
}
}
// Update progress bar
function updateProgressBar() {
var songLength = 30000; // 30 seconds
var progress = Math.min(1, gameTime / songLength);
progressBarFill.scaleX = progress;
if (progress >= 1 && isPlaying) {
endGame();
}
}
// End the game
function endGame() {
isPlaying = false;
// Update high score
if (LK.getScore() > storage.highScore) {
storage.highScore = LK.getScore();
}
// Update total plays
storage.totalPlays = (storage.totalPlays || 0) + 1;
// Show game over
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
}
// Initialize the game
initGame();
// Game loop
game.update = function () {
if (!isPlaying) {
return;
}
// Update game time
gameTime += 1000 / 60; // Approximate ms per frame at 60fps
// Update notes
for (var i = 0; i < notes.length; i++) {
var note = notes[i];
// Check if note should start approaching
if (note.active && !note.approaching && gameTime >= note.timing - APPROACH_TIME) {
note.approaching = true;
game.addChild(note);
// Animate note approaching
tween(note, {
scaleX: 1,
scaleY: 1
}, {
duration: APPROACH_TIME,
easing: tween.linear
});
}
}
// Check for auto-missed notes
checkMissedNotes();
// Update progress bar
updateProgressBar();
};
// Touch controls
game.down = function (x, y, obj) {
// Check if any note was directly tapped
// Note: The Note class's down method will handle the hit logic
};
// Handle direct taps on the game area that aren't on notes
game.move = function (x, y, obj) {
// Not needed for this game
};
game.up = function (x, y, obj) {
// Not needed for this game
}; ===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,422 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+var storage = LK.import("@upit/storage.v1", {
+ highScore: 0,
+ totalPlays: 0
+});
+
+/****
+* Classes
+****/
+var HitEffect = Container.expand(function (rating) {
+ var self = Container.call(this);
+ // Create effect circle
+ var effect = self.attachAsset('hitEffect', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0.7
+ });
+ // Create rating text
+ var text = new Text2(rating, {
+ size: 60,
+ fill: 0xFFFFFF
+ });
+ text.anchor.set(0.5, 0.5);
+ self.addChild(text);
+ // Set initial scale
+ self.scale.set(0.8);
+ // Animate and destroy
+ tween(self, {
+ alpha: 0,
+ scaleX: 1.5,
+ scaleY: 1.5
+ }, {
+ duration: 500,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ self.destroy();
+ }
+ });
+ return self;
+});
+var MissEffect = Container.expand(function () {
+ var self = Container.call(this);
+ // Create miss effect
+ var effect = self.attachAsset('missEffect', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0.7
+ });
+ // Create X text
+ var text = new Text2("MISS", {
+ size: 60,
+ fill: 0xFFFFFF
+ });
+ text.anchor.set(0.5, 0.5);
+ self.addChild(text);
+ // Set initial scale
+ self.scale.set(0.8);
+ // Animate and destroy
+ tween(self, {
+ alpha: 0,
+ scaleX: 1.5,
+ scaleY: 1.5
+ }, {
+ duration: 500,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ self.destroy();
+ }
+ });
+ return self;
+});
+var Note = Container.expand(function (timing, lane) {
+ var self = Container.call(this);
+ self.timing = timing;
+ self.lane = lane;
+ self.active = true;
+ self.hit = false;
+ self.approaching = false;
+ // Create outer ring
+ var ring = self.attachAsset('targetRing', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0.7
+ });
+ // Create inner circle
+ var circle = self.attachAsset('targetCircle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0.8
+ });
+ // Set initial scale for approach animation
+ self.scale.set(0.1);
+ // Define tap/click interaction
+ self.down = function (x, y, obj) {
+ if (!self.active || !self.approaching) {
+ return;
+ }
+ // Check timing
+ var currentTime = gameTime;
+ var timeDiff = Math.abs(currentTime - self.timing);
+ if (timeDiff <= PERFECT_TIMING) {
+ hitNote(self, "PERFECT", 100);
+ } else if (timeDiff <= GREAT_TIMING) {
+ hitNote(self, "GREAT", 75);
+ } else if (timeDiff <= GOOD_TIMING) {
+ hitNote(self, "GOOD", 50);
+ } else if (timeDiff <= OK_TIMING) {
+ hitNote(self, "OK", 25);
+ } else {
+ // Too early or too late
+ missNote(self);
+ }
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0x2c3e50
+});
+
+/****
+* Game Code
+****/
+// Constants
+var PERFECT_TIMING = 100; // ms
+var GREAT_TIMING = 150; // ms
+var GOOD_TIMING = 200; // ms
+var OK_TIMING = 250; // ms
+var APPROACH_TIME = 1500; // ms
+var START_DELAY = 3000; // ms
+// Game state variables
+var gameTime = 0;
+var isPlaying = false;
+var currentCombo = 0;
+var maxCombo = 0;
+var perfectCount = 0;
+var greatCount = 0;
+var goodCount = 0;
+var okCount = 0;
+var missCount = 0;
+// Notes and patterns
+var notes = [];
+var noteLanes = [{
+ x: 2048 * 0.2,
+ y: 2732 * 0.5
+}, {
+ x: 2048 * 0.4,
+ y: 2732 * 0.4
+}, {
+ x: 2048 * 0.6,
+ y: 2732 * 0.4
+}, {
+ x: 2048 * 0.8,
+ y: 2732 * 0.5
+}];
+// UI Elements
+var comboText = new Text2("", {
+ size: 120,
+ fill: 0xFFFFFF
+});
+comboText.anchor.set(0.5, 0.5);
+comboText.position.set(2048 / 2, 400);
+game.addChild(comboText);
+var scoreText = new Text2("SCORE: 0", {
+ size: 80,
+ fill: 0xFFFFFF
+});
+scoreText.anchor.set(1.0, 0);
+scoreText.position.set(2048 - 50, 50);
+game.addChild(scoreText);
+var progressBarBackground = LK.getAsset('progressBar', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ tint: 0x34495e,
+ scaleX: 1,
+ scaleY: 1,
+ x: 2048 / 2,
+ y: 100
+});
+game.addChild(progressBarBackground);
+var progressBarFill = LK.getAsset('progressBar', {
+ anchorX: 0,
+ anchorY: 0.5,
+ tint: 0x9b59b6,
+ scaleX: 0,
+ scaleY: 1,
+ x: (2048 - 1800) / 2,
+ y: 100
+});
+game.addChild(progressBarFill);
+// Generate a sample beat pattern
+function generateBeatPattern() {
+ var pattern = [];
+ var songLength = 30000; // 30 seconds
+ var beatInterval = 500; // 500ms between beats
+ for (var time = START_DELAY; time < songLength; time += beatInterval) {
+ // Simple pattern: alternate between lanes
+ var lane = Math.floor(Math.random() * noteLanes.length);
+ pattern.push({
+ timing: time,
+ lane: lane
+ });
+ // Occasionally add a double note
+ if (Math.random() < 0.2) {
+ var secondLane = (lane + 1 + Math.floor(Math.random() * (noteLanes.length - 1))) % noteLanes.length;
+ pattern.push({
+ timing: time,
+ lane: secondLane
+ });
+ }
+ }
+ return pattern;
+}
+// Initialize game
+function initGame() {
+ gameTime = 0;
+ isPlaying = true;
+ currentCombo = 0;
+ maxCombo = 0;
+ perfectCount = 0;
+ greatCount = 0;
+ goodCount = 0;
+ okCount = 0;
+ missCount = 0;
+ // Clear previous notes
+ for (var i = 0; i < notes.length; i++) {
+ if (notes[i].parent) {
+ notes[i].parent.removeChild(notes[i]);
+ }
+ }
+ notes = [];
+ // Reset score
+ LK.setScore(0);
+ scoreText.setText("SCORE: 0");
+ comboText.setText("");
+ progressBarFill.scaleX = 0;
+ // Generate beat pattern
+ var pattern = generateBeatPattern();
+ for (var j = 0; j < pattern.length; j++) {
+ var noteInfo = pattern[j];
+ var note = new Note(noteInfo.timing, noteInfo.lane);
+ note.position.set(noteLanes[noteInfo.lane].x, noteLanes[noteInfo.lane].y);
+ notes.push(note);
+ }
+ // Start music after countdown
+ LK.setTimeout(function () {
+ LK.playMusic('gameMusic', {
+ loop: false
+ });
+ }, START_DELAY);
+}
+// Hit a note successfully
+function hitNote(note, rating, points) {
+ if (!note.active || note.hit) {
+ return;
+ }
+ note.active = false;
+ note.hit = true;
+ // Update combo
+ currentCombo++;
+ if (currentCombo > maxCombo) {
+ maxCombo = currentCombo;
+ }
+ // Update stats
+ switch (rating) {
+ case "PERFECT":
+ perfectCount++;
+ break;
+ case "GREAT":
+ greatCount++;
+ break;
+ case "GOOD":
+ goodCount++;
+ break;
+ case "OK":
+ okCount++;
+ break;
+ }
+ // Calculate score with combo multiplier
+ var comboMultiplier = Math.min(4, 1 + Math.floor(currentCombo / 10) * 0.1);
+ var finalPoints = Math.floor(points * comboMultiplier);
+ // Update score
+ LK.setScore(LK.getScore() + finalPoints);
+ scoreText.setText("SCORE: " + LK.getScore());
+ // Update combo display
+ comboText.setText("COMBO " + currentCombo);
+ tween(comboText, {
+ scaleX: 1.2,
+ scaleY: 1.2
+ }, {
+ duration: 100,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ tween(comboText, {
+ scaleX: 1.0,
+ scaleY: 1.0
+ }, {
+ duration: 100,
+ easing: tween.easeIn
+ });
+ }
+ });
+ // Play hit sound
+ LK.getSound('hitSound').play();
+ // Special effects for higher combos
+ if (currentCombo % 10 === 0) {
+ LK.getSound('comboSound').play();
+ LK.effects.flashScreen(0x9b59b6, 300);
+ }
+ // Create hit effect
+ var effect = new HitEffect(rating);
+ effect.position.set(note.x, note.y);
+ game.addChild(effect);
+ // Remove the note
+ if (note.parent) {
+ note.parent.removeChild(note);
+ }
+}
+// Miss a note
+function missNote(note) {
+ if (!note.active || note.hit) {
+ return;
+ }
+ note.active = false;
+ note.hit = true;
+ // Reset combo
+ currentCombo = 0;
+ missCount++;
+ // Play miss sound
+ LK.getSound('missSound').play();
+ // Update combo display
+ comboText.setText("");
+ // Create miss effect
+ var effect = new MissEffect();
+ effect.position.set(note.x, note.y);
+ game.addChild(effect);
+ // Remove the note
+ if (note.parent) {
+ note.parent.removeChild(note);
+ }
+}
+// Check for auto-miss (when a note passes without being tapped)
+function checkMissedNotes() {
+ for (var i = 0; i < notes.length; i++) {
+ var note = notes[i];
+ if (note.active && note.approaching && gameTime > note.timing + OK_TIMING) {
+ missNote(note);
+ }
+ }
+}
+// Update progress bar
+function updateProgressBar() {
+ var songLength = 30000; // 30 seconds
+ var progress = Math.min(1, gameTime / songLength);
+ progressBarFill.scaleX = progress;
+ if (progress >= 1 && isPlaying) {
+ endGame();
+ }
+}
+// End the game
+function endGame() {
+ isPlaying = false;
+ // Update high score
+ if (LK.getScore() > storage.highScore) {
+ storage.highScore = LK.getScore();
+ }
+ // Update total plays
+ storage.totalPlays = (storage.totalPlays || 0) + 1;
+ // Show game over
+ LK.setTimeout(function () {
+ LK.showGameOver();
+ }, 1000);
+}
+// Initialize the game
+initGame();
+// Game loop
+game.update = function () {
+ if (!isPlaying) {
+ return;
+ }
+ // Update game time
+ gameTime += 1000 / 60; // Approximate ms per frame at 60fps
+ // Update notes
+ for (var i = 0; i < notes.length; i++) {
+ var note = notes[i];
+ // Check if note should start approaching
+ if (note.active && !note.approaching && gameTime >= note.timing - APPROACH_TIME) {
+ note.approaching = true;
+ game.addChild(note);
+ // Animate note approaching
+ tween(note, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: APPROACH_TIME,
+ easing: tween.linear
+ });
+ }
+ }
+ // Check for auto-missed notes
+ checkMissedNotes();
+ // Update progress bar
+ updateProgressBar();
+};
+// Touch controls
+game.down = function (x, y, obj) {
+ // Check if any note was directly tapped
+ // Note: The Note class's down method will handle the hit logic
+};
+// Handle direct taps on the game area that aren't on notes
+game.move = function (x, y, obj) {
+ // Not needed for this game
+};
+game.up = function (x, y, obj) {
+ // Not needed for this game
+};
\ No newline at end of file