User prompt
The game does not end after 10 laps. The game should end when the flag is passed. And at the start, the cars should line up in order, like in a Formula 1 race. At the start of the game, five traffic lights should appear horizontally on the screen and turn from red to green in sequence. When the fifth light turns green, the word “START” should appear and the game should begin. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
There should be no red effect when hitting cars. There should be no green effect when passing. The finish line should be in the shape of a checkered flag and have poles. The race should end when passing that point.
User prompt
I want the race to have 8 cars and start in order. And I want the edges of the background to look like a real track.
User prompt
Let the race be 10 laps and be like a ranking. Don't let the green light come on when you pass your opponent. Write the result when the race is over.
User prompt
ı want to f1 races and 10 bot player.
Code edit (1 edits merged)
Please save this source code
User prompt
Space Defender
Initial prompt
hi. I want to spaceshooter game.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var BotCar = Container.expand(function (carIndex) { var self = Container.call(this); // Use different car assets based on car index var carAssetName = 'botCar' + (carIndex % 8 + 1); var carGraphics = self.attachAsset(carAssetName, { anchorX: 0.5, anchorY: 0.5 }); // Create more varied bot speeds based on car index for distinct racing styles var speedVariations = [14, // Fast aggressive bot 11, // Medium-fast bot 16, // Very fast bot 9, // Slower cautious bot 13, // Medium bot 18, // Fastest bot 8, // Slowest bot 15 // Fast bot ]; self.baseSpeed = speedVariations[carIndex % 8]; // Assign specific speed based on car index self.currentSpeed = 0; // Start stationary until race begins self.lanePosition = Math.floor(Math.random() * 3); // 0=left, 1=center, 2=right self.targetX = 400 + self.lanePosition * 600; // Lane positions self.aiTimer = 0; self.carNumber = carIndex + 1; self.isChangingLanes = false; // Track lane changing state self.collisionCooldown = 0; // Prevent frequent collisions // Bot cars now use their default asset colors without tinting self.lapCount = 1; self.raceDistance = 0; self.hasFinished = false; self.update = function () { // Only move if race has started if (!raceStarted) { return; } // AI behavior - change lanes occasionally self.aiTimer++; if (self.aiTimer > 120 && Math.random() < 0.03 && !self.isChangingLanes) { // Every 2 seconds, 3% chance, only if not already changing lanes self.aiTimer = 0; var newLane = Math.floor(Math.random() * 3); if (newLane !== self.lanePosition) { self.lanePosition = newLane; self.targetX = 400 + self.lanePosition * 600; self.isChangingLanes = true; // Smooth lane change with tween and rotation var direction = self.targetX > self.x ? 1 : -1; // Add slight rotation during lane change for stability (reduced rotation) tween(carGraphics, { rotation: direction * 0.05 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { // Return to straight after lane change (faster return) tween(carGraphics, { rotation: 0 }, { duration: 150, easing: tween.easeInOut }); } }); // Smooth X movement (faster lane change) tween(self, { x: self.targetX }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { self.isChangingLanes = false; } }); } } // Update collision cooldown if (self.collisionCooldown > 0) { self.collisionCooldown--; } // Check for collision with other bot cars before moving var canMove = true; var needsLaneChange = false; for (var b = 0; b < botCars.length; b++) { var otherBot = botCars[b]; if (otherBot !== self && otherBot.intersects) { var distanceX = Math.abs(otherBot.x - self.x); var distanceY = Math.abs(otherBot.y - self.y); // Direct collision detection with cooldown if (otherBot.intersects(self) && self.collisionCooldown <= 0) { // Set collision cooldown to prevent frequent collisions self.collisionCooldown = 60; // 1 second cooldown otherBot.collisionCooldown = 60; // Reduce speed less drastically self.currentSpeed = Math.max(4, self.currentSpeed - 2); otherBot.currentSpeed = Math.max(4, otherBot.currentSpeed - 2); // Push cars apart more gently if (self.x < otherBot.x) { self.x -= 30; otherBot.x += 30; } else { self.x += 30; otherBot.x -= 30; } canMove = false; } // Improved collision avoidance - larger detection zone else if (distanceX < 250 && distanceY < 500) { // Bot is getting close, take evasive action if (otherBot.y < self.y && distanceY < 350) { // Bot ahead, slow down and consider lane change self.currentSpeed = Math.max(6, self.currentSpeed - 1); needsLaneChange = true; canMove = false; } else if (otherBot.y > self.y && distanceY < 300) { // Bot behind is catching up, speed up slightly self.currentSpeed = Math.min(self.baseSpeed + 2, self.currentSpeed + 0.5); } } } } // Trigger lane change more aggressively when needed if (needsLaneChange && !self.isChangingLanes && Math.random() < 0.15) { // 15% chance to change lanes when avoiding collision var availableLanes = []; for (var lane = 0; lane < 3; lane++) { if (lane !== self.lanePosition) { availableLanes.push(lane); } } if (availableLanes.length > 0) { var newLane = availableLanes[Math.floor(Math.random() * availableLanes.length)]; self.lanePosition = newLane; self.targetX = 400 + self.lanePosition * 600; self.isChangingLanes = true; // Smooth lane change with tween and rotation var direction = self.targetX > self.x ? 1 : -1; // Add slight rotation during lane change for stability (reduced rotation) tween(carGraphics, { rotation: direction * 0.05 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { // Return to straight after lane change (faster return) tween(carGraphics, { rotation: 0 }, { duration: 150, easing: tween.easeInOut }); } }); // Smooth X movement (faster lane change) tween(self, { x: self.targetX }, { duration: 300, // Faster lane change for collision avoidance easing: tween.easeInOut, onFinish: function onFinish() { self.isChangingLanes = false; } }); } } // Move forward (relative to player) - faster movement if (canMove) { self.y += raceSpeed - self.currentSpeed; } else { self.y += raceSpeed - self.currentSpeed * 0.5; // Move slower when blocked } // Keep within screen bounds if (self.x < 100) { self.x = 100; } if (self.x > 1948) { self.x = 1948; } // Track bot lap progress if (!self.hasFinished) { self.raceDistance += self.currentSpeed; if (self.raceDistance >= lapDistance) { self.raceDistance = 0; self.lapCount++; // Finish after completing 10 laps if (self.lapCount > 10) { self.hasFinished = true; botsFinished++; } } } }; return self; }); var FinishLine = Container.expand(function () { var self = Container.call(this); // Create left pole var leftPole = self.attachAsset('finishPole', { anchorX: 0.5, anchorY: 1 }); leftPole.x = -800; leftPole.y = 0; // Create right pole var rightPole = self.attachAsset('finishPole', { anchorX: 0.5, anchorY: 1 }); rightPole.x = 800; rightPole.y = 0; // Create checkered pattern for (var i = 0; i < 16; i++) { for (var j = 0; j < 2; j++) { var isBlack = (i + j) % 2 === 0; var square = self.attachAsset(isBlack ? 'checkeredSquare1' : 'checkeredSquare2', { anchorX: 0.5, anchorY: 0.5 }); square.x = (i - 7.5) * 100; square.y = (j - 0.5) * 40; } } self.update = function () { self.y += raceSpeed; }; return self; }); var GameMenu = Container.expand(function () { var self = Container.call(this); // Menu background var menuBg = self.attachAsset('menuBackground', { anchorX: 0.5, anchorY: 0.5 }); menuBg.alpha = 0.95; // Menu panel var menuPanel = self.attachAsset('menuPanel', { anchorX: 0.5, anchorY: 0.5 }); menuPanel.y = -100; // Title var titleText = new Text2('RACING CHAMPIONSHIP', { size: 70, fill: 0xFFD700 }); titleText.anchor.set(0.5, 0.5); titleText.y = -600; self.addChild(titleText); // Instructions var instructionText = new Text2('Drag to steer your car\nComplete 10 laps to win!\nAvoid barriers and other cars', { size: 40, fill: 0xFFFFFF, align: 'center' }); instructionText.anchor.set(0.5, 0.5); instructionText.y = -400; self.addChild(instructionText); // Car selection display self.selectedCarIndex = 0; var carPreview = self.attachAsset('playerCar', { anchorX: 0.5, anchorY: 0.5 }); carPreview.y = -150; carPreview.scaleX = 0.8; carPreview.scaleY = 0.8; var carLabel = new Text2('Your Car', { size: 35, fill: 0x00FF00 }); carLabel.anchor.set(0.5, 0.5); carLabel.y = -50; self.addChild(carLabel); // Start button var startButton = new MenuButton('start', 'START RACE', function () { self.startRace(); }); startButton.y = 200; self.addChild(startButton); // Best time display (placeholder) var bestTimeText = new Text2('Best Position: Not Set', { size: 30, fill: 0xCCCCCC }); bestTimeText.anchor.set(0.5, 0.5); bestTimeText.y = 350; self.addChild(bestTimeText); self.startRace = function () { // Hide menu with animation tween(self, { alpha: 0, y: self.y - 200 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { self.destroy(); // Start the race countdown LK.setTimeout(function () { raceStartSequence = true; }, 500); } }); }; return self; }); var MenuButton = Container.expand(function (buttonType, text, onClick) { var self = Container.call(this); var buttonAsset = buttonType === 'start' ? 'startButton' : 'carSelectButton'; var buttonGraphics = self.attachAsset(buttonAsset, { anchorX: 0.5, anchorY: 0.5 }); var buttonText = new Text2(text, { size: buttonType === 'start' ? 50 : 35, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); self.addChild(buttonText); self.onClick = onClick; self.down = function (x, y, obj) { // Button press effect tween(buttonGraphics, { scaleX: 0.95, scaleY: 0.95 }, { duration: 100, easing: tween.easeOut }); if (self.onClick) { self.onClick(); } }; self.up = function (x, y, obj) { // Button release effect tween(buttonGraphics, { scaleX: 1.0, scaleY: 1.0 }, { duration: 100, easing: tween.easeOut }); }; return self; }); var PlayerCar = Container.expand(function () { var self = Container.call(this); var carGraphics = self.attachAsset('playerCar', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.maxSpeed = 350; self.acceleration = 0.2; self.currentSpeed = 0; self.update = function () { // Simulate acceleration/deceleration based on movement if (Math.abs(self.lastX - self.x) > 5) { self.currentSpeed = Math.min(self.maxSpeed, self.currentSpeed + self.acceleration); } else { self.currentSpeed = Math.max(0, self.currentSpeed - self.acceleration * 0.5); } self.lastX = self.x; }; return self; }); var RoadLine = Container.expand(function () { var self = Container.call(this); var lineGraphics = self.attachAsset('roadLine', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.update = function () { self.y += raceSpeed; }; return self; }); var TrackEdge = Container.expand(function (barrierIndex) { var self = Container.call(this); // Use different barrier assets based on index (1-5) var barrierAssetName = 'trackBarrier' + (barrierIndex % 5 + 1); var edgeGraphics = self.attachAsset(barrierAssetName, { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { self.y += raceSpeed; }; return self; }); var TrafficLight = Container.expand(function () { var self = Container.call(this); // Create larger frame background (5x bigger to fit 5 lights) var lightBackground = self.attachAsset('trafficLight', { anchorX: 0.5, anchorY: 0.5 }); lightBackground.scaleX = 5; lightBackground.scaleY = 3; // Create 5 lights in a horizontal row self.lights = []; for (var i = 0; i < 5; i++) { var light = self.attachAsset('lightRed', { anchorX: 0.5, anchorY: 0.5 }); light.scaleX = 2; light.scaleY = 2; light.x = (i - 2) * 160; // Space lights 160 pixels apart so they barely touch (80px radius * 2 scale * 2 = 160px diameter) light.alpha = 1.0; light.tint = 0xff0000; // Start all red self.lights.push(light); } self.setAllRed = function () { for (var i = 0; i < self.lights.length; i++) { self.lights[i].tint = 0xff0000; // Red color // Add glow effect with tween tween(self.lights[i], { scaleX: 2.3, scaleY: 2.3 }, { duration: 200, easing: tween.easeOut }); } }; self.setLightGreen = function (lightIndex) { if (lightIndex >= 0 && lightIndex < self.lights.length) { var light = self.lights[lightIndex]; light.tint = 0x00ff00; // Green color light.alpha = 1.0; // Ensure full visibility // Force the light to render on top by moving to front self.removeChild(light); self.addChild(light); // Add glow effect with tween tween(light, { scaleX: 2.3, scaleY: 2.3 }, { duration: 200, easing: tween.easeOut }); } }; self.setOff = function () { for (var i = 0; i < self.lights.length; i++) { self.lights[i].alpha = 0.2; // Reset scale self.lights[i].scaleX = 2; self.lights[i].scaleY = 2; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2d2d2d }); /**** * Game Code ****/ // Game variables var player; var botCars = []; var roadLines = []; var finishLines = []; var trackEdges = []; var botSpawnTimer = 0; var lineSpawnTimer = 0; var checkpointTimer = 0; var raceSpeed = 8; var lapNumber = 1; var position = 9; // Starting position (9th out of 9) var raceDistance = 0; var lapDistance = 5000; // Distance for one lap var raceFinished = false; var finalPosition = 9; var botsFinished = 0; var raceStarted = false; var trafficLights = []; var lightSequenceStep = 0; var lightSequenceTimer = 0; var raceStartSequence = false; var startText = null; var gameMenu = null; var menuActive = true; // Show menu first gameMenu = new GameMenu(); gameMenu.x = 1024; gameMenu.y = 1366; game.addChild(gameMenu); // UI Elements (initially hidden) var positionTxt = new Text2('Position: 9/9', { size: 50, fill: 0xFFFFFF }); positionTxt.anchor.set(0, 0); positionTxt.alpha = 0; // Hide initially LK.gui.topLeft.addChild(positionTxt); positionTxt.x = 120; positionTxt.y = 20; var lapTxt = new Text2('Lap: 1/10', { size: 40, fill: 0xFFFF00 }); lapTxt.anchor.set(1, 0); lapTxt.alpha = 0; // Hide initially LK.gui.topRight.addChild(lapTxt); lapTxt.x = -20; lapTxt.y = 20; var speedTxt = new Text2('Speed: 0', { size: 35, fill: 0x00FF00 }); speedTxt.anchor.set(0, 0); speedTxt.alpha = 0; // Hide initially LK.gui.topLeft.addChild(speedTxt); speedTxt.x = 120; speedTxt.y = 80; // Initialize player player = new PlayerCar(); player.x = 1300; player.y = 2450; // Player starts at the back of the vertical line player.lastX = player.x; game.addChild(player); // Create starting grid with 8 bot cars in vertical single-file line var startingPositions = [ // Position 1 (pole position) { x: 700, y: 500 }, // Position 2 { x: 700, y: 1050 }, // Position 3 { x: 700, y: 1600 }, // Position 4 { x: 700, y: 2150 }, // Position 5 { x: 1300, y: 250 }, // Position 6 { x: 1300, y: 800 }, // Position 7 { x: 1300, y: 1350 }, // Position 8 { x: 1300, y: 1900 } // Last position ]; for (var i = 0; i < 8; i++) { var bot = new BotCar(i); bot.x = startingPositions[i].x; bot.y = startingPositions[i].y; bot.startingPosition = i + 1; botCars.push(bot); game.addChild(bot); } // Create 5-light traffic light system var trafficLight = new TrafficLight(); trafficLight.x = 1024; // Center of screen trafficLight.y = 600; trafficLight.setAllRed(); trafficLights.push(trafficLight); game.addChild(trafficLight); // Create start text (initially hidden) startText = new Text2('START!', { size: 120, fill: 0x00ff00 }); startText.anchor.set(0.5, 0.5); startText.x = 1024; startText.y = 1000; startText.alpha = 0; game.addChild(startText); // Initialize race elements but don't start sequence yet (menu controls this) // Race elements are created but hidden until menu starts the race // Create track barriers - 5 continuous barriers on each side with no gaps var leftBarriers = []; var rightBarriers = []; for (var i = 0; i < 5; i++) { // Left side barriers var leftBarrier = new TrackEdge(i); leftBarrier.x = 150; leftBarrier.y = i * 546 - 200; // Position barriers adjacently based on barrier height (546px) leftBarrier.side = 'left'; leftBarriers.push(leftBarrier); trackEdges.push(leftBarrier); game.addChild(leftBarrier); // Right side barriers var rightBarrier = new TrackEdge(i); rightBarrier.x = 1898; rightBarrier.y = i * 546 - 200; // Position barriers adjacently based on barrier height (546px) rightBarrier.side = 'right'; rightBarriers.push(rightBarrier); trackEdges.push(rightBarrier); game.addChild(rightBarrier); } // Create initial road lines (add to background first) for (var i = 0; i < 15; i++) { var line1 = new RoadLine(); line1.x = 700; line1.y = i * 200 - 100; roadLines.push(line1); game.addChildAt(line1, 0); var line2 = new RoadLine(); line2.x = 1300; line2.y = i * 200 - 100; roadLines.push(line2); game.addChildAt(line2, 0); } // Input handling var dragTarget = null; game.down = function (x, y, obj) { if (!menuActive) { dragTarget = player; handleMove(x, y, obj); } }; game.up = function (x, y, obj) { dragTarget = null; }; function handleMove(x, y, obj) { if (dragTarget && !menuActive) { dragTarget.x = x; // Keep player within racing lanes if (dragTarget.x < 300) { dragTarget.x = 300; } if (dragTarget.x > 1748) { dragTarget.x = 1748; } // Increase speed based on movement if (Math.abs(dragTarget.x - dragTarget.lastX) > 5) { raceSpeed = Math.min(25, raceSpeed + 0.2); } } } game.move = handleMove; // Spawn bot car function function spawnBotCar() { var carIndex = Math.floor(Math.random() * 8); var bot = new BotCar(carIndex); bot.x = 400 + Math.floor(Math.random() * 3) * 600; // Random lane bot.y = -100; botCars.push(bot); game.addChild(bot); } // Spawn road lines function function spawnRoadLines() { var line1 = new RoadLine(); line1.x = 700; line1.y = -50; roadLines.push(line1); game.addChildAt(line1, 0); var line2 = new RoadLine(); line2.x = 1300; line2.y = -50; roadLines.push(line2); game.addChildAt(line2, 0); } // Main game update game.update = function () { // Only update race mechanics if race has started if (raceStarted) { // Update race distance raceDistance += raceSpeed; // Update speed display (scale to show realistic racing speeds) speedTxt.setText('Speed: ' + Math.floor(raceSpeed * 10) + ' km/h'); } else { // Show stationary speed during countdown speedTxt.setText('Speed: 0'); } // Only handle race mechanics if race has started if (raceStarted) { // Check for lap completion or finish line crossing if (raceDistance >= lapDistance && !raceFinished) { raceDistance = 0; lapNumber++; lapTxt.setText('Lap: ' + lapNumber + '/10'); LK.getSound('checkpoint').play(); } } // Check for race completion after lap 10 if (lapNumber >= 10 && !raceFinished) { raceFinished = true; finalPosition = botsFinished + 1; // Player finishes after bots that already finished // Show single checkered flag at finish line var finishFlag = new FinishLine(); finishFlag.x = 1024; // Center of screen finishFlag.y = -200; // Start above screen finishLines.push(finishFlag); game.addChild(finishFlag); // Create result text var resultTxt = new Text2('Race Finished!\nFinal Position: ' + finalPosition + '/9\nLaps Completed: 10', { size: 60, fill: 0xFFFFFF, align: 'center' }); resultTxt.anchor.set(0.5, 0.5); resultTxt.x = 1024; resultTxt.y = 1366; game.addChild(resultTxt); LK.setScore(900 - finalPosition * 100); // Higher score for better position // Show you win after 3 seconds LK.setTimeout(function () { LK.showYouWin(); }, 3000); } // Handle traffic light sequence if (raceStartSequence && !raceStarted) { // Show UI elements when race sequence starts if (lightSequenceTimer === 0) { tween(positionTxt, { alpha: 1 }, { duration: 500 }); tween(lapTxt, { alpha: 1 }, { duration: 500 }); tween(speedTxt, { alpha: 1 }, { duration: 500 }); menuActive = false; } lightSequenceTimer++; // Turn lights green one by one every 60 frames (1 second each) if (lightSequenceTimer === 60) { // First light turns green after 1 second trafficLights[0].setLightGreen(0); } else if (lightSequenceTimer === 120) { // Second light turns green after 2 seconds trafficLights[0].setLightGreen(1); } else if (lightSequenceTimer === 180) { // Third light turns green after 3 seconds trafficLights[0].setLightGreen(2); } else if (lightSequenceTimer === 240) { // Fourth light turns green after 4 seconds trafficLights[0].setLightGreen(3); } else if (lightSequenceTimer === 300) { // Fifth light turns green after 5 seconds - RACE STARTS trafficLights[0].setLightGreen(4); // Show START text with tween animation tween(startText, { alpha: 1, scaleX: 1.5, scaleY: 1.5 }, { duration: 500, easing: tween.bounceOut, onFinish: function onFinish() { // Fade out start text after 1 second tween(startText, { alpha: 0 }, { duration: 1000 }); } }); raceStarted = true; // Activate bot cars with adjusted speeds for (var i = 0; i < botCars.length; i++) { botCars[i].currentSpeed = botCars[i].baseSpeed; // Add very slight speed variation to maintain distinct speeds botCars[i].currentSpeed += Math.random() * 0.5 - 0.25; // +/- 0.25 speed variation (very small range) } // Hide traffic light with animation tween(trafficLights[0], { alpha: 0, y: trafficLights[0].y - 100 }, { duration: 1000, easing: tween.easeOut }); } } // Spawn road lines lineSpawnTimer++; if (lineSpawnTimer >= 25) { lineSpawnTimer = 0; spawnRoadLines(); } // Update and check bot cars for (var i = botCars.length - 1; i >= 0; i--) { var bot = botCars[i]; if (bot.lastY === undefined) { bot.lastY = bot.y; } if (bot.lastIntersecting === undefined) { bot.lastIntersecting = false; } // Gradual speed recovery to maintain bot speed differences if (raceStarted && bot.currentSpeed < bot.baseSpeed) { bot.currentSpeed = Math.min(bot.baseSpeed, bot.currentSpeed + 0.1); } // Remove bots that go off screen if (bot.lastY <= 2800 && bot.y > 2800) { bot.destroy(); botCars.splice(i, 1); continue; } // Check collision with player var currentIntersecting = bot.intersects(player); if (!bot.lastIntersecting && currentIntersecting) { LK.getSound('collision').play(); // Slow down both cars on collision raceSpeed = Math.max(3, raceSpeed - 2); // Move player away from collision if (player.x < bot.x) { player.x -= 30; } else { player.x += 30; } } bot.lastY = bot.y; bot.lastIntersecting = currentIntersecting; } // Update road lines for (var i = roadLines.length - 1; i >= 0; i--) { var line = roadLines[i]; if (line.lastY === undefined) { line.lastY = line.y; } // Remove lines that go off screen if (line.lastY <= 2800 && line.y > 2800) { line.destroy(); roadLines.splice(i, 1); continue; } line.lastY = line.y; } // Update track edges and check collisions for (var i = trackEdges.length - 1; i >= 0; i--) { var edge = trackEdges[i]; if (edge.lastY === undefined) { edge.lastY = edge.y; } // Check collision with player if (edge.intersects && edge.intersects(player)) { // Play collision sound LK.getSound('collision').play(); // Reduce speed significantly raceSpeed = Math.max(2, raceSpeed - 4); // Bounce player away from barrier if (edge.side === 'left') { // Hit left barrier, push player right tween(player, { x: player.x + 100 }, { duration: 300, easing: tween.easeOut }); } else if (edge.side === 'right') { // Hit right barrier, push player left tween(player, { x: player.x - 100 }, { duration: 300, easing: tween.easeOut }); } // Flash the barrier red to show collision tween(edge, { tint: 0xff0000 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(edge, { tint: 0xffffff }, { duration: 300, easing: tween.easeIn }); } }); } // Check collisions with bot cars for (var j = 0; j < botCars.length; j++) { var bot = botCars[j]; if (edge.intersects && edge.intersects(bot)) { // Reduce bot speed bot.currentSpeed = Math.max(2, bot.currentSpeed - 3); // Bounce bot away from barrier if (edge.side === 'left') { // Hit left barrier, push bot right tween(bot, { x: bot.x + 80 }, { duration: 250, easing: tween.easeOut }); } else if (edge.side === 'right') { // Hit right barrier, push bot left tween(bot, { x: bot.x - 80 }, { duration: 250, easing: tween.easeOut }); } } } // Remove edges that go off screen and spawn new ones if (edge.lastY <= 2800 && edge.y > 2800) { edge.destroy(); trackEdges.splice(i, 1); continue; } edge.lastY = edge.y; } // Update finish lines for (var i = finishLines.length - 1; i >= 0; i--) { var finishLine = finishLines[i]; if (finishLine.lastY === undefined) { finishLine.lastY = finishLine.y; } // Remove finish lines that go off screen if (finishLine.lastY <= 2800 && finishLine.y > 2800) { finishLine.destroy(); finishLines.splice(i, 1); continue; } finishLine.lastY = finishLine.y; } // Spawn new track barriers - maintain continuous coverage with no gaps if (LK.ticks % 40 === 0) { // Spawn more frequently to ensure continuous coverage var barrierIndex = Math.floor(Math.random() * 5); // Find the topmost barrier position for each side to place new ones adjacent var leftTopY = -400; var rightTopY = -400; // Find the highest (lowest Y value) barrier on each side for (var k = 0; k < leftBarriers.length; k++) { if (leftBarriers[k].y < leftTopY) { leftTopY = leftBarriers[k].y; } } for (var k = 0; k < rightBarriers.length; k++) { if (rightBarriers[k].y < rightTopY) { rightTopY = rightBarriers[k].y; } } var leftBarrier = new TrackEdge(barrierIndex); leftBarrier.x = 150; // Position new barrier to be adjacent to the topmost barrier (accounting for barrier height) leftBarrier.y = leftTopY - 546; // Use barrier height to ensure no gap leftBarrier.side = 'left'; leftBarriers.push(leftBarrier); trackEdges.push(leftBarrier); game.addChild(leftBarrier); var rightBarrier = new TrackEdge(barrierIndex); rightBarrier.x = 1898; // Position new barrier to be adjacent to the topmost barrier (accounting for barrier height) rightBarrier.y = rightTopY - 546; // Use barrier height to ensure no gap rightBarrier.side = 'right'; rightBarriers.push(rightBarrier); trackEdges.push(rightBarrier); game.addChild(rightBarrier); // Remove barriers from arrays when they go off screen for (var k = leftBarriers.length - 1; k >= 0; k--) { if (leftBarriers[k].y > 2800) { leftBarriers.splice(k, 1); } } for (var k = rightBarriers.length - 1; k >= 0; k--) { if (rightBarriers[k].y > 2800) { rightBarriers.splice(k, 1); } } } // Calculate position based on bot cars behind player and finished bots if (!raceFinished && raceStarted) { var carsAhead = botsFinished; // Count finished bots as ahead for (var i = 0; i < botCars.length; i++) { var bot = botCars[i]; if (!bot.hasFinished) { // Compare lap progress for unfinished bots if (bot.lapCount > lapNumber || bot.lapCount === lapNumber && bot.raceDistance > raceDistance) { carsAhead++; } } } position = Math.max(1, Math.min(9, carsAhead + 1)); // Clamp position between 1-9 positionTxt.setText('Position: ' + position + '/9'); } else if (raceFinished) { positionTxt.setText('Position: ' + finalPosition + '/9 - FINISHED'); } else { // Before race starts, show starting position positionTxt.setText('Position: 9/9'); } // Gradually increase race speed if (LK.ticks % 300 === 0) { // Every 5 seconds raceSpeed = Math.min(12, raceSpeed + 0.1); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BotCar = Container.expand(function (carIndex) {
var self = Container.call(this);
// Use different car assets based on car index
var carAssetName = 'botCar' + (carIndex % 8 + 1);
var carGraphics = self.attachAsset(carAssetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Create more varied bot speeds based on car index for distinct racing styles
var speedVariations = [14,
// Fast aggressive bot
11,
// Medium-fast bot
16,
// Very fast bot
9,
// Slower cautious bot
13,
// Medium bot
18,
// Fastest bot
8,
// Slowest bot
15 // Fast bot
];
self.baseSpeed = speedVariations[carIndex % 8]; // Assign specific speed based on car index
self.currentSpeed = 0; // Start stationary until race begins
self.lanePosition = Math.floor(Math.random() * 3); // 0=left, 1=center, 2=right
self.targetX = 400 + self.lanePosition * 600; // Lane positions
self.aiTimer = 0;
self.carNumber = carIndex + 1;
self.isChangingLanes = false; // Track lane changing state
self.collisionCooldown = 0; // Prevent frequent collisions
// Bot cars now use their default asset colors without tinting
self.lapCount = 1;
self.raceDistance = 0;
self.hasFinished = false;
self.update = function () {
// Only move if race has started
if (!raceStarted) {
return;
}
// AI behavior - change lanes occasionally
self.aiTimer++;
if (self.aiTimer > 120 && Math.random() < 0.03 && !self.isChangingLanes) {
// Every 2 seconds, 3% chance, only if not already changing lanes
self.aiTimer = 0;
var newLane = Math.floor(Math.random() * 3);
if (newLane !== self.lanePosition) {
self.lanePosition = newLane;
self.targetX = 400 + self.lanePosition * 600;
self.isChangingLanes = true;
// Smooth lane change with tween and rotation
var direction = self.targetX > self.x ? 1 : -1;
// Add slight rotation during lane change for stability (reduced rotation)
tween(carGraphics, {
rotation: direction * 0.05
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Return to straight after lane change (faster return)
tween(carGraphics, {
rotation: 0
}, {
duration: 150,
easing: tween.easeInOut
});
}
});
// Smooth X movement (faster lane change)
tween(self, {
x: self.targetX
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.isChangingLanes = false;
}
});
}
}
// Update collision cooldown
if (self.collisionCooldown > 0) {
self.collisionCooldown--;
}
// Check for collision with other bot cars before moving
var canMove = true;
var needsLaneChange = false;
for (var b = 0; b < botCars.length; b++) {
var otherBot = botCars[b];
if (otherBot !== self && otherBot.intersects) {
var distanceX = Math.abs(otherBot.x - self.x);
var distanceY = Math.abs(otherBot.y - self.y);
// Direct collision detection with cooldown
if (otherBot.intersects(self) && self.collisionCooldown <= 0) {
// Set collision cooldown to prevent frequent collisions
self.collisionCooldown = 60; // 1 second cooldown
otherBot.collisionCooldown = 60;
// Reduce speed less drastically
self.currentSpeed = Math.max(4, self.currentSpeed - 2);
otherBot.currentSpeed = Math.max(4, otherBot.currentSpeed - 2);
// Push cars apart more gently
if (self.x < otherBot.x) {
self.x -= 30;
otherBot.x += 30;
} else {
self.x += 30;
otherBot.x -= 30;
}
canMove = false;
}
// Improved collision avoidance - larger detection zone
else if (distanceX < 250 && distanceY < 500) {
// Bot is getting close, take evasive action
if (otherBot.y < self.y && distanceY < 350) {
// Bot ahead, slow down and consider lane change
self.currentSpeed = Math.max(6, self.currentSpeed - 1);
needsLaneChange = true;
canMove = false;
} else if (otherBot.y > self.y && distanceY < 300) {
// Bot behind is catching up, speed up slightly
self.currentSpeed = Math.min(self.baseSpeed + 2, self.currentSpeed + 0.5);
}
}
}
}
// Trigger lane change more aggressively when needed
if (needsLaneChange && !self.isChangingLanes && Math.random() < 0.15) {
// 15% chance to change lanes when avoiding collision
var availableLanes = [];
for (var lane = 0; lane < 3; lane++) {
if (lane !== self.lanePosition) {
availableLanes.push(lane);
}
}
if (availableLanes.length > 0) {
var newLane = availableLanes[Math.floor(Math.random() * availableLanes.length)];
self.lanePosition = newLane;
self.targetX = 400 + self.lanePosition * 600;
self.isChangingLanes = true;
// Smooth lane change with tween and rotation
var direction = self.targetX > self.x ? 1 : -1;
// Add slight rotation during lane change for stability (reduced rotation)
tween(carGraphics, {
rotation: direction * 0.05
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Return to straight after lane change (faster return)
tween(carGraphics, {
rotation: 0
}, {
duration: 150,
easing: tween.easeInOut
});
}
});
// Smooth X movement (faster lane change)
tween(self, {
x: self.targetX
}, {
duration: 300,
// Faster lane change for collision avoidance
easing: tween.easeInOut,
onFinish: function onFinish() {
self.isChangingLanes = false;
}
});
}
}
// Move forward (relative to player) - faster movement
if (canMove) {
self.y += raceSpeed - self.currentSpeed;
} else {
self.y += raceSpeed - self.currentSpeed * 0.5; // Move slower when blocked
}
// Keep within screen bounds
if (self.x < 100) {
self.x = 100;
}
if (self.x > 1948) {
self.x = 1948;
}
// Track bot lap progress
if (!self.hasFinished) {
self.raceDistance += self.currentSpeed;
if (self.raceDistance >= lapDistance) {
self.raceDistance = 0;
self.lapCount++;
// Finish after completing 10 laps
if (self.lapCount > 10) {
self.hasFinished = true;
botsFinished++;
}
}
}
};
return self;
});
var FinishLine = Container.expand(function () {
var self = Container.call(this);
// Create left pole
var leftPole = self.attachAsset('finishPole', {
anchorX: 0.5,
anchorY: 1
});
leftPole.x = -800;
leftPole.y = 0;
// Create right pole
var rightPole = self.attachAsset('finishPole', {
anchorX: 0.5,
anchorY: 1
});
rightPole.x = 800;
rightPole.y = 0;
// Create checkered pattern
for (var i = 0; i < 16; i++) {
for (var j = 0; j < 2; j++) {
var isBlack = (i + j) % 2 === 0;
var square = self.attachAsset(isBlack ? 'checkeredSquare1' : 'checkeredSquare2', {
anchorX: 0.5,
anchorY: 0.5
});
square.x = (i - 7.5) * 100;
square.y = (j - 0.5) * 40;
}
}
self.update = function () {
self.y += raceSpeed;
};
return self;
});
var GameMenu = Container.expand(function () {
var self = Container.call(this);
// Menu background
var menuBg = self.attachAsset('menuBackground', {
anchorX: 0.5,
anchorY: 0.5
});
menuBg.alpha = 0.95;
// Menu panel
var menuPanel = self.attachAsset('menuPanel', {
anchorX: 0.5,
anchorY: 0.5
});
menuPanel.y = -100;
// Title
var titleText = new Text2('RACING CHAMPIONSHIP', {
size: 70,
fill: 0xFFD700
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -600;
self.addChild(titleText);
// Instructions
var instructionText = new Text2('Drag to steer your car\nComplete 10 laps to win!\nAvoid barriers and other cars', {
size: 40,
fill: 0xFFFFFF,
align: 'center'
});
instructionText.anchor.set(0.5, 0.5);
instructionText.y = -400;
self.addChild(instructionText);
// Car selection display
self.selectedCarIndex = 0;
var carPreview = self.attachAsset('playerCar', {
anchorX: 0.5,
anchorY: 0.5
});
carPreview.y = -150;
carPreview.scaleX = 0.8;
carPreview.scaleY = 0.8;
var carLabel = new Text2('Your Car', {
size: 35,
fill: 0x00FF00
});
carLabel.anchor.set(0.5, 0.5);
carLabel.y = -50;
self.addChild(carLabel);
// Start button
var startButton = new MenuButton('start', 'START RACE', function () {
self.startRace();
});
startButton.y = 200;
self.addChild(startButton);
// Best time display (placeholder)
var bestTimeText = new Text2('Best Position: Not Set', {
size: 30,
fill: 0xCCCCCC
});
bestTimeText.anchor.set(0.5, 0.5);
bestTimeText.y = 350;
self.addChild(bestTimeText);
self.startRace = function () {
// Hide menu with animation
tween(self, {
alpha: 0,
y: self.y - 200
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.destroy();
// Start the race countdown
LK.setTimeout(function () {
raceStartSequence = true;
}, 500);
}
});
};
return self;
});
var MenuButton = Container.expand(function (buttonType, text, onClick) {
var self = Container.call(this);
var buttonAsset = buttonType === 'start' ? 'startButton' : 'carSelectButton';
var buttonGraphics = self.attachAsset(buttonAsset, {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2(text, {
size: buttonType === 'start' ? 50 : 35,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.onClick = onClick;
self.down = function (x, y, obj) {
// Button press effect
tween(buttonGraphics, {
scaleX: 0.95,
scaleY: 0.95
}, {
duration: 100,
easing: tween.easeOut
});
if (self.onClick) {
self.onClick();
}
};
self.up = function (x, y, obj) {
// Button release effect
tween(buttonGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeOut
});
};
return self;
});
var PlayerCar = Container.expand(function () {
var self = Container.call(this);
var carGraphics = self.attachAsset('playerCar', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.maxSpeed = 350;
self.acceleration = 0.2;
self.currentSpeed = 0;
self.update = function () {
// Simulate acceleration/deceleration based on movement
if (Math.abs(self.lastX - self.x) > 5) {
self.currentSpeed = Math.min(self.maxSpeed, self.currentSpeed + self.acceleration);
} else {
self.currentSpeed = Math.max(0, self.currentSpeed - self.acceleration * 0.5);
}
self.lastX = self.x;
};
return self;
});
var RoadLine = Container.expand(function () {
var self = Container.call(this);
var lineGraphics = self.attachAsset('roadLine', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += raceSpeed;
};
return self;
});
var TrackEdge = Container.expand(function (barrierIndex) {
var self = Container.call(this);
// Use different barrier assets based on index (1-5)
var barrierAssetName = 'trackBarrier' + (barrierIndex % 5 + 1);
var edgeGraphics = self.attachAsset(barrierAssetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
self.y += raceSpeed;
};
return self;
});
var TrafficLight = Container.expand(function () {
var self = Container.call(this);
// Create larger frame background (5x bigger to fit 5 lights)
var lightBackground = self.attachAsset('trafficLight', {
anchorX: 0.5,
anchorY: 0.5
});
lightBackground.scaleX = 5;
lightBackground.scaleY = 3;
// Create 5 lights in a horizontal row
self.lights = [];
for (var i = 0; i < 5; i++) {
var light = self.attachAsset('lightRed', {
anchorX: 0.5,
anchorY: 0.5
});
light.scaleX = 2;
light.scaleY = 2;
light.x = (i - 2) * 160; // Space lights 160 pixels apart so they barely touch (80px radius * 2 scale * 2 = 160px diameter)
light.alpha = 1.0;
light.tint = 0xff0000; // Start all red
self.lights.push(light);
}
self.setAllRed = function () {
for (var i = 0; i < self.lights.length; i++) {
self.lights[i].tint = 0xff0000; // Red color
// Add glow effect with tween
tween(self.lights[i], {
scaleX: 2.3,
scaleY: 2.3
}, {
duration: 200,
easing: tween.easeOut
});
}
};
self.setLightGreen = function (lightIndex) {
if (lightIndex >= 0 && lightIndex < self.lights.length) {
var light = self.lights[lightIndex];
light.tint = 0x00ff00; // Green color
light.alpha = 1.0; // Ensure full visibility
// Force the light to render on top by moving to front
self.removeChild(light);
self.addChild(light);
// Add glow effect with tween
tween(light, {
scaleX: 2.3,
scaleY: 2.3
}, {
duration: 200,
easing: tween.easeOut
});
}
};
self.setOff = function () {
for (var i = 0; i < self.lights.length; i++) {
self.lights[i].alpha = 0.2;
// Reset scale
self.lights[i].scaleX = 2;
self.lights[i].scaleY = 2;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2d2d2d
});
/****
* Game Code
****/
// Game variables
var player;
var botCars = [];
var roadLines = [];
var finishLines = [];
var trackEdges = [];
var botSpawnTimer = 0;
var lineSpawnTimer = 0;
var checkpointTimer = 0;
var raceSpeed = 8;
var lapNumber = 1;
var position = 9; // Starting position (9th out of 9)
var raceDistance = 0;
var lapDistance = 5000; // Distance for one lap
var raceFinished = false;
var finalPosition = 9;
var botsFinished = 0;
var raceStarted = false;
var trafficLights = [];
var lightSequenceStep = 0;
var lightSequenceTimer = 0;
var raceStartSequence = false;
var startText = null;
var gameMenu = null;
var menuActive = true;
// Show menu first
gameMenu = new GameMenu();
gameMenu.x = 1024;
gameMenu.y = 1366;
game.addChild(gameMenu);
// UI Elements (initially hidden)
var positionTxt = new Text2('Position: 9/9', {
size: 50,
fill: 0xFFFFFF
});
positionTxt.anchor.set(0, 0);
positionTxt.alpha = 0; // Hide initially
LK.gui.topLeft.addChild(positionTxt);
positionTxt.x = 120;
positionTxt.y = 20;
var lapTxt = new Text2('Lap: 1/10', {
size: 40,
fill: 0xFFFF00
});
lapTxt.anchor.set(1, 0);
lapTxt.alpha = 0; // Hide initially
LK.gui.topRight.addChild(lapTxt);
lapTxt.x = -20;
lapTxt.y = 20;
var speedTxt = new Text2('Speed: 0', {
size: 35,
fill: 0x00FF00
});
speedTxt.anchor.set(0, 0);
speedTxt.alpha = 0; // Hide initially
LK.gui.topLeft.addChild(speedTxt);
speedTxt.x = 120;
speedTxt.y = 80;
// Initialize player
player = new PlayerCar();
player.x = 1300;
player.y = 2450; // Player starts at the back of the vertical line
player.lastX = player.x;
game.addChild(player);
// Create starting grid with 8 bot cars in vertical single-file line
var startingPositions = [
// Position 1 (pole position)
{
x: 700,
y: 500
},
// Position 2
{
x: 700,
y: 1050
},
// Position 3
{
x: 700,
y: 1600
},
// Position 4
{
x: 700,
y: 2150
},
// Position 5
{
x: 1300,
y: 250
},
// Position 6
{
x: 1300,
y: 800
},
// Position 7
{
x: 1300,
y: 1350
},
// Position 8
{
x: 1300,
y: 1900
} // Last position
];
for (var i = 0; i < 8; i++) {
var bot = new BotCar(i);
bot.x = startingPositions[i].x;
bot.y = startingPositions[i].y;
bot.startingPosition = i + 1;
botCars.push(bot);
game.addChild(bot);
}
// Create 5-light traffic light system
var trafficLight = new TrafficLight();
trafficLight.x = 1024; // Center of screen
trafficLight.y = 600;
trafficLight.setAllRed();
trafficLights.push(trafficLight);
game.addChild(trafficLight);
// Create start text (initially hidden)
startText = new Text2('START!', {
size: 120,
fill: 0x00ff00
});
startText.anchor.set(0.5, 0.5);
startText.x = 1024;
startText.y = 1000;
startText.alpha = 0;
game.addChild(startText);
// Initialize race elements but don't start sequence yet (menu controls this)
// Race elements are created but hidden until menu starts the race
// Create track barriers - 5 continuous barriers on each side with no gaps
var leftBarriers = [];
var rightBarriers = [];
for (var i = 0; i < 5; i++) {
// Left side barriers
var leftBarrier = new TrackEdge(i);
leftBarrier.x = 150;
leftBarrier.y = i * 546 - 200; // Position barriers adjacently based on barrier height (546px)
leftBarrier.side = 'left';
leftBarriers.push(leftBarrier);
trackEdges.push(leftBarrier);
game.addChild(leftBarrier);
// Right side barriers
var rightBarrier = new TrackEdge(i);
rightBarrier.x = 1898;
rightBarrier.y = i * 546 - 200; // Position barriers adjacently based on barrier height (546px)
rightBarrier.side = 'right';
rightBarriers.push(rightBarrier);
trackEdges.push(rightBarrier);
game.addChild(rightBarrier);
}
// Create initial road lines (add to background first)
for (var i = 0; i < 15; i++) {
var line1 = new RoadLine();
line1.x = 700;
line1.y = i * 200 - 100;
roadLines.push(line1);
game.addChildAt(line1, 0);
var line2 = new RoadLine();
line2.x = 1300;
line2.y = i * 200 - 100;
roadLines.push(line2);
game.addChildAt(line2, 0);
}
// Input handling
var dragTarget = null;
game.down = function (x, y, obj) {
if (!menuActive) {
dragTarget = player;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
dragTarget = null;
};
function handleMove(x, y, obj) {
if (dragTarget && !menuActive) {
dragTarget.x = x;
// Keep player within racing lanes
if (dragTarget.x < 300) {
dragTarget.x = 300;
}
if (dragTarget.x > 1748) {
dragTarget.x = 1748;
}
// Increase speed based on movement
if (Math.abs(dragTarget.x - dragTarget.lastX) > 5) {
raceSpeed = Math.min(25, raceSpeed + 0.2);
}
}
}
game.move = handleMove;
// Spawn bot car function
function spawnBotCar() {
var carIndex = Math.floor(Math.random() * 8);
var bot = new BotCar(carIndex);
bot.x = 400 + Math.floor(Math.random() * 3) * 600; // Random lane
bot.y = -100;
botCars.push(bot);
game.addChild(bot);
}
// Spawn road lines function
function spawnRoadLines() {
var line1 = new RoadLine();
line1.x = 700;
line1.y = -50;
roadLines.push(line1);
game.addChildAt(line1, 0);
var line2 = new RoadLine();
line2.x = 1300;
line2.y = -50;
roadLines.push(line2);
game.addChildAt(line2, 0);
}
// Main game update
game.update = function () {
// Only update race mechanics if race has started
if (raceStarted) {
// Update race distance
raceDistance += raceSpeed;
// Update speed display (scale to show realistic racing speeds)
speedTxt.setText('Speed: ' + Math.floor(raceSpeed * 10) + ' km/h');
} else {
// Show stationary speed during countdown
speedTxt.setText('Speed: 0');
}
// Only handle race mechanics if race has started
if (raceStarted) {
// Check for lap completion or finish line crossing
if (raceDistance >= lapDistance && !raceFinished) {
raceDistance = 0;
lapNumber++;
lapTxt.setText('Lap: ' + lapNumber + '/10');
LK.getSound('checkpoint').play();
}
}
// Check for race completion after lap 10
if (lapNumber >= 10 && !raceFinished) {
raceFinished = true;
finalPosition = botsFinished + 1; // Player finishes after bots that already finished
// Show single checkered flag at finish line
var finishFlag = new FinishLine();
finishFlag.x = 1024; // Center of screen
finishFlag.y = -200; // Start above screen
finishLines.push(finishFlag);
game.addChild(finishFlag);
// Create result text
var resultTxt = new Text2('Race Finished!\nFinal Position: ' + finalPosition + '/9\nLaps Completed: 10', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
resultTxt.anchor.set(0.5, 0.5);
resultTxt.x = 1024;
resultTxt.y = 1366;
game.addChild(resultTxt);
LK.setScore(900 - finalPosition * 100); // Higher score for better position
// Show you win after 3 seconds
LK.setTimeout(function () {
LK.showYouWin();
}, 3000);
}
// Handle traffic light sequence
if (raceStartSequence && !raceStarted) {
// Show UI elements when race sequence starts
if (lightSequenceTimer === 0) {
tween(positionTxt, {
alpha: 1
}, {
duration: 500
});
tween(lapTxt, {
alpha: 1
}, {
duration: 500
});
tween(speedTxt, {
alpha: 1
}, {
duration: 500
});
menuActive = false;
}
lightSequenceTimer++;
// Turn lights green one by one every 60 frames (1 second each)
if (lightSequenceTimer === 60) {
// First light turns green after 1 second
trafficLights[0].setLightGreen(0);
} else if (lightSequenceTimer === 120) {
// Second light turns green after 2 seconds
trafficLights[0].setLightGreen(1);
} else if (lightSequenceTimer === 180) {
// Third light turns green after 3 seconds
trafficLights[0].setLightGreen(2);
} else if (lightSequenceTimer === 240) {
// Fourth light turns green after 4 seconds
trafficLights[0].setLightGreen(3);
} else if (lightSequenceTimer === 300) {
// Fifth light turns green after 5 seconds - RACE STARTS
trafficLights[0].setLightGreen(4);
// Show START text with tween animation
tween(startText, {
alpha: 1,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 500,
easing: tween.bounceOut,
onFinish: function onFinish() {
// Fade out start text after 1 second
tween(startText, {
alpha: 0
}, {
duration: 1000
});
}
});
raceStarted = true;
// Activate bot cars with adjusted speeds
for (var i = 0; i < botCars.length; i++) {
botCars[i].currentSpeed = botCars[i].baseSpeed;
// Add very slight speed variation to maintain distinct speeds
botCars[i].currentSpeed += Math.random() * 0.5 - 0.25; // +/- 0.25 speed variation (very small range)
}
// Hide traffic light with animation
tween(trafficLights[0], {
alpha: 0,
y: trafficLights[0].y - 100
}, {
duration: 1000,
easing: tween.easeOut
});
}
}
// Spawn road lines
lineSpawnTimer++;
if (lineSpawnTimer >= 25) {
lineSpawnTimer = 0;
spawnRoadLines();
}
// Update and check bot cars
for (var i = botCars.length - 1; i >= 0; i--) {
var bot = botCars[i];
if (bot.lastY === undefined) {
bot.lastY = bot.y;
}
if (bot.lastIntersecting === undefined) {
bot.lastIntersecting = false;
}
// Gradual speed recovery to maintain bot speed differences
if (raceStarted && bot.currentSpeed < bot.baseSpeed) {
bot.currentSpeed = Math.min(bot.baseSpeed, bot.currentSpeed + 0.1);
}
// Remove bots that go off screen
if (bot.lastY <= 2800 && bot.y > 2800) {
bot.destroy();
botCars.splice(i, 1);
continue;
}
// Check collision with player
var currentIntersecting = bot.intersects(player);
if (!bot.lastIntersecting && currentIntersecting) {
LK.getSound('collision').play();
// Slow down both cars on collision
raceSpeed = Math.max(3, raceSpeed - 2);
// Move player away from collision
if (player.x < bot.x) {
player.x -= 30;
} else {
player.x += 30;
}
}
bot.lastY = bot.y;
bot.lastIntersecting = currentIntersecting;
}
// Update road lines
for (var i = roadLines.length - 1; i >= 0; i--) {
var line = roadLines[i];
if (line.lastY === undefined) {
line.lastY = line.y;
}
// Remove lines that go off screen
if (line.lastY <= 2800 && line.y > 2800) {
line.destroy();
roadLines.splice(i, 1);
continue;
}
line.lastY = line.y;
}
// Update track edges and check collisions
for (var i = trackEdges.length - 1; i >= 0; i--) {
var edge = trackEdges[i];
if (edge.lastY === undefined) {
edge.lastY = edge.y;
}
// Check collision with player
if (edge.intersects && edge.intersects(player)) {
// Play collision sound
LK.getSound('collision').play();
// Reduce speed significantly
raceSpeed = Math.max(2, raceSpeed - 4);
// Bounce player away from barrier
if (edge.side === 'left') {
// Hit left barrier, push player right
tween(player, {
x: player.x + 100
}, {
duration: 300,
easing: tween.easeOut
});
} else if (edge.side === 'right') {
// Hit right barrier, push player left
tween(player, {
x: player.x - 100
}, {
duration: 300,
easing: tween.easeOut
});
}
// Flash the barrier red to show collision
tween(edge, {
tint: 0xff0000
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(edge, {
tint: 0xffffff
}, {
duration: 300,
easing: tween.easeIn
});
}
});
}
// Check collisions with bot cars
for (var j = 0; j < botCars.length; j++) {
var bot = botCars[j];
if (edge.intersects && edge.intersects(bot)) {
// Reduce bot speed
bot.currentSpeed = Math.max(2, bot.currentSpeed - 3);
// Bounce bot away from barrier
if (edge.side === 'left') {
// Hit left barrier, push bot right
tween(bot, {
x: bot.x + 80
}, {
duration: 250,
easing: tween.easeOut
});
} else if (edge.side === 'right') {
// Hit right barrier, push bot left
tween(bot, {
x: bot.x - 80
}, {
duration: 250,
easing: tween.easeOut
});
}
}
}
// Remove edges that go off screen and spawn new ones
if (edge.lastY <= 2800 && edge.y > 2800) {
edge.destroy();
trackEdges.splice(i, 1);
continue;
}
edge.lastY = edge.y;
}
// Update finish lines
for (var i = finishLines.length - 1; i >= 0; i--) {
var finishLine = finishLines[i];
if (finishLine.lastY === undefined) {
finishLine.lastY = finishLine.y;
}
// Remove finish lines that go off screen
if (finishLine.lastY <= 2800 && finishLine.y > 2800) {
finishLine.destroy();
finishLines.splice(i, 1);
continue;
}
finishLine.lastY = finishLine.y;
}
// Spawn new track barriers - maintain continuous coverage with no gaps
if (LK.ticks % 40 === 0) {
// Spawn more frequently to ensure continuous coverage
var barrierIndex = Math.floor(Math.random() * 5);
// Find the topmost barrier position for each side to place new ones adjacent
var leftTopY = -400;
var rightTopY = -400;
// Find the highest (lowest Y value) barrier on each side
for (var k = 0; k < leftBarriers.length; k++) {
if (leftBarriers[k].y < leftTopY) {
leftTopY = leftBarriers[k].y;
}
}
for (var k = 0; k < rightBarriers.length; k++) {
if (rightBarriers[k].y < rightTopY) {
rightTopY = rightBarriers[k].y;
}
}
var leftBarrier = new TrackEdge(barrierIndex);
leftBarrier.x = 150;
// Position new barrier to be adjacent to the topmost barrier (accounting for barrier height)
leftBarrier.y = leftTopY - 546; // Use barrier height to ensure no gap
leftBarrier.side = 'left';
leftBarriers.push(leftBarrier);
trackEdges.push(leftBarrier);
game.addChild(leftBarrier);
var rightBarrier = new TrackEdge(barrierIndex);
rightBarrier.x = 1898;
// Position new barrier to be adjacent to the topmost barrier (accounting for barrier height)
rightBarrier.y = rightTopY - 546; // Use barrier height to ensure no gap
rightBarrier.side = 'right';
rightBarriers.push(rightBarrier);
trackEdges.push(rightBarrier);
game.addChild(rightBarrier);
// Remove barriers from arrays when they go off screen
for (var k = leftBarriers.length - 1; k >= 0; k--) {
if (leftBarriers[k].y > 2800) {
leftBarriers.splice(k, 1);
}
}
for (var k = rightBarriers.length - 1; k >= 0; k--) {
if (rightBarriers[k].y > 2800) {
rightBarriers.splice(k, 1);
}
}
}
// Calculate position based on bot cars behind player and finished bots
if (!raceFinished && raceStarted) {
var carsAhead = botsFinished; // Count finished bots as ahead
for (var i = 0; i < botCars.length; i++) {
var bot = botCars[i];
if (!bot.hasFinished) {
// Compare lap progress for unfinished bots
if (bot.lapCount > lapNumber || bot.lapCount === lapNumber && bot.raceDistance > raceDistance) {
carsAhead++;
}
}
}
position = Math.max(1, Math.min(9, carsAhead + 1)); // Clamp position between 1-9
positionTxt.setText('Position: ' + position + '/9');
} else if (raceFinished) {
positionTxt.setText('Position: ' + finalPosition + '/9 - FINISHED');
} else {
// Before race starts, show starting position
positionTxt.setText('Position: 9/9');
}
// Gradually increase race speed
if (LK.ticks % 300 === 0) {
// Every 5 seconds
raceSpeed = Math.min(12, raceSpeed + 0.1);
}
};
formula one race car 2d redbull vertical. In-Game asset. 2d. High contrast. No shadows
mclaren f1 race car vertical. In-Game asset. 2d. High contrast. No shadows
f1 grand prix barriers vertical 2d. In-Game asset. 2d. High contrast. No shadows
bird's-eye view of the F1 spectator crowd. In-Game asset. 2d. High contrast. No shadows
f1 mercedes car 2d vertical. In-Game asset. 2d. High contrast. No shadows