Meta Description" name="description" />
<!DOCTYPE html>
<html>
<head>
<title>Hill Climb JS</title>
<style>
body { margin: 0; overflow: hidden; background: #87CEEB; font-family: sans-serif; }
canvas { display: block; }
#ui { position: absolute; top: 20px; left: 20px; color: white; text-shadow: 2px 2px 4px #000; pointer-events: none; }
</style>
</head>
<body>
<div id="ui">
<h1>Hill Climb JS</h1>
<p>Hold <b>SPACE</b> or <b>CLICK</b> to Accelerate</p>
</div>
<canvas id="gameCanvas"></canvas>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// --- Game Settings ---
let speed = 0;
let accel = 0;
let pos = 0; // Horizontal scroll position
const gravity = 0.15;
const friction = 0.98;
// --- Terrain Generation ---
const points = [];
let nextPoint = 0;
function generateTerrain() {
while (points.length < 200) {
const x = nextPoint;
// Using sine waves to create "curved" hills
const y = (canvas.height * 0.7) +
Math.sin(nextPoint * 0.01) * 50 +
Math.sin(nextPoint * 0.005) * 100;
// Randomly decide to place a tree
const hasTree = Math.random() > 0.95 && nextPoint > 500;
points.push({ x, y, hasTree });
nextPoint += 20;
}
}
// --- Player Object ---
const player = {
x: 150,
y: 0,
vY: 0,
angle: 0,
wheelRot: 0
};
// --- Input Handling ---
let isAccelerating = false;
window.addEventListener('keydown', (e) => { if (e.code === 'Space') isAccelerating = true; });
window.addEventListener('keyup', (e) => { if (e.code === 'Space') isAccelerating = false; });
window.addEventListener('mousedown', () => isAccelerating = true);
window.addEventListener('mouseup', () => isAccelerating = false);
function drawTree(x, y) {
ctx.fillStyle = '#644117'; // Trunk
ctx.fillRect(x - 5, y - 40, 10, 40);
ctx.fillStyle = '#0B5345'; // Leaves
ctx.beginPath();
ctx.arc(x, y - 50, 25, 0, Math.PI * 2);
ctx.fill();
}
function update() {
generateTerrain();
// Physics logic
if (isAccelerating) accel += 0.2;
else accel *= 0.9;
speed = (speed + accel) * friction;
if (speed < 0) speed = 0;
pos += speed;
// Find current ground height
const currentPoint = points.find(p => p.x >= pos + player.x);
const groundY = currentPoint ? currentPoint.y : canvas.height;
// Simple gravity and collision
if (player.y < groundY - 20) {
player.vY += gravity;
} else {
player.vY = 0;
player.y = groundY - 20;
// Adjust angle based on hill slope
const nextP = points.find(p => p.x >= pos + player.x + 20);
if (nextP) {
player.angle = Math.atan2(nextP.y - currentPoint.y, 20);
}
}
player.y += player.vY;
player.wheelRot += speed * 0.1;
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw Terrain & Trees
ctx.beginPath();
ctx.moveTo(0, canvas.height);
ctx.fillStyle = '#228B22';
for (let i = 0; i < points.length; i++) {
const p = points[i];
const screenX = p.x - pos;
if (screenX < -50) continue;
if (screenX > canvas.width + 50) break;
ctx.lineTo(screenX, p.y);
if (p.hasTree) drawTree(screenX, p.y);
}
ctx.lineTo(canvas.width, canvas.height);
ctx.fill();
// Draw Car
ctx.save();
ctx.translate(player.x, player.y);
ctx.rotate(player.angle);
// Car Body
ctx.fillStyle = '#E74C3C';
ctx.fillRect(-25, -15, 50, 20);
ctx.fillStyle = '#3498DB'; // Window
ctx.fillRect(0, -12, 15, 8);
// Wheels
const drawWheel = (wx) => {
ctx.save();
ctx.translate(wx, 5);
ctx.rotate(player.wheelRot);
ctx.fillStyle = '#333';
ctx.beginPath();
ctx.arc(0, 0, 8, 0, Math.PI * 2);
ctx.fill();
ctx.strokeStyle = 'white';
ctx.moveTo(0, 0); ctx.lineTo(8, 0); ctx.stroke();
ctx.restore();
};
drawWheel(-15);
drawWheel(15);
ctx.restore();
requestAnimationFrame(() => {
update();
draw();
});
}
// Start
generateTerrain();
draw();
</script>
</body>
</html>1
1
5KB
5KB
131.0ms
196.0ms
131.0ms