文件名:game3.html

贪吃蛇网页游戏,运行的效果还不错,直接po图和代码。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>贪吃蛇</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            background: #0f0f1e;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            font-family: "Microsoft YaHei", sans-serif;
            color: #fff;
        }
        h1 {
            font-size: 36px;
            color: #4caf50;
            margin-bottom: 6px;
            text-shadow: 0 0 20px rgba(76,175,80,0.4);
        }
        #info {
            display: flex;
            gap: 40px;
            margin-bottom: 12px;
            font-size: 20px;
        }
        #info span { color: #4caf50; font-weight: bold; }
        #game-container {
            position: relative;
            border-radius: 12px;
            overflow: hidden;
            box-shadow: 0 0 40px rgba(76,175,80,0.15), 0 20px 60px rgba(0,0,0,0.5);
        }
        canvas { display: block; background: #1a1a2e; }
        #controls {
            margin-top: 15px;
            display: flex;
            gap: 12px;
        }
        .btn {
            padding: 10px 28px;
            font-size: 16px;
            font-weight: bold;
            color: #fff;
            background: linear-gradient(135deg, #667eea, #764ba2);
            border: none;
            border-radius: 8px;
            cursor: pointer;
            transition: transform 0.15s;
        }
        .btn:hover { transform: scale(1.06); }
        .btn.green { background: linear-gradient(135deg, #4caf50, #2e7d32); }
        #speed-select {
            padding: 10px 18px;
            font-size: 16px;
            border-radius: 8px;
            border: 1px solid #4caf50;
            background: #1a1a2e;
            color: #fff;
            cursor: pointer;
        }
        #overlay {
            position: absolute;
            top: 0; left: 0; right: 0; bottom: 0;
            background: rgba(0,0,0,0.75);
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            z-index: 10;
        }
        #overlay h2 { font-size: 42px; color: #4caf50; margin-bottom: 12px; }
        #overlay p { font-size: 22px; margin-bottom: 8px; color: #ccc; }
        #overlay .score { color: #ffd700; font-size: 26px; margin-bottom: 25px; }
        #overlay button {
            padding: 12px 40px;
            font-size: 20px;
            font-weight: bold;
            color: #fff;
            background: linear-gradient(135deg, #4caf50, #2e7d32);
            border: none;
            border-radius: 30px;
            cursor: pointer;
            box-shadow: 0 4px 15px rgba(76,175,80,0.4);
            transition: transform 0.2s;
        }
        #overlay button:hover { transform: scale(1.08); }
        #hint {
            margin-top: 10px;
            font-size: 14px;
            color: rgba(255,255,255,0.4);
        }
        /* 移动端方向键 */
        #mobile-controls {
            display: none;
            margin-top: 15px;
            grid-template-columns: repeat(3, 60px);
            grid-template-rows: repeat(3, 60px);
            gap: 6px;
        }
        #mobile-controls button {
            background: rgba(76,175,80,0.2);
            border: 1px solid #4caf50;
            border-radius: 10px;
            color: #4caf50;
            font-size: 24px;
            cursor: pointer;
        }
        #mobile-controls .up { grid-column: 2; grid-row: 1; }
        #mobile-controls .left { grid-column: 1; grid-row: 2; }
        #mobile-controls .right { grid-column: 3; grid-row: 2; }
        #mobile-controls .down { grid-column: 2; grid-row: 3; }
        @media (max-width: 768px) {
            #mobile-controls { display: grid; }
        }
    </style>
</head>
<body>
    <h1>贪吃蛇</h1>
    <div id="info">
        <div>分数: <span id="score">0</span></div>
        <div>最高: <span id="high">0</span></div>
        <div>长度: <span id="length">3</span></div>
    </div>
    <div id="game-container">
        <canvas id="game" width="480" height="480"></canvas>
        <div id="overlay">
            <h2 id="ov-title">贪吃蛇</h2>
            <p id="ov-text">吃掉食物变长,不要撞墙或撞到自己</p>
            <button onclick="startGame()">开始游戏</button>
        </div>
    </div>
    <div id="controls">
        <select id="speed-select">
            <option value="150">慢速</option>
            <option value="100" selected>中速</option>
            <option value="65">快速</option>
            <option value="40">极速</option>
        </select>
        <button class="btn green" onclick="startGame()">重新开始</button>
    </div>
    <div id="mobile-controls">
        <button class="up" onclick="setDir(0,-1)">▲</button>
        <button class="left" onclick="setDir(-1,0)">◀</button>
        <button class="right" onclick="setDir(1,0)">▶</button>
        <button class="down" onclick="setDir(0,1)">▼</button>
    </div>
    <div id="hint">方向键 / WASD 控制 · 空格暂停 · 选择速度后开始</div>

<script>
var canvas = document.getElementById('game');
var ctx = canvas.getContext('2d');
var GRID = 24;
var COLS = canvas.width / GRID;
var ROWS = canvas.height / GRID;

var snake, dir, nextDir, food, score, highScore, speed, gameLoop, isPaused, state;

highScore = parseInt(localStorage.getItem('snakeHigh') || '0');
document.getElementById('high').textContent = highScore;

