HTML实现2048小游戏

游戏概述

 

实现一个基本的2048游戏涉及综合运用HTML、CSS和JavaScript这三种关键技术。

 

HTML用于构建游戏的结构框架,包括游戏容器、网格布局以及可能的用户交互元素(如按钮或得分显示)。

 

CSS则负责美化游戏界面,通过样式表定义网格的样式、瓷砖的外观、动画效果以及整体布局的视觉呈现。

 

JavaScript则是游戏逻辑的核心,它处理用户的输入(如键盘事件监听),控制游戏状态的更新(如瓷砖的移动和合并),以及动态地更新DOM以反映游戏状态的变化。

 

通过这三者的紧密协作,可以创建出一个既美观又富有挑战性的2048游戏。

 

使用工具

 

本篇文章有用到GPT代码更正,国内可稳定使用,感兴趣的大佬可以试试。

 

传送门:ChatGPT-4o


 

实现步骤

 

 

HTML结构

代码部分:

<div id="gameContainer">
    <div id="score">Score: 0</div>
    <div id="gridContainer"></div>
    <div id="gameOverContainer">
        <div id="gameOver">Game Over!</div>
        <button id="restartButton">Restart</button>
    </div>
</div>

 

解释:

  • gameContainer: 包含整个游戏的容器。
  • score: 显示当前分数。
  • gridContainer: 游戏的主要区域,包含瓷砖。
  • gameOverContainer: 游戏结束时显示的消息和重启按钮。

 


 

CSS样式

 

代码部分:

body {
    text-align: center;
    margin-top: 50px;
    font-family: Arial, sans-serif;
    background-color: #f0f0f0;
}

#gridContainer {
    width: 360px;
    height: 360px;
    border: 1px solid #8b8b8b;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-gap: 5px;
    background-color: #dcdcdc;
    padding: 10px;
}

.tile {
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 36px;
    font-weight: bold;
    border-radius: 6px;
    width: 80px;
    height: 80px;
    color: #fff;
}

 

解释:

 

  • body: 设置页面的基本样式,包括字体、背景颜色和对齐方式。
  • gridContainer: 使用 CSS Grid 布局创建了一个 4x4 的网格,定义了网格的宽度、高度以及瓷砖之间的间距。
  • .tile: 定义了瓷砖的样式,包括大小、颜色和字体。

 


 

JavaScript逻辑

 

初始化

 

function init() {
    score = 0;
    gameOverFlag = false;
    updateScore();
    gameOverContainer.style.display = "none";
    resetGrid();
    tiles = Array(width * width).fill(null);
    addTile();
    addTile();
    drawTiles();
}

解释:

  • 初始化游戏状态:重置分数和游戏结束标志。
  • resetGrid(): 清空现有的网格。
  • addTile(): 添加两个初始瓷砖。
  • drawTiles(): 绘制当前的瓷砖状态。

 

 

 

添加瓷砖

 

function addTile() {
    const emptyTiles = tiles.map((tile, index) => tile === null ? index : null).filter(index => index !== null);

    if (emptyTiles.length > 0) {
        const randomIndex = emptyTiles[Math.floor(Math.random() * emptyTiles.length)];
        tiles[randomIndex] = Math.random() < 0.9 ? 2 : 4;
    }
}

 

解释:

  • 查找空位置:创建一个包含所有空位置索引的数组。
  • 随机添加:在空位置随机添加一个值为 2 或 4 的瓷砖。

 

 

绘制瓷砖

 

function drawTiles() {
    resetGrid();
    tiles.forEach(tile => {
        const tileElement = document.createElement("div");
        tileElement.classList.add("tile");
        if (tile !== null) {
            tileElement.textContent = tile;
            tileElement.classList.add(`tile-${tile}`);
        }
        gridContainer.appendChild(tileElement);
    });
}

解释:

 

  • 清空网格:每次重新绘制时清空网格。
  • 创建瓷砖元素:为每个瓷砖创建一个新的 div 元素并设置其样式和内容。

 

 

 

移动逻辑

 

function move(direction) {
    if (gameOverFlag) return;
    let hasChanged = false;

    const moveTile = (currentIndex, newIndex) => {
        if (tiles[newIndex] === null) {
            tiles[newIndex] = tiles[currentIndex];
            tiles[currentIndex] = null;
            hasChanged = true;
        } else if (tiles[newIndex] === tiles[currentIndex]) {
            tiles[newIndex] *= 2;
            tiles[currentIndex] = null;
            score += tiles[newIndex];
            hasChanged = true;
        }
    };

    // Define moveUp, moveDown, moveLeft, moveRight

    switch (direction) {
        case "up":
            moveUp();
            break;
        case "down":
            moveDown();
            break;
        case "left":
            moveLeft();
            break;
        case "right":
            moveRight();
            break;
    }

    if (hasChanged) {
        addTile();
        drawTiles();
        updateScore();
        checkGameOver();
    }
}

