Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> | |
<title>Desert Road Runner</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<style> | |
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap'); | |
body { | |
margin: 0; | |
padding: 0; | |
overflow: hidden; | |
font-family: 'Press Start 2P', cursive; | |
background-color: #f7f7f7; | |
} | |
#game-container { | |
position: relative; | |
width: 100vw; | |
height: 100vh; | |
background: linear-gradient(to bottom, #87CEEB 0%, #E6E6FA 50%, #F5DEB3 100%); | |
overflow: hidden; | |
} | |
#road { | |
position: absolute; | |
bottom: 0; | |
width: 100%; | |
height: 120px; | |
background-color: #333; | |
z-index: 5; | |
} | |
.road-marking { | |
position: absolute; | |
bottom: 45px; | |
width: 50px; | |
height: 10px; | |
background-color: #fff; | |
z-index: 6; | |
} | |
#car { | |
position: absolute; | |
bottom: 0px; | |
left: 40px; | |
width: 200px; | |
height: 200px; | |
background-image: url('https://www.chennaipainter.in/wp-content/uploads/2025/04/car-1024x434.png'); | |
background-size: contain; | |
background-repeat: no-repeat; | |
z-index: 15; | |
transform: scale(0.7); | |
} | |
.obstacle { | |
position: absolute; | |
bottom: 80px; | |
width: 80px; | |
height: 70px; | |
background-image: url('https://www.chennaipainter.in/wp-content/uploads/2025/04/obstacle-150x150.png'); | |
background-size: contain; | |
background-repeat: no-repeat; | |
z-index: 5; | |
} | |
.cloud { | |
position: absolute; | |
width: 100px; | |
height: 60px; | |
background-image: url('https://www.chennaipainter.in/wp-content/uploads/2025/04/cloud-300x300.png'); | |
background-size: contain; | |
background-repeat: no-repeat; | |
opacity: 0.8; | |
z-index: 2; | |
} | |
.cactus { | |
position: absolute; | |
bottom: 118px; | |
width: 200px; | |
height: 500px; | |
background-image: url('https://www.chennaipainter.in/wp-content/uploads/2025/04/tree-150x150.png'); | |
background-size: contain; | |
background-repeat: no-repeat; | |
z-index: 4; | |
} | |
#score { | |
position: absolute; | |
top: 20px; | |
right: 20px; | |
font-size: 20px; | |
color: #333; | |
z-index: 20; | |
background-color: rgba(255, 255, 255, 0.7); | |
padding: 10px 15px; | |
border-radius: 10px; | |
} | |
#game-over { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
font-size: 40px; | |
color: #ff3333; | |
text-align: center; | |
display: none; | |
z-index: 20; | |
background-color: rgba(255, 255, 255, 0.8); | |
padding: 30px; | |
border-radius: 20px; | |
box-shadow: 0 0 20px rgba(0, 0, 0, 0.3); | |
} | |
#start-screen { | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background-color: rgba(240, 230, 210, 0.9); | |
display: flex; | |
flex-direction: column; | |
justify-content: center; | |
align-items: center; | |
z-index: 30; | |
text-align: center; | |
} | |
#start-button { | |
margin-top: 20px; | |
padding: 15px 40px; | |
font-size: 20px; | |
background-color: #4CAF50; | |
color: white; | |
border: none; | |
border-radius: 10px; | |
cursor: pointer; | |
font-family: 'Press Start 2P', cursive; | |
transition: all 0.3s; | |
box-shadow: 0 5px 0 #2E7D32; | |
} | |
#start-button:hover { | |
background-color: #45a049; | |
transform: translateY(-2px); | |
} | |
#start-button:active { | |
transform: translateY(3px); | |
box-shadow: 0 2px 0 #2E7D32; | |
} | |
.controls { | |
margin-top: 30px; | |
display: flex; | |
gap: 20px; | |
} | |
.control-key { | |
background-color: #333; | |
color: white; | |
padding: 10px 15px; | |
border-radius: 5px; | |
font-size: 16px; | |
} | |
.sun { | |
position: absolute; | |
width: 80px; | |
height: 80px; | |
background: radial-gradient(circle, #FFD700 30%, #FFA500 70%); | |
border-radius: 50%; | |
box-shadow: 0 0 40px #FFD700; | |
z-index: 1; | |
} | |
@keyframes birdFly { | |
0% { transform: translateX(0) translateY(0); } | |
50% { transform: translateX(0) translateY(-10px); } | |
100% { transform: translateX(0) translateY(0); } | |
} | |
.bird { | |
animation: birdFly 1s infinite ease-in-out; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="game-container"> | |
<div class="sun" style="top: 50px; right: 100px;"></div> | |
<div id="road"></div> | |
<div id="score">0</div> | |
<div id="game-over"> | |
GAME OVER<br> | |
<span id="final-score">0</span><br> | |
<div class="mt-4 text-xl">Press Space to Restart</div> | |
<div class="mt-4 text-sm">Or tap the screen on mobile</div> | |
</div> | |
<div id="start-screen"> | |
<h1 class="text-4xl md:text-6xl mb-6 text-orange-600">play with Viateur AI</h1> | |
<p class="mb-8 text-lg md:text-xl max-w-2xl px-4">Avoid the obstacles and survive as long as you can in this endless desert adventure!</p> | |
<button id="start-button">START GAME to Viateur AI</button> | |
<div class="controls mt-8"> | |
<div class="control-key"><i class="fas fa-arrow-up"></i> JUMP</div> | |
</div> | |
<div class="mt-8 text-sm text-gray-600"> | |
<i class="fas fa-mobile-alt"></i> Tap the screen to jump on mobile | |
</div> | |
</div> | |
<div id="car"></div> | |
</div> | |
<script> | |
const gameContainer = document.getElementById('game-container'); | |
const car = document.getElementById('car'); | |
const scoreEl = document.getElementById('score'); | |
const gameOverEl = document.getElementById('game-over'); | |
const finalScoreEl = document.getElementById('final-score'); | |
const startScreen = document.getElementById('start-screen'); | |
const startButton = document.getElementById('start-button'); | |
let gameSpeed = 5; | |
let score = 0; | |
let isJumping = false; | |
let isGameOver = false; | |
let gameStarted = false; | |
let jumpStart = 0; | |
let animationId; | |
const obstacles = []; | |
const clouds = []; | |
const birds = []; | |
const cacti = []; | |
const mountains = []; | |
const roadMarkings = []; | |
// Create initial road markings | |
for (let i = 0; i < 10; i++) { | |
const mark = document.createElement('div'); | |
mark.className = 'road-marking'; | |
mark.style.left = (i * 200) + 'px'; | |
gameContainer.appendChild(mark); | |
roadMarkings.push({ el: mark, x: i * 200 }); | |
} | |
// Create initial clouds | |
for (let i = 0; i < 5; i++) { | |
createCloud(Math.random() * window.innerWidth, Math.random() * 200); | |
} | |
// Create initial desert scenery | |
for (let i = 0; i < 3; i++) { | |
createCactus(window.innerWidth + (i * 400), Math.random() * 50 + 50); | |
} | |
for (let i = 0; i < 2; i++) { | |
createMountain(window.innerWidth + (i * 600), Math.random() * 100); | |
} | |
function startGame() { | |
gameStarted = true; | |
isGameOver = false; | |
score = 0; | |
gameSpeed = 5; | |
scoreEl.textContent = '0'; | |
startScreen.style.display = 'none'; | |
gameOverEl.style.display = 'none'; | |
// Clear all game elements | |
obstacles.forEach(o => o.el.remove()); | |
clouds.forEach(c => c.el.remove()); | |
birds.forEach(b => b.el.remove()); | |
cacti.forEach(c => c.el.remove()); | |
mountains.forEach(m => m.el.remove()); | |
obstacles.length = clouds.length = birds.length = cacti.length = mountains.length = 0; | |
// Create initial elements | |
for (let i = 0; i < 5; i++) { | |
createCloud(Math.random() * window.innerWidth, Math.random() * 200); | |
} | |
for (let i = 0; i < 3; i++) { | |
createCactus(window.innerWidth + (i * 400), Math.random() * 50 + 50); | |
} | |
for (let i = 0; i < 2; i++) { | |
createMountain(window.innerWidth + (i * 600), Math.random() * 100); | |
} | |
animationId = requestAnimationFrame(gameLoop); | |
} | |
function gameLoop() { | |
if (isGameOver) return cancelAnimationFrame(animationId); | |
score += 0.1; | |
scoreEl.textContent = Math.floor(score); | |
if (Math.floor(score) % 500 === 0) gameSpeed += 0.5; | |
updateCarPosition(); | |
updateObstacles(); | |
updateClouds(); | |
updateBirds(); | |
updateCacti(); | |
updateMountains(); | |
updateRoadMarks(); | |
detectCollisions(); | |
animationId = requestAnimationFrame(gameLoop); | |
} | |
function updateCarPosition() { | |
if (!isJumping) return; | |
const jumpDuration = 700; | |
const t = (Date.now() - jumpStart) / jumpDuration; | |
if (t >= 1) { | |
car.style.bottom = '0px'; | |
isJumping = false; | |
} else { | |
const height = Math.round(220 * Math.sin(Math.PI * t)); | |
car.style.bottom = height + 'px'; | |
} | |
} | |
function updateObstacles() { | |
if (Math.random() < 0.0025 * gameSpeed) { | |
if (obstacles.length === 0 || obstacles[obstacles.length - 1].x < window.innerWidth - 400) { | |
createObstacle(); | |
} | |
} | |
obstacles.forEach((obj, idx) => { | |
obj.x -= gameSpeed; | |
obj.el.style.left = obj.x + 'px'; | |
if (obj.x < -200) { | |
obj.el.remove(); | |
obstacles.splice(idx, 1); | |
} | |
}); | |
} | |
function updateClouds() { | |
if (Math.random() < 0.002) createCloud(window.innerWidth, Math.random() * 200); | |
clouds.forEach((c, idx) => { | |
c.x -= gameSpeed * 0.5; | |
c.el.style.left = c.x + 'px'; | |
if (c.x < -100) { | |
c.el.remove(); | |
clouds.splice(idx, 1); | |
} | |
}); | |
} | |
function updateBirds() { | |
if (Math.random() < 0.0005 * gameSpeed) createBird(window.innerWidth, Math.random() * 150 + 50); | |
birds.forEach((b, idx) => { | |
b.x -= gameSpeed * 0.8; | |
b.el.style.left = b.x + 'px'; | |
if (b.x < -100) { | |
b.el.remove(); | |
birds.splice(idx, 1); | |
} | |
}); | |
} | |
function updateCacti() { | |
if (Math.random() < 0.0008 * gameSpeed) createCactus(window.innerWidth, Math.random() * 50 + 50); | |
cacti.forEach((c, idx) => { | |
c.x -= gameSpeed * 0.3; | |
c.el.style.left = c.x + 'px'; | |
if (c.x < -100) { | |
c.el.remove(); | |
cacti.splice(idx, 1); | |
} | |
}); | |
} | |
function updateMountains() { | |
if (mountains.length < 2 || mountains[mountains.length - 1].x < window.innerWidth - 400) { | |
createMountain(window.innerWidth, Math.random() * 100); | |
} | |
mountains.forEach((m, idx) => { | |
m.x -= gameSpeed * 0.2; | |
m.el.style.left = m.x + 'px'; | |
if (m.x < -300) { | |
m.el.remove(); | |
mountains.splice(idx, 1); | |
} | |
}); | |
} | |
function updateRoadMarks() { | |
roadMarkings.forEach(mark => { | |
mark.x -= gameSpeed; | |
if (mark.x < -50) mark.x = window.innerWidth; | |
mark.el.style.left = mark.x + 'px'; | |
}); | |
} | |
function detectCollisions() { | |
const carRect = car.getBoundingClientRect(); | |
// Fine-tuned shrink values | |
const shrinkLeft = 30; | |
const shrinkRight = 30; | |
const shrinkTop = 10; | |
const shrinkBottom = 10; | |
const carBox = { | |
left: carRect.left + shrinkLeft, | |
right: carRect.right - shrinkRight, | |
top: carRect.top + shrinkTop, | |
bottom: carRect.bottom - shrinkBottom, | |
}; | |
for (let obj of obstacles) { | |
const r = obj.el.getBoundingClientRect(); | |
const obstacleBox = { | |
left: r.left + 20, | |
right: r.right - 20, | |
top: r.top + 20, | |
bottom: r.bottom - 20, | |
}; | |
if ( | |
carBox.left < obstacleBox.right && | |
carBox.right > obstacleBox.left && | |
carBox.top < obstacleBox.bottom && | |
carBox.bottom > obstacleBox.top | |
) { | |
return triggerGameOver(); | |
} | |
} | |
} | |
function triggerGameOver() { | |
isGameOver = true; | |
finalScoreEl.textContent = Math.floor(score); | |
gameOverEl.style.display = 'block'; | |
} | |
function createObstacle() { | |
const el = document.createElement('div'); | |
el.className = 'obstacle'; | |
el.style.left = window.innerWidth + 'px'; | |
gameContainer.appendChild(el); | |
obstacles.push({ el, x: window.innerWidth }); | |
} | |
function createCloud(x, y) { | |
const el = document.createElement('div'); | |
el.className = 'cloud'; | |
el.style.left = x + 'px'; | |
el.style.top = y + 'px'; | |
gameContainer.appendChild(el); | |
clouds.push({ el, x }); | |
} | |
function createBird(x, y) { | |
const el = document.createElement('div'); | |
el.className = 'bird'; | |
el.style.left = x + 'px'; | |
el.style.top = y + 'px'; | |
gameContainer.appendChild(el); | |
birds.push({ el, x }); | |
} | |
function createCactus(x, height) { | |
const el = document.createElement('div'); | |
el.className = 'cactus'; | |
el.style.left = x + 'px'; | |
el.style.height = height + 'px'; | |
gameContainer.appendChild(el); | |
cacti.push({ el, x }); | |
} | |
function createMountain(x, height) { | |
const el = document.createElement('div'); | |
el.className = 'mountain'; | |
el.style.left = x + 'px'; | |
el.style.height = (150 + height) + 'px'; | |
gameContainer.appendChild(el); | |
mountains.push({ el, x }); | |
} | |
function jump() { | |
if (!gameStarted || isGameOver || isJumping) return; | |
isJumping = true; | |
jumpStart = Date.now(); | |
} | |
document.addEventListener('keydown', e => { | |
if ((e.code === 'Space' || e.key === 'ArrowUp') && !gameStarted) return startGame(); | |
if ((e.code === 'Space' || e.key === 'ArrowUp') && isGameOver) return startGame(); | |
if ((e.code === 'Space' || e.key === 'ArrowUp')) return jump(); | |
}); | |
startButton.addEventListener('click', startGame); | |
document.addEventListener('touchstart', (e) => { | |
e.preventDefault(); | |
if (!gameStarted || isGameOver) return startGame(); | |
jump(); | |
}); | |
// Prevent scrolling on mobile when touching the game area | |
document.addEventListener('touchmove', (e) => { | |
if (gameStarted) e.preventDefault(); | |
}, { passive: false }); | |
</script> | |
</body> | |
</html> |