function init() {
    snake = [
        { x: Math.floor(COLS/2), y: Math.floor(ROWS/2) },
        { x: Math.floor(COLS/2) - 1, y: Math.floor(ROWS/2) },
        { x: Math.floor(COLS/2) - 2, y: Math.floor(ROWS/2) }
    ];
    dir = { x: 1, y: 0 };
    nextDir = { x: 1, y: 0 };
    score = 0;
    isPaused = false;
    state = 'playing';
    spawnFood();
    updateUI();
}

function spawnFood() {
    var tries = 0;
    while (tries < 200) {
        var fx = Math.floor(Math.random() * COLS);
        var fy = Math.floor(Math.random() * ROWS);
        var onSnake = false;
        for (var i = 0; i < snake.length; i++) {
            if (snake[i].x === fx && snake[i].y === fy) { onSnake = true; break; }
        }
        if (!onSnake) {
            food = { x: fx, y: fy };
            return;
        }
        tries++;
    }
}

function setDir(dx, dy) {
    if (state !== 'playing' || isPaused) return;
    // 不能180度掉头
    if (snake.length > 1 && dx === -dir.x && dy === -dir.y) return;
    nextDir = { x: dx, y: dy };
}

function update() {
    if (state !== 'playing' || isPaused) return;

    dir = nextDir;
    var head = { x: snake[0].x + dir.x, y: snake[0].y + dir.y };

    // 撞墙
    if (head.x < 0 || head.x >= COLS || head.y < 0 || head.y >= ROWS) {
        gameOver();
        return;
    }

    // 撞自己
    for (var i = 0; i < snake.length; i++) {
        if (snake[i].x === head.x && snake[i].y === head.y) {
            gameOver();
            return;
        }
    }

    snake.unshift(head);

    // 吃到食物
    if (head.x === food.x && head.y === food.y) {
        score += 10;
        if (score > highScore) {
            highScore = score;
            localStorage.setItem('snakeHigh', highScore);
        }
        spawnFood();
    } else {
        snake.pop();
    }

    updateUI();
}

function gameOver() {
    state = 'gameover';
    clearInterval(gameLoop);
    showOverlay('游戏结束', '撞到了!', '重新开始');
}

function startGame() {
    hideOverlay();
    speed = parseInt(document.getElementById('speed-select').value);
    init();
    clearInterval(gameLoop);
    gameLoop = setInterval(function() {
        update();
        draw();
    }, speed);
    draw();
}

function togglePause() {
    if (state !== 'playing') return;
    isPaused = !isPaused;
}

function updateUI() {
    document.getElementById('score').textContent = score;
    document.getElementById('high').textContent = highScore;
    document.getElementById('length').textContent = snake.length;
}

function showOverlay(title, text, btn) {
    document.getElementById('ov-title').textContent = title;
    document.getElementById('ov-text').textContent = text;
    var ov = document.getElementById('overlay');
    ov.style.display = 'flex';
    // 更新按钮文字
    var btnEl = ov.querySelector('button');
    btnEl.textContent = btn;
}

function hideOverlay() {
    document.getElementById('overlay').style.display = 'none';
}

// ============ 渲染 ============

function draw() {
    // 背景
    ctx.fillStyle = '#1a1a2e';
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // 网格
    ctx.strokeStyle = 'rgba(255,255,255,0.03)';
    ctx.lineWidth = 1;
    for (var i = 0; i <= COLS; i++) {
        ctx.beginPath();
        ctx.moveTo(i * GRID, 0);
        ctx.lineTo(i * GRID, canvas.height);
        ctx.stroke();
    }
    for (var j = 0; j <= ROWS; j++) {
        ctx.beginPath();
        ctx.moveTo(0, j * GRID);
        ctx.lineTo(canvas.width, j * GRID);
        ctx.stroke();
    }

    // 食物
    drawFood();

    // 蛇
    for (var k = snake.length - 1; k >= 0; k--) {
        drawSnakeSegment(snake[k], k === 0, k);
    }

    // 暂停提示
    if (isPaused && state === 'playing') {
        ctx.fillStyle = 'rgba(0,0,0,0.5)';
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = '#fff';
        ctx.font = 'bold 32px "Microsoft YaHei"';
        ctx.textAlign = 'center';
        ctx.fillText('暂停', canvas.width / 2, canvas.height / 2);
    }
}