解释:

 

  • 检查变动:检测移动是否改变了网格。
  • 移动函数:定义不同方向的移动逻辑,合并相同的瓷砖。

 

 

 

检查游戏结束

代码部分:

 

function checkGameOver() {
    if (!tiles.includes(null) && !checkMove()) {
        gameOverFlag = true;
        gameOverContainer.style.display = "block";
        restartButton.addEventListener("click", restart, { once: true });
    }
}

 

解释:

  • 游戏结束条件:当没有空格且不能移动时,游戏结束。
  • 显示游戏结束信息:并提供重启按钮。

 

 

重启游戏

function restart() {
    init();
}

重新初始化游戏:调用 init() 函数,重置游戏状态。

 

 

事件监听

document.addEventListener("keydown", event => {
    switch (event.key) {
        case "ArrowUp":
        case "w":
            move("up");
            break;
        case "ArrowDown":
        case "s":
            move("down");
            break;
        case "ArrowLeft":
        case "a":
            move("left");
            break;
        case "ArrowRight":
        case "d":
            move("right");
            break;
    }
});
  • 键盘事件:监听方向键和 WASD 键来移动瓷砖。

 


 

运行界面:

 

d7a5f6905ea24856b904f350387c249c.png

 

 

 以下是完整代码:

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2048 Game</title>
    <style>
        body {
            text-align: center;
            margin-top: 50px;
            font-family: Arial, sans-serif;
            background-color: #f0f0f0;
        }

        h1 {
            font-size: 50px;
            color: #333;
        }

        #gameContainer {
            margin-top: 20px;
            display: inline-block;
        }

        #score {
            font-size: 24px;
            margin-bottom: 10px;
            color: #333;
        }

        #gridContainer {
            width: 360px;
            height: 360px;
            border: 1px solid #8b8b8b;
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            grid-gap: 5px;
            background-color: #dcdcdc;
            padding: 10px;
        }

        .tile {
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 36px;
            font-weight: bold;
            border-radius: 6px;
            width: 80px;
            height: 80px;
            color: #fff;
        }

        .tile-2 { background-color: #f78c6b; }
        .tile-4 { background-color: #f7b26b; }
        .tile-8 { background-color: #f7d06b; }
        .tile-16 { background-color: #f4f76b; }
        .tile-32 { background-color: #a3f76b; }
        .tile-64 { background-color: #6bf7c4; }
        .tile-128 { background-color: #6baff7; }
        .tile-256 { background-color: #a26bf7; }
        .tile-512 { background-color: #f76bf7; }
        .tile-1024 { background-color: #f76b9f; }
        .tile-2048 { background-color: #f76b6b; }

        #gameOverContainer {
            display: none;
            margin-top: 20px;
        }

        #gameOver {
            font-size: 36px;
            color: #333;
            margin-bottom: 10px;
        }

        #restartButton {
            font-size: 20px;
            padding: 8px 20px;
            border-radius: 6px;
            background-color: #444;
            color: #fff;
            border: none;
            cursor: pointer;
            transition: background-color 0.3s;
        }

        #restartButton:hover {
            background-color: #666;
        }
    </style>
</head>
<body>
    <h1>2048</h1>

    <div id="gameContainer">
        <div id="score">Score: 0</div>
        <div id="gridContainer"></div>
        <div id="gameOverContainer">
            <div id="gameOver">Game Over!</div>
            <button id="restartButton">Restart</button>
        </div>
    </div>

    <script>
        document.addEventListener("DOMContentLoaded", () => {
            const gridContainer = document.getElementById("gridContainer");
            const scoreDisplay = document.getElementById("score");
            const gameOverContainer = document.getElementById("gameOverContainer");
            const restartButton = document.getElementById("restartButton");

            const width = 4;
            let tiles = [];
            let score = 0;
            let gameOverFlag = false;

            function init() {
                score = 0;
                gameOverFlag = false;
                updateScore();
                gameOverContainer.style.display = "none";
                resetGrid();
                tiles = Array(width * width).fill(null);
                addTile();
                addTile();
                drawTiles();
            }

            function resetGrid() {
                gridContainer.innerHTML = "";
            }

            function addTile() {
                const emptyTiles = tiles.map((tile, index) => tile === null ? index : null).filter(index => index !== null);

                if (emptyTiles.length > 0) {
                    const randomIndex = emptyTiles[Math.floor(Math.random() * emptyTiles.length)];
                    tiles[randomIndex] = Math.random() < 0.9 ? 2 : 4;
                }
            }

            function drawTiles() {
                resetGrid();
                tiles.forEach(tile => {
                    const tileElement = document.createElement("div");
                    tileElement.classList.add("tile");
                    if (tile !== null) {
                        tileElement.textContent = tile;
                        tileElement.classList.add(`tile-${tile}`);
                    }
                    gridContainer.appendChild(tileElement);
                });
            }

            function updateScore() {
                scoreDisplay.textContent = `Score: ${score}`;
            }

            function move(direction) {
                if (gameOverFlag) return;
                let hasChanged = false;

                const moveTile = (currentIndex, newIndex) => {
                    if (tiles[newIndex] === null) {
                        tiles[newIndex] = tiles[currentIndex];
                        tiles[currentIndex] = null;
                        hasChanged = true;
                    } else if (tiles[newIndex] === tiles[currentIndex]) {
                        tiles[newIndex] *= 2;
                        tiles[currentIndex] = null;
                        score += tiles[newIndex];
                        hasChanged = true;
                    }
                };

                const moveUp = () => {
                    for (let col = 0; col < width; col++) {
                        for (let row = 1; row < width; row++) {
                            let currentIndex = col + row * width;
                            while (currentIndex >= width && tiles[currentIndex] !== null) {
                                moveTile(currentIndex, currentIndex - width);
                                currentIndex -= width;
                            }
                        }
                    }
                };

                const moveDown = () => {
                    for (let col = 0; col < width; col++) {
                        for (let row = width - 2; row >= 0; row--) {
                            let currentIndex = col + row * width;
                            while (currentIndex + width < width * width && tiles[currentIndex] !== null) {
                                moveTile(currentIndex, currentIndex + width);
                                currentIndex += width;
                            }
                        }
                    }
                };

                const moveLeft = () => {
                    for (let row = 0; row < width; row++) {
                        for (let col = 1; col < width; col++) {
                            let currentIndex = row * width + col;
                            while (currentIndex % width !== 0 && tiles[currentIndex] !== null) {
                                moveTile(currentIndex, currentIndex - 1);
                                currentIndex--;
                            }
                        }
                    }
                };

                const moveRight = () => {
                    for (let row = 0; row < width; row++) {
                        for (let col = width - 2; col >= 0; col--) {
                            let currentIndex = row * width + col;
                            while ((currentIndex + 1) % width !== 0 && tiles[currentIndex] !== null) {
                                moveTile(currentIndex, currentIndex + 1);
                                currentIndex++;
                            }
                        }
                    }
                };

                switch (direction) {
                    case "up":
                        moveUp();
                        break;
                    case "down":
                        moveDown();
                        break;
                    case "left":
                        moveLeft();
                        break;
                    case "right":
                        moveRight();
                        break;
                }

                if (hasChanged) {
                    addTile();
                    drawTiles();
                    updateScore();
                    checkGameOver();
                }
            }

            function checkGameOver() {
                if (!tiles.includes(null) && !checkMove()) {
                    gameOverFlag = true;
                    gameOverContainer.style.display = "block";
                    restartButton.addEventListener("click", restart, { once: true });
                }
            }

            function checkMove() {
                for (let i = 0; i < tiles.length; i++) {
                    if (tiles[i] === null) return true;
                    if (i % width !== width - 1 && tiles[i] === tiles[i + 1]) return true;
                    if (i < tiles.length - width && tiles[i] === tiles[i + width]) return true;
                }
                return false;
            }

            function restart() {
                init();
            }

            document.addEventListener("keydown", event => {
                switch (event.key) {
                    case "ArrowUp":
                    case "w":
                        move("up");
                        break;
                    case "ArrowDown":
                    case "s":
                        move("down");
                        break;
                    case "ArrowLeft":
                    case "a":
                        move("left");
                        break;
                    case "ArrowRight":
                    case "d":
                        move("right");
                        break;
                }
            });

            init();
        });
    </script>
</body>
</html>

 

通过这篇文章,你可以了解如何从头开始构建一个简单的 2048 游戏,包括 HTML、CSS 和 JavaScript 的使用。

希望你能从中学习到有用的知识!

 

感谢阅读!!!

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值