Untitled

 avatar
unknown
plain_text
19 days ago
7.2 kB
4
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