🎮ArcadeLab

贪吃蛇 带最高分记录版

by AtomicBunny84
190 lines6.2 KB
▶ Play
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>贪吃蛇 带最高分记录版</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{background:#111;color:#0f0;font-family:system-ui,monospace;text-align:center;padding:10px}
#score{font-size:22px;margin-bottom:4px}
#highScore{font-size:16px;color:#cccccc;margin-bottom:8px}
.game{border:3px solid #0f0;background:#000;margin:0 auto}
#tip{color:#aaa;margin:8px 0;font-size:14px}
.btn-box{margin-top:12px;display:grid;grid-template-columns:repeat(3,85px);gap:8px;justify-content:center}
.ctrl-btn{width:85px;height:85px;font-size:30px;background:#222;color:#0f0;border:2px solid #0f0;border-radius:8px;-webkit-tap-highlight-color:transparent;cursor:pointer}
.ctrl-btn:active{background:#0f0;color:#000}
#restart{margin-top:12px;padding:8px 16px;font-size:18px;display:none}
</style>
</head>
<body>
<div id="score">本局得分:0</div>
<div id="highScore">历史最高分:0</div>
<canvas class="game" id="snake" width="320" height="320"></canvas>
<div id="tip">滑动画布 或 点击下方按键控制</div>
<div class="btn-box">
<div></div><button class="ctrl-btn" data-dir="up">↑</button><div></div>
<button class="ctrl-btn" data-dir="left">←</button><div></div><button class="ctrl-btn" data-dir="right">→</button>
<div></div><button class="ctrl-btn" data-dir="down">↓</button><div></div>
</div>
<button id="restart">游戏结束,点击重启</button>
<script>
let audioCtx;
function initAudio(){
    audioCtx = new (window.AudioContext || window.webkitAudioContext)();
}
function playTone(freq, dur, type="square", vol=0.2){
    if(!audioCtx) return;
    const osc = audioCtx.createOscillator();
    const gain = audioCtx.createGain();
    osc.type = type;
    osc.frequency.value = freq;
    gain.gain.setValueAtTime(vol, audioCtx.currentTime);
    gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + dur);
    osc.connect(gain);
    gain.connect(audioCtx.destination);
    osc.start();
    osc.stop(audioCtx.currentTime + dur);
}
function playEat(){
    playTone(880,0.1,"sine",0.25);
    setTimeout(()=>playTone(1320,0.12,"sine",0.2),80);
}
function playGameOver(){
    playTone(220,0.25,"sawtooth",0.25);
    setTimeout(()=>playTone(165,0.3,"sawtooth",0.2),200);
}
let bgmTimer;
function startBGM(){
    let beat = 0;
    bgmTimer = setInterval(()=>{
        const note = [330,392,440,392][beat%4];
        playTone(note,0.18,"square",0.08);
        beat++;
    },220);
}
function stopBGM(){
    clearInterval(bgmTimer);
}

const canvas = document.getElementById('snake');
const ctx = canvas.getContext('2d');
const scoreDom = document.getElementById('score');
const highScoreDom = document.getElementById('highScore');
const restartBtn = document.getElementById('restart');
const size = 16;

// 读取本地保存的最高分
let highScore = Number(localStorage.getItem("snakeHigh")) || 0;
highScoreDom.textContent = "历史最高分:" + highScore;

let snake = [{x:160,y:160}];
let dir = 'right';
let nextDir = 'right';
let food = randomFood();
let score = 0;
let timer;
let touchStart = {x:0,y:0};
let gameRunning = true;

function randomFood(){
    return {
        x: Math.floor(Math.random() * (canvas.width / size)) * size,
        y: Math.floor(Math.random() * (canvas.height / size)) * size
    }
}
function draw(){
    ctx.fillStyle = "#000";
    ctx.fillRect(0,0,canvas.width,canvas.height);
    ctx.fillStyle = "#0f0";
    snake.forEach(p=>ctx.fillRect(p.x,p.y,size-2,size-2));
    ctx.fillStyle = "#f33";
    ctx.fillRect(food.x,food.y,size-2,size-2);
}
function update(){
    if(!gameRunning) return;
    dir = nextDir;
    const head = {...snake[0]};
    if(dir === 'up') head.y -= size;
    if(dir === 'down') head.y += size;
    if(dir === 'left') head.x -= size;
    if(dir === 'right') head.x += size;

    // 撞墙/撞到自己 游戏结束
    if(head.x < 0 || head.y <0 || head.x >= canvas.width || head.y >= canvas.height || snake.some(p=>p.x===head.x && p.y===head.y)){
        gameRunning = false;
        clearInterval(timer);
        stopBGM();
        playGameOver();
        // 判断是否刷新最高分,保存到本地
        if(score > highScore){
            highScore = score;
            localStorage.setItem("snakeHigh", highScore);
            highScoreDom.textContent = "历史最高分:" + highScore + "【新纪录!】";
        }
        restartBtn.style.display = "block";
        return;
    }
    snake.unshift(head);
    if(head.x === food.x && head.y === food.y){
        score += 10;
        scoreDom.textContent = "本局得分:"+score;
        food = randomFood();
        playEat();
    }else{
        snake.pop();
    }
    draw();
}
function restartGame(){
    initAudio();
    restartBtn.style.display = "none";
    gameRunning = true;
    snake = [{x:160,y:160}];
    dir = 'right';
    nextDir = 'right';
    food = randomFood();
    score = 0;
    scoreDom.textContent = "本局得分:0";
    highScoreDom.textContent = "历史最高分:" + highScore;
    draw();
    stopBGM();
    startBGM();
    timer = setInterval(update,120);
}
// 按键控制
document.querySelectorAll('.ctrl-btn').forEach(btn=>{
    btn.addEventListener('click',()=>{
        if(!gameRunning) return;
        const d = btn.dataset.dir;
        if( (dir==='up'&&d!=='down') || (dir==='down'&&d!=='up') || (dir==='left'&&d!=='right') || (dir==='right'&&d!=='left') ){
            nextDir = d;
        }
    })
})
// 滑动控制
canvas.addEventListener('touchstart',(e)=>{
    touchStart.x = e.touches[0].clientX;
    touchStart.y = e.touches[0].clientY;
});
canvas.addEventListener('touchend',(e)=>{
    if(!gameRunning) return;
    const endX = e.changedTouches[0].clientX;
    const endY = e.changedTouches[0].clientY;
    const dx = endX - touchStart.x;
    const dy = endY - touchStart.y;
    if(Math.abs(dx) > Math.abs(dy)){
        if(dx > 30 && dir !== 'left') nextDir = 'right';
        else if(dx < -30 && dir !== 'right') nextDir = 'left';
    }else{
        if(dy > 30 && dir !== 'up') nextDir = 'down';
        else if(dy < -30 && dir !== 'down') nextDir = 'up';
    }
});
restartBtn.onclick = restartGame;
draw();
timer = setInterval(update,120);
</script>
</body>
</html>

Game Source: 贪吃蛇 带最高分记录版

Creator: AtomicBunny84

Libraries: none

Complexity: moderate (190 lines, 6.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: game-atomicbunny84-msz0ynzn" to link back to the original. Then publish at arcadelab.ai/publish.