🎮ArcadeLab

Remix of 20×20蛇对战 | 8食物3炸弹

by RapidBunny56
229 lines6.5 KB
▶ Play
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>20×20蛇对战 | 8食物3炸弹</title>
<style>
*{box-sizing:border-box;font-family:system-ui}
body{display:flex;flex-direction:column;align-items:center;background:#111;color:#fff;margin:4px}
#game{border:2px solid #aaa;background:#000}
.info{margin:6px 0;font-size:14px;text-align:center}
#restartBtn{
    margin:8px 0;
    padding:6px 16px;
    font-size:16px;
    background:#22aa44;
    color:white;
    border:none;
    border-radius:4px;
    cursor:pointer;
}
#restartBtn:hover{background:#2cc450}
</style>
</head>
<body>
<div class="info">
玩家1:WASD(绿)|玩家2:方向键(橙)<br>
吃到食物+1长度|撞炸弹-2长度|头撞对方身体直接淘汰<br>
蛇死亡遗留方块数量 = 生前吃掉的食物总数
</div>
<button id="restartBtn">🔄 重新开始游戏</button>
<canvas id="game" width="400" height="400" tabindex="0"></canvas>
<script>
// 等待DOM加载完毕再启动游戏,解决执行时机问题
document.addEventListener('DOMContentLoaded', function(){
const size = 20;
const cell = 20;
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
if(!ctx){
    console.log("画布上下文获取失败");
    return;
}
const restartBtn = document.getElementById("restartBtn");

//创建蛇
function createSnake(x,y,dx,dy,color){
    return {
        body:[{x,y},{x-dx,y-dy},{x-dx*2,y-dy*2}],
        dx, dy,
        color,
        foodEaten:0,
        alive:true
    }
}

let s1, s2;
let foods = [];
let bombs = [];

//获取空坐标
function getEmptyPos(){
    while(true){
        let p = {x:Math.floor(Math.random()*size), y:Math.floor(Math.random()*size)};
        let occupied = false;
        s1.body.forEach(b=>{if(b.x===p.x&&b.y===p.y) occupied=true;})
        s2.body.forEach(b=>{if(b.x===p.x&&b.y===p.y) occupied=true;})
        foods.forEach(f=>{if(f.x===p.x&&f.y===p.y) occupied=true;})
        bombs.forEach(b=>{if(b.x===p.x&&b.y===p.y) occupied=true;})
        if(!occupied) return p;
    }
}

//重置游戏
function resetGame(){
    s1 = createSnake(5,10,1,0,'#39ff66');
    s2 = createSnake(14,10,-1,0,'#ff9933');
    foods = [];
    bombs = [];
    for(let i=0;i<8;i++) foods.push(getEmptyPos());
    for(let i=0;i<3;i++) bombs.push(getEmptyPos());
    canvas.focus();
}

resetGame();

//键盘
const keys = {};
document.addEventListener('keydown',e=>{
    keys[e.key.toLowerCase()] = true;
    keys[e.code] = true;
})
document.addEventListener('keyup',e=>{
    keys[e.key.toLowerCase()]=false;
    keys[e.code]=false;
})

restartBtn.onclick = resetGame;

//更新方向
function updateDir(){
    //玩家1 WASD
    if(keys['w'] && s1.dy!==1){s1.dx=0;s1.dy=-1}
    if(keys['s'] && s1.dy!==-1){s1.dx=0;s1.dy=1}
    if(keys['a'] && s1.dx!==1){s1.dx=-1;s1.dy=0}
    if(keys['d'] && s1.dx!==-1){s1.dx=1;s1.dy=0}
    //玩家2方向键
    if(keys['ArrowUp'] && s2.dy!==1){s2.dx=0;s2.dy=-1}
    if(keys['ArrowDown'] && s2.dy!==-1){s2.dx=0;s2.dy=1}
    if(keys['ArrowLeft'] && s2.dx!==1){s2.dx=-1;s2.dy=0}
    if(keys['ArrowRight'] && s2.dx!==-1){s2.dx=1;s2.dy=0}
}

//移动蛇
function moveSnake(snake){
    if(!snake.alive) return;
    const head = snake.body[0];
    const newHead = {x:head.x + snake.dx, y:head.y + snake.dy};

    //穿墙环绕
    if(newHead.x<0) newHead.x = size-1;
    if(newHead.x>=size) newHead.x = 0;
    if(newHead.y<0) newHead.y = size-1;
    if(newHead.y>=size) newHead.y = 0;

    //头碰到对手身体 → 淘汰
    const enemy = snake === s1 ? s2 : s1;
    for(let seg of enemy.body){
        if(newHead.x === seg.x && newHead.y === seg.y){
            snake.alive = false;
            return;
        }
    }

    let ateFood = false;
    for(let i=0;i<foods.length;i++){
        const f = foods[i];
        if(newHead.x === f.x && newHead.y === f.y){
            foods.splice(i,1);
            snake.foodEaten +=1;
            ateFood = true;
            foods.push(getEmptyPos());
            break;
        }
    }

    let hitBomb = false;
    for(let i=0;i<bombs.length;i++){
        const b = bombs[i];
        if(newHead.x === b.x && newHead.y === b.y){
            hitBomb = true;
            bombs.splice(i,1);
            bombs.push(getEmptyPos());
            break;
        }
    }

    snake.body.unshift(newHead);
    if(ateFood){
        //吃到食物,长度+1,不删尾巴
    }else if(hitBomb){
        //撞炸弹,删除2节,最少保留1节
        for(let t=0;t<2 && snake.body.length>1;t++) snake.body.pop();
    }else{
        snake.body.pop();
    }
}

//绘制画面
function draw(){
    ctx.fillStyle="#000";
    ctx.fillRect(0,0,canvas.width,canvas.height);

    //网格线
    ctx.strokeStyle="#222";
    for(let i=0;i<=size;i++){
        ctx.beginPath();ctx.moveTo(i*cell,0);ctx.lineTo(i*cell,size*cell);ctx.stroke();
        ctx.beginPath();ctx.moveTo(0,i*cell);ctx.lineTo(size*cell,i*cell);ctx.stroke();
    }

    //食物黄色
    ctx.fillStyle='#ffdd00';
    foods.forEach(f=>ctx.fillRect(f.x*cell+1,f.y*cell+1,cell-2,cell-2));
    //炸弹红色
    ctx.fillStyle='#ff2222';
    bombs.forEach(b=>ctx.fillRect(b.x*cell+1,b.y*cell+1,cell-2,cell-2));

    //画蛇
    function drawSnake(s){
        ctx.fillStyle = s.color;
        s.body.forEach(seg=>ctx.fillRect(seg.x*cell+1,seg.y*cell+1,cell-2,cell-2));
    }
    drawSnake(s1);
    drawSnake(s2);

    //死亡遗留方块,数量=吃掉食物总数
    if(!s1.alive){
        ctx.fillStyle="rgba(57,255,102,0.4)";
        for(let i=0;i<s1.foodEaten;i++){
            const seg = s1.body[i];
            ctx.fillRect(seg.x*cell+1,seg.y*cell+1,cell-2,cell-2);
        }
    }
    if(!s2.alive){
        ctx.fillStyle="rgba(255,153,51,0.4)";
        for(let i=0;i<s2.foodEaten;i++){
            const seg = s2.body[i];
            ctx.fillRect(seg.x*cell+1,seg.y*cell+1,cell-2,cell-2);
        }
    }

    ctx.fillStyle="#fff";
    ctx.font="14px sans-serif";
    ctx.fillText(`绿蛇 存活:${s1.alive} 吃食物:${s1.foodEaten}`,5,395);
    ctx.fillText(`橙蛇 存活:${s2.alive} 吃食物:${s2.foodEaten}`,200,395);
}

const gameTimer = setInterval(()=>{
    updateDir();
    moveSnake(s1);
    moveSnake(s2);
    draw();
},180);
setTimeout(draw,50); //延迟首帧渲染,解决iframe初始化黑屏
});
</script>
</body>
</html>

Game Source: Remix of 20×20蛇对战 | 8食物3炸弹

Creator: RapidBunny56

Libraries: none

Complexity: complex (229 lines, 6.5 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: remix-of-20-20-8-3-rapidbunny56" to link back to the original. Then publish at arcadelab.ai/publish.