HTML5小游戏 - 数字消除 · 合并2048

ChatGPT编写的一款小游戏:数字融合2048 · 休闲版

玩法说明:

  • 方向键控制移动

  • 相同数字会合并

  • 每步都会随机生成新数字

  • 手机滑动支持(触屏滑动)

  • 胜利提示(达到 2048)

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>数字融合2048 · 休闲版</title>

<style>
html,body{
    margin:0;
    height:100%;
    overflow:hidden;
    position:fixed;
    width:100%;
    touch-action:none;
    overscroll-behavior:none;
    background:url("https://amitofo.icu/lianchi.jpg") center/cover no-repeat fixed;
    font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto;
    color:white;
    display:flex;
    justify-content:center;
    align-items:center;
}

.container{
    text-align:center;
    backdrop-filter:blur(8px);
    background:rgba(0,0,0,0.35);
    padding:20px;
    border-radius:20px;
}

.board{
    width:92vw;
    max-width:420px;
    aspect-ratio:1/1;
    background:rgba(255,255,255,0.08);
    border-radius:20px;
    padding:12px;
    display:grid;
    grid-template-columns:repeat(4,1fr);
    gap:12px;
}

.cell{
    border-radius:14px;
    display:flex;
    justify-content:center;
    align-items:center;
    font-weight:bold;
    font-size:clamp(18px,5vw,28px);
    background:rgba(255,255,255,0.08);
    transition:0.15s;
}

.overlay{
    position:fixed;
    inset:0;
    background:rgba(0,0,0,0.75);
    display:none;
    justify-content:center;
    align-items:center;
    flex-direction:column;
    font-size:24px;
}

.fall{
    position:fixed;
    top:-50px;
    font-size:26px;
    animation:fall 1.8s linear forwards;
}

@keyframes fall{
    to{
        transform:translateY(110vh) rotate(360deg);
        opacity:0;
    }
}
</style>
</head>

<body>

<audio id="bgm" src="https://amitofo.icu/beijing.ogg" loop></audio>
<audio id="fx" src="https://amitofo.icu/xiaochu.mp3"></audio>

<div class="container">
    <h2>数字融合2048</h2>
   <div> 游戏说明:方向键移动,相同数字合并</div>
    分数: <span id="score">0</span>
    <div class="board" id="board"></div>
    <button onclick="restart()">重新开始</button>
</div>

<div class="overlay" id="overlay">
    <div id="message"></div>
    <button onclick="restart()">再来一局</button>
</div>

<script>
const size=4;
let board=[],score=0,lastMilestone=0;
let bgStarted=false;

function init(){
    board=Array(size).fill().map(()=>Array(size).fill(0));
    score=0;
    lastMilestone=0;
    addRandom(); addRandom();
    render();
}

function restart(){
    document.getElementById("overlay").style.display="none";
    init();
}

function render(){
    const b=document.getElementById("board");
    b.innerHTML="";
    board.flat().forEach(num=>{
        const cell=document.createElement("div");
        cell.className="cell";
        if(num){
            cell.textContent=num;
            cell.style.background=`hsl(${Math.log2(num)*28},70%,55%)`;
        }
        b.appendChild(cell);
    });
    document.getElementById("score").textContent=score;

    if(score>=lastMilestone+1000){
        lastMilestone+=1000;
        celebrate();
    }
}

function celebrate(){
    const fx=document.getElementById("fx");
    fx.currentTime=0;
    fx.play();

    for(let i=0;i<15;i++){
        let el=document.createElement("div");
        el.className="fall";
        el.textContent="🌸";
        el.style.left=Math.random()*100+"vw";
        document.body.appendChild(el);
        setTimeout(()=>el.remove(),1800);
    }
}

function addRandom(){
    let empty=[];
    for(let i=0;i<size;i++)
        for(let j=0;j<size;j++)
            if(!board[i][j]) empty.push({i,j});
    if(!empty.length) return;
    const {i,j}=empty[Math.floor(Math.random()*empty.length)];
    board[i][j]=Math.random()<0.9?2:4;
}

