Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>F1 Racing Simulator</title> | |
| <link rel="icon" type="image/x-icon" href="/static/favicon.ico"> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <style> | |
| body { | |
| background-color: #15151E; | |
| color: #F4F4F4; | |
| overflow: hidden; | |
| font-family: 'Arial', sans-serif; | |
| } | |
| .track { | |
| position: relative; | |
| height: 100vh; | |
| width: 100%; | |
| background-color: #333; | |
| overflow: hidden; | |
| } | |
| .road { | |
| position: absolute; | |
| width: 400px; | |
| height: 200%; | |
| background-color: #555; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| border-left: 5px dashed #ccc; | |
| border-right: 5px dashed #ccc; | |
| background-image: linear-gradient(#777 1px, transparent 1px); | |
| background-size: 100% 40px; | |
| } | |
| .car { | |
| position: absolute; | |
| width: 80px; | |
| height: 160px; | |
| background-image: url('http://static.photos/automotive/200x200/1'); | |
| background-size: contain; | |
| background-repeat: no-repeat; | |
| bottom: 50px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| z-index: 10; | |
| } | |
| .opponent { | |
| position: absolute; | |
| width: 80px; | |
| height: 160px; | |
| background-image: url('http://static.photos/automotive/200x200/2'); | |
| background-size: contain; | |
| background-repeat: no-repeat; | |
| z-index: 5; | |
| } | |
| .score-board { | |
| position: absolute; | |
| top: 20px; | |
| left: 20px; | |
| background-color: rgba(0,0,0,0.7); | |
| padding: 10px; | |
| border-radius: 5px; | |
| border-left: 4px solid #E10600; | |
| z-index: 20; | |
| } | |
| .game-over { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| background-color: rgba(0,0,0,0.9); | |
| padding: 30px; | |
| border-radius: 10px; | |
| text-align: center; | |
| display: none; | |
| z-index: 30; | |
| } | |
| .controls { | |
| position: absolute; | |
| bottom: 20px; | |
| width: 100%; | |
| display: flex; | |
| justify-content: center; | |
| gap: 20px; | |
| z-index: 20; | |
| } | |
| .control-btn { | |
| background-color: #E10600; | |
| color: white; | |
| border: none; | |
| padding: 15px 25px; | |
| border-radius: 50%; | |
| font-size: 20px; | |
| cursor: pointer; | |
| } | |
| .lap-time { | |
| position: absolute; | |
| top: 20px; | |
| right: 20px; | |
| background-color: rgba(0,0,0,0.7); | |
| padding: 10px; | |
| border-radius: 5px; | |
| border-right: 4px solid #E10600; | |
| z-index: 20; | |
| } | |
| .grass { | |
| position: absolute; | |
| width: 100%; | |
| height: 200%; | |
| background-color: #2a5c2a; | |
| background-image: url('http://static.photos/nature/200x200/5'); | |
| background-size: 200px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="track" id="gameArea"> | |
| <div class="grass"></div> | |
| <div class="road"></div> | |
| <div class="score-board"> | |
| <h3 class="font-bold">LAP: <span id="lap">1</span>/3</h3> | |
| <p>POSITION: <span id="position">1</span></p> | |
| </div> | |
| <div class="lap-time"> | |
| <h3 class="font-bold">LAP TIME: <span id="time">0:00.000</span></h3> | |
| <p>BEST: <span id="bestTime">0:00.000</span></p> | |
| </div> | |
| <div class="car" id="playerCar"></div> | |
| <div class="game-over" id="gameOver"> | |
| <h2 class="text-3xl font-bold text-red-500 mb-4">RACE FINISHED</h2> | |
| <p class="text-xl mb-2">Final Position: <span id="finalPosition">1</span></p> | |
| <p class="text-xl mb-4">Best Lap: <span id="finalBestTime">0:00.000</span></p> | |
| <button id="restartBtn" class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-6 rounded"> | |
| New Race | |
| </button> | |
| </div> | |
| <div class="controls"> | |
| <button class="control-btn" id="leftBtn"><i data-feather="chevron-left"></i></button> | |
| <button class="control-btn" id="rightBtn"><i data-feather="chevron-right"></i></button> | |
| </div> | |
| <div class="text-center mt-3"> | |
| <a href="motorsport.html" class="text-white hover:text-red-500">Try Our New Motorsport Game</a> | |
| </div> | |
| </div> | |
| <script> | |
| feather.replace(); | |
| // Game variables | |
| let lap = 1; | |
| let position = 1; | |
| let lapStartTime = 0; | |
| let currentLapTime = 0; | |
| let bestLapTime = 0; | |
| let gameSpeed = 5; | |
| let isRaceOver = false; | |
| let opponents = []; | |
| let animationId; | |
| let roadPosition = 0; | |
| // DOM elements | |
| const gameArea = document.getElementById('gameArea'); | |
| const road = document.querySelector('.road'); | |
| const playerCar = document.getElementById('playerCar'); | |
| const lapElement = document.getElementById('lap'); | |
| const positionElement = document.getElementById('position'); | |
| const timeElement = document.getElementById('time'); | |
| const bestTimeElement = document.getElementById('bestTime'); | |
| const finalPositionElement = document.getElementById('finalPosition'); | |
| const finalBestTimeElement = document.getElementById('finalBestTime'); | |
| const gameOverElement = document.getElementById('gameOver'); | |
| const restartBtn = document.getElementById('restartBtn'); | |
| const leftBtn = document.getElementById('leftBtn'); | |
| const rightBtn = document.getElementById('rightBtn'); | |
| // Initialize game | |
| function initGame() { | |
| lapStartTime = Date.now(); | |
| updateLapTime(); | |
| createOpponents(); | |
| requestAnimationFrame(gameLoop); | |
| } | |
| // Game loop | |
| function gameLoop() { | |
| if (isRaceOver) return; | |
| // Move road to simulate car moving | |
| roadPosition += gameSpeed; | |
| road.style.backgroundPositionY = `${roadPosition}px`; | |
| // Move opponents | |
| moveOpponents(); | |
| // Check lap completion (simplified) | |
| if (roadPosition % 5000 < gameSpeed) { | |
| completeLap(); | |
| } | |
| animationId = requestAnimationFrame(gameLoop); | |
| } | |
| // Update lap time display | |
| function updateLapTime() { | |
| currentLapTime = Date.now() - lapStartTime; | |
| timeElement.textContent = formatTime(currentLapTime); | |
| setTimeout(updateLapTime, 10); | |
| } | |
| // Format time (milliseconds to M:SS.mmm) | |
| function formatTime(ms) { | |
| const minutes = Math.floor(ms / 60000); | |
| const seconds = Math.floor((ms % 60000) / 1000); | |
| const milliseconds = ms % 1000; | |
| return `${minutes}:${seconds.toString().padStart(2, '0')}.${milliseconds.toString().padStart(3, '0')}`; | |
| } | |
| // Create AI opponents | |
| function createOpponents() { | |
| for (let i = 0; i < 5; i++) { | |
| const opponent = document.createElement('div'); | |
| opponent.className = 'opponent'; | |
| opponent.style.left = `${Math.random() * 300 + 50}px`; | |
| opponent.style.top = `${Math.random() * 500}px`; | |
| gameArea.appendChild(opponent); | |
| opponents.push({ | |
| element: opponent, | |
| speed: 3 + Math.random() * 2, | |
| x: parseInt(opponent.style.left), | |
| y: parseInt(opponent.style.top) | |
| }); | |
| } | |
| } | |
| // Move AI opponents | |
| function moveOpponents() { | |
| opponents.forEach((opponent, index) => { | |
| opponent.y += opponent.speed; | |
| opponent.x += Math.sin(Date.now() / 1000 + index) * 2; // side-to-side movement | |
| // Wrap around track | |
| if (opponent.y > gameArea.offsetHeight) { | |
| opponent.y = -200; | |
| } | |
| opponent.element.style.top = `${opponent.y}px`; | |
| opponent.element.style.left = `${opponent.x}px`; | |
| // Simple collision detection | |
| if (Math.abs(opponent.x - parseInt(playerCar.style.left)) < 60 && | |
| Math.abs(opponent.y - parseInt(playerCar.style.top)) < 100) { | |
| handleCollision(); | |
| } | |
| }); | |
| } | |
| // Handle collision | |
| function handleCollision() { | |
| gameSpeed = Math.max(1, gameSpeed - 1); // Slow down on collision | |
| } | |
| // Complete a lap | |
| function completeLap() { | |
| lap++; | |
| lapElement.textContent = lap; | |
| // Update best lap time | |
| if (currentLapTime < bestLapTime || bestLapTime === 0) { | |
| bestLapTime = currentLapTime; | |
| bestTimeElement.textContent = formatTime(bestLapTime); | |
| } | |
| // Increase difficulty | |
| gameSpeed += 0.5; | |
| // Check race completion | |
| if (lap > 3) { | |
| finishRace(); | |
| } else { | |
| lapStartTime = Date.now(); // Reset lap timer | |
| } | |
| } | |
| // Finish race | |
| function finishRace() { | |
| isRaceOver = true; | |
| cancelAnimationFrame(animationId); | |
| finalPositionElement.textContent = position; | |
| finalBestTimeElement.textContent = formatTime(bestLapTime); | |
| gameOverElement.style.display = 'block'; | |
| } | |
| // Restart game | |
| function restartGame() { | |
| // Clear opponents | |
| opponents.forEach(opponent => opponent.element.remove()); | |
| opponents = []; | |
| // Reset variables | |
| lap = 1; | |
| position = 1; | |
| gameSpeed = 5; | |
| isRaceOver = false; | |
| roadPosition = 0; | |
| // Update displays | |
| lapElement.textContent = lap; | |
| positionElement.textContent = position; | |
| timeElement.textContent = '0:00.000'; | |
| gameOverElement.style.display = 'none'; | |
| // Start game | |
| initGame(); | |
| } | |
| // Player car position | |
| let carX = gameArea.offsetWidth / 2; | |
| // Keyboard controls | |
| document.addEventListener('keydown', (e) => { | |
| if (e.key === 'ArrowLeft') { | |
| carX = Math.max(50, carX - 20); | |
| } else if (e.key === 'ArrowRight') { | |
| carX = Math.min(gameArea.offsetWidth - 50, carX + 20); | |
| } | |
| playerCar.style.left = `${carX}px`; | |
| }); | |
| // Touch controls | |
| leftBtn.addEventListener('click', () => { | |
| carX = Math.max(50, carX - 20); | |
| playerCar.style.left = `${carX}px`; | |
| }); | |
| rightBtn.addEventListener('click', () => { | |
| carX = Math.min(gameArea.offsetWidth - 50, carX + 20); | |
| playerCar.style.left = `${carX}px`; | |
| }); | |
| restartBtn.addEventListener('click', restartGame); | |
| // Start game | |
| initGame(); | |
| </script> | |
| </body> | |
| </html> | |