User prompt
Add the floor back
User prompt
Stretch the floor to the bottom of the screen
User prompt
Add a scrolling backround
User prompt
Delete the doted line
User prompt
Make a dotted line between the two places you can tap
User prompt
Make it so that the longer you play for, the more enemies spawn
User prompt
Make it so that the more force you get the faster it goes
User prompt
Change the name of the shields counter to lives
User prompt
Move the force token counter to below the distance counter
User prompt
Add a force token counter
User prompt
Make the x wing’s lasers red and make the other lasers straight ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Fix it
User prompt
Please fix the bug: 'TypeError: null is not an object (evaluating 'obstacle.graphic.height')' in or related to this line: 'obstacle.y = Math.random() < 0.5 ? game.trenchTop + obstacle.graphic.height / 2 : game.trenchBottom - obstacle.graphic.height / 2;' Line Number: 360
User prompt
Add the assets
User prompt
Add the assets
User prompt
Add the assets
User prompt
Implement the gameplay to the assets ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Rebel Runner
Initial prompt
Star Wars game that only requires one input
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Collectible = Container.expand(function () { var self = Container.call(this); // Type determines appearance and effect self.type = ''; self.speed = 0; self.lastX = 0; self.lastIsColliding = false; self.graphic = null; self.init = function (type, speed) { self.type = type; self.speed = speed; // Create different visuals based on type if (type === 'forceToken') { self.graphic = self.attachAsset('forceToken', { anchorX: 0.5, anchorY: 0.5 }); } else if (type === 'shield') { self.graphic = self.attachAsset('shield', { anchorX: 0.5, anchorY: 0.5 }); } // Record initial position self.lastX = self.x; }; // Float animation self.update = function () { // Save last position self.lastX = self.x; // Move left self.x -= self.speed; // Floating animation self.y += Math.sin(LK.ticks * 0.1) * 0.5; self.graphic.rotation += 0.02; }; return self; }); var Obstacle = Container.expand(function () { var self = Container.call(this); // Type determines appearance and behavior self.type = ''; self.speed = 0; self.lastX = 0; self.lastY = 0; self.lastIsColliding = false; self.graphic = null; self.init = function (type, speed) { self.type = type; self.speed = speed; // Create different visuals based on type if (type === 'wall') { self.graphic = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5 }); } else if (type === 'laser') { self.graphic = self.attachAsset('laser', { anchorX: 0.5, anchorY: 0.5 }); self.graphic.rotation = Math.PI / 4; // 45 degrees } else if (type === 'tieFighter') { self.graphic = self.attachAsset('tieFighter', { anchorX: 0.5, anchorY: 0.5 }); } // Record initial position self.lastX = self.x; self.lastY = self.y; }; // Move obstacle self.update = function () { // Save last position self.lastX = self.x; self.lastY = self.y; // Move left self.x -= self.speed; // Special movement patterns based on type if (self.type === 'tieFighter') { // TIE fighters move in a slight wave pattern self.y += Math.sin(self.x * 0.01) * 2; } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); // Create X-wing graphic var xwingGraphic = self.attachAsset('xwing', { anchorX: 0.5, anchorY: 0.5 }); // Physics properties self.velocityY = 0; self.gravity = 0.5; self.jumpForce = -12; self.maxVelocity = 15; self.isJumping = false; self.isShielded = false; self.shieldTimer = 0; // Track last position for collision detection self.lastY = 0; self.lastIsColliding = false; // Shield visual effect var shieldGraphic = self.attachAsset('shield', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); // Apply physics and update position self.update = function () { // Save last position for collision detection self.lastY = self.y; // Apply gravity self.velocityY += self.gravity; // Cap terminal velocity if (self.velocityY > self.maxVelocity) { self.velocityY = self.maxVelocity; } // Update position self.y += self.velocityY; // Floor boundary if (self.y > game.trenchBottom - xwingGraphic.height / 2) { self.y = game.trenchBottom - xwingGraphic.height / 2; self.velocityY = 0; self.isJumping = false; } // Ceiling boundary if (self.y < game.trenchTop + xwingGraphic.height / 2) { self.y = game.trenchTop + xwingGraphic.height / 2; self.velocityY = 1; } // Visual tilt based on velocity (diving down = nose down, rising up = nose up) xwingGraphic.rotation = self.velocityY * 0.05; // Shield timer if (self.isShielded) { self.shieldTimer--; if (self.shieldTimer <= 0) { self.isShielded = false; shieldGraphic.alpha = 0; } } }; // Jump method when screen is tapped self.jump = function () { self.velocityY = self.jumpForce; self.isJumping = true; }; // Activate shield power-up self.activateShield = function (duration) { self.isShielded = true; self.shieldTimer = duration; shieldGraphic.alpha = 0.6; }; return self; }); var TrenchSegment = Container.expand(function () { var self = Container.call(this); self.speed = 0; self.lastX = 0; self.init = function (speed) { self.speed = speed; // Add walls var topWall = self.attachAsset('trenchWall', { anchorX: 0, anchorY: 0 }); var bottomWall = self.attachAsset('trenchWall', { anchorX: 0, anchorY: 1 }); // Position walls topWall.y = game.trenchTop; bottomWall.y = game.trenchBottom; self.lastX = self.x; }; self.update = function () { self.lastX = self.x; self.x -= self.speed; }; return self; }); /**** * Initialize Game ****/ // Game constants var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Game constants //Minimalistic tween library which should be used for animations over time // Initialize game assets var GAME_SPEED_INITIAL = 8; var GAME_SPEED_INCREMENT = 0.001; var OBSTACLE_SPAWN_RATE_INITIAL = 120; // frames var COLLECTIBLE_SPAWN_RATE = 180; // frames var TRENCH_WIDTH = 1600; var TRENCH_HEIGHT = 1400; // Game variables var gameSpeed = GAME_SPEED_INITIAL; var obstacleSpawnRate = OBSTACLE_SPAWN_RATE_INITIAL; var obstacleTimer = 0; var collectibleTimer = 0; var distance = 0; var lives = 3; var trenchSegments = []; var obstacles = []; var collectibles = []; var player = null; var isGameActive = true; // Set up trench boundaries game.trenchTop = (2732 - TRENCH_HEIGHT) / 2; game.trenchBottom = game.trenchTop + TRENCH_HEIGHT; game.trenchLeft = (2048 - TRENCH_WIDTH) / 2; game.trenchRight = game.trenchLeft + TRENCH_WIDTH; // Initialize trench floor var trenchFloor = LK.getAsset('trenchFloor', { anchorX: 0.5, anchorY: 0 }); trenchFloor.x = 2048 / 2; trenchFloor.y = game.trenchBottom; game.addChild(trenchFloor); // Create GUI elements var distanceText = new Text2('DISTANCE: 0', { size: 70, fill: 0xFFFFFF }); distanceText.anchor.set(0, 0); distanceText.x = 120; distanceText.y = 50; LK.gui.topLeft.addChild(distanceText); var livesText = new Text2('SHIELDS: 3', { size: 70, fill: 0xFFFFFF }); livesText.anchor.set(1, 0); livesText.x = -120; livesText.y = 50; LK.gui.topRight.addChild(livesText); // Create player (X-wing) player = new Player(); player.x = 400; player.y = (game.trenchTop + game.trenchBottom) / 2; game.addChild(player); // Initialize trench segments function createInitialTrench() { var segments = 3; // Number of initial trench segments var segmentWidth = 2048; // Width of each segment for (var i = 0; i < segments; i++) { var segment = new TrenchSegment(); segment.x = i * segmentWidth; segment.init(gameSpeed); trenchSegments.push(segment); game.addChild(segment); } } // Create a new obstacle function spawnObstacle() { var obstacle = new Obstacle(); // Random obstacle type var types = ['wall', 'laser', 'tieFighter']; var type = types[Math.floor(Math.random() * types.length)]; // Position at right side of screen obstacle.x = 2048 + 100; // Random vertical position within trench if (type === 'wall') { // Walls randomly appear at top or bottom obstacle.y = Math.random() < 0.5 ? game.trenchTop + obstacle.graphic.height / 2 : game.trenchBottom - obstacle.graphic.height / 2; } else { // Other obstacles can appear anywhere in trench obstacle.y = game.trenchTop + 150 + Math.random() * (TRENCH_HEIGHT - 300); } obstacle.init(type, gameSpeed); obstacles.push(obstacle); game.addChild(obstacle); } // Create a new collectible function spawnCollectible() { var collectible = new Collectible(); // Random collectible type (force tokens more common than shields) var type = Math.random() < 0.8 ? 'forceToken' : 'shield'; // Position at right side of screen collectible.x = 2048 + 100; // Random vertical position within trench collectible.y = game.trenchTop + 150 + Math.random() * (TRENCH_HEIGHT - 300); collectible.init(type, gameSpeed); collectibles.push(collectible); game.addChild(collectible); } // Update score and distance display function updateHUD() { var distanceKm = Math.floor(distance / 100); distanceText.setText('DISTANCE: ' + distanceKm + ' KM'); livesText.setText('SHIELDS: ' + lives); } // Handle player hit function playerHit() { if (player.isShielded) return; // Shield protects from damage lives--; livesText.setText('SHIELDS: ' + lives); // Flash player to indicate damage LK.effects.flashObject(player, 0xFF0000, 500); if (lives <= 0) { gameOver(); } } // Game over function gameOver() { isGameActive = false; LK.setScore(Math.floor(distance / 10)); LK.showGameOver(); } // Handle screen tap/click input game.down = function (x, y, obj) { if (isGameActive) { player.jump(); } }; // Main game update loop createInitialTrench(); game.update = function () { if (!isGameActive) return; // Increase game speed over time gameSpeed += GAME_SPEED_INCREMENT; // Update distance traveled distance += gameSpeed; // Update player player.update(); // Manage trench segments for (var i = trenchSegments.length - 1; i >= 0; i--) { var segment = trenchSegments[i]; segment.speed = gameSpeed; segment.update(); // If segment moves off screen, remove and create new one at the end if (segment.x + 2048 < 0) { segment.destroy(); trenchSegments.splice(i, 1); var newSegment = new TrenchSegment(); newSegment.x = trenchSegments[trenchSegments.length - 1].x + 2048; newSegment.init(gameSpeed); trenchSegments.push(newSegment); game.addChild(newSegment); } } // Manage obstacles obstacleTimer++; if (obstacleTimer >= obstacleSpawnRate) { spawnObstacle(); obstacleTimer = 0; // Gradually decrease spawn rate as game speed increases obstacleSpawnRate = Math.max(60, OBSTACLE_SPAWN_RATE_INITIAL - Math.floor(distance / 1000)); } for (var j = obstacles.length - 1; j >= 0; j--) { var obstacle = obstacles[j]; obstacle.speed = gameSpeed; obstacle.update(); // Check collision with player var isCollidingNow = obstacle.intersects(player); if (!obstacle.lastIsColliding && isCollidingNow) { playerHit(); LK.getSound('explosion').play(); // Remove obstacle after collision obstacle.destroy(); obstacles.splice(j, 1); continue; } obstacle.lastIsColliding = isCollidingNow; // Remove if off screen if (obstacle.x + 100 < 0) { obstacle.destroy(); obstacles.splice(j, 1); } } // Manage collectibles collectibleTimer++; if (collectibleTimer >= COLLECTIBLE_SPAWN_RATE) { spawnCollectible(); collectibleTimer = 0; } for (var k = collectibles.length - 1; k >= 0; k--) { var collectible = collectibles[k]; collectible.speed = gameSpeed; collectible.update(); // Check collision with player var isCollectingNow = collectible.intersects(player); if (!collectible.lastIsColliding && isCollectingNow) { // Apply effect based on type if (collectible.type === 'forceToken') { // Increase score LK.setScore(LK.getScore() + 10); tween(collectible.graphic, { alpha: 0 }, { duration: 300 }); LK.getSound('collect').play(); } else if (collectible.type === 'shield') { // Activate shield player.activateShield(600); // Shield lasts 10 seconds (600 frames) tween(collectible.graphic, { alpha: 0 }, { duration: 300 }); LK.getSound('collect').play(); } // Remove collectible collectible.destroy(); collectibles.splice(k, 1); continue; } collectible.lastIsColliding = isCollectingNow; // Remove if off screen if (collectible.x + 50 < 0) { collectible.destroy(); collectibles.splice(k, 1); } } // Update HUD updateHUD(); };
===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,437 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+
+/****
+* Classes
+****/
+var Collectible = Container.expand(function () {
+ var self = Container.call(this);
+ // Type determines appearance and effect
+ self.type = '';
+ self.speed = 0;
+ self.lastX = 0;
+ self.lastIsColliding = false;
+ self.graphic = null;
+ self.init = function (type, speed) {
+ self.type = type;
+ self.speed = speed;
+ // Create different visuals based on type
+ if (type === 'forceToken') {
+ self.graphic = self.attachAsset('forceToken', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ } else if (type === 'shield') {
+ self.graphic = self.attachAsset('shield', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ }
+ // Record initial position
+ self.lastX = self.x;
+ };
+ // Float animation
+ self.update = function () {
+ // Save last position
+ self.lastX = self.x;
+ // Move left
+ self.x -= self.speed;
+ // Floating animation
+ self.y += Math.sin(LK.ticks * 0.1) * 0.5;
+ self.graphic.rotation += 0.02;
+ };
+ return self;
+});
+var Obstacle = Container.expand(function () {
+ var self = Container.call(this);
+ // Type determines appearance and behavior
+ self.type = '';
+ self.speed = 0;
+ self.lastX = 0;
+ self.lastY = 0;
+ self.lastIsColliding = false;
+ self.graphic = null;
+ self.init = function (type, speed) {
+ self.type = type;
+ self.speed = speed;
+ // Create different visuals based on type
+ if (type === 'wall') {
+ self.graphic = self.attachAsset('obstacle', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ } else if (type === 'laser') {
+ self.graphic = self.attachAsset('laser', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.graphic.rotation = Math.PI / 4; // 45 degrees
+ } else if (type === 'tieFighter') {
+ self.graphic = self.attachAsset('tieFighter', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ }
+ // Record initial position
+ self.lastX = self.x;
+ self.lastY = self.y;
+ };
+ // Move obstacle
+ self.update = function () {
+ // Save last position
+ self.lastX = self.x;
+ self.lastY = self.y;
+ // Move left
+ self.x -= self.speed;
+ // Special movement patterns based on type
+ if (self.type === 'tieFighter') {
+ // TIE fighters move in a slight wave pattern
+ self.y += Math.sin(self.x * 0.01) * 2;
+ }
+ };
+ return self;
+});
+var Player = Container.expand(function () {
+ var self = Container.call(this);
+ // Create X-wing graphic
+ var xwingGraphic = self.attachAsset('xwing', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Physics properties
+ self.velocityY = 0;
+ self.gravity = 0.5;
+ self.jumpForce = -12;
+ self.maxVelocity = 15;
+ self.isJumping = false;
+ self.isShielded = false;
+ self.shieldTimer = 0;
+ // Track last position for collision detection
+ self.lastY = 0;
+ self.lastIsColliding = false;
+ // Shield visual effect
+ var shieldGraphic = self.attachAsset('shield', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0
+ });
+ // Apply physics and update position
+ self.update = function () {
+ // Save last position for collision detection
+ self.lastY = self.y;
+ // Apply gravity
+ self.velocityY += self.gravity;
+ // Cap terminal velocity
+ if (self.velocityY > self.maxVelocity) {
+ self.velocityY = self.maxVelocity;
+ }
+ // Update position
+ self.y += self.velocityY;
+ // Floor boundary
+ if (self.y > game.trenchBottom - xwingGraphic.height / 2) {
+ self.y = game.trenchBottom - xwingGraphic.height / 2;
+ self.velocityY = 0;
+ self.isJumping = false;
+ }
+ // Ceiling boundary
+ if (self.y < game.trenchTop + xwingGraphic.height / 2) {
+ self.y = game.trenchTop + xwingGraphic.height / 2;
+ self.velocityY = 1;
+ }
+ // Visual tilt based on velocity (diving down = nose down, rising up = nose up)
+ xwingGraphic.rotation = self.velocityY * 0.05;
+ // Shield timer
+ if (self.isShielded) {
+ self.shieldTimer--;
+ if (self.shieldTimer <= 0) {
+ self.isShielded = false;
+ shieldGraphic.alpha = 0;
+ }
+ }
+ };
+ // Jump method when screen is tapped
+ self.jump = function () {
+ self.velocityY = self.jumpForce;
+ self.isJumping = true;
+ };
+ // Activate shield power-up
+ self.activateShield = function (duration) {
+ self.isShielded = true;
+ self.shieldTimer = duration;
+ shieldGraphic.alpha = 0.6;
+ };
+ return self;
+});
+var TrenchSegment = Container.expand(function () {
+ var self = Container.call(this);
+ self.speed = 0;
+ self.lastX = 0;
+ self.init = function (speed) {
+ self.speed = speed;
+ // Add walls
+ var topWall = self.attachAsset('trenchWall', {
+ anchorX: 0,
+ anchorY: 0
+ });
+ var bottomWall = self.attachAsset('trenchWall', {
+ anchorX: 0,
+ anchorY: 1
+ });
+ // Position walls
+ topWall.y = game.trenchTop;
+ bottomWall.y = game.trenchBottom;
+ self.lastX = self.x;
+ };
+ self.update = function () {
+ self.lastX = self.x;
+ self.x -= self.speed;
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
+// Game constants
var game = new LK.Game({
backgroundColor: 0x000000
-});
\ No newline at end of file
+});
+
+/****
+* Game Code
+****/
+// Game constants
+//Minimalistic tween library which should be used for animations over time
+// Initialize game assets
+var GAME_SPEED_INITIAL = 8;
+var GAME_SPEED_INCREMENT = 0.001;
+var OBSTACLE_SPAWN_RATE_INITIAL = 120; // frames
+var COLLECTIBLE_SPAWN_RATE = 180; // frames
+var TRENCH_WIDTH = 1600;
+var TRENCH_HEIGHT = 1400;
+// Game variables
+var gameSpeed = GAME_SPEED_INITIAL;
+var obstacleSpawnRate = OBSTACLE_SPAWN_RATE_INITIAL;
+var obstacleTimer = 0;
+var collectibleTimer = 0;
+var distance = 0;
+var lives = 3;
+var trenchSegments = [];
+var obstacles = [];
+var collectibles = [];
+var player = null;
+var isGameActive = true;
+// Set up trench boundaries
+game.trenchTop = (2732 - TRENCH_HEIGHT) / 2;
+game.trenchBottom = game.trenchTop + TRENCH_HEIGHT;
+game.trenchLeft = (2048 - TRENCH_WIDTH) / 2;
+game.trenchRight = game.trenchLeft + TRENCH_WIDTH;
+// Initialize trench floor
+var trenchFloor = LK.getAsset('trenchFloor', {
+ anchorX: 0.5,
+ anchorY: 0
+});
+trenchFloor.x = 2048 / 2;
+trenchFloor.y = game.trenchBottom;
+game.addChild(trenchFloor);
+// Create GUI elements
+var distanceText = new Text2('DISTANCE: 0', {
+ size: 70,
+ fill: 0xFFFFFF
+});
+distanceText.anchor.set(0, 0);
+distanceText.x = 120;
+distanceText.y = 50;
+LK.gui.topLeft.addChild(distanceText);
+var livesText = new Text2('SHIELDS: 3', {
+ size: 70,
+ fill: 0xFFFFFF
+});
+livesText.anchor.set(1, 0);
+livesText.x = -120;
+livesText.y = 50;
+LK.gui.topRight.addChild(livesText);
+// Create player (X-wing)
+player = new Player();
+player.x = 400;
+player.y = (game.trenchTop + game.trenchBottom) / 2;
+game.addChild(player);
+// Initialize trench segments
+function createInitialTrench() {
+ var segments = 3; // Number of initial trench segments
+ var segmentWidth = 2048; // Width of each segment
+ for (var i = 0; i < segments; i++) {
+ var segment = new TrenchSegment();
+ segment.x = i * segmentWidth;
+ segment.init(gameSpeed);
+ trenchSegments.push(segment);
+ game.addChild(segment);
+ }
+}
+// Create a new obstacle
+function spawnObstacle() {
+ var obstacle = new Obstacle();
+ // Random obstacle type
+ var types = ['wall', 'laser', 'tieFighter'];
+ var type = types[Math.floor(Math.random() * types.length)];
+ // Position at right side of screen
+ obstacle.x = 2048 + 100;
+ // Random vertical position within trench
+ if (type === 'wall') {
+ // Walls randomly appear at top or bottom
+ obstacle.y = Math.random() < 0.5 ? game.trenchTop + obstacle.graphic.height / 2 : game.trenchBottom - obstacle.graphic.height / 2;
+ } else {
+ // Other obstacles can appear anywhere in trench
+ obstacle.y = game.trenchTop + 150 + Math.random() * (TRENCH_HEIGHT - 300);
+ }
+ obstacle.init(type, gameSpeed);
+ obstacles.push(obstacle);
+ game.addChild(obstacle);
+}
+// Create a new collectible
+function spawnCollectible() {
+ var collectible = new Collectible();
+ // Random collectible type (force tokens more common than shields)
+ var type = Math.random() < 0.8 ? 'forceToken' : 'shield';
+ // Position at right side of screen
+ collectible.x = 2048 + 100;
+ // Random vertical position within trench
+ collectible.y = game.trenchTop + 150 + Math.random() * (TRENCH_HEIGHT - 300);
+ collectible.init(type, gameSpeed);
+ collectibles.push(collectible);
+ game.addChild(collectible);
+}
+// Update score and distance display
+function updateHUD() {
+ var distanceKm = Math.floor(distance / 100);
+ distanceText.setText('DISTANCE: ' + distanceKm + ' KM');
+ livesText.setText('SHIELDS: ' + lives);
+}
+// Handle player hit
+function playerHit() {
+ if (player.isShielded) return; // Shield protects from damage
+ lives--;
+ livesText.setText('SHIELDS: ' + lives);
+ // Flash player to indicate damage
+ LK.effects.flashObject(player, 0xFF0000, 500);
+ if (lives <= 0) {
+ gameOver();
+ }
+}
+// Game over
+function gameOver() {
+ isGameActive = false;
+ LK.setScore(Math.floor(distance / 10));
+ LK.showGameOver();
+}
+// Handle screen tap/click input
+game.down = function (x, y, obj) {
+ if (isGameActive) {
+ player.jump();
+ }
+};
+// Main game update loop
+createInitialTrench();
+game.update = function () {
+ if (!isGameActive) return;
+ // Increase game speed over time
+ gameSpeed += GAME_SPEED_INCREMENT;
+ // Update distance traveled
+ distance += gameSpeed;
+ // Update player
+ player.update();
+ // Manage trench segments
+ for (var i = trenchSegments.length - 1; i >= 0; i--) {
+ var segment = trenchSegments[i];
+ segment.speed = gameSpeed;
+ segment.update();
+ // If segment moves off screen, remove and create new one at the end
+ if (segment.x + 2048 < 0) {
+ segment.destroy();
+ trenchSegments.splice(i, 1);
+ var newSegment = new TrenchSegment();
+ newSegment.x = trenchSegments[trenchSegments.length - 1].x + 2048;
+ newSegment.init(gameSpeed);
+ trenchSegments.push(newSegment);
+ game.addChild(newSegment);
+ }
+ }
+ // Manage obstacles
+ obstacleTimer++;
+ if (obstacleTimer >= obstacleSpawnRate) {
+ spawnObstacle();
+ obstacleTimer = 0;
+ // Gradually decrease spawn rate as game speed increases
+ obstacleSpawnRate = Math.max(60, OBSTACLE_SPAWN_RATE_INITIAL - Math.floor(distance / 1000));
+ }
+ for (var j = obstacles.length - 1; j >= 0; j--) {
+ var obstacle = obstacles[j];
+ obstacle.speed = gameSpeed;
+ obstacle.update();
+ // Check collision with player
+ var isCollidingNow = obstacle.intersects(player);
+ if (!obstacle.lastIsColliding && isCollidingNow) {
+ playerHit();
+ LK.getSound('explosion').play();
+ // Remove obstacle after collision
+ obstacle.destroy();
+ obstacles.splice(j, 1);
+ continue;
+ }
+ obstacle.lastIsColliding = isCollidingNow;
+ // Remove if off screen
+ if (obstacle.x + 100 < 0) {
+ obstacle.destroy();
+ obstacles.splice(j, 1);
+ }
+ }
+ // Manage collectibles
+ collectibleTimer++;
+ if (collectibleTimer >= COLLECTIBLE_SPAWN_RATE) {
+ spawnCollectible();
+ collectibleTimer = 0;
+ }
+ for (var k = collectibles.length - 1; k >= 0; k--) {
+ var collectible = collectibles[k];
+ collectible.speed = gameSpeed;
+ collectible.update();
+ // Check collision with player
+ var isCollectingNow = collectible.intersects(player);
+ if (!collectible.lastIsColliding && isCollectingNow) {
+ // Apply effect based on type
+ if (collectible.type === 'forceToken') {
+ // Increase score
+ LK.setScore(LK.getScore() + 10);
+ tween(collectible.graphic, {
+ alpha: 0
+ }, {
+ duration: 300
+ });
+ LK.getSound('collect').play();
+ } else if (collectible.type === 'shield') {
+ // Activate shield
+ player.activateShield(600); // Shield lasts 10 seconds (600 frames)
+ tween(collectible.graphic, {
+ alpha: 0
+ }, {
+ duration: 300
+ });
+ LK.getSound('collect').play();
+ }
+ // Remove collectible
+ collectible.destroy();
+ collectibles.splice(k, 1);
+ continue;
+ }
+ collectible.lastIsColliding = isCollectingNow;
+ // Remove if off screen
+ if (collectible.x + 50 < 0) {
+ collectible.destroy();
+ collectibles.splice(k, 1);
+ }
+ }
+ // Update HUD
+ updateHUD();
+};
\ No newline at end of file