Untitled
unknown
plain_text
a month ago
7.2 kB
5
Indexable
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Retro Neon Racer</title>
<style>
* { box-sizing: border-box; }
body {
margin: 0;
padding: 0;
background: #0a0a1a;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
font-family: 'Courier New', Courier, monospace;
color: #00ffcc;
overflow: hidden;
}
h1 {
margin: 5px 0;
text-shadow: 0 0 10px #ff0077, 0 0 20px #ff0077;
font-size: 2rem;
letter-spacing: 4px;
}
#gameCanvas {
border: 4px solid #ff0077;
box-shadow: 0 0 30px #ff0077;
background: #111;
max-width: 100%;
}
.instructions {
margin-top: 15px;
font-size: 0.9rem;
text-align: center;
color: #8899af;
}
span { color: #fff; font-weight: bold; }
</style>
</head>
<body>
<h1>NEON RACER</h1>
<canvas id="gameCanvas" width="800" height="400"></canvas>
<div class="instructions">
[UP] Accelerate | [DOWN] Brake | [LEFT/RIGHT] Steer
</div>
<script>
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
// Game Settings & State
const width = canvas.width;
const height = canvas.height;
const roadW = 800;
const segL = 200; // Segment length
const camD = 0.84; // Camera depth
let speed = 0;
const maxSpeed = 200;
const accel = 2;
const decel = -1.5;
const breaking = -5;
const friction = -0.7;
let playerX = 0; // Player offset from center (-1 to 1)
let position = 0; // Current position along the track
let score = 0;
// Keyboard Input
const keys = { ArrowLeft: false, ArrowRight: false, ArrowUp: false, ArrowDown: false };
window.addEventListener('keydown', e => { if(keys[e.key] !== undefined) keys[e.key] = true; });
window.addEventListener('keyup', e => { if(keys[e.key] !== undefined) keys[e.key] = false; });
// Generate Track Segments
const segments = [];
for (let i = 0; i < 1500; i++) {
let curve = 0;
// Add curves to make it interesting
if (i > 200 && i < 400) curve = 1.5;
if (i > 600 && i < 800) curve = -2;
if (i > 900 && i < 1100) curve = 3;
if (i > 1200 && i < 1400) curve = -1.5;
segments.push({
p1: { x: 0, y: 0, z: i * segL },
p2: { x: 0, y: 0, z: (i + 1) * segL },
curve: curve,
color: Math.floor(i / 3) % 2 ? { road: '#2a2a3b', grass: '#0d0d1e', line: '#ff0077' }
: { road: '#1e1e2f', grass: '#080815', line: '#00ffcc' }
});
}
const trackLength = segments.length * segL;
// 3D Projection math
function project(p, cameraX, cameraY, cameraZ, cameraDepth, width, height, roadWidth) {
let transX = p.x - cameraX;
let transY = p.y - cameraY;
let transZ = p.z - cameraZ;
let scale = cameraDepth / transZ;
return {
x: Math.round((width / 2) + (scale * transX * width / 2)),
y: Math.round((height / 2) - (scale * transY * height / 2)),
w: Math.round(scale * roadWidth * width / 2)
};
}
// Render loop
function update() {
// --- Physics & Movement ---
if (keys.ArrowUp) speed += accel;
else if (keys.ArrowDown) speed += breaking;
else speed += friction;
// Clamp speed
speed = Math.max(0, Math.min(speed, maxSpeed));
// Handle steering
if (speed > 0) {
if (keys.ArrowLeft) playerX -= 0.04 * (speed / maxSpeed);
if (keys.ArrowRight) playerX += 0.04 * (speed / maxSpeed);
}
// Move along track
position += speed;
if (position >= trackLength) position -= trackLength; // Loop track
score += Math.floor(speed / 10);
// --- Drawing ---
ctx.clearRect(0, 0, width, height);
// Draw Sky & Horizon
let grad = ctx.createLinearGradient(0, 0, 0, height/2);
grad.addColorStop(0, '#050014');
grad.addColorStop(1, '#2d004d');
ctx.fillStyle = grad;
ctx.fillRect(0, 0, width, height / 2);
// Find starting segment
let startPos = Math.floor(position / segL);
let camH = 1500; // Camera height
let maxSegments = 300; // Draw distance
let x = 0;
let dx = 0;
// Render segments from back to front
for (let n = 0; n < maxSegments; n++) {
let l = segments[(startPos + n) % segments.length];
// Loop handling for track wrap-around
let p1z = l.p1.z - (startPos + n >= segments.length ? trackLength : 0);
let p2z = l.p2.z - (startPos + n >= segments.length ? trackLength : 0);
// Adjust for curves
dx += l.curve;
x += dx;
let s1 = project({ x: l.p1.x + x, y: l.p1.y, z: p1z }, playerX * roadW, camH, position, camD, width, height, roadW);
let s2 = project({ x: l.p2.x + x - dx, y: l.p2.y, z: p2z }, playerX * roadW, camH, position, camD, width, height, roadW);
// Clip distant segments behind camera
if (p1z <= position) continue;
// Draw Grass
ctx.fillStyle = l.color.grass;
ctx.fillRect(0, s2.y, width, s1.y - s2.y);
// Draw Road
ctx.fillStyle = l.color.road;
ctx.beginPath();
ctx.moveTo(s1.x - s1.w, s1.y);
ctx.lineTo(s1.x + s1.w, s1.y);
ctx.lineTo(s2.x + s2.w, s2.y);
ctx.lineTo(s2.x - s2.w, s2.y);
ctx.fill();
// Draw Side Lines (Rumble strips)
ctx.fillStyle = l.color.line;
ctx.beginPath();
ctx.moveTo(s1.x - s1.w, s1.y);
ctx.lineTo(s1.x - s1.w * 0.95, s1.y);
ctx.lineTo(s2.x - s2.w * 0.95, s2.y);
ctx.lineTo(s2.x - s2.w, s2.y);
ctx.fill();
ctx.beginPath();
ctx.moveTo(s1.x + s1.w, s1.y);
ctx.lineTo(s1.x + s1.w * 0.95, s1.y);
ctx.lineTo(s2.x + s2.w * 0.95, s2.y);
ctx.lineTo(s2.x + s2.w, s2.y);
ctx.fill();
}
// --- Draw Player Car (Simple Synthwave Delorean style) ---
let carX = width / 2;
let carY = height - 40;
// Outer Chassis
ctx.fillStyle = '#445566';
ctx.fillRect(carX - 45, carY - 20, 90, 30);
// Neon underglow
ctx.shadowBlur = 15;
ctx.shadowColor = '#00ffcc';
ctx.fillStyle = '#00ffcc';
ctx.fillRect(carX - 40, carY + 10, 80, 5);
ctx.shadowBlur = 0; // reset
// Windshield
ctx.fillStyle = '#111';
ctx.fillRect(carX - 35, carY - 35, 70, 15);
// Tail Lights
ctx.fillStyle = speed > 0 && keys.ArrowDown ? '#ff0000' : '#990000';
ctx.fillRect(carX - 40, carY - 15, 20, 8);
ctx.fillRect(carX + 20, carY - 15, 20, 8);
// Wheels
ctx.fillStyle = '#000';
ctx.fillRect(carX - 53, carY, 12, 25);
ctx.fillRect(carX + 41, carY, 12, 25);
// --- HUD (Heads Up Display) ---
ctx.fillStyle = '#ff0077';
ctx.font = 'bold 16px "Courier New"';
ctx.fillText(`SPEED: ${Math.floor(speed)} MPH`, 20, 40);
ctx.fillText(`SCORE: ${score}`, 20, 70);
requestAnimationFrame(update);
}
// Start Game Loop
update();
</script>
</body>
</html>Editor is loading...
Leave a Comment