🎮ArcadeLab

Zombie Survival

by AtomicBolt46
244 lines5.2 KB
▶ Play

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Zombie Survival</title>

<style>
* { box-sizing: border-box; }

body {
    margin: 0;
    background: #101820;
    color: white;
    font-family: Arial;
    text-align: center;
}

h1 { color: #5cff72; }

canvas {
    background: #26352d;
    border: 4px solid #5cff72;
    max-width: 95vw;
}

button {
    padding: 12px 25px;
    background: #5cff72;
    border: none;
    border-radius: 8px;
    font-size: 18px;
    font-weight: bold;
    cursor: pointer;
}

p { color: #ddd; }
</style>
</head>

<body>

<h1>🧟 ZOMBIE SURVIVAL</h1>
<p>WASD or arrow keys to move | SPACE to shoot</p>
<h3>Score: <span id="score">0</span> |
Health: <span id="health">100</span></h3>

<canvas id="game" width="800" height="500"></canvas>

<p><button onclick="restart()">RESTART GAME</button></p>

<script>
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");

let keys = {};
let player, zombies, bullets, score, health, gameOver;
let spawnTimer;

function restart() {
    player = {x:400, y:250, size:24, speed:4};
    zombies = [];
    bullets = [];
    score = 0;
    health = 100;
    gameOver = false;
    spawnTimer = 0;
    updateHUD();
}

document.addEventListener("keydown", e => {
    keys[e.key.toLowerCase()] = true;
    if(e.code === "Space") e.preventDefault();
});

document.addEventListener("keyup", e => {
    keys[e.key.toLowerCase()] = false;
});

function shoot() {
    bullets.push({
        x:player.x,
        y:player.y,
        dx:0,
        dy:-7,
        life:70
    });
}

let shooting = false;
document.addEventListener("keydown", e => {
    if(e.code === "Space" && !shooting && !gameOver) {
        shoot();
        shooting = true;
    }
});
document.addEventListener("keyup", e => {
    if(e.code === "Space") shooting = false;
});

function updateHUD() {
    document.getElementById("score").textContent = score;
    document.getElementById("health").textContent = health;
}

function update() {
    if(gameOver) return;

    if(keys["w"] || keys["arrowup"]) player.y -= player.speed;
    if(keys["s"] || keys["arrowdown"]) player.y += player.speed;
    if(keys["a"] || keys["arrowleft"]) player.x -= player.speed;
    if(keys["d"] || keys["arrowright"]) player.x += player.speed;

    player.x = Math.max(15, Math.min(785, player.x));
    player.y = Math.max(15, Math.min(485, player.y));

    spawnTimer++;
    if(spawnTimer > Math.max(20, 90-score)) {
        let side = Math.floor(Math.random()*4);
        let x = side===0 ? 0 : side===1 ? 800 : Math.random()*800;
        let y = side===2 ? 0 : side===3 ? 500 : Math.random()*500;

        zombies.push({x,y,size:25,speed:1+score*0.015});
        spawnTimer = 0;
    }

    zombies.forEach(z => {
        let dx = player.x-z.x;
        let dy = player.y-z.y;
        let d = Math.hypot(dx,dy) || 1;

        z.x += dx/d*z.speed;
        z.y += dy/d*z.speed;

        if(d < 30 && !z.hitCooldown) {
            health -= 10;
            z.hitCooldown = 45;
            updateHUD();
        }
        if(z.hitCooldown) z.hitCooldown--;
    });

    bullets.forEach(b => {
        b.x += b.dx;
        b.y += b.dy;
        b.life--;
    });

    bullets = bullets.filter(b => b.life>0);

    bullets.forEach(b => {
        zombies.forEach(z => {
            if(Math.hypot(b.x-z.x,b.y-z.y)<25 && !z.dead) {
                z.dead = true;
                b.life = 0;
                score += 10;
                updateHUD();
            }
        });
    });

    zombies = zombies.filter(z => !z.dead);

    if(health <= 0) {
        health = 0;
        gameOver = true;
        updateHUD();
    }
}

function draw() {
    ctx.clearRect(0,0,800,500);

    // Background grid
    ctx.strokeStyle = "#34483b";
    for(let x=0;x<800;x+=50) {
        ctx.beginPath();
        ctx.moveTo(x,0);
        ctx.lineTo(x,500);
        ctx.stroke();
    }
    for(let y=0;y<500;y+=50) {
        ctx.beginPath();
        ctx.moveTo(0,y);
        ctx.lineTo(800,y);
        ctx.stroke();
    }

    // Player
    ctx.fillStyle = "#38aaff";
    ctx.fillRect(player.x-12,player.y-12,24,24);
    ctx.fillStyle = "white";
    ctx.fillRect(player.x-5,player.y-5,4,4);
    ctx.fillRect(player.x+3,player.y-5,4,4);

    // Zombies
    zombies.forEach(z => {
        ctx.fillStyle = "#7cff48";
        ctx.beginPath();
        ctx.arc(z.x,z.y,13,0,Math.PI*2);
        ctx.fill();

        ctx.fillStyle = "black";
        ctx.fillRect(z.x-7,z.y-4,4,4);
        ctx.fillRect(z.x+3,z.y-4,4,4);
    });

    // Bullets
    bullets.forEach(b => {
        ctx.fillStyle = "yellow";
        ctx.beginPath();
        ctx.arc(b.x,b.y,5,0,Math.PI*2);
        ctx.fill();
    });

    if(gameOver) {
        ctx.fillStyle = "rgba(0,0,0,0.8)";
        ctx.fillRect(0,0,800,500);

        ctx.fillStyle = "red";
        ctx.font = "bold 55px Arial";
        ctx.textAlign = "center";
        ctx.fillText("GAME OVER",400,220);

        ctx.fillStyle = "white";
        ctx.font = "25px Arial";
        ctx.fillText("Final Score: "+score,400,270);
        ctx.fillText("Press RESTART to play again",400,320);
    }
}

function loop() {
    update();
    draw();
    requestAnimationFrame(loop);
}

restart();
loop();
</script>

</body>
</html>

Game Source: Zombie Survival

Creator: AtomicBolt46

Libraries: none

Complexity: complex (244 lines, 5.2 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: zombie-survival-atomicbolt46" to link back to the original. Then publish at arcadelab.ai/publish.