Code edit (2 edits merged)
Please save this source code
User prompt
Ok, now make final orb scale twice bigger
User prompt
now orb have the correct orientation, but they don't move and grow like gates. Please step back and analyse then fix orbs behaviour to match gate's one
User prompt
adjust orbs spawning and update so that they look like gates in term of position and movement
Code edit (1 edits merged)
Please save this source code
User prompt
Now make orbs behave like gates after they are spawned, orbs should follow a similar trajectory
User prompt
in GateManager.spawnGateAtTime(), call in OrbManager to spawn an Orb at the 'free angle' when spawning 2 gates.
User prompt
in OrbManager and Orb, use the same technique as GateManager.spawnGateAtTime() and Gate.updateScale() to spawn orbs and make them move ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
enhance orbs move : clone the system used for gates
User prompt
add an obrManager to spawn orbs; they should spanw from center with small scale and increase scale like the system used for gates ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'scaleX')' in or related to this line: 'var currentScale = orb.orbAsset.scaleX;' Line Number: 622
User prompt
Please fix the bug: 'TypeError: Cannot set properties of undefined (setting 'rotation')' in or related to this line: 'orb.orbAsset.rotation = orbAngle; // Assuming orbAsset exists and needs rotation' Line Number: 589
User prompt
Please fix the bug: 'TypeError: orb.setColor is not a function' in or related to this line: 'orb.setColor(orbColor); // Assuming Orb class has a setColor method' Line Number: 562
User prompt
Please fix the bug: 'ReferenceError: orbManager is not defined' in or related to this line: 'lastTick = now;' Line Number: 3174
User prompt
Please fix the bug: 'ReferenceError: orbManager is not defined' in or related to this line: 'if (orbManager) {' Line Number: 3176
User prompt
add an obrManager to spawn orbs like gates
User prompt
Ok now add and orb class but don't use it. add an anim using assets orb0,orb1,orb2,orb3,orb4 and orb5 as frames and changing their alpha in sequence ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
ok, add some randomness in WorldManager target's selection : don't only go to left border of left BG then right border of right BG but selected a random target between the two and animate to it then choose another random target ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
ok, add some randomness in WorldManager target's selection
Code edit (1 edits merged)
Please save this source code
User prompt
in WorldManager make BGs shift very slowly during song play to left the right ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
add a new worldManager to display 2 worldBackground side by side (one with scaleX = -1)
Code edit (1 edits merged)
Please save this source code
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ /***********************************************************************************/ var BackgroundManager = Container.expand(function () { var self = Container.call(this); // Create three background instances for smoother tunnel effect self.bg0 = self.attachAsset('background01', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 2.4, scaleY: 2.4, alpha: 1 }); self.bg1 = self.attachAsset('background01', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 1.1, scaleY: 1.1, alpha: 1 }); self.bg2 = self.attachAsset('background01', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 0.5, scaleY: 0.5, alpha: 1 }); self.bg3 = self.attachAsset('background01', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 0.22, scaleY: 0.22, alpha: 1 }); self.bg4 = self.attachAsset('background01', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 0.11, scaleY: 0.11, alpha: 1 }); self.tore0 = self.attachAsset('tore', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 1.2, scaleY: 1.2, alpha: 0 }); self.tore1 = self.attachAsset('tore', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 0.6, scaleY: 0.6, alpha: 0 }); self.tore2 = self.attachAsset('tore', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 0.26, scaleY: 0.26, alpha: 0 }); self.tore3 = self.attachAsset('tore', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 0.12, scaleY: 0.12, alpha: 0 }); // Apply different tints in debug mode if (isDebug) { self.bg1.tint = 0xFF0000; // Red tint for first background self.tore1.tint = 0x00FF00; // Green tint for first tore self.bg2.tint = 0x00FFFF; // Cyan tint for second background self.tore2.tint = 0xFF00FF; // Magenta tint for second tore self.bg3.tint = 0xfff200; // Yellow tint for third background } // Animation properties self.bgAnimationSpeed = globalSpeed / 1000; //0.002; self.bgAnimationAcceleration = 2; // Add tore assets between backgrounds for animation self.backgrounds = [self.bg0, self.tore0, self.bg1, self.tore1, self.bg2, self.tore2, self.bg3, self.tore3, self.bg4]; //self.backgrounds = [self.bg0, self.bg1, self.bg2, self.bg3]; // Define initial scale for each background/torus (tore: 0.26, bg: 0.22) //self.bgInitialScales = [0.22, 0.22, 0.22, 0.22, 0.22]; //self.bgInitialScales = [0, 0, 0, 0, 0]; //self.bgInitialScales = [0, 0, 0, 0, 0, 0, 0, 0, 0]; // Animation state: single startTime for all backgrounds/torus self.bgAnimStartTime = Date.now(); // Update method - handle background/torus scale animation self.update = function () { // Don't update if song hasn't started if (!songStarted) { return; } var now = Date.now(); var elapsed = now - self.bgAnimStartTime; var resetTriggered = false; for (var i = 0; i < self.backgrounds.length; i++) { var bg = self.backgrounds[i]; // Make the scale speed increase as the scale increases (e.g. exponential or quadratic growth) //var scaleMultiplier = bg.scaleX * self.bgAnimationAcceleration; //bg.scaleX += self.bgAnimationSpeed * scaleMultiplier; bg.scaleX += self.bgAnimationSpeed * bg.scaleX; bg.scaleY = bg.scaleX; if (bg.scaleX > 3.0) { bg.scaleX = 0.12; //self.bgInitialScales[i]; bg.scaleY = bg.scaleX; } //bg.alpha = Math.min(1, bg.scaleX + 0.66); bg.tint = 0x1697b8; // 0x33FF33; } }; return self; }); // Initialize the game; /***********************************************************************************/ /********************************** BALL CLASS *************************************/ /***********************************************************************************/ var Ball = Container.expand(function () { var self = Container.call(this); // Create and attach ball asset var ballGraphics = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, tint: currentColor, alpha: 1 }); // Initialize ball properties self.speedX = 0; self.speedY = 0; // Track last intersecting state for each gate self.lastIntersectingGates = {}; // Update method to follow runner's position self.update = function () { // Don't process gates until song has started if (!songStarted) { return; } // Make ball follow runner's exact position if (runner) { self.x = runner.x; self.y = runner.y; } // Check for collisions with gates if (gateManager && gateManager.gates) { // Iterate backwards to avoid index shifting issues when removing gates for (var i = gateManager.gates.length - 1; i >= 0; i--) { var gate = gateManager.gates[i]; var gateId = gate.gateId; // Initialize tracking if needed if (self.lastIntersectingGates[gateId] === undefined) { self.lastIntersectingGates[gateId] = false; } // Check intersection with bounding box instead of gate asset, but ignore if gate scale > 1.0 var currentIntersecting = gate.gateAsset.scaleX >= minDetectionScale && gate.gateAsset.scaleX <= maxDetectionScale && self.intersects(gate.boundingBox); // Detect transition from not intersecting to intersecting if (!self.lastIntersectingGates[gateId] && currentIntersecting) { // Play hit sound playHitSound(); // Mark gate as being destroyed to prevent multiple triggers if (!gate.isDestroying) { gate.isDestroying = true; // Animate scale down tween(gate, { scaleX: 0, scaleY: 0 }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { // Request destruction from gate manager gateManager.destroyGate(gate); } }); } } // Update last intersecting state self.lastIntersectingGates[gateId] = currentIntersecting; } // No cleanup needed - gate IDs are unique and won't be reused } }; return self; }); /***********************************************************************************/ /********************************** GATE CLASS *************************************/ /***********************************************************************************/ var Gate = Container.expand(function () { var self = Container.call(this); // Create gate asset with initial properties self.gateAsset = self.attachAsset('gate', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 0.26, // Start at same scale as tore2 scaleY: 0.26, alpha: 1, visible: false }); // Store direction angle for this gate self.directionAngle = 0; // Add bounding box for collision detection self.boundingBox = self.attachAsset('boundingBox', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 2450, alpha: 1 // Invisible by default }); /* width: 200, heigh: 100, */ // Store the color for this gate self.gateColor = 0xFFFFFF; // Default white, will be set by manager // Store unique ID for this gate self.gateId = null; // Will be set by manager // Set the tint to match the gate color self.setColor = function (color) { self.gateColor = color; self.gateAsset.tint = color; // Show bounding box in debug mode //if (isDebug) { self.boundingBox.alpha = 0.8; //self.boundingBox.tint = color; } }; // Update scale to match background animation self.updateScale = function (newScale) { /* self.scaleX = newScale; self.scaleY = newScale; self.alpha = Math.min(1, newScale + 0.66); */ self.gateAsset.scaleX = newScale; self.gateAsset.scaleY = newScale; self.gateAsset.alpha = Math.min(1, newScale + 0.66); // Scale bounding box proportionally self.boundingBox.scaleX = newScale; // 3 * newScale / 0.26; // Maintain 300px width relative to gate scale self.boundingBox.scaleY = newScale; //0.3 * newScale / 0.26; // Maintain 30px height relative to gate scale // Calculate boundingBox position using directionAngle and scale // Calculate distance from center based on scale var distance = (2450 - 1366) * newScale; // Use directionAngle to position boundingBox self.boundingBox.x = centerX + distance * Math.cos(self.directionAngle + Math.PI * 0.5); self.boundingBox.y = centerY + distance * Math.sin(self.directionAngle + Math.PI * 0.5); self.boundingBox.rotation = self.directionAngle; }; return self; }); /***********************************************************************************/ /********************************** GATE MANAGER CLASS *****************************/ /***********************************************************************************/ var GateManager = Container.expand(function () { var self = Container.call(this); // Array to hold gates self.gates = []; // Animation timing self.gateAnimStartTime = Date.now(); self.gateAnimationSpeed = globalSpeed / 1000; // Same as background // Song timing properties self.currentSong = songListV3[0]; self.songStartTime = Date.now(); self.currentNoteIndex = 0; self.noteSpawnScale = 0.12; // Initial scale for new gates matching smallest tore self.lastGateAngle = null; // Track last gate angle for path continuity self.lastProcessedBeat = -1; // Track last processed beat for speaker synchronization // Spawn a single gate at current time self.spawnGateAtTime = function () { // Get the current beat value var beatValue = null; if (self.currentNoteIndex < self.currentSong.songBeats.length) { beatValue = self.currentSong.songBeats[self.currentNoteIndex].beat; } // Map beat to key for color selection var keyNumber = parseInt(beatValue, 10) || 1; // Default to 1 if parse fails var noteKey = 'Key' + keyNumber; var keyColor = keyColorMap[noteKey] || currentColor; // Default to currentColor if key not found // --- Calculate gate travel time so it reaches the player at the correct beat time --- var startScale = self.noteSpawnScale; var endScale = 1.0; // The scale at which the gate should reach the player (runner) var speed = self.gateAnimationSpeed; // This is the per-tick scale growth factor var timeToReachPlayer = Math.log(endScale / startScale) / speed; // ms // Determine beat angle (where the safe path is) var beatAngle = centerAngle; if (beatValue === "1") { beatAngle = rightAngle; } else if (beatValue === "2") { beatAngle = leftAngle; } else { // Beat values 0 and 3 go to center beatAngle = centerAngle; } // Store this angle for the next gate self.lastGateAngle = beatAngle; // Create array of all possible angles var allAngles = [leftAngle, centerAngle, rightAngle]; var gateAngles = []; // Filter out the beat angle to get the non-beat angles for (var i = 0; i < allAngles.length; i++) { if (allAngles[i] !== beatAngle) { gateAngles.push(allAngles[i]); } } // Calculate timing var beatTime = self.currentSong.songBeats[self.currentNoteIndex].time; var now = Date.now(); var songElapsed = now - self.songStartTime; var spawnTime = beatTime - timeToReachPlayer; // Create gates at non-beat positions for (var j = 0; j < gateAngles.length; j++) { var gate = new Gate(); gate.setColor(keyColor); gate.updateScale(self.noteSpawnScale); // Store spawn time for tracking gate.spawnTime = Date.now() + 200 / globalSpeed; gate.colorIndex = 0; // Assign unique ID to gate gate.gateId = getNextGateId(); // Store the note key for this gate gate.noteKey = noteKey; // Set the direction angle for this gate gate.directionAngle = gateAngles[j]; // Apply rotation to gate asset gate.gateAsset.rotation = gateAngles[j]; // Spawn or schedule the gate if (songElapsed >= spawnTime) { // Spawn now self.gates.push(gate); self.addChild(gate); } else { // Schedule spawn for later (function (g) { LK.setTimeout(function () { self.gates.push(g); self.addChild(g); }, spawnTime - songElapsed); })(gate); } } }; // Update gates animation self.update = function () { // Don't update if song hasn't started if (!songStarted) { return; } var now = Date.now(); var songElapsed = now - self.songStartTime; // Check if we need to spawn a new gate based on song timing if (self.currentNoteIndex < self.currentSong.songBeats.length) { var nextBeat = self.currentSong.songBeats[self.currentNoteIndex]; if (songElapsed >= nextBeat.time) { // Check if enough time has passed since last beat if (now - lastBeatTime >= skipBeatDelay) { // Spawn a new gate for this beat self.spawnGateAtTime(); lastBeatTime = now; } self.currentNoteIndex++; } } // Animate existing gates for (var i = self.gates.length - 1; i >= 0; i--) { var gate = self.gates[i]; var currentScale = gate.gateAsset.scaleX; // Increase scale with acceleration var newScale = currentScale + self.gateAnimationSpeed * currentScale; // Remove gate when too large if (newScale > 3.0) { gate.destroy(); self.gates.splice(i, 1); } else { gate.updateScale(newScale); } } // Check if song has ended and needs restart self.checkSongEnd(); }; // Reset song when it ends self.resetSong = function () { self.songStartTime = Date.now(); self.currentNoteIndex = 0; self.lastGateAngle = null; // Reset angle tracking for new song }; // Check if song has ended and restart self.checkSongEnd = function () { if (self.currentNoteIndex >= self.currentSong.songBeats.length) { // All beats have been spawned, check if we should restart var lastBeatTime = self.currentSong.songBeats[self.currentSong.songBeats.length - 1].time; var songElapsed = Date.now() - self.songStartTime; // Wait a bit after the last beat before restarting if (songElapsed > lastBeatTime + 5000) { self.resetSong(); } } }; // Destroy a specific gate self.destroyGate = function (gate) { var index = self.gates.indexOf(gate); if (index > -1) { self.gates.splice(index, 1); gate.destroy(); } }; return self; }); var Note = Container.expand(function () { var self = Container.call(this); // Create and attach note asset var noteGraphics = self.attachAsset('note', { anchorX: 0.5, anchorY: 0.5, alpha: 1 }); // Note properties self.velocity = { x: 0, y: -5 }; // Default upward movement self.lifetime = 500; // 1.5 seconds (reduced from 3 seconds) self.spawnTime = Date.now(); self.noteType = 'note'; // Can be 'note' or 'note2' self.noteColor = 0xFFFFFF; // Default white // Set note type and appearance self.setNoteType = function (type) { self.noteType = type; // Remove current graphics noteGraphics.destroy(); // Create new graphics with appropriate asset noteGraphics = self.attachAsset(type, { anchorX: 0.5, anchorY: 0.5, alpha: 1 }); noteGraphics.tint = self.noteColor; }; // Set note color self.setColor = function (color) { self.noteColor = color; noteGraphics.tint = color; }; // Set velocity self.setVelocity = function (vx, vy) { self.velocity.x = vx; self.velocity.y = vy; }; // Update method self.update = function () { // Move based on velocity self.x += self.velocity.x; self.y += self.velocity.y; // Check lifetime var elapsed = Date.now() - self.spawnTime; if (elapsed > self.lifetime) { self.alpha = Math.max(0, 1 - (elapsed - self.lifetime) / 500); // Fade out over 500ms } // Destroy if too old or out of bounds if (elapsed > self.lifetime + 500 || self.y < -100 || self.y > 2832 || self.x < -100 || self.x > 2148) { self.shouldDestroy = true; } }; return self; }); var NoteSparks = Container.expand(function () { var self = Container.call(this); // Array to track active notes self.notes = []; // Spawn configuration self.spawnRate = 500; // Milliseconds between spawns self.lastSpawnTime = 0; self.spawnEnabled = false; // Default spawn properties self.spawnX = 1024; self.spawnY = 1366; self.spawnVelocityRange = { x: { min: -2, max: 2 }, y: { min: -8, max: -4 } }; self.noteColors = [0xFF073A, 0x39FF14, 0x00FFFF, 0xF3F315, 0xFF61F6]; // Set spawn position self.setSpawnPosition = function (x, y) { self.spawnX = x; self.spawnY = y; }; // Enable/disable spawning self.setSpawnEnabled = function (enabled) { self.spawnEnabled = enabled; }; // Set spawn rate self.setSpawnRate = function (rate) { self.spawnRate = rate; }; // Spawn a single note self.spawnNote = function () { var note = new Note(); // Set random position around spawn point note.x = self.spawnX + (Math.random() - 0.5) * 50; note.y = self.spawnY; // Set random velocity var vx = self.spawnVelocityRange.x.min + Math.random() * (self.spawnVelocityRange.x.max - self.spawnVelocityRange.x.min); var vy = self.spawnVelocityRange.y.min + Math.random() * (self.spawnVelocityRange.y.max - self.spawnVelocityRange.y.min); note.setVelocity(vx, vy); // Set random note type note.setNoteType(Math.random() > 0.5 ? 'note' : 'note2'); // Set random color var color = self.noteColors[Math.floor(Math.random() * self.noteColors.length)]; note.setColor(color); // Add slight rotation note.rotation = (Math.random() - 0.5) * 0.5; // Add to arrays and stage self.notes.push(note); self.addChild(note); }; // Spawn multiple notes at once (burst) self.spawnBurst = function (count) { for (var i = 0; i < count; i++) { self.spawnNote(); } }; // Update method self.update = function () { var now = Date.now(); // Spawn new notes if enabled if (self.spawnEnabled && now - self.lastSpawnTime >= self.spawnRate) { self.spawnNote(); self.lastSpawnTime = now; } // Update and clean up notes for (var i = self.notes.length - 1; i >= 0; i--) { var note = self.notes[i]; if (note.shouldDestroy) { note.destroy(); self.notes.splice(i, 1); } } }; return self; }); /***********************************************************************************/ /********************************** RUNNER CLASS ***********************************/ /***********************************************************************************/ var Runner = Container.expand(function () { var self = Container.call(this); // Create and attach runner asset var runnerGraphics = self.attachAsset('runnerDir4_001', { anchorX: 0.5, anchorY: 0.5, tint: 0xFFFFFF, //currentColor, alpha: 1 }); // Initialize runner properties self.speedX = 0; self.speedY = 0; // Track last intersecting state for each gate self.lastIntersectingGates = {}; // Add tick counter for scale flipping self.tickCounter = 0; self.update = function () { // Don't update if song hasn't started if (!songStarted) { return; } // Calculate angle based on runner's position relative to center var angle = Math.atan2(self.y - centerY, self.x - centerX); // Apply rotation to runner self.rotation = angle - Math.PI * 0.5; // Add PI/2 to orient correctly // Increment tick counter and flip scale only every 60 ticks self.tickCounter++; if (self.tickCounter >= 5) { self.scaleX *= -1; self.tickCounter = 0; // Reset counter } }; return self; }); var Speaker = Container.expand(function () { var self = Container.call(this); // Create and attach speaker asset var speakerGraphics = self.attachAsset('speaker', { anchorX: 0.5, anchorY: 0.5, alpha: 1 }); // Initialize speaker properties self.volume = 1.0; self.isPlaying = false; // Method to set speaker volume self.setVolume = function (vol) { self.volume = Math.max(0, Math.min(1, vol)); }; // Method to toggle speaker state self.toggleState = function () { self.isPlaying = !self.isPlaying; speakerGraphics.alpha = self.isPlaying ? 1.0 : 0.5; }; return self; }); var SpeakerManager = Container.expand(function () { var self = Container.call(this); // Array to hold 3 speakers self.speakers = []; self.lastBeatTime = 0; self.beatCooldown = 200; // Minimum time between beat animations // Properties for tracking speakersBeat data self.speakerSongData = speakersBeat[0]; // Use first song from speakersBeat self.currentBeatIndex = 0; self.songStartTime = 0; // Will be set when song starts // Initialize 3 speakers self.initializeSpeakers = function () { var speakerPositions = [{ x: 1024 - 400, y: 512 }, // Left speaker { x: 1024, y: 512 }, // Center speaker { x: 1024 + 400, y: 512 } // Right speaker ]; for (var i = 0; i < 3; i++) { var speaker = new Speaker(); speaker.x = speakerPositions[i].x; speaker.y = speakerPositions[i].y; speaker.scaleX = 0.8; speaker.scaleY = 0.8; speaker.isBigBumping = false; // Track if big bump animation is running self.speakers.push(speaker); self.addChild(speaker); } }; // Animate specific speaker based on beat value self.onBeat = function (beatValue) { var now = Date.now(); if (now - self.lastBeatTime >= self.beatCooldown) { // Determine which speaker to animate based on beat value var speakerIndex = -1; if (beatValue === "1") { speakerIndex = 0; // Left speaker } else if (beatValue === "2") { speakerIndex = 2; // Right speaker } else { speakerIndex = 1; // Center speaker for beats 0 and 3 } // Animate speaker based on whether it's the main speaker for this beat if (speakerIndex >= 0 && speakerIndex < self.speakers.length) { var mainSpeaker = self.speakers[speakerIndex]; // Set flag to indicate big bump is running mainSpeaker.isBigBumping = true; // Spawn note sparks around the speaker self.spawnNoteSparks(mainSpeaker); // Big bump for the main speaker tween(mainSpeaker, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { // Return to base scale (will be overridden by continuous animation) tween(mainSpeaker, { scaleX: 0.8, scaleY: 0.8 }, { duration: 200, easing: tween.easeIn, onFinish: function onFinish() { // Clear flag when big bump is complete mainSpeaker.isBigBumping = false; } }); } }); } self.lastBeatTime = now; } }; // Update method to check for beats and add swing animation self.update = function () { // Add continuous twist (rotation) and small bump (scale) animations when song is playing if (songStarted) { for (var i = 0; i < self.speakers.length; i++) { var speaker = self.speakers[i]; var time = Date.now() * 0.003; // Time scale for animations // Twist animation (rotation) - always apply var twistAngle = Math.sin(time + i * 0.5) * 0.15; // Rotation with phase offset speaker.rotation = twistAngle; // Small bump animation (scale) - only apply if big bump is not running if (!speaker.isBigBumping) { var scaleValue = 0.8 + Math.sin(time * 2 + i * 0.3) * 0.05; // Small scale oscillation speaker.scaleX = scaleValue; speaker.scaleY = scaleValue; } } } // Check for beat events if (!songStarted) { return; } // Check if a new beat has occurred using speakersBeat data var now = Date.now(); var songElapsed = now - self.songStartTime; // Process beats from speakersBeat data if (self.currentBeatIndex < self.speakerSongData.songBeats.length) { var nextBeat = self.speakerSongData.songBeats[self.currentBeatIndex]; if (songElapsed >= nextBeat.time) { // Trigger beat self.onBeat(nextBeat.beat); self.currentBeatIndex++; } } else { // Check if song has ended and needs restart var lastBeatTime = self.speakerSongData.songBeats[self.speakerSongData.songBeats.length - 1].time; // Wait a bit after the last beat before restarting if (songElapsed > lastBeatTime + 5000) { self.songStartTime = Date.now(); self.currentBeatIndex = 0; } } }; // Method to spawn note sparks around a speaker self.spawnNoteSparks = function (speaker) { // Spawn 3-5 notes around the speaker var noteCount = 3 + Math.floor(Math.random() * 3); for (var i = 0; i < noteCount; i++) { var note = new Note(); // Position notes randomly around the speaker var angle = Math.random() * Math.PI * 2; var distance = 50 + Math.random() * 100; note.x = speaker.x + Math.cos(angle) * distance; note.y = speaker.y + Math.sin(angle) * distance; // Set random velocity outward from speaker var speed = 3 + Math.random() * 5; note.setVelocity(Math.cos(angle) * speed, Math.sin(angle) * speed - 2); // Randomly choose note or note2 note.setNoteType(Math.random() > 0.5 ? 'note' : 'note2'); // Use only futuristic violet and blue colors var futuristicColors = [0x32cbec]; //[0xe77d82, 0x48dae9, 0x0b4aa4, 0x9d2c44]; // Violet and Blue var color = futuristicColors[Math.floor(Math.random() * futuristicColors.length)]; note.setColor(color); // Add slight rotation note.rotation = (Math.random() - 0.5) * 0.5; note.scaleX = 0.5; note.scaleY = 0.5; // Add to noteSparks manager if it exists if (noteSparks) { noteSparks.notes.push(note); noteSparks.addChild(note); } } }; // Initialize speakers when created self.initializeSpeakers(); return self; }); // Music will be started by start button // LK.playMusic('track_02'); // test gate /* var testGate = new Gate(); var newScale = 1; testGate.scaleX = newScale; testGate.scaleY = newScale; testGate.gateAsset.scaleX = newScale; testGate.gateAsset.scaleY = newScale; testGate.boundingBox.scaleX = newScale; testGate.boundingBox.scaleY = newScale; testGate.boundingBox.alpha = 0.3; game.addChild(testGate); */ var StartButton = Container.expand(function () { var self = Container.call(this); // Create button background self.buttonBg = self.attachAsset('start', { anchorX: 0.5, anchorY: 0.5 }); // Position at center of screen self.x = centerX; self.y = centerY; // Handle button press self.down = function () { // Prevent multiple presses if (songStarted) { return; } // Mark song as started songStarted = true; // Start the music LK.playMusic('track_01'); // Reset gate manager timing if (gateManager) { gateManager.songStartTime = Date.now(); gateManager.currentNoteIndex = 0; } // Reset speaker manager timing if (speakerManager) { speakerManager.songStartTime = Date.now(); speakerManager.currentBeatIndex = 0; } // Enable note sparks spawning if (noteSparks) { //noteSparks.setSpawnEnabled(true); } // Fade out and remove button tween(self, { alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); /***********************************************************************************/ /******************************* UTILITY FUNCTIONS *********************************/ /***********************************************************************************/ /******************************* WORLD MANAGER CLASS *******************************/ /***********************************************************************************/ var WorldManager = Container.expand(function () { var self = Container.call(this); // Constants for calculation var assetWidth = 2732; // Width of 'world01' asset var screenWidth = 2048; // Virtual screen width var totalContentWidth = assetWidth * 2; // Properties for shifting self.x_center_view = -(totalContentWidth / 2 - screenWidth / 2); self.shiftAmplitude = 200; // How many pixels to shift left/right from center view self.shiftDurationOneWay = 15000; // 15 seconds for one leg of the shift (very slow) self._isShifting = false; // Internal state to track if shifting sequence is active // --- Shift methods (defined first as per guidelines) --- self._animateToRight = function () { // Check if we should continue (song playing and shifting enabled) if (!songStarted || !self._isShifting) { self._isShifting = false; // Ensure flag is clear tween.stop(self, { x: true }); // Stop any x-tween on self return; } var targetX = self.x_center_view + self.shiftAmplitude; tween(self, { x: targetX }, { duration: self.shiftDurationOneWay, easing: tween.easeInOut, // Smooth easing onFinish: self._animateToLeft // Chain to the other direction }); }; self._animateToLeft = function () { if (!songStarted || !self._isShifting) { self._isShifting = false; tween.stop(self, { x: true }); return; } var targetX = self.x_center_view - self.shiftAmplitude; tween(self, { x: targetX }, { duration: self.shiftDurationOneWay, easing: tween.easeInOut, onFinish: self._animateToRight // Chain back }); }; self._initiateShift = function () { // This function is called once when shifting should start. if (self._isShifting) return; self._isShifting = true; // Start the oscillation by moving towards one of the extremes from current position. // Let's say, always try to move towards the right extreme first when initiating. self._animateToRight(); }; // Update method: Called by the engine every frame self.update = function () { if (songStarted && !self._isShifting) { // Song is playing, and we are not currently in a shift sequence. Start one. self._initiateShift(); } else if (!songStarted && self._isShifting) { // Song has stopped, but we were in a shift sequence. Stop it. self._isShifting = false; // This flag will be checked by onFinish callbacks. tween.stop(self, { x: true }); // Immediately stop any ongoing x-tween. // Smoothly tween back to the default centered position. tween(self, { x: self.x_center_view }, { duration: 1000, // 1 second to return easing: tween.easeOut // Use easeOut for a gentle stop }); } }; // --- Constructor Logic (runs after method definitions) --- // Set initial position for WorldManager itself. This affects where its children are seen. self.x = self.x_center_view; self.y = 0; // WorldManager is typically at y=0. // Attach background assets. Their x,y are relative to WorldManager's origin. self.bgLeft = self.attachAsset('world01', { anchorX: 0.5, anchorY: 0.5, x: assetWidth / 2, // Center of bgLeft is at assetWidth/2 from WM origin y: centerY, // Vertically center on game screen using global centerY scaleX: 1.0, // Normal orientation scaleY: 1.0 }); self.bgRight = self.attachAsset('world01', { anchorX: 0.5, anchorY: 0.5, x: assetWidth / 2 + assetWidth, // Center of bgRight is next to bgLeft y: centerY, // Vertically centered using global centerY scaleX: -1.0, // Mirrored horizontally scaleY: 1.0 }); return self; }); /**** * Initialize Game ****/ // Utility function to draw a polygon using drawLine var game = new LK.Game({ backgroundColor: 0x000c33 // Initialize game with a black background }); /**** * Game Code ****/ // Global center coordinates var centerX = 1024; var centerY = 1366; // Global array of 6 neon colors var neonColors = [0x39FF14, // Neon Green 0xFF073A, // Neon Red 0x00FFFF, // Neon Cyan 0xF3F315, // Neon Yellow 0xFF61F6, // Neon Pink 0xFF9900 // Neon Orange ]; // Map keys to colors - 15 keys (0-14) mapped to neon colors var keyColorMap = { 'Key0': 0x39FF14, // Neon Green 'Key1': 0xFF073A, // Neon Red 'Key2': 0x00FFFF, // Neon Cyan 'Key3': 0xF3F315, // Neon Yellow 'Key4': 0xFF61F6, // Neon Pink 'Key5': 0xFF9900, // Neon Orange 'Key6': 0x39FF14, // Neon Green (repeat) 'Key7': 0xFF073A, // Neon Red (repeat) 'Key8': 0x00FFFF, // Neon Cyan (repeat) 'Key9': 0xF3F315, // Neon Yellow (repeat) 'Key10': 0xFF61F6, // Neon Pink (repeat) 'Key11': 0xFF9900, // Neon Orange (repeat) 'Key12': 0x39FF14, // Neon Green (repeat) 'Key13': 0xFF073A, // Neon Red (repeat) 'Key14': 0x00FFFF // Neon Cyan (repeat) }; // Global currentColor, set to a random neon color var currentColor = neonColors[Math.floor(Math.random() * neonColors.length)]; /***********************************************************************************/ /******************************* UTILITY FUNCTIONS *********************************/ /***********************************************************************************/ function drawPolygon(coordinates, tint) { log("drawPolygon ", coordinates); var lines = []; for (var i = 0; i < coordinates.length; i++) { var startPoint = coordinates[i]; var endPoint = coordinates[(i + 1) % coordinates.length]; // Loop back to the first point var line = drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y, tint); lines.push(line); } return lines; } function updatePolygon(lines, newCoordinates, scale) { log("updatePolygon ", lines, scale); // Ensure lines and newCoordinates have the same length if (lines.length !== newCoordinates.length) { error("updatePolygon error: lines and newCoordinates length mismatch"); return lines; } // Update each line with new coordinates for (var i = 0; i < lines.length; i++) { var startPoint = newCoordinates[i]; var endPoint = newCoordinates[(i + 1) % newCoordinates.length]; // Loop back to the first point for the last line updateLine(lines[i], startPoint.x, startPoint.y, endPoint.x, endPoint.y, scale); } return lines; } // Utility function to draw lines between two points function drawLine(x1, y1, x2, y2, tint) { log("drawLine ", x1, y1); var line = LK.getAsset('line', { anchorX: 0.0, anchorY: 0.0, x: x1, y: y1, tint: tint }); line.startX = x1; line.startY = y1; line.endX = x2; line.endY = y2; // Calculate the distance between the two points var distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); // Set the width of the line to the distance between the points line.width = distance; // Calculate the angle between the two points var angle = Math.atan2(y2 - y1, x2 - x1); // Correct angle calculation for all quadrants line.rotation = angle; return line; } // Utility function to draw lines between two points function updateLine(line, newX1, newY1, newX2, newY2, scale) { log("updateLine ", line); scale = scale === undefined ? 1 : scale; // Calculate midpoint of the original line var midX = (newX1 + newX2) / 2; var midY = (newY1 + newY2) / 2; // Adjust start and end points based on scale newX1 = midX + (newX1 - midX) * scale; newY1 = midY + (newY1 - midY) * scale; newX2 = midX + (newX2 - midX) * scale; newY2 = midY + (newY2 - midY) * scale; // Update line start and end coordinates after scaling line.x = newX1; line.y = newY1; line.startX = newX1; line.startY = newY1; line.endX = newX2; line.endY = newY2; // Recalculate the distance between the new scaled points var distance = Math.sqrt(Math.pow(newX2 - newX1, 2) + Math.pow(newY2 - newY1, 2)); // Update the width of the line to the new distance line.width = distance; // Recalculate the angle between the new points var angle = Math.atan2(newY2 - newY1, newX2 - newX1); // Update the rotation of the line to the new angle line.rotation = angle; return line; } function log() { if (isDebug) { console.log(arguments); } } /***********************************************************************************/ /******************************* GAME VARIABLES*********************************/ /***********************************************************************************/ var songListV3 = [{ "name": "Words Fly Fast", "songBeats": [{ "time": 651, "beat": "1" }, { "time": 1256, "beat": "1" }, { "time": 1800, "beat": "1" }, { "time": 2364, "beat": "1" }, { "time": 2828, "beat": "1" }, { "time": 3324, "beat": "1" }, { "time": 3708, "beat": "1" }, { "time": 4192, "beat": "2" }, { "time": 4804, "beat": "2" }, { "time": 5320, "beat": "2" }, { "time": 5796, "beat": "2" }, { "time": 6240, "beat": "2" }, { "time": 6692, "beat": "2" }, { "time": 7176, "beat": "2" }, { "time": 7640, "beat": "2" }, { "time": 8092, "beat": "1" }, { "time": 8576, "beat": "2" }, { "time": 9124, "beat": "1" }, { "time": 9580, "beat": "2" }, { "time": 10084, "beat": "1" }, { "time": 10548, "beat": "2" }, { "time": 11072, "beat": "1" }, { "time": 11556, "beat": "2" }, { "time": 12092, "beat": "1" }, { "time": 12564, "beat": "2" }, { "time": 13008, "beat": "1" }, { "time": 13400, "beat": "2" }, { "time": 13916, "beat": "1" }, { "time": 14460, "beat": "2" }, { "time": 14944, "beat": "1" }, { "time": 15360, "beat": "2" }, { "time": 17340, "beat": "1" }, { "time": 18260, "beat": "2" }, { "time": 19196, "beat": "1" }, { "time": 20064, "beat": "2" }, { "time": 21092, "beat": "1" }, { "time": 22072, "beat": "2" }, { "time": 23100, "beat": "1" }, { "time": 24040, "beat": "2" }, { "time": 24988, "beat": "1" }, { "time": 25884, "beat": "2" }, { "time": 26876, "beat": "1" }, { "time": 27892, "beat": "2" }, { "time": 28820, "beat": "1" }, { "time": 29688, "beat": "2" }, { "time": 30728, "beat": "1" }, { "time": 31696, "beat": "2" }, { "time": 32656, "beat": "1" }, { "time": 33624, "beat": "2" }, { "time": 34673, "beat": "1" }, { "time": 35692, "beat": "2" }, { "time": 36628, "beat": "1" }, { "time": 37536, "beat": "2" }, { "time": 38516, "beat": "1" }, { "time": 39372, "beat": "2" }, { "time": 40524, "beat": "1" }, { "time": 41028, "beat": "1" }, { "time": 43080, "beat": "2" }, { "time": 43564, "beat": "2" }, { "time": 44400, "beat": "1" }, { "time": 44948, "beat": "1" }, { "time": 46888, "beat": "2" }, { "time": 47412, "beat": "2" }, { "time": 48260, "beat": "1" }, { "time": 48818, "beat": "1" }, { "time": 50816, "beat": "2" }, { "time": 51524, "beat": "2" }, { "time": 52168, "beat": "1" }, { "time": 52684, "beat": "1" }, { "time": 54540, "beat": "3" }, { "time": 55488, "beat": "3" }, { "time": 56448, "beat": "3" }, { "time": 57436, "beat": "3" }, { "time": 58412, "beat": "1" }, { "time": 59240, "beat": "2" }, { "time": 59988, "beat": "1" }, { "time": 60808, "beat": "2" }, { "time": 61636, "beat": "1" }, { "time": 63800, "beat": "1" }, { "time": 64688, "beat": "2" }, { "time": 65656, "beat": "1" }, { "time": 66544, "beat": "2" }, { "time": 67492, "beat": "1" }, { "time": 68432, "beat": "2" }, { "time": 69369, "beat": "1" }, { "time": 70336, "beat": "2" }, { "time": 71416, "beat": "1" }, { "time": 72324, "beat": "2" }, { "time": 73408, "beat": "1" }, { "time": 74316, "beat": "2" }, { "time": 75276, "beat": "1" }, { "time": 76204, "beat": "2" }, { "time": 77248, "beat": "2" }, { "time": 78232, "beat": "1" }, { "time": 79168, "beat": "2" }, { "time": 81048, "beat": "1" }, { "time": 81996, "beat": "2" }, { "time": 83096, "beat": "1" }, { "time": 84064, "beat": "2" }, { "time": 85040, "beat": "1" }, { "time": 86008, "beat": "2" }, { "time": 86956, "beat": "1" }, { "time": 87976, "beat": "2" }, { "time": 88832, "beat": "1" }, { "time": 89844, "beat": "2" }, { "time": 90832, "beat": "1" }, { "time": 92672, "beat": "3" }, { "time": 93156, "beat": "3" }, { "time": 93720, "beat": "3" }, { "time": 94329, "beat": "3" }, { "time": 94812, "beat": "3" }, { "time": 95256, "beat": "3" }, { "time": 95720, "beat": "1" }, { "time": 96496, "beat": "2" }, { "time": 97556, "beat": "3" }, { "time": 98112, "beat": "3" }, { "time": 98624, "beat": "3" }, { "time": 99100, "beat": "3" }, { "time": 99604, "beat": "1" }, { "time": 100440, "beat": "2" }, { "time": 101420, "beat": "1" }, { "time": 102388, "beat": "2" }, { "time": 103336, "beat": "1" }, { "time": 104324, "beat": "2" }, { "time": 105172, "beat": "1" }, { "time": 106260, "beat": "3" }, { "time": 106836, "beat": "3" }, { "time": 107392, "beat": "3" }, { "time": 108028, "beat": "2" }, { "time": 108692, "beat": "1" }, { "time": 109540, "beat": "2" }, { "time": 110488, "beat": "1" }, { "time": 111932, "beat": "2" }, { "time": 112888, "beat": "1" }, { "time": 113796, "beat": "2" }, { "time": 114864, "beat": "1" }, { "time": 115904, "beat": "2" }, { "time": 116864, "beat": "2" }, { "time": 117792, "beat": "1" }, { "time": 119184, "beat": "2" }, { "time": 120244, "beat": "1" }, { "time": 121112, "beat": "2" }, { "time": 122121, "beat": "3" }, { "time": 122744, "beat": "3" }, { "time": 123260, "beat": "3" }, { "time": 123784, "beat": "3" }, { "time": 124217, "beat": "3" }, { "time": 125620, "beat": "1" }, { "time": 126668, "beat": "2" }, { "time": 127788, "beat": "1" }, { "time": 128796, "beat": "2" }, { "time": 129716, "beat": "1" }, { "time": 130884, "beat": "2" }, { "time": 131936, "beat": "1" }, { "time": 132932, "beat": "2" }, { "time": 134092, "beat": "1" }, { "time": 135124, "beat": "2" }, { "time": 136160, "beat": "1" }, { "time": 137128, "beat": "2" }, { "time": 139693, "beat": "3" }, { "time": 140208, "beat": "3" }, { "time": 140712, "beat": "3" }, { "time": 141216, "beat": "3" }, { "time": 141700, "beat": "3" }, { "time": 142164, "beat": "3" }, { "time": 142668, "beat": "3" }, { "time": 143164, "beat": "3" }, { "time": 143668, "beat": "1" }, { "time": 144484, "beat": "2" }, { "time": 145412, "beat": "1" }, { "time": 146340, "beat": "2" }, { "time": 147708, "beat": "3" }, { "time": 148304, "beat": "3" }, { "time": 148820, "beat": "3" }, { "time": 149312, "beat": "3" }, { "time": 149908, "beat": "1" }, { "time": 150804, "beat": "2" }, { "time": 151784, "beat": "3" }, { "time": 152328, "beat": "3" }, { "time": 152832, "beat": "3" }, { "time": 153236, "beat": "3" }, { "time": 153680, "beat": "1" }, { "time": 154518, "beat": "2" }] }]; var speakersBeat = [{ "name": "Words Fly Fast", "songBeats": [{ "time": 128, "beat": "1" }, { "time": 443, "beat": "1" }, { "time": 747, "beat": "1" }, { "time": 1232, "beat": "1" }, { "time": 1579, "beat": "1" }, { "time": 1893, "beat": "1" }, { "time": 2224, "beat": "1" }, { "time": 2549, "beat": "1" }, { "time": 2853, "beat": "1" }, { "time": 3269, "beat": "1" }, { "time": 3712, "beat": "1" }, { "time": 4037, "beat": "1" }, { "time": 4352, "beat": "1" }, { "time": 4661, "beat": "1" }, { "time": 5072, "beat": "1" }, { "time": 5392, "beat": "1" }, { "time": 5728, "beat": "1" }, { "time": 6176, "beat": "1" }, { "time": 6587, "beat": "1" }, { "time": 7141, "beat": "1" }, { "time": 7541, "beat": "1" }, { "time": 7856, "beat": "1" }, { "time": 8171, "beat": "3" }, { "time": 8501, "beat": "2" }, { "time": 8816, "beat": "2" }, { "time": 9120, "beat": "3" }, { "time": 9424, "beat": "2" }, { "time": 9728, "beat": "2" }, { "time": 10032, "beat": "3" }, { "time": 10347, "beat": "2" }, { "time": 10651, "beat": "1" }, { "time": 10971, "beat": "2" }, { "time": 11275, "beat": "2" }, { "time": 11600, "beat": "2" }, { "time": 11915, "beat": "1" }, { "time": 12240, "beat": "2" }, { "time": 12560, "beat": "3" }, { "time": 12875, "beat": "2" }, { "time": 13184, "beat": "3" }, { "time": 13493, "beat": "2" }, { "time": 13813, "beat": "3" }, { "time": 14176, "beat": "3" }, { "time": 14517, "beat": "1" }, { "time": 14843, "beat": "2" }, { "time": 15147, "beat": "2" }, { "time": 15472, "beat": "3" }, { "time": 15781, "beat": "2" }, { "time": 16112, "beat": "2" }, { "time": 16432, "beat": "2" }, { "time": 16741, "beat": "2" }, { "time": 17056, "beat": "3" }, { "time": 17360, "beat": "3" }, { "time": 17685, "beat": "2" }, { "time": 17995, "beat": "3" }, { "time": 18299, "beat": "3" }, { "time": 18608, "beat": "3" }, { "time": 18917, "beat": "3" }, { "time": 19232, "beat": "1" }, { "time": 19547, "beat": "2" }, { "time": 19851, "beat": "3" }, { "time": 20171, "beat": "1" }, { "time": 20475, "beat": "3" }, { "time": 20805, "beat": "3" }, { "time": 21120, "beat": "1" }, { "time": 21435, "beat": "2" }, { "time": 21749, "beat": "2" }, { "time": 22059, "beat": "2" }, { "time": 22373, "beat": "2" }, { "time": 22699, "beat": "2" }, { "time": 23019, "beat": "1" }, { "time": 23323, "beat": "3" }, { "time": 23627, "beat": "3" }, { "time": 23931, "beat": "2" }, { "time": 24240, "beat": "2" }, { "time": 24549, "beat": "2" }, { "time": 24869, "beat": "2" }, { "time": 25189, "beat": "3" }, { "time": 25493, "beat": "2" }, { "time": 25819, "beat": "2" }, { "time": 26133, "beat": "1" }, { "time": 26437, "beat": "2" }, { "time": 26757, "beat": "2" }, { "time": 27061, "beat": "2" }, { "time": 27365, "beat": "2" }, { "time": 27669, "beat": "3" }, { "time": 27973, "beat": "3" }, { "time": 28277, "beat": "3" }, { "time": 28581, "beat": "3" }, { "time": 28885, "beat": "3" }, { "time": 29221, "beat": "2" }, { "time": 29557, "beat": "3" }, { "time": 29861, "beat": "2" }, { "time": 30197, "beat": "2" }, { "time": 30507, "beat": "2" }, { "time": 30827, "beat": "2" }, { "time": 31141, "beat": "2" }, { "time": 31483, "beat": "2" }, { "time": 31787, "beat": "2" }, { "time": 32091, "beat": "3" }, { "time": 32395, "beat": "3" }, { "time": 32731, "beat": "1" }, { "time": 33045, "beat": "2" }, { "time": 33349, "beat": "3" }, { "time": 33653, "beat": "2" }, { "time": 33957, "beat": "3" }, { "time": 34261, "beat": "2" }, { "time": 34576, "beat": "2" }, { "time": 34901, "beat": "2" }, { "time": 35211, "beat": "3" }, { "time": 35520, "beat": "2" }, { "time": 35829, "beat": "3" }, { "time": 36149, "beat": "3" }, { "time": 36464, "beat": "2" }, { "time": 36779, "beat": "3" }, { "time": 37083, "beat": "2" }, { "time": 37403, "beat": "2" }, { "time": 37707, "beat": "2" }, { "time": 38037, "beat": "2" }, { "time": 38341, "beat": "3" }, { "time": 38661, "beat": "2" }, { "time": 38976, "beat": "1" }, { "time": 39301, "beat": "1" }, { "time": 39611, "beat": "2" }, { "time": 39941, "beat": "2" }, { "time": 40256, "beat": "1" }, { "time": 40565, "beat": "2" }, { "time": 40885, "beat": "1" }, { "time": 41189, "beat": "1" }, { "time": 41499, "beat": "1" }, { "time": 41851, "beat": "1" }, { "time": 42155, "beat": "1" }, { "time": 42459, "beat": "1" }, { "time": 42976, "beat": "1" }, { "time": 43429, "beat": "1" }, { "time": 43733, "beat": "1" }, { "time": 44043, "beat": "1" }, { "time": 44357, "beat": "2" }, { "time": 44672, "beat": "2" }, { "time": 44987, "beat": "1" }, { "time": 45312, "beat": "1" }, { "time": 45744, "beat": "1" }, { "time": 46059, "beat": "1" }, { "time": 46373, "beat": "1" }, { "time": 46683, "beat": "2" }, { "time": 46992, "beat": "2" }, { "time": 47301, "beat": "1" }, { "time": 47605, "beat": "2" }, { "time": 47920, "beat": "2" }, { "time": 48229, "beat": "1" }, { "time": 48544, "beat": "2" }, { "time": 48859, "beat": "1" }, { "time": 49168, "beat": "1" }, { "time": 49477, "beat": "2" }, { "time": 49845, "beat": "1" }, { "time": 50160, "beat": "1" }, { "time": 50496, "beat": "2" }, { "time": 50805, "beat": "1" }, { "time": 51173, "beat": "1" }, { "time": 51477, "beat": "2" }, { "time": 51792, "beat": "2" }, { "time": 52107, "beat": "2" }, { "time": 52421, "beat": "1" }, { "time": 52869, "beat": "1" }, { "time": 53189, "beat": "1" }, { "time": 53595, "beat": "1" }, { "time": 53979, "beat": "1" }, { "time": 54288, "beat": "1" }, { "time": 54597, "beat": "3" }, { "time": 54907, "beat": "2" }, { "time": 55227, "beat": "3" }, { "time": 55531, "beat": "3" }, { "time": 55851, "beat": "2" }, { "time": 56165, "beat": "3" }, { "time": 56475, "beat": "3" }, { "time": 56789, "beat": "2" }, { "time": 57104, "beat": "2" }, { "time": 57408, "beat": "1" }, { "time": 57728, "beat": "3" }, { "time": 58053, "beat": "2" }, { "time": 58363, "beat": "1" }, { "time": 58667, "beat": "3" }, { "time": 58976, "beat": "3" }, { "time": 59307, "beat": "1" }, { "time": 59621, "beat": "3" }, { "time": 59947, "beat": "3" }, { "time": 60304, "beat": "2" }, { "time": 60608, "beat": "3" }, { "time": 60971, "beat": "1" }, { "time": 61339, "beat": "2" }, { "time": 61701, "beat": "1" }, { "time": 62032, "beat": "1" }, { "time": 62336, "beat": "3" }, { "time": 62640, "beat": "3" }, { "time": 62955, "beat": "3" }, { "time": 63269, "beat": "3" }, { "time": 63584, "beat": "2" }, { "time": 63904, "beat": "3" }, { "time": 64213, "beat": "3" }, { "time": 64555, "beat": "2" }, { "time": 64864, "beat": "3" }, { "time": 65179, "beat": "2" }, { "time": 65499, "beat": "2" }, { "time": 65824, "beat": "3" }, { "time": 66144, "beat": "2" }, { "time": 66464, "beat": "2" }, { "time": 66768, "beat": "3" }, { "time": 67093, "beat": "1" }, { "time": 67403, "beat": "3" }, { "time": 67707, "beat": "3" }, { "time": 68053, "beat": "2" }, { "time": 68368, "beat": "2" }, { "time": 68709, "beat": "1" }, { "time": 69024, "beat": "1" }, { "time": 69333, "beat": "2" }, { "time": 69648, "beat": "3" }, { "time": 69968, "beat": "2" }, { "time": 70283, "beat": "2" }, { "time": 70587, "beat": "3" }, { "time": 70896, "beat": "3" }, { "time": 71205, "beat": "3" }, { "time": 71525, "beat": "3" }, { "time": 71835, "beat": "3" }, { "time": 72139, "beat": "3" }, { "time": 72448, "beat": "3" }, { "time": 72779, "beat": "3" }, { "time": 73083, "beat": "2" }, { "time": 73387, "beat": "3" }, { "time": 73691, "beat": "1" }, { "time": 74016, "beat": "3" }, { "time": 74336, "beat": "2" }, { "time": 74667, "beat": "3" }, { "time": 74987, "beat": "3" }, { "time": 75296, "beat": "1" }, { "time": 75605, "beat": "3" }, { "time": 75915, "beat": "3" }, { "time": 76224, "beat": "3" }, { "time": 76549, "beat": "3" }, { "time": 76853, "beat": "3" }, { "time": 77157, "beat": "3" }, { "time": 77461, "beat": "3" }, { "time": 77776, "beat": "1" }, { "time": 78107, "beat": "2" }, { "time": 78421, "beat": "2" }, { "time": 78731, "beat": "2" }, { "time": 79045, "beat": "2" }, { "time": 79365, "beat": "2" }, { "time": 79669, "beat": "1" }, { "time": 79995, "beat": "1" }, { "time": 80309, "beat": "1" }, { "time": 80640, "beat": "1" }, { "time": 80965, "beat": "1" }, { "time": 81296, "beat": "1" }, { "time": 81856, "beat": "1" }, { "time": 82165, "beat": "2" }, { "time": 82491, "beat": "2" }, { "time": 82827, "beat": "2" }, { "time": 83136, "beat": "2" }, { "time": 83451, "beat": "2" }, { "time": 83765, "beat": "1" }, { "time": 84080, "beat": "1" }, { "time": 84389, "beat": "1" }, { "time": 84704, "beat": "1" }, { "time": 85040, "beat": "1" }, { "time": 85392, "beat": "2" }, { "time": 85696, "beat": "1" }, { "time": 86016, "beat": "1" }, { "time": 86325, "beat": "1" }, { "time": 86688, "beat": "1" }, { "time": 87019, "beat": "2" }, { "time": 87323, "beat": "2" }, { "time": 87643, "beat": "1" }, { "time": 87947, "beat": "1" }, { "time": 88272, "beat": "1" }, { "time": 88587, "beat": "1" }, { "time": 88901, "beat": "1" }, { "time": 89216, "beat": "1" }, { "time": 89520, "beat": "1" }, { "time": 89872, "beat": "1" }, { "time": 90176, "beat": "2" }, { "time": 90485, "beat": "2" }, { "time": 90800, "beat": "2" }, { "time": 91115, "beat": "1" }, { "time": 91424, "beat": "1" }, { "time": 91819, "beat": "1" }, { "time": 92304, "beat": "1" }, { "time": 92667, "beat": "1" }, { "time": 92992, "beat": "2" }, { "time": 93301, "beat": "3" }, { "time": 93659, "beat": "1" }, { "time": 93968, "beat": "3" }, { "time": 94272, "beat": "3" }, { "time": 94581, "beat": "2" }, { "time": 94885, "beat": "3" }, { "time": 95205, "beat": "3" }, { "time": 95525, "beat": "2" }, { "time": 95845, "beat": "3" }, { "time": 96155, "beat": "1" }, { "time": 96485, "beat": "2" }, { "time": 96811, "beat": "3" }, { "time": 97125, "beat": "3" }, { "time": 97451, "beat": "2" }, { "time": 97755, "beat": "3" }, { "time": 98069, "beat": "3" }, { "time": 98411, "beat": "2" }, { "time": 98725, "beat": "3" }, { "time": 99035, "beat": "3" }, { "time": 99349, "beat": "3" }, { "time": 99680, "beat": "1" }, { "time": 100000, "beat": "2" }, { "time": 100304, "beat": "3" }, { "time": 100624, "beat": "3" }, { "time": 100928, "beat": "3" }, { "time": 101232, "beat": "3" }, { "time": 101557, "beat": "3" }, { "time": 101888, "beat": "2" }, { "time": 102192, "beat": "3" }, { "time": 102507, "beat": "3" }, { "time": 102853, "beat": "3" }, { "time": 103157, "beat": "3" }, { "time": 103461, "beat": "3" }, { "time": 103765, "beat": "2" }, { "time": 104085, "beat": "1" }, { "time": 104395, "beat": "1" }, { "time": 104709, "beat": "2" }, { "time": 105013, "beat": "3" }, { "time": 105317, "beat": "2" }, { "time": 105627, "beat": "1" }, { "time": 105931, "beat": "3" }, { "time": 106240, "beat": "2" }, { "time": 106555, "beat": "3" }, { "time": 106864, "beat": "3" }, { "time": 107168, "beat": "3" }, { "time": 107472, "beat": "1" }, { "time": 107781, "beat": "2" }, { "time": 108091, "beat": "3" }, { "time": 108416, "beat": "2" }, { "time": 108731, "beat": "2" }, { "time": 109045, "beat": "2" }, { "time": 109349, "beat": "3" }, { "time": 109669, "beat": "2" }, { "time": 109979, "beat": "2" }, { "time": 110293, "beat": "3" }, { "time": 110597, "beat": "3" }, { "time": 110917, "beat": "3" }, { "time": 111221, "beat": "3" }, { "time": 111525, "beat": "3" }, { "time": 111840, "beat": "2" }, { "time": 112144, "beat": "3" }, { "time": 112459, "beat": "3" }, { "time": 112773, "beat": "3" }, { "time": 113093, "beat": "3" }, { "time": 113419, "beat": "3" }, { "time": 113723, "beat": "3" }, { "time": 114032, "beat": "2" }, { "time": 114336, "beat": "2" }, { "time": 114651, "beat": "3" }, { "time": 114965, "beat": "1" }, { "time": 115280, "beat": "2" }, { "time": 115584, "beat": "3" }, { "time": 115888, "beat": "3" }, { "time": 116208, "beat": "2" }, { "time": 116523, "beat": "3" }, { "time": 116837, "beat": "2" }, { "time": 117147, "beat": "3" }, { "time": 117451, "beat": "3" }, { "time": 117787, "beat": "3" }, { "time": 118101, "beat": "3" }, { "time": 118405, "beat": "3" }, { "time": 118741, "beat": "2" }, { "time": 119051, "beat": "2" }, { "time": 119360, "beat": "1" }, { "time": 119664, "beat": "2" }, { "time": 119973, "beat": "3" }, { "time": 120283, "beat": "2" }, { "time": 120603, "beat": "3" }, { "time": 120912, "beat": "3" }, { "time": 121216, "beat": "3" }, { "time": 121525, "beat": "3" }, { "time": 121829, "beat": "3" }, { "time": 122293, "beat": "1" }, { "time": 122779, "beat": "1" }, { "time": 123099, "beat": "1" }, { "time": 123504, "beat": "1" }, { "time": 123845, "beat": "1" }, { "time": 124352, "beat": "1" }, { "time": 124699, "beat": "1" }, { "time": 125024, "beat": "1" }, { "time": 125333, "beat": "1" }, { "time": 125648, "beat": "1" }, { "time": 125979, "beat": "1" }, { "time": 126325, "beat": "1" }, { "time": 126635, "beat": "1" }, { "time": 126960, "beat": "1" }, { "time": 127275, "beat": "1" }, { "time": 127589, "beat": "1" }, { "time": 128059, "beat": "1" }, { "time": 128523, "beat": "1" }, { "time": 128837, "beat": "2" }, { "time": 129173, "beat": "1" }, { "time": 129483, "beat": "2" }, { "time": 129813, "beat": "2" }, { "time": 130133, "beat": "1" }, { "time": 130437, "beat": "1" }, { "time": 130741, "beat": "1" }, { "time": 131109, "beat": "1" }, { "time": 131413, "beat": "1" }, { "time": 131739, "beat": "2" }, { "time": 132080, "beat": "2" }, { "time": 132384, "beat": "2" }, { "time": 132784, "beat": "1" }, { "time": 133093, "beat": "1" }, { "time": 133408, "beat": "2" }, { "time": 133739, "beat": "1" }, { "time": 134069, "beat": "1" }, { "time": 134389, "beat": "1" }, { "time": 134709, "beat": "1" }, { "time": 135019, "beat": "1" }, { "time": 135355, "beat": "1" }, { "time": 135733, "beat": "1" }, { "time": 136064, "beat": "2" }, { "time": 136384, "beat": "3" }, { "time": 136693, "beat": "1" }, { "time": 137024, "beat": "2" }, { "time": 137339, "beat": "2" }, { "time": 137776, "beat": "1" }, { "time": 138261, "beat": "1" }, { "time": 138741, "beat": "1" }, { "time": 139109, "beat": "1" }, { "time": 139435, "beat": "1" }, { "time": 139744, "beat": "2" }, { "time": 140048, "beat": "2" }, { "time": 140352, "beat": "3" }, { "time": 140656, "beat": "3" }, { "time": 140987, "beat": "2" }, { "time": 141291, "beat": "3" }, { "time": 141605, "beat": "3" }, { "time": 141909, "beat": "3" }, { "time": 142251, "beat": "1" }, { "time": 142555, "beat": "1" }, { "time": 142859, "beat": "3" }, { "time": 143184, "beat": "3" }, { "time": 143504, "beat": "3" }, { "time": 143813, "beat": "2" }, { "time": 144123, "beat": "3" }, { "time": 144427, "beat": "2" }, { "time": 144741, "beat": "3" }, { "time": 145061, "beat": "3" }, { "time": 145365, "beat": "3" }, { "time": 145685, "beat": "3" }, { "time": 146000, "beat": "3" }, { "time": 146309, "beat": "2" }, { "time": 146624, "beat": "3" }, { "time": 146928, "beat": "2" }, { "time": 147237, "beat": "2" }, { "time": 147547, "beat": "3" }, { "time": 147851, "beat": "2" }, { "time": 148165, "beat": "3" }, { "time": 148480, "beat": "3" }, { "time": 148784, "beat": "2" }, { "time": 149104, "beat": "3" }, { "time": 149413, "beat": "3" }, { "time": 149717, "beat": "3" }, { "time": 150032, "beat": "2" }, { "time": 150336, "beat": "2" }, { "time": 150651, "beat": "2" }, { "time": 150971, "beat": "3" }, { "time": 151285, "beat": "2" }, { "time": 151600, "beat": "3" }, { "time": 151904, "beat": "3" }, { "time": 152208, "beat": "3" }, { "time": 152517, "beat": "3" }, { "time": 152821, "beat": "3" }, { "time": 153248, "beat": "1" }, { "time": 153728, "beat": "1" }, { "time": 154208, "beat": "1" }, { "time": 154576, "beat": "1" }] }]; var isDebug = false; var globalSpeed = 20; var currentRotationAngle = 0; var fullLog = []; var fpsText; var lastTick; var frameCount; var debugText; // Removed drag-related variables - using tap controls now var worldManager; var backgroundManager; var gateManager; var targetManager; var ball; var runner; var speakerManager; var noteSparks; var minDetectionScale = 0.4; var maxDetectionScale = 0.6; var borderLimitAngle = Math.PI * 0.08; var gateLimitAngle = Math.PI * 0.2; var gateUniqueId = 0; var songStarted = false; // Define three fixed positions: left, center, right var leftAngle = -Math.PI * 0.5 + gateLimitAngle; var centerAngle = -Math.PI * 0.5 + Math.PI / 2; var rightAngle = -Math.PI * 0.5 + Math.PI - gateLimitAngle; // Function to get next gate ID function getNextGateId() { return gateUniqueId++; } // Function to play hit sound with protection against multiple simultaneous plays var lastHitSoundTime = 0; var hitSoundCooldown = 100; // Minimum time between hit sounds in milliseconds var skipBeatDelay = 900; var lastBeatTime = 0; function playHitSound() { var now = Date.now(); if (now - lastHitSoundTime >= hitSoundCooldown) { LK.getSound('hit').play(); lastHitSoundTime = now; } } /***********************************************************************************/ /***************************** GAME INITIALIZATION *********************************/ /***********************************************************************************/ function gameInitialize() { // Initialize WorldManager for the furthest background layer (behind tunnel effect) worldManager = new WorldManager(); game.addChild(worldManager); // Initialize background manager for tunnel effect backgroundManager = new BackgroundManager(); game.addChild(backgroundManager); // Initialize runner at center position //updateSnapPosition(snapPositions.center); // Initialize gate manager gateManager = new GateManager(); game.addChild(gateManager); // Initialize speaker manager speakerManager = new SpeakerManager(); game.addChild(speakerManager); // Initialize note sparks (but don't enable spawning yet) noteSparks = new NoteSparks(); game.addChild(noteSparks); // Create and position ball ball = new Ball(); ball.x = 1024; ball.y = 2000; ball.alpha = true; game.addChild(ball); runner = new Runner(); runner.x = 1024; runner.y = 2000; game.addChild(runner); // Create start button if song hasn't started if (!songStarted) { var startButton = new StartButton(); game.addChild(startButton); } if (isDebug) { var debugMarker = LK.getAsset('debugMarker', { anchorX: 0.5, anchorY: 0.5, x: 2048 * 0.5, y: 2732 / 2 }); game.addChild(debugMarker); fpsText = new Text2('FPS: 0', { size: 50, fill: 0xFFFFFF }); // Position FPS text at the bottom-right corner fpsText.anchor.set(1, 1); // Anchor to the bottom-right LK.gui.bottomRight.addChild(fpsText); // Update FPS display every second lastTick = Date.now(); frameCount = 0; debugText = new Text2('Debug Info', { size: 50, fill: 0xFFFFFF }); debugText.anchor.set(0.5, 0); // Anchor to the bottom-right LK.gui.top.addChild(debugText); // Create sound test button var soundTestButton = new Container(); var buttonBg = LK.getAsset('line', { anchorX: 0.5, anchorY: 0.5, scaleX: 50, scaleY: 15, tint: 0x333333 }); soundTestButton.addChild(buttonBg); var buttonText = new Text2('SOUND TEST', { size: 40, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); soundTestButton.addChild(buttonText); // Position at top right soundTestButton.x = -100; soundTestButton.y = 50; LK.gui.topRight.addChild(soundTestButton); // Add click handler soundTestButton.down = function () { // Play all key sounds with 600ms delay for (var i = 0; i <= 14; i++) { (function (index) { LK.setTimeout(function () { LK.getSound('key' + index).play(); }, index * 600); })(i); } }; } } /***********************************************************************************/ /******************************** MAIN GAME LOOP ***********************************/ /***********************************************************************************/ game.update = function () { if (isDebug) { // FPS var now = Date.now(); frameCount++; if (now - lastTick >= 1000) { // Update every second fpsText.setText('FPS: ' + frameCount); frameCount = 0; lastTick = now; } } }; // Define magnetic snap positions var snapPositions = { left: 0, center: 1, right: 2 }; // Current snap position (start at center) var currentSnapPosition = snapPositions.center; // Track last mouse position to detect transitions var lastMouseZone = 1; // Start at center zone // Function to update runner position based on snap function updateSnapPosition(snapPos) { currentSnapPosition = snapPos; // Use global angle definitions var targetAngle = centerAngle; if (snapPos === snapPositions.left) { targetAngle = leftAngle; } else if (snapPos === snapPositions.center) { targetAngle = centerAngle; } else if (snapPos === snapPositions.right) { targetAngle = rightAngle; } // Calculate position on the ellipse path var radiusX = 924; var radiusY = 634; // Calculate new position runner.x = centerX + radiusX * Math.cos(targetAngle + Math.PI * 0.5); runner.y = centerY + radiusY * Math.sin(targetAngle + Math.PI * 0.5); // Apply rotation based on position var rotationMap = { 0: -0.5, // left 1: 0, // center 2: 0.5 // right }; currentRotationAngle = rotationMap[snapPos] * Math.PI * 0.5; } // Function to animate runner to new snap position function animateToSnapPosition(snapPos, jump) { // Check if we need to pass through center (moving from left to right or right to left) var needsIntermediateStep = false; if (currentSnapPosition === snapPositions.left && snapPos === snapPositions.right || currentSnapPosition === snapPositions.right && snapPos === snapPositions.left) { needsIntermediateStep = true; } // If we need intermediate step, first move to center if (needsIntermediateStep && !jump) { // Use global angle definitions // Calculate position on the ellipse path var radiusX = 924; var radiusY = 634; // Calculate center position (intermediate step) var centerPosX = centerX + radiusX * Math.cos(centerAngle + Math.PI * 0.5); var centerPosY = centerY + radiusY * Math.sin(centerAngle + Math.PI * 0.5); // Animate to center first tween(runner, { x: centerPosX, y: centerPosY }, { duration: 100, easing: tween.easeInOut, onFinish: function onFinish() { // After reaching center, continue to final destination performSnapAnimation(snapPos, jump); } }); // Update rotation for center position currentRotationAngle = 0; } else { // Direct movement (no intermediate step needed) performSnapAnimation(snapPos, jump); } } // Helper function to perform the actual snap animation function performSnapAnimation(snapPos, jump) { currentSnapPosition = snapPos; // Use global angle definitions var targetAngle = centerAngle; if (snapPos === snapPositions.left) { targetAngle = rightAngle; } else if (snapPos === snapPositions.center) { targetAngle = centerAngle; } else if (snapPos === snapPositions.right) { targetAngle = leftAngle; } // Calculate position on the ellipse path var radiusX = 924; var radiusY = 634; // Calculate target position var targetX = centerX + radiusX * Math.cos(targetAngle + Math.PI * 0.5); var targetY = centerY + radiusY * Math.sin(targetAngle + Math.PI * 0.5); // Apply rotation based on position var rotationMap = { 0: -0.5, // left 1: 0, // center 2: 0.5 // right }; var targetRotation = rotationMap[snapPos] * Math.PI * 0.5; // Check if jump is requested if (jump) { // Jump to y=1200 first, then return to normal position tween(runner, { y: 1200 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { // After jump, animate back to target position tween(runner, { x: targetX, y: targetY }, { duration: 200, easing: tween.easeIn }); } }); } else { // Normal animation without jump tween(runner, { x: targetX, y: targetY }, { duration: 300, easing: tween.easeInOut }); } // Update rotation angle for continuous rotation currentRotationAngle = targetRotation; } // Add game event handlers for runner control game.down = function (x, y, obj) { // Don't process input if song hasn't started if (!songStarted) { return; } // Divide screen into thirds var screenWidth = 2048; var thirdWidth = screenWidth / 3; // Determine which zone was tapped var tapZone; if (x < thirdWidth) { tapZone = 0; // Left third } else if (x < thirdWidth * 2) { tapZone = 1; // Center third } else { tapZone = 2; // Right third } // Animate to the corresponding snap position based on tap if (tapZone === 0) { animateToSnapPosition(snapPositions.left); } else if (tapZone === 1) { animateToSnapPosition(snapPositions.center); } else { animateToSnapPosition(snapPositions.right); } // Update lastMouseZone to match the tapped zone lastMouseZone = tapZone; }; game.move = function (x, y, obj) { // Don't process input if song hasn't started if (!songStarted) { return; } // Divide screen into thirds var screenWidth = 2048; var thirdWidth = screenWidth / 3; // Determine which zone the mouse is in var currentZone; if (x < thirdWidth) { currentZone = 0; // Left third } else if (x < thirdWidth * 2) { currentZone = 1; // Center third } else { currentZone = 2; // Right third } // Only update position if we've moved to a different zone if (currentZone !== lastMouseZone) { // Animate to the corresponding snap position if (currentZone === 0) { animateToSnapPosition(snapPositions.left); } else if (currentZone === 1) { animateToSnapPosition(snapPositions.center); } else { animateToSnapPosition(snapPositions.right); } lastMouseZone = currentZone; } }; game.up = function (x, y, obj) { // Empty - no longer needed for mouse-based controls }; gameInitialize();
===================================================================
--- original.js
+++ change.js
@@ -861,34 +861,110 @@
/******************************* WORLD MANAGER CLASS *******************************/
/***********************************************************************************/
var WorldManager = Container.expand(function () {
var self = Container.call(this);
- // Width of the 'world01' asset is 2732, as defined in Assets.
- // centerY is a global variable (1366).
- var assetWidth = 2732;
+ // Constants for calculation
+ var assetWidth = 2732; // Width of 'world01' asset
+ var screenWidth = 2048; // Virtual screen width
+ var totalContentWidth = assetWidth * 2;
+ // Properties for shifting
+ self.x_center_view = -(totalContentWidth / 2 - screenWidth / 2);
+ self.shiftAmplitude = 200; // How many pixels to shift left/right from center view
+ self.shiftDurationOneWay = 15000; // 15 seconds for one leg of the shift (very slow)
+ self._isShifting = false; // Internal state to track if shifting sequence is active
+ // --- Shift methods (defined first as per guidelines) ---
+ self._animateToRight = function () {
+ // Check if we should continue (song playing and shifting enabled)
+ if (!songStarted || !self._isShifting) {
+ self._isShifting = false; // Ensure flag is clear
+ tween.stop(self, {
+ x: true
+ }); // Stop any x-tween on self
+ return;
+ }
+ var targetX = self.x_center_view + self.shiftAmplitude;
+ tween(self, {
+ x: targetX
+ }, {
+ duration: self.shiftDurationOneWay,
+ easing: tween.easeInOut,
+ // Smooth easing
+ onFinish: self._animateToLeft // Chain to the other direction
+ });
+ };
+ self._animateToLeft = function () {
+ if (!songStarted || !self._isShifting) {
+ self._isShifting = false;
+ tween.stop(self, {
+ x: true
+ });
+ return;
+ }
+ var targetX = self.x_center_view - self.shiftAmplitude;
+ tween(self, {
+ x: targetX
+ }, {
+ duration: self.shiftDurationOneWay,
+ easing: tween.easeInOut,
+ onFinish: self._animateToRight // Chain back
+ });
+ };
+ self._initiateShift = function () {
+ // This function is called once when shifting should start.
+ if (self._isShifting) return;
+ self._isShifting = true;
+ // Start the oscillation by moving towards one of the extremes from current position.
+ // Let's say, always try to move towards the right extreme first when initiating.
+ self._animateToRight();
+ };
+ // Update method: Called by the engine every frame
+ self.update = function () {
+ if (songStarted && !self._isShifting) {
+ // Song is playing, and we are not currently in a shift sequence. Start one.
+ self._initiateShift();
+ } else if (!songStarted && self._isShifting) {
+ // Song has stopped, but we were in a shift sequence. Stop it.
+ self._isShifting = false; // This flag will be checked by onFinish callbacks.
+ tween.stop(self, {
+ x: true
+ }); // Immediately stop any ongoing x-tween.
+ // Smoothly tween back to the default centered position.
+ tween(self, {
+ x: self.x_center_view
+ }, {
+ duration: 1000,
+ // 1 second to return
+ easing: tween.easeOut // Use easeOut for a gentle stop
+ });
+ }
+ };
+ // --- Constructor Logic (runs after method definitions) ---
+ // Set initial position for WorldManager itself. This affects where its children are seen.
+ self.x = self.x_center_view;
+ self.y = 0; // WorldManager is typically at y=0.
+ // Attach background assets. Their x,y are relative to WorldManager's origin.
self.bgLeft = self.attachAsset('world01', {
anchorX: 0.5,
anchorY: 0.5,
x: assetWidth / 2,
- // Position center of bgLeft at assetWidth/2 from WorldManager's origin
+ // Center of bgLeft is at assetWidth/2 from WM origin
y: centerY,
- // Vertically center on the game screen
+ // Vertically center on game screen using global centerY
scaleX: 1.0,
// Normal orientation
scaleY: 1.0
});
self.bgRight = self.attachAsset('world01', {
anchorX: 0.5,
anchorY: 0.5,
x: assetWidth / 2 + assetWidth,
- // Position center of bgRight next to bgLeft
+ // Center of bgRight is next to bgLeft
y: centerY,
- // Vertically center on the game screen
+ // Vertically centered using global centerY
scaleX: -1.0,
// Mirrored horizontally
scaleY: 1.0
});
- // No update method is needed for static backgrounds at this time.
return self;
});
/****
remove background
remove background
Futuristic speaker in the shape of a white orb. Face view
white video camera icon
landscape of a furturistic world by night
a white music note
white sparkles emiting from the center. back background
clean red-violet beam from above
button in the shape of a protorealistic holographic futuristc Rectangle . Front view.
above the clouds by a bright night, no visible moon Photorealistic
White Clef de sol
A 20 nodes straight metalic lock chain. High definition. In-Game asset. 2d. High contrast. No shadows
a closed metalic padlock. No visible key hole.
white menu icon
in white