贪吃蛇 带最高分记录版
by AtomicBunny84190 lines6.2 KB
<!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.