function slide(row){
    let arr=row.filter(n=>n);
    for(let i=0;i<arr.length-1;i++){
        if(arr[i]===arr[i+1]){
            arr[i]*=2;
            score+=arr[i];
            arr[i+1]=0;
        }
    }
    arr=arr.filter(n=>n);
    while(arr.length<size) arr.push(0);
    return arr;
}

function move(dir){
    let old=JSON.stringify(board);

    if(dir==="left")
        for(let i=0;i<size;i++) board[i]=slide(board[i]);

    if(dir==="right")
        for(let i=0;i<size;i++) board[i]=slide(board[i].reverse()).reverse();

    if(dir==="up"){
        for(let c=0;c<size;c++){
            let col=[];
            for(let r=0;r<size;r++) col.push(board[r][c]);
            col=slide(col);
            for(let r=0;r<size;r++) board[r][c]=col[r];
        }
    }

    if(dir==="down"){
        for(let c=0;c<size;c++){
            let col=[];
            for(let r=0;r<size;r++) col.push(board[r][c]);
            col=slide(col.reverse()).reverse();
            for(let r=0;r<size;r++) board[r][c]=col[r];
        }
    }

    if(JSON.stringify(board)!==old){
        addRandom();
        render();
        if(checkLose()) gameOver();
    }
}

function checkLose(){
    for(let i=0;i<size;i++)
        for(let j=0;j<size;j++){
            if(!board[i][j]) return false;
            if(j<3 && board[i][j]===board[i][j+1]) return false;
            if(i<3 && board[i][j]===board[i+1][j]) return false;
        }
    return true;
}

function gameOver(){
    const fx=document.getElementById("fx");
    fx.currentTime=0;
    fx.play();
    document.getElementById("message").textContent="游戏结束";
    document.getElementById("overlay").style.display="flex";
}

document.addEventListener("keydown",e=>{
    if(e.key==="ArrowLeft")move("left");
    if(e.key==="ArrowRight")move("right");
    if(e.key==="ArrowUp")move("up");
    if(e.key==="ArrowDown")move("down");
});

const boardEl=document.getElementById("board");
let startX,startY;

boardEl.addEventListener("touchstart",e=>{
    e.preventDefault();

    if(!bgStarted){
        document.getElementById("bgm").play();
        bgStarted=true;
    }

    startX=e.touches[0].clientX;
    startY=e.touches[0].clientY;
},{passive:false});

boardEl.addEventListener("touchend",e=>{
    e.preventDefault();
    let dx=e.changedTouches[0].clientX-startX;
    let dy=e.changedTouches[0].clientY-startY;

    if(Math.abs(dx)>Math.abs(dy)){
        if(dx>30)move("right");
        else if(dx<-30)move("left");
    }else{
        if(dy>30)move("down");
        else if(dy<-30)move("up");
    }
},{passive:false});

init();
</script>

</body>
</html>
相关推荐
扶苏10022 小时前
详解Vue2和Vue3的变化
前端·javascript·vue.js
Hello eveybody2 小时前
如何将十进制转为二进制、八进制、十六进制?
前端·javascript·数据库
We་ct3 小时前
LeetCode 25. K个一组翻转链表:两种解法详解+避坑指南
前端·算法·leetcode·链表·typescript
悦悦子a啊3 小时前
CSS 知识点
开发语言·前端·css
ssshooter3 小时前
Transform 提高了渲染性能,但是代价是什么?
前端
光影少年3 小时前
前端工程化
前端·webpack·taro
牛奶3 小时前
你不知道的JS(中):程序性能与测试
前端·javascript·电子书
林恒smileZAZ3 小时前
为什么 SVG 能在现代前端中胜出?
前端
牛奶3 小时前
你不知道的JS(中):Promise与生成器
前端·javascript·电子书