Spaces:
Sleeping
Sleeping
<html> | |
<head> | |
<style> | |
body { margin: 0; } | |
#game-container { | |
width: 100%; | |
height: 400px; | |
background: black; | |
position: relative; | |
overflow: hidden; | |
} | |
.lander { | |
width: 32px; | |
height: 32px; | |
position: absolute; | |
transform: translate(-50%, -50%); | |
} | |
.thrust-particle { | |
width: 4px; | |
height: 4px; | |
background: red; | |
position: absolute; | |
border-radius: 50%; | |
} | |
.flag { | |
width: 4px; | |
height: 24px; | |
background: yellow; | |
position: absolute; | |
bottom: 48px; | |
} | |
.hud { | |
position: absolute; | |
color: white; | |
padding: 16px; | |
font-family: sans-serif; | |
} | |
.controls { | |
position: absolute; | |
top: 16px; | |
right: 16px; | |
color: white; | |
font-family: sans-serif; | |
font-size: 14px; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="game-container"> | |
<div class="hud"> | |
<div id="fuel">Fuel: 100%</div> | |
<div id="velocity">Velocity: 0 m/s</div> | |
<div id="score"></div> | |
</div> | |
<div class="controls"> | |
<div>↑ - Thrust</div> | |
<div>← → - Move</div> | |
<div>Land between flags</div> | |
</div> | |
</div> | |
<script> | |
class LunarLander { | |
constructor() { | |
this.container = document.getElementById('game-container'); | |
this.position = { x: 200, y: 50 }; | |
this.velocity = { x: 0, y: 0 }; | |
this.fuel = 100; | |
this.gameState = 'playing'; | |
this.thrust = false; | |
this.GRAVITY = 0.05; | |
this.THRUST = 0.15; | |
this.LANDING_SPEED = 3; | |
this.groundPoints = [ | |
{x: 0, y: 380}, {x: 100, y: 360}, {x: 150, y: 370}, | |
{x: 200, y: 350}, {x: 300, y: 350}, {x: 350, y: 370}, | |
{x: 400, y: 360}, {x: 450, y: 380}, {x: 500, y: 370} | |
]; | |
this.setupGame(); | |
} | |
setupGame() { | |
this.createLander(); | |
this.createFlags(); | |
this.createGround(); | |
this.setupControls(); | |
this.gameLoop(); | |
} | |
createLander() { | |
this.lander = document.createElement('div'); | |
this.lander.className = 'lander'; | |
this.lander.style.background = '#a855f7'; | |
this.container.appendChild(this.lander); | |
} | |
createFlags() { | |
const flag1 = document.createElement('div'); | |
flag1.className = 'flag'; | |
flag1.style.left = '190px'; | |
const flag2 = document.createElement('div'); | |
flag2.className = 'flag'; | |
flag2.style.left = '310px'; | |
this.container.appendChild(flag1); | |
this.container.appendChild(flag2); | |
} | |
createGround() { | |
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); | |
svg.style.position = 'absolute'; | |
svg.style.bottom = '0'; | |
svg.style.width = '100%'; | |
svg.style.height = '100%'; | |
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); | |
const d = `M${this.groundPoints.map(p => `${p.x} ${p.y}`).join(' L')}`; | |
path.setAttribute('d', d); | |
path.setAttribute('stroke', 'white'); | |
path.setAttribute('fill', 'none'); | |
path.setAttribute('stroke-width', '2'); | |
svg.appendChild(path); | |
this.container.appendChild(svg); | |
} | |
setupControls() { | |
document.addEventListener('keydown', (e) => { | |
if (this.gameState !== 'playing' || this.fuel <= 0) return; | |
switch (e.key) { | |
case 'ArrowUp': | |
this.thrust = true; | |
this.velocity.y -= this.THRUST; | |
this.fuel = Math.max(0, this.fuel - 0.5); | |
this.updateThrust(); | |
break; | |
case 'ArrowLeft': | |
this.velocity.x -= 0.1; | |
this.fuel = Math.max(0, this.fuel - 0.2); | |
break; | |
case 'ArrowRight': | |
this.velocity.x += 0.1; | |
this.fuel = Math.max(0, this.fuel - 0.2); | |
break; | |
} | |
}); | |
document.addEventListener('keyup', (e) => { | |
if (e.key === 'ArrowUp') { | |
this.thrust = false; | |
this.updateThrust(); | |
} | |
}); | |
} | |
updateThrust() { | |
const particles = this.lander.querySelectorAll('.thrust-particle'); | |
particles.forEach(p => p.remove()); | |
if (this.thrust) { | |
for (let i = 0; i < 4; i++) { | |
const particle = document.createElement('div'); | |
particle.className = 'thrust-particle'; | |
particle.style.left = `${Math.random() * 8 - 4}px`; | |
particle.style.top = `${Math.random() * 8}px`; | |
this.lander.appendChild(particle); | |
} | |
} | |
} | |
checkCollision() { | |
for (let i = 0; i < this.groundPoints.length - 1; i++) { | |
const p1 = this.groundPoints[i]; | |
const p2 = this.groundPoints[i + 1]; | |
if (this.position.x >= p1.x && this.position.x <= p2.x) { | |
const groundY = p1.y + ((p2.y - p1.y) * (this.position.x - p1.x)) / (p2.x - p1.x); | |
if (this.position.y >= groundY - 10) { | |
if (this.velocity.y < this.LANDING_SPEED * 1.5 && | |
Math.abs(this.velocity.x) < 2 && | |
this.position.x >= 190 && | |
this.position.x <= 310) { | |
this.gameState = 'won'; | |
document.getElementById('score').textContent = `Score: ${Math.floor(this.fuel * 100)}`; | |
} else { | |
this.gameState = 'crashed'; | |
} | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
updateHUD() { | |
document.getElementById('fuel').textContent = `Fuel: ${Math.floor(this.fuel)}%`; | |
document.getElementById('velocity').textContent = `Velocity: ${Math.floor(this.velocity.y * 10)} m/s`; | |
} | |
gameLoop = () => { | |
if (this.gameState === 'playing') { | |
this.position.x += this.velocity.x; | |
this.position.y += this.velocity.y; | |
this.velocity.x *= 0.99; | |
this.velocity.y += this.GRAVITY; | |
this.lander.style.left = `${this.position.x}px`; | |
this.lander.style.top = `${this.position.y}px`; | |
this.updateHUD(); | |
this.checkCollision(); | |
} | |
requestAnimationFrame(this.gameLoop); | |
} | |
} | |
new LunarLander(); | |
</script> | |
</body> | |
</html> |