function drawFood() {
    var cx = food.x * GRID + GRID / 2;
    var cy = food.y * GRID + GRID / 2;
    var r = GRID / 2 - 3;

    // 光晕
    var glow = ctx.createRadialGradient(cx, cy, 0, cx, cy, r + 6);
    glow.addColorStop(0, 'rgba(255,82,82,0.4)');
    glow.addColorStop(1, 'rgba(255,82,82,0)');
    ctx.fillStyle = glow;
    ctx.beginPath();
    ctx.arc(cx, cy, r + 6, 0, Math.PI * 2);
    ctx.fill();

    // 苹果
    var grad = ctx.createRadialGradient(cx - 3, cy - 3, 0, cx, cy, r);
    grad.addColorStop(0, '#ff8a80');
    grad.addColorStop(0.5, '#ff5252');
    grad.addColorStop(1, '#c62828');
    ctx.fillStyle = grad;
    ctx.beginPath();
    ctx.arc(cx, cy, r, 0, Math.PI * 2);
    ctx.fill();

    // 高光
    ctx.fillStyle = 'rgba(255,255,255,0.5)';
    ctx.beginPath();
    ctx.ellipse(cx - r * 0.3, cy - r * 0.3, r * 0.25, r * 0.15, -0.5, 0, Math.PI * 2);
    ctx.fill();

    // 叶子
    ctx.fillStyle = '#4caf50';
    ctx.beginPath();
    ctx.ellipse(cx + 2, cy - r, 4, 2, -0.8, 0, Math.PI * 2);
    ctx.fill();
}

function drawSnakeSegment(seg, isHead, idx) {
    var px = seg.x * GRID;
    var py = seg.y * GRID;
    var pad = 2;
    var size = GRID - pad * 2;

    // 颜色渐变:头部最亮
    var t = idx / Math.max(snake.length, 1);
    var r = Math.floor(76 + (129 - 76) * t);
    var g = Math.floor(175 + (199 - 175) * t);
    var b = Math.floor(80 + (132 - 80) * t);
    var color = 'rgb(' + r + ',' + g + ',' + b + ')';

    if (isHead) {
        // 头部带光晕
        ctx.shadowColor = '#4caf50';
        ctx.shadowBlur = 10;
    }

    ctx.fillStyle = color;
    roundRect(px + pad, py + pad, size, size, 6);
    ctx.fill();

    ctx.shadowBlur = 0;

    // 高光
    ctx.fillStyle = 'rgba(255,255,255,0.15)';
    roundRect(px + pad + 2, py + pad + 2, size - 4, 5, 3);
    ctx.fill();

    // 头部画眼睛
    if (isHead) {
        ctx.fillStyle = '#fff';
        var eyeSize = 3;
        var cx = px + GRID / 2;
        var cy = py + GRID / 2;

        var e1x, e1y, e2x, e2y;
        if (dir.x === 1) {
            e1x = cx + 4; e1y = cy - 4; e2x = cx + 4; e2y = cy + 4;
        } else if (dir.x === -1) {
            e1x = cx - 4; e1y = cy - 4; e2x = cx - 4; e2y = cy + 4;
        } else if (dir.y === 1) {
            e1x = cx - 4; e1y = cy + 4; e2x = cx + 4; e2y = cy + 4;
        } else {
            e1x = cx - 4; e1y = cy - 4; e2x = cx + 4; e2y = cy - 4;
        }

        ctx.beginPath();
        ctx.arc(e1x, e1y, eyeSize, 0, Math.PI * 2);
        ctx.arc(e2x, e2y, eyeSize, 0, Math.PI * 2);
        ctx.fill();

        ctx.fillStyle = '#000';
        ctx.beginPath();
        ctx.arc(e1x + dir.x, e1y + dir.y, 1.5, 0, Math.PI * 2);
        ctx.arc(e2x + dir.x, e2y + dir.y, 1.5, 0, Math.PI * 2);
        ctx.fill();
    }
}

function roundRect(x, y, w, h, r) {
    ctx.beginPath();
    ctx.moveTo(x + r, y);
    ctx.lineTo(x + w - r, y);
    ctx.quadraticCurveTo(x + w, y, x + w, y + r);
    ctx.lineTo(x + w, y + h - r);
    ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
    ctx.lineTo(x + r, y + h);
    ctx.quadraticCurveTo(x, y + h, x, y + h - r);
    ctx.lineTo(x, y + r);
    ctx.quadraticCurveTo(x, y, x + r, y);
    ctx.closePath();
}

// ============ 输入 ============

document.addEventListener('keydown', function(e) {
    switch(e.code) {
        case 'ArrowUp': case 'KeyW': e.preventDefault(); setDir(0, -1); break;
        case 'ArrowDown': case 'KeyS': e.preventDefault(); setDir(0, 1); break;
        case 'ArrowLeft': case 'KeyA': e.preventDefault(); setDir(-1, 0); break;
        case 'ArrowRight': case 'KeyD': e.preventDefault(); setDir(1, 0); break;
        case 'Space': e.preventDefault(); togglePause(); break;
    }
});

// 触屏滑动
var touchStart = null;
canvas.addEventListener('touchstart', function(e) {
    e.preventDefault();
    touchStart = { x: e.touches[0].clientX, y: e.touches[0].clientY };
});
canvas.addEventListener('touchend', function(e) {
    if (!touchStart) return;
    var dx = e.changedTouches[0].clientX - touchStart.x;
    var dy = e.changedTouches[0].clientY - touchStart.y;
    if (Math.abs(dx) > Math.abs(dy)) {
        setDir(dx > 0 ? 1 : -1, 0);
    } else {
        setDir(0, dy > 0 ? 1 : -1);
    }
    touchStart = null;
});

// 初始画面
draw();
</script>
</body>
</html>

Logo

AtomGit AI 社区提供模型库、数据集、Agent、Token等资源

更多推荐