Spaces:
Running
Running
| <html lang="es"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Sun Runner - Escape del Sol</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| #gameCanvas { | |
| background-color: #87CEEB; | |
| border: 4px solid #1E3A8A; | |
| border-radius: 8px; | |
| box-shadow: 0 0 20px rgba(0, 0, 0, 0.3); | |
| } | |
| .sun { | |
| background: radial-gradient(circle at 30% 30%, #FDB813, #FDB813 30%, #FCAF17 40%, #F6961E 60%, #E46B2C); | |
| box-shadow: 0 0 100px #FDB813; | |
| } | |
| .jump-animation { | |
| animation: jump 0.5s ease-out; | |
| } | |
| @keyframes jump { | |
| 0% { transform: translateY(0); } | |
| 50% { transform: translateY(-100px); } | |
| 100% { transform: translateY(0); } | |
| } | |
| .game-over { | |
| animation: shake 0.5s cubic-bezier(.36,.07,.19,.97) both; | |
| } | |
| @keyframes shake { | |
| 10%, 90% { transform: translate3d(-1px, 0, 0); } | |
| 20%, 80% { transform: translate3d(2px, 0, 0); } | |
| 30%, 50%, 70% { transform: translate3d(-4px, 0, 0); } | |
| 40%, 60% { transform: translate3d(4px, 0, 0); } | |
| } | |
| .cloud { | |
| background: radial-gradient(circle at 30% 30%, white, white 80%, rgba(255,255,255,0.8) 100%); | |
| border-radius: 50%; | |
| } | |
| .parallax { | |
| background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="20" viewBox="0 0 100 20"><path d="M0,10 C20,15 30,5 50,10 S80,5 100,10" fill="none" stroke="%23FFFFFF" stroke-width="1"/></svg>'); | |
| opacity: 0.3; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900 h-screen flex flex-col items-center justify-center font-mono overflow-hidden"> | |
| <div class="absolute top-0 w-full h-16 bg-gradient-to-b from-gray-900 to-transparent z-10"></div> | |
| <h1 class="text-4xl font-bold mb-4 text-white tracking-wider">SUN RUNNER</h1> | |
| <h2 class="text-xl text-yellow-200 mb-4">Escapa del sol poniente</h2> | |
| <div class="relative"> | |
| <div id="sun" class="sun absolute rounded-full w-24 h-24 -top-12 -right-12 z-20"></div> | |
| <!-- Menú principal --> | |
| <div id="mainMenu" class="absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-70 z-30 transition-opacity duration-500"> | |
| <div class="bg-gray-800 p-8 rounded-lg max-w-md w-full text-center"> | |
| <h3 class="text-2xl text-yellow-300 font-bold mb-4">Instrucciones</h3> | |
| <p class="text-gray-200 mb-6">¡El sol se está poniendo y te está alcanzando! Corre hacia la derecha evitando los obstáculos.</p> | |
| <p class="text-gray-300 mb-6"><span class="font-bold text-yellow-300">Controles:</span><br>SPACE para saltar<br>También puedes hacer click/tocar la pantalla</p> | |
| <p class="text-sm text-gray-400 mb-6"> | |
| <span id="highScoreDisplay" class="block text-yellow-200">Mejor puntuación: 0</span> | |
| </p> | |
| <button id="startButton" class="bg-yellow-500 hover:bg-yellow-400 text-gray-900 font-bold py-3 px-6 rounded-full transition-all transform hover:scale-105 focus:outline-none"> | |
| ¡Comenzar! | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Pantalla de Game Over --> | |
| <div id="gameOverMenu" class="absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-70 z-30 hidden"> | |
| <div class="bg-gray-800 p-8 rounded-lg max-w-md w-full text-center"> | |
| <h3 class="text-2xl text-red-400 font-bold mb-4">¡Has sido alcanzado!</h3> | |
| <p class="text-4xl text-yellow-200 font-bold mb-6" id="scoreDisplay">0</p> | |
| <p class="text-gray-300 mb-6">¡Inténtalo de nuevo para mejorar tu puntuación!</p> | |
| <button id="restartButton" class="bg-yellow-500 hover:bg-yellow-400 text-gray-900 font-bold py-3 px-6 rounded-full transition-all transform hover:scale-105 focus:outline-none"> | |
| Reintentar | |
| </button> | |
| </div> | |
| </div> | |
| <canvas id="gameCanvas" width="800" height="400" class="relative z-10"></canvas> | |
| <!-- Nubes de fondo --> | |
| <div class="absolute top-20 left-20 cloud w-32 h-16 opacity-70"></div> | |
| <div class="absolute top-40 right-40 cloud w-24 h-12 opacity-60"></div> | |
| <div class="absolute top-80 left-40 cloud w-20 h-10 opacity-50"></div> | |
| </div> | |
| <div class="absolute bottom-0 w-full h-16 bg-gradient-to-t from-gray-900 to-transparent z-10"></div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const canvas = document.getElementById('gameCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| const startButton = document.getElementById('startButton'); | |
| const restartButton = document.getElementById('restartButton'); | |
| const mainMenu = document.getElementById('mainMenu'); | |
| const gameOverMenu = document.getElementById('gameOverMenu'); | |
| const scoreDisplay = document.getElementById('scoreDisplay'); | |
| const highScoreDisplay = document.getElementById('highScoreDisplay'); | |
| const sunElement = document.getElementById('sun'); | |
| // Variables del juego | |
| let gameRunning = false; | |
| let score = 0; | |
| let highScore = localStorage.getItem('highScore') || 0; | |
| let animationFrameId; | |
| let gameSpeed = 5; | |
| let sunSpeed = 2; | |
| let obstacles = []; | |
| let clouds = []; | |
| let groundHeight = 50; | |
| let parallaxOffset = 0; | |
| // Posición del personaje | |
| const player = { | |
| x: 100, | |
| y: canvas.height - groundHeight - 50, | |
| width: 40, | |
| height: 50, | |
| velocityY: 0, | |
| jumping: false, | |
| jumpPower: 15, | |
| gravity: 0.7 | |
| }; | |
| // Actualizar la mejor puntuación mostrada | |
| highScoreDisplay.textContent = `Mejor puntuación: ${highScore}`; | |
| // Configurar el personaje del juego | |
| function drawPlayer() { | |
| ctx.fillStyle = '#4B5563'; | |
| ctx.fillRect(player.x, player.y - 30, player.width, 10); // Sombrero | |
| ctx.fillStyle = '#1E40AF'; | |
| ctx.fillRect(player.x, player.y - 20, player.width, player.height); // Cuerpo | |
| ctx.fillStyle = '#9CA3AF'; | |
| ctx.fillRect(player.x + 10, player.y, 5, 10); // Pierna izquierda | |
| ctx.fillRect(player.x + 25, player.y, 5, 10); // Pierna derecha | |
| ctx.fillStyle = '#F59E0B'; | |
| ctx.fillRect(player.x + 15, player.y + 10, 10, 5); // Cinturón | |
| } | |
| // Dibujar el terreno | |
| function drawGround() { | |
| // Suelo principal | |
| ctx.fillStyle = '#374151'; | |
| ctx.fillRect(0, canvas.height - groundHeight, canvas.width, groundHeight); | |
| // Detalles del terreno | |
| ctx.fillStyle = '#1F2937'; | |
| for (let i = 0; i < canvas.width; i += 50) { | |
| ctx.fillRect(i, canvas.height - groundHeight, 30, 5); | |
| } | |
| // Efecto parallax | |
| ctx.save(); | |
| ctx.translate(-parallaxOffset % 100, 0); | |
| for (let i = -100; i < canvas.width + 100; i += 100) { | |
| ctx.fillStyle = '#1E293B'; | |
| ctx.beginPath(); | |
| ctx.moveTo(i, canvas.height - groundHeight); | |
| ctx.bezierCurveTo(i + 25, canvas.height - groundHeight - 10, | |
| i + 75, canvas.height - groundHeight + 5, | |
| i + 100, canvas.height - groundHeight); | |
| ctx.fill(); | |
| } | |
| ctx.restore(); | |
| } | |
| // Generar obstáculos | |
| function generateObstacle() { | |
| if (Math.random() < 0.02 && obstacles.length < 3) { | |
| const types = ['rock', 'cactus']; | |
| const type = types[Math.floor(Math.random() * types.length)]; | |
| const height = type === 'rock' ? 30 : 50; | |
| obstacles.push({ | |
| x: canvas.width, | |
| y: canvas.height - groundHeight - height, | |
| width: type === 'rock' ? 50 : 30, | |
| height: height, | |
| type: type | |
| }); | |
| } | |
| } | |
| // Dibujar obstáculos | |
| function drawObstacles() { | |
| obstacles.forEach(obstacle => { | |
| if (obstacle.type === 'rock') { | |
| ctx.fillStyle = '#6B7280'; | |
| ctx.beginPath(); | |
| ctx.arc(obstacle.x + 25, obstacle.y + 25, 25, 0, Math.PI * 2); | |
| ctx.fill(); | |
| } else { // cactus | |
| ctx.fillStyle = '#10B981'; | |
| ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height); | |
| ctx.fillRect(obstacle.x - 5, obstacle.y, 10, obstacle.height * 0.7); | |
| ctx.fillRect(obstacle.x + obstacle.width - 5, obstacle.y, 10, obstacle.height * 0.5); | |
| } | |
| }); | |
| } | |
| // Mover y eliminar obstáculos | |
| function updateObstacles() { | |
| for (let i = obstacles.length - 1; i >= 0; i--) { | |
| obstacles[i].x -= gameSpeed; | |
| // Eliminar obstáculos fuera de la pantalla | |
| if (obstacles[i].x + obstacles[i].width < 0) { | |
| obstacles.splice(i, 1); | |
| score += 10; | |
| } | |
| // Detección de colisiones | |
| if (player.x < obstacles[i].x + obstacles[i].width && | |
| player.x + player.width > obstacles[i].x && | |
| player.y < obstacles[i].y + obstacles[i].height && | |
| player.y + player.height > obstacles[i].y) { | |
| gameOver(); | |
| } | |
| } | |
| } | |
| // Generar nubes de fondo | |
| function generateClouds() { | |
| if (Math.random() < 0.01 && clouds.length < 5) { | |
| clouds.push({ | |
| x: canvas.width, | |
| y: Math.random() * (canvas.height / 3), | |
| width: 60 + Math.random() * 60, | |
| speed: 1 + Math.random() * 2 | |
| }); | |
| } | |
| } | |
| // Dibujar nubes | |
| function drawClouds() { | |
| clouds.forEach(cloud => { | |
| ctx.fillStyle = 'rgba(255, 255, 255, 0.7)'; | |
| ctx.beginPath(); | |
| ctx.arc(cloud.x + cloud.width * 0.3, cloud.y + cloud.width * 0.1, cloud.width * 0.2, 0, Math.PI * 2); | |
| ctx.arc(cloud.x + cloud.width * 0.6, cloud.y + cloud.width * 0.1, cloud.width * 0.3, 0, Math.PI * 2); | |
| ctx.arc(cloud.x + cloud.width * 0.2, cloud.y + cloud.width * 0.2, cloud.width * 0.2, 0, Math.PI * 2); | |
| ctx.fill(); | |
| }); | |
| } | |
| // Mover y eliminar nubes | |
| function updateClouds() { | |
| for (let i = clouds.length - 1; i >= 0; i--) { | |
| clouds[i].x -= clouds[i].speed; | |
| if (clouds[i].x + clouds[i].width * 1.5 < 0) { | |
| clouds.splice(i, 1); | |
| } | |
| } | |
| } | |
| // Actualizar posición del jugador y gravedad | |
| function updatePlayer() { | |
| // Gravedad | |
| player.velocityY += player.gravity; | |
| player.y += player.velocityY; | |
| // Limitar al suelo | |
| if (player.y >= canvas.height - groundHeight - player.height) { | |
| player.y = canvas.height - groundHeight - player.height; | |
| player.velocityY = 0; | |
| player.jumping = false; | |
| } | |
| } | |
| // Hacer que el jugador salte | |
| function jump() { | |
| if (!player.jumping) { | |
| player.velocityY = -player.jumpPower; | |
| player.jumping = true; | |
| document.getElementById('gameCanvas').classList.add('jump-animation'); | |
| setTimeout(() => { | |
| document.getElementById('gameCanvas').classList.remove('jump-animation'); | |
| }, 500); | |
| } | |
| } | |
| // Actualizar la posición del sol | |
| function updateSunPosition() { | |
| const sunX = -30 + (canvas.width - player.x) * 0.3; | |
| const sunY = -30 + (canvas.height - player.y) * 0.1; | |
| sunElement.style.transform = `translate(${sunX}px, ${sunY}px)`; | |
| // Comprobar si el sol alcanzó al jugador | |
| if (player.x < 50) { | |
| gameOver(); | |
| } | |
| } | |
| // Actualizar la velocidad del sol (dificultad progresiva) | |
| function updateSunSpeed() { | |
| sunSpeed = 2 + Math.floor(score / 500); | |
| gameSpeed = 5 + Math.floor(score / 300); | |
| } | |
| // Función de Game Over | |
| function gameOver() { | |
| gameRunning = false; | |
| cancelAnimationFrame(animationFrameId); | |
| // Actualizar mejor puntuación | |
| if (score > highScore) { | |
| highScore = score; | |
| localStorage.setItem('highScore', highScore); | |
| highScoreDisplay.textContent = `Mejor puntuación: ${highScore}`; | |
| } | |
| scoreDisplay.textContent = score; | |
| gameOverMenu.classList.remove('hidden'); | |
| document.getElementById('gameCanvas').classList.add('game-over'); | |
| } | |
| // Reiniciar el juego | |
| function resetGame() { | |
| score = 0; | |
| gameSpeed = 5; | |
| sunSpeed = 2; | |
| obstacles = []; | |
| clouds = []; | |
| player.y = canvas.height - groundHeight - player.height; | |
| player.velocityY = 0; | |
| player.jumping = false; | |
| document.getElementById('gameCanvas').classList.remove('game-over'); | |
| gameOverMenu.classList.add('hidden'); | |
| } | |
| // Bucle principal del juego | |
| function gameLoop() { | |
| // Limpiar el canvas | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| // Fondo gradiente | |
| const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height); | |
| gradient.addColorStop(0, '#1E3A8A'); | |
| gradient.addColorStop(0.4, '#2563EB'); | |
| gradient.addColorStop(1, '#7DD3FC'); | |
| ctx.fillStyle = gradient; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| // Dibujar elementos del juego | |
| drawClouds(); | |
| drawGround(); | |
| drawObstacles(); | |
| drawPlayer(); | |
| // Actualizar lógica del juego | |
| updatePlayer(); | |
| updateObstacles(); | |
| updateClouds(); | |
| updateSunPosition(); | |
| updateSunSpeed(); | |
| // Generar nuevos elementos | |
| generateObstacle(); | |
| generateClouds(); | |
| // Actualizar posición parallax | |
| parallaxOffset += gameSpeed * 0.5; | |
| // Continuar el bucle si el juego está en marcha | |
| if (gameRunning) { | |
| animationFrameId = requestAnimationFrame(gameLoop); | |
| } | |
| } | |
| // Event listeners | |
| startButton.addEventListener('click', () => { | |
| resetGame(); | |
| mainMenu.classList.add('opacity-0'); | |
| setTimeout(() => { | |
| mainMenu.classList.add('hidden'); | |
| gameRunning = true; | |
| gameLoop(); | |
| }, 500); | |
| }); | |
| restartButton.addEventListener('click', () => { | |
| resetGame(); | |
| gameOverMenu.classList.add('hidden'); | |
| gameRunning = true; | |
| gameLoop(); | |
| }); | |
| // Controles del juego | |
| document.addEventListener('keydown', (e) => { | |
| if (e.code === 'Space' && gameRunning) { | |
| e.preventDefault(); | |
| jump(); | |
| } | |
| }); | |
| canvas.addEventListener('click', () => { | |
| if (gameRunning) { | |
| jump(); | |
| } | |
| }); | |
| // Efecto de movimiento inicial del sol | |
| setTimeout(() => { | |
| sunElement.style.transition = 'transform 0.5s ease-out'; | |
| }, 1000); | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - <a href="https://enzostvs-deepsite.hf.space?remix=lucks17/sun-runner" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body> | |
| </html> |