从零搭建纯前端飞机大战游戏(附源码)

目录

前言

一、游戏概览与技术选型

二、HTML 结构搭建和CSS样式美化

三、JavaScript 核心逻辑

1.变量声明与初始化

2.玩家飞机控制函数

3.射击与子弹管理函数

4.敌机生成与管理函数

5.碰撞检测与得分更新函数

6.游戏主循环与启动函数

四、完整代码


前言

        在前端开发的广阔天地里,利用 HTML、CSS 和 JavaScript 创造出富有互动性与趣味性的游戏,是一件极具挑战性与成就感的事情。今天,我将带大家深入解读一款纯前端飞机大战游戏的代码实现,从基础架构到核心逻辑,一步步揭示其背后的奥秘。

一、游戏概览与技术选型

        这款飞机大战游戏完全基于前端技术构建,无需后端服务器的支持,仅依靠 HTML 构建页面结构,CSS 负责样式呈现,JavaScript 掌控游戏的动态逻辑。这种纯前端的实现方式使得游戏能够在浏览器中直接运行,方便快捷,且易于分享与传播。

二、HTML 结构搭建和CSS样式美化

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        /* CSS 样式 */

        #game-board {
            width: 480px; /*  设置游戏面板的宽度为480像素 */
            height: 500px; /*  设置游戏面板的高度为500像素 */
            border: 1px solid black; /*  给游戏面板添加1像素的黑色边框 */
            position: relative; /*  设置相对定位,方便内部元素进行定位 */
            margin: 0 auto; /*  使游戏面板在页面中水平居中 */
        }

        #player {
            width: 50px; /*  设置玩家飞机元素的宽度为50像素 */
            height: 50px; /*  设置玩家飞机元素的高度为50像素 */
            background-color: red; /*  设置玩家飞机的背景颜色为红色 */
            position: absolute; /*  绝对定位,便于精确控制其在游戏面板中的位置 */
            bottom: 0; /*  将玩家飞机定位在游戏面板底部 */
            left: 50%; /*  初始水平位置在游戏面板的水平居中处 */
            transform: translateX(-50%); /*  通过水平平移使其精准居中,因为left: 50%是基于元素自身宽度的一半,需要微调 */
        }

       .bullet {
            width: 5px; /*  设置子弹元素的宽度为5像素 */
            height: 20px; /*  设置子弹元素的高度为20像素 */
            background-color: black; /*  设置子弹的背景颜色为黑色 */
            position: absolute; /*  绝对定位,用于后续控制其在游戏面板中的位置变化 */
        }

       .enemy {
            width: 40px; /*  设置敌机元素的宽度为40像素 */
            height: 40px; /*  设置敌机元素的高度为40像素 */
            background-color: blue; /*  设置敌机的背景颜色为蓝色 */
            position: absolute; /*  绝对定位,方便控制敌机在游戏面板的位置 */
        }

        #score {
            font-size: 24px; /*  设置得分显示的字体大小为24像素 */
            text-align: center; /*  使得分文本在其容器内居中显示 */
        }
    </style>
</head>

<body>
    <div id="game-board">
        <div id="player"></div>
        <div id="score">Score: 0</div>
    </div>
</body>
  •         在 HTML 部分,我们创建了一个 #game-board 的 div 元素作为游戏的主容器,它设定了游戏的整体尺寸(480px 宽,500px 高)并添加了边框,通过 margin: 0 auto 使其在页面中水平居中。#player 元素代表玩家的飞机,初始位置在游戏板底部居中,红色背景使其醒目。#score 元素则用于展示玩家的得分,位于游戏板上方居中,初始得分为 0。bullet 和 enemy 类分别定义了子弹和敌机的样式,它们的具体位置和动态效果将由 JavaScript 代码来控制。 
  •         对于 #game-board,其相对定位为内部绝对定位元素(如玩家飞机、子弹和敌机)提供了参照系。玩家飞机的样式不仅定义了其大小和颜色,还通过精确的定位属性(bottom: 0 和 left: 50% 结合 transform: translateX(-50%))确保其在底部水平居中。子弹和敌机的样式则分别规定了它们的尺寸和颜色,这些简单而有效的样式设置,为游戏构建了一个清晰、直观的视觉界面。

三、JavaScript 核心逻辑

1.变量声明与初始化

// JavaScript 代码
    
    // 获取游戏面板元素,后续用于操作游戏面板内的各种元素以及获取面板相关属性(如宽高)
    let gameBoard = document.getElementById("game-board");
    // 获取代表玩家飞机的元素,用于控制玩家飞机的位置、样式等操作
    let player = document.getElementById("player");
    // 获取用于显示得分的元素,方便更新得分信息
    let scoreElement = document.getElementById("score");
    
    // 用于存储所有已发射的子弹元素,每个元素都是页面中的一个DOM节点,方便统一管理和操作
    let bullets = [];
    // 用于存储所有出现的敌机元素,同样每个元素是页面中的DOM节点,便于管理敌机的创建、移动和销毁等操作
    let enemies = [];
    // 用于记录玩家的得分,初始化为0,每当击中敌机时会相应增加
    let score = 0;

        首先,我们获取了游戏板、玩家飞机和得分元素的引用,这些引用将在后续的代码中频繁使用,用于操作和更新相应的元素。同时,初始化了三个重要的数组:bullets 用于存储所有发射的子弹,enemies 用于存放生成的敌机,score 则记录玩家的得分,初始值为 0。 

2.玩家飞机控制函数

// 函数用于移动玩家飞机,根据传入的方向参数来改变飞机的水平位置
    function movePlayer(direction) {
        // 获取玩家飞机当前的水平位置(left属性值),如果获取失败(可能是未设置过)则默认为0, parseInt用于将字符串类型的像素值转换为数字类型
        let currentPosition = parseInt(player.style.left) || 0;
    
        if (direction === "left") {
            currentPosition -= 20; // 如果方向是向左,将当前位置减少20像素,实现向左移动
        } else if (direction === "right") {
            currentPosition += 20; // 如果方向是向右,将当前位置增加20像素,实现向右移动
        }
    
        // 限制飞机移动范围在游戏板内,通过 Math.max确保不小于0(游戏板左侧边界),Math.min确保不大于游戏板宽度减去飞机自身宽度(游戏板右侧边界)
        currentPosition = Math.max(0, Math.min(currentPosition, gameBoard.offsetWidth - player.offsetWidth));
    
        player.style.left = currentPosition + "px"; // 更新玩家飞机的水平位置样式属性,将数字转换为带单位的像素值字符串
    }

   movePlayer 函数实现了玩家飞机的左右移动功能。通过获取玩家飞机当前的 left 样式值并转换为数字类型,根据传入的方向参数(left 或 right)来增减其水平位置。为了确保飞机不会移出游戏板边界,使用 Math.max 和 Math.min 函数对位置进行限制,最后更新玩家飞机的 left 样式属性,实现平滑的移动效果。 

3.射击与子弹管理函数

// 函数用于发射子弹,创建一个新的子弹元素并添加到游戏面板中,同时将其添加到bullets数组进行管理
    function shoot() {
        // 创建一个新的div元素,用于表示子弹
        let bullet = document.createElement("div");
        bullet.className = "bullet"; // 为子弹元素添加对应的类名,以便应用CSS样式
        // 设置子弹的水平位置,使其在玩家飞机的中心位置,减去2.5像素是为了精准居中(因为子弹宽度为5像素)
        bullet.style.left = parseInt(player.style.left) + player.offsetWidth / 2 - 2.5 + "px";
        bullet.style.bottom = player.offsetHeight + "px"; // 设置子弹的垂直位置,在玩家飞机的上方(底部对齐)
        gameBoard.appendChild(bullet); // 将子弹元素添加到游戏面板中,使其在页面上显示出来
        bullets.push(bullet); // 将新创建的子弹元素添加到bullets数组中,方便后续统一管理
    }
    
    // 函数用于移动所有已发射的子弹,更新它们在游戏面板中的垂直位置
    function moveBullets() {
        bullets.forEach((bullet, bulletIndex) => {
            // 获取子弹当前的垂直位置(bottom属性值),如果获取失败则默认为0,同样进行类型转换
            let currentPosition = parseInt(bullet.style.bottom) || 0;
            currentPosition += 10; // 将子弹的垂直位置增加10像素,实现向上移动
    
            if (currentPosition >= gameBoard.offsetHeight) {
                // 如果子弹超出了游戏面板的高度(到达顶部边界),则从页面中移除该子弹元素
                bullet.remove();
                bullets.splice(bulletIndex, 1); // 同时从bullets数组中移除对应的元素,保持数组和页面元素的一致性
            } else {
                bullet.style.bottom = currentPosition + "px"; // 如果未超出边界,更新子弹的垂直位置样式属性
            }
        });
    }

   shoot 函数用于创建并发射子弹。当玩家按下空格键时,该函数被触发。它创建一个新的 div 元素作为子弹,设置其类名、初始位置(在玩家飞机中心上方),并将其添加到游戏板中,同时将子弹元素添加到 bullets 数组中以便后续管理。moveBullets 函数则负责更新子弹的位置,遍历 bullets 数组,使每个子弹向上移动(增加 bottom 样式值),一旦子弹移出游戏板顶部,就将其从页面和数组中移除,确保游戏资源的有效管理。 

4.敌机生成与管理函数

// 函数用于创建一个新的敌机元素,并添加到游戏面板中,同时将其添加到enemies数组进行管理
    function createEnemy() {
        let enemy = document.createElement("div");
        enemy.className = "enemy"; // 为敌机元素添加对应的类名,以便应用CSS样式
        // 设置敌机的水平位置,使其在游戏面板内随机出现(在游戏面板宽度减去敌机自身宽度的范围内随机取值)
        enemy.style.left = Math.random() * (gameBoard.offsetWidth - 40) + "px";
        enemy.style.top = "0px"; // 设置敌机的初始垂直位置在游戏面板顶部
        gameBoard.appendChild(enemy); // 将敌机元素添加到游戏面板中,使其在页面上显示出来
        enemies.push(enemy); // 将新创建的敌机元素添加到enemies数组中,方便后续统一管理
    }
    
    // 函数用于移动所有的敌机,更新它们在游戏面板中的垂直位置,实现敌机向下移动的效果
    function moveEnemies() {
        enemies.forEach((enemy, enemyIndex) => {
            // 获取敌机当前的垂直位置(top属性值),如果获取失败则默认为0,进行类型转换
            let currentPosition = parseInt(enemy.style.top) || 0;
            currentPosition += 5; // 将敌机的垂直位置增加5像素,实现向下移动
    
            if (currentPosition >= gameBoard.offsetHeight) {
                // 如果敌机超出了游戏面板的高度(到达底部边界),则从页面中移除该敌机元素
                enemy.remove();
                enemies.splice(enemyIndex, 1); // 同时从enemies数组中移除对应的元素,保持数组和页面元素的一致性
            } else {
                enemy.style.top = currentPosition + "px"; // 如果未超出边界,更新敌机的垂直位置样式属性
            }
        });
    }

   createEnemy 函数用于随机生成敌机。它创建一个新的敌机 div 元素,设置其类名和初始位置(在游戏板顶部随机水平位置),然后添加到游戏板并放入 enemies 数组。moveEnemies 函数负责移动敌机,使每个敌机向下移动(增加 top 样式值),当敌机移出游戏板底部时,将其从页面和数组中移除,模拟敌机的飞行和消失过程。 

5.碰撞检测与得分更新函数

// 函数用于检测子弹与敌机之间是否发生碰撞,如果发生碰撞则进行相应的处理(移除子弹和敌机,增加得分等)
    function detectCollision() {
        bullets.forEach((bullet, bulletIndex) => {
    
            enemies.forEach((enemy, enemyIndex) => {
                // 通过比较子弹和敌机的边界位置来判断是否发生碰撞,检测条件涵盖了上下左右四个方向的位置关系
                if (
                    bullet.offsetTop <= enemy.offsetTop + enemy.offsetHeight &&
                    bullet.offsetTop + bullet.offsetHeight >= enemy.offsetTop &&
                    bullet.offsetLeft <= enemy.offsetLeft + enemy.offsetWidth &&
                    bullet.offsetLeft + bullet.offsetWidth >= enemy.offsetLeft
                ) {
                    bullet.remove(); // 如果发生碰撞,从页面中移除子弹元素
                    bullets.splice(bulletIndex, 1); // 从bullets数组中移除对应的子弹元素
                    enemy.remove(); // 从页面中移除敌机元素
                    enemies.splice(enemyIndex, 1); // 从enemies数组中移除对应的敌机元素
                    score += 10; // 玩家得分增加10分
                    scoreElement.innerHTML = "Score: " + score; // 更新页面上显示的得分信息
                }
            });
        });
    }

           detectCollision 函数是游戏的核心逻辑之一,它负责检测子弹与敌机之间的碰撞。通过遍历 bullets 和 enemies 数组,比较每个子弹和敌机的边界位置关系,一旦检测到碰撞,就移除相应的子弹和敌机元素,并从数组中删除它们,同时增加玩家的得分并更新得分显示元素的内容,让玩家及时了解自己的游戏成果。 

6.游戏主循环与启动函数

// 游戏主循环函数,在每次循环中调用移动子弹、移动敌机和检测碰撞的函数,实现游戏的持续更新和逻辑处理
    function gameLoop() {
        moveBullets();
        moveEnemies();
        detectCollision();
    }
    
    // 函数用于启动游戏,初始化游戏相关的各种状态,包括得分、元素位置、清除已有的子弹和敌机等,并设置定时创建敌机和执行游戏主循环的定时器
    function startGame() {
        score = 0; // 将得分重置为0
        scoreElement.innerHTML = "Score: " + score; // 更新页面上显示的得分信息为0
    
        // 将玩家飞机的水平位置重新设置为游戏面板的水平居中处
        player.style.left = (gameBoard.offsetWidth - player.offsetWidth) / 2 + "px";
    
        // 移除所有已存在的子弹元素,并清空bullets数组
        bullets.forEach(bullet => bullet.remove());
        bullets = [];
    
        // 移除所有已存在的敌机元素,并清空enemies数组
        enemies.forEach(enemy => enemy.remove());
        enemies = [];
    
        // 设置每隔1000毫秒(1秒)创建一个新的敌机,不断增加游戏中的敌机数量,增加游戏难度
        setInterval(createEnemy, 1000);
        // 设置每隔30毫秒执行一次游戏主循环,实现游戏的流畅更新和逻辑处理,控制游戏的帧率
        setInterval(gameLoop, 30);
    }
    
    // 监听键盘按下事件,根据按下的不同按键来执行相应的游戏操作,如移动玩家飞机或发射子弹
    document.addEventListener("keydown", function (event) {
        if (event.code === "ArrowLeft") {
            movePlayer("left"); // 如果按下向左箭头键,调用函数向左移动玩家飞机
        } else if (event.code === "ArrowRight") {
            movePlayer("right"); // 如果按下向右箭头键,调用函数向右移动玩家飞机
        } else if (event.code === "Space") {
            shoot(); // 如果按下空格键,调用函数发射子弹
        }
    });
    
    startGame(); // 启动游戏,开始整个游戏流程

    gameLoop 函数作为游戏的主循环,在每次循环中依次调用 moveBulletsmoveEnemies 和 detectCollision 函数,实现子弹移动、敌机移动和碰撞检测的持续更新,保证游戏的动态运行。startGame 函数用于初始化游戏状态,重置得分、玩家飞机位置,清空子弹和敌机数组,然后设置两个定时器:一个每隔 1 秒调用 createEnemy 函数生成新的敌机,增加游戏难度;另一个每隔 30 毫秒调用 gameLoop 函数,控制游戏的帧率,使游戏运行更加流畅。最后,通过监听键盘按下事件,根据不同的按键触发相应的游戏操作函数,如玩家飞机移动和射击。 

四、完整代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        /* CSS 样式 */

        #game-board {
            width: 480px; /*  设置游戏面板的宽度为480像素 */
            height: 500px; /*  设置游戏面板的高度为500像素 */
            border: 1px solid black; /*  给游戏面板添加1像素的黑色边框 */
            position: relative; /*  设置相对定位,方便内部元素进行定位 */
            margin: 0 auto; /*  使游戏面板在页面中水平居中 */
        }

        #player {
            width: 50px; /*  设置玩家飞机元素的宽度为50像素 */
            height: 50px; /*  设置玩家飞机元素的高度为50像素 */
            background-color: red; /*  设置玩家飞机的背景颜色为红色 */
            position: absolute; /*  绝对定位,便于精确控制其在游戏面板中的位置 */
            bottom: 0; /*  将玩家飞机定位在游戏面板底部 */
            left: 50%; /*  初始水平位置在游戏面板的水平居中处 */
            transform: translateX(-50%); /*  通过水平平移使其精准居中,因为left: 50%是基于元素自身宽度的一半,需要微调 */
        }

       .bullet {
            width: 5px; /*  设置子弹元素的宽度为5像素 */
            height: 20px; /*  设置子弹元素的高度为20像素 */
            background-color: black; /*  设置子弹的背景颜色为黑色 */
            position: absolute; /*  绝对定位,用于后续控制其在游戏面板中的位置变化 */
        }

       .enemy {
            width: 40px; /*  设置敌机元素的宽度为40像素 */
            height: 40px; /*  设置敌机元素的高度为40像素 */
            background-color: blue; /*  设置敌机的背景颜色为蓝色 */
            position: absolute; /*  绝对定位,方便控制敌机在游戏面板的位置 */
        }

        #score {
            font-size: 24px; /*  设置得分显示的字体大小为24像素 */
            text-align: center; /*  使得分文本在其容器内居中显示 */
        }
    </style>
</head>

<body>
    <div id="game-board">
        <div id="player"></div>
        <div id="score">Score: 0</div>
    </div>
</body>
<script>
    // JavaScript 代码
    
    // 获取游戏面板元素,后续用于操作游戏面板内的各种元素以及获取面板相关属性(如宽高)
    let gameBoard = document.getElementById("game-board");
    // 获取代表玩家飞机的元素,用于控制玩家飞机的位置、样式等操作
    let player = document.getElementById("player");
    // 获取用于显示得分的元素,方便更新得分信息
    let scoreElement = document.getElementById("score");
    
    // 用于存储所有已发射的子弹元素,每个元素都是页面中的一个DOM节点,方便统一管理和操作
    let bullets = [];
    // 用于存储所有出现的敌机元素,同样每个元素是页面中的DOM节点,便于管理敌机的创建、移动和销毁等操作
    let enemies = [];
    // 用于记录玩家的得分,初始化为0,每当击中敌机时会相应增加
    let score = 0;
    
    // 函数用于移动玩家飞机,根据传入的方向参数来改变飞机的水平位置
    function movePlayer(direction) {
        // 获取玩家飞机当前的水平位置(left属性值),如果获取失败(可能是未设置过)则默认为0, parseInt用于将字符串类型的像素值转换为数字类型
        let currentPosition = parseInt(player.style.left) || 0;
    
        if (direction === "left") {
            currentPosition -= 20; // 如果方向是向左,将当前位置减少20像素,实现向左移动
        } else if (direction === "right") {
            currentPosition += 20; // 如果方向是向右,将当前位置增加20像素,实现向右移动
        }
    
        // 限制飞机移动范围在游戏板内,通过 Math.max确保不小于0(游戏板左侧边界),Math.min确保不大于游戏板宽度减去飞机自身宽度(游戏板右侧边界)
        currentPosition = Math.max(0, Math.min(currentPosition, gameBoard.offsetWidth - player.offsetWidth));
    
        player.style.left = currentPosition + "px"; // 更新玩家飞机的水平位置样式属性,将数字转换为带单位的像素值字符串
    }
    
    // 函数用于发射子弹,创建一个新的子弹元素并添加到游戏面板中,同时将其添加到bullets数组进行管理
    function shoot() {
        // 创建一个新的div元素,用于表示子弹
        let bullet = document.createElement("div");
        bullet.className = "bullet"; // 为子弹元素添加对应的类名,以便应用CSS样式
        // 设置子弹的水平位置,使其在玩家飞机的中心位置,减去2.5像素是为了精准居中(因为子弹宽度为5像素)
        bullet.style.left = parseInt(player.style.left) + player.offsetWidth / 2 - 2.5 + "px";
        bullet.style.bottom = player.offsetHeight + "px"; // 设置子弹的垂直位置,在玩家飞机的上方(底部对齐)
        gameBoard.appendChild(bullet); // 将子弹元素添加到游戏面板中,使其在页面上显示出来
        bullets.push(bullet); // 将新创建的子弹元素添加到bullets数组中,方便后续统一管理
    }
    
    // 函数用于移动所有已发射的子弹,更新它们在游戏面板中的垂直位置
    function moveBullets() {
        bullets.forEach((bullet, bulletIndex) => {
            // 获取子弹当前的垂直位置(bottom属性值),如果获取失败则默认为0,同样进行类型转换
            let currentPosition = parseInt(bullet.style.bottom) || 0;
            currentPosition += 10; // 将子弹的垂直位置增加10像素,实现向上移动
    
            if (currentPosition >= gameBoard.offsetHeight) {
                // 如果子弹超出了游戏面板的高度(到达顶部边界),则从页面中移除该子弹元素
                bullet.remove();
                bullets.splice(bulletIndex, 1); // 同时从bullets数组中移除对应的元素,保持数组和页面元素的一致性
            } else {
                bullet.style.bottom = currentPosition + "px"; // 如果未超出边界,更新子弹的垂直位置样式属性
            }
        });
    }
    
    // 函数用于创建一个新的敌机元素,并添加到游戏面板中,同时将其添加到enemies数组进行管理
    function createEnemy() {
        let enemy = document.createElement("div");
        enemy.className = "enemy"; // 为敌机元素添加对应的类名,以便应用CSS样式
        // 设置敌机的水平位置,使其在游戏面板内随机出现(在游戏面板宽度减去敌机自身宽度的范围内随机取值)
        enemy.style.left = Math.random() * (gameBoard.offsetWidth - 40) + "px";
        enemy.style.top = "0px"; // 设置敌机的初始垂直位置在游戏面板顶部
        gameBoard.appendChild(enemy); // 将敌机元素添加到游戏面板中,使其在页面上显示出来
        enemies.push(enemy); // 将新创建的敌机元素添加到enemies数组中,方便后续统一管理
    }
    
    // 函数用于移动所有的敌机,更新它们在游戏面板中的垂直位置,实现敌机向下移动的效果
    function moveEnemies() {
        enemies.forEach((enemy, enemyIndex) => {
            // 获取敌机当前的垂直位置(top属性值),如果获取失败则默认为0,进行类型转换
            let currentPosition = parseInt(enemy.style.top) || 0;
            currentPosition += 5; // 将敌机的垂直位置增加5像素,实现向下移动
    
            if (currentPosition >= gameBoard.offsetHeight) {
                // 如果敌机超出了游戏面板的高度(到达底部边界),则从页面中移除该敌机元素
                enemy.remove();
                enemies.splice(enemyIndex, 1); // 同时从enemies数组中移除对应的元素,保持数组和页面元素的一致性
            } else {
                enemy.style.top = currentPosition + "px"; // 如果未超出边界,更新敌机的垂直位置样式属性
            }
        });
    }
    
    // 函数用于检测子弹与敌机之间是否发生碰撞,如果发生碰撞则进行相应的处理(移除子弹和敌机,增加得分等)
    function detectCollision() {
        bullets.forEach((bullet, bulletIndex) => {
    
            enemies.forEach((enemy, enemyIndex) => {
                // 通过比较子弹和敌机的边界位置来判断是否发生碰撞,检测条件涵盖了上下左右四个方向的位置关系
                if (
                    bullet.offsetTop <= enemy.offsetTop + enemy.offsetHeight &&
                    bullet.offsetTop + bullet.offsetHeight >= enemy.offsetTop &&
                    bullet.offsetLeft <= enemy.offsetLeft + enemy.offsetWidth &&
                    bullet.offsetLeft + bullet.offsetWidth >= enemy.offsetLeft
                ) {
                    bullet.remove(); // 如果发生碰撞,从页面中移除子弹元素
                    bullets.splice(bulletIndex, 1); // 从bullets数组中移除对应的子弹元素
                    enemy.remove(); // 从页面中移除敌机元素
                    enemies.splice(enemyIndex, 1); // 从enemies数组中移除对应的敌机元素
                    score += 10; // 玩家得分增加10分
                    scoreElement.innerHTML = "Score: " + score; // 更新页面上显示的得分信息
                }
            });
        });
    }
    
    // 游戏主循环函数,在每次循环中调用移动子弹、移动敌机和检测碰撞的函数,实现游戏的持续更新和逻辑处理
    function gameLoop() {
        moveBullets();
        moveEnemies();
        detectCollision();
    }
    
    // 函数用于启动游戏,初始化游戏相关的各种状态,包括得分、元素位置、清除已有的子弹和敌机等,并设置定时创建敌机和执行游戏主循环的定时器
    function startGame() {
        score = 0; // 将得分重置为0
        scoreElement.innerHTML = "Score: " + score; // 更新页面上显示的得分信息为0
    
        // 将玩家飞机的水平位置重新设置为游戏面板的水平居中处
        player.style.left = (gameBoard.offsetWidth - player.offsetWidth) / 2 + "px";
    
        // 移除所有已存在的子弹元素,并清空bullets数组
        bullets.forEach(bullet => bullet.remove());
        bullets = [];
    
        // 移除所有已存在的敌机元素,并清空enemies数组
        enemies.forEach(enemy => enemy.remove());
        enemies = [];
    
        // 设置每隔1000毫秒(1秒)创建一个新的敌机,不断增加游戏中的敌机数量,增加游戏难度
        setInterval(createEnemy, 1000);
        // 设置每隔30毫秒执行一次游戏主循环,实现游戏的流畅更新和逻辑处理,控制游戏的帧率
        setInterval(gameLoop, 30);
    }
    
    // 监听键盘按下事件,根据按下的不同按键来执行相应的游戏操作,如移动玩家飞机或发射子弹
    document.addEventListener("keydown", function (event) {
        if (event.code === "ArrowLeft") {
            movePlayer("left"); // 如果按下向左箭头键,调用函数向左移动玩家飞机
        } else if (event.code === "ArrowRight") {
            movePlayer("right"); // 如果按下向右箭头键,调用函数向右移动玩家飞机
        } else if (event.code === "Space") {
            shoot(); // 如果按下空格键,调用函数发射子弹
        }
    });
    
    startGame(); // 启动游戏,开始整个游戏流程
</script>

</html>

运行结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/940329.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【MAC】深入浅出 Homebrew 下 Nginx 的安装与配置指南

硬件&#xff1a;Apple M4 Pro 16寸 系统&#xff1a; macos Sonoma 15.1.1 Nginx 是一款高性能的 Web 服务器和反向代理服务器&#xff0c;广泛应用于全球各地的网站和企业应用中。本文将详细介绍如何在 macOS 环境下使用 Homebrew 安装、启动、管理以及优化配置 Nginx&#x…

简单了解图注意力机制

简单了解图注意力机制 如果对传统的图匹配的聚合方式进行创新的话&#xff0c;也就是对h这一个节点的聚合方式进行创新。 h i ( l 1 ) Norm ⁡ ( σ ( h i ( l ) α ∥ h i ( l ) ∥ m i ( l ) ∥ m i ( l ) ∥ ) ) , \mathbf{h}_{i}^{(l1)}\operatorname{Norm}\left(\sigm…

aosp15 - App冷启动

纸上得来终觉浅&#xff0c;绝知此事要躬行。 —— [宋]陆游 基于aosp_cf_x86_64_phone-trunk_staging-eng &#xff0c; 下面是具体断点位置。 第一部分&#xff0c;桌面launcher进程 com.android.launcher3.touch.ItemClickHandler onClickonClickAppShortcutstartAppShor…

arcgisPro相接多个面要素转出为完整独立线要素

1、使用【面转线】工具&#xff0c;并取消勾选“识别和存储面邻域信息”&#xff0c;如下&#xff1a; 2、得到的线要素&#xff0c;如下&#xff1a;

树莓派4B 搭建openwrt内置超多插件docker,nas等等使用教程

刷入固件 (想要固件的加我vx wyy7293) bleachwrt-plus-20241112-bcm27xx-bcm2711-rpi-4-squashfs-factory.img上电,并且把网线两头分别插在pi网口上和电脑的网口上(电脑必须断网) 等待网口灯亮,进入192.168.1.1 默认账密 root password 进入系统后更改openwrt的网关地址相关…

Java开发经验——数据库开发经验

摘要 本文主要介绍了Java开发中的数据库操作规范&#xff0c;包括数据库建表规范、索引规约、SQL规范和ORM规约。强调了在数据库设计和操作中应遵循的最佳实践&#xff0c;如字段命名、数据类型选择、索引创建、SQL语句编写和ORM映射&#xff0c;旨在提高数据库操作的性能和安…

springboot462学生心理压力咨询评判(论文+源码)_kaic

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装学生心理压力咨询评判软件来发挥其高效地信息处理的作用&am…

练习题:一维数组

练习题 第一题 键盘录入一组数列&#xff0c;利用冒泡排序将数据由大到小排序 代码 #include <stdio.h>int arr_home01() {int arr[10];int i,j,temp;printf("请输入10个测试整数&#xff1a;\n");int len sizeof(arr) / sizeof(arr[0]);for(i 0;i < …

基于LR/GNB/SVM/KNN/DT算法的鸢尾花分类和K-Means算法的聚类分析

花瓣轮廓&#xff1a; 分类与聚类 使用各种模型进行鸢尾花分类和聚类 1. | 介绍 &#x1f44b; &#x1f914; 数据集问题 鸢尾花分类项目是使用简单数据集实现机器学习模型的实际演示。数据集本身包含有关花瓣和萼片大小的信息&#xff0c;包括鸢尾属物种。通过分析鸢尾花的…

创新驱动医疗变革:SSM+Vue 医院预约挂号系统的设计与实践

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理医院预约挂号系统的相关信息成为必然。开发…

【Java】HashMap的简单教程

HashMap 是 Java 中最常用的数据结构之一&#xff0c;属于 java.util 包&#xff0c;主要用于以键值对&#xff08;key-value&#xff09;形式存储数据。 基本用法 1.创建 HashMap 使用泛型&#xff0c;存储键值对。 import java.util.HashMap;HashMap<KeyType, ValueTy…

windwos defender实现白名单效果(除了指定应用或端口其它一律禁止)禁止服务器上网

一、应用场景说明 当我们的一台windows服务器中毒&#xff0c;变成别人肉鸡&#xff0c;不断向外请示非法网站或攻击其它服务器。 要彻底清除相关木马或病毒往往需要的时间比较长&#xff0c;比较有效的方法是禁止服务器主动向外发包除了网站端口和远程程序除外。 其实这就是一…

免费送源码:Java+ssm++MVC+HTML+CSS+MySQL springboot 社区医院信息管理系统的设计与实现 计算机毕业设计原创定制

摘 要 随着互联网趋势的到来&#xff0c;各行各业都在考虑利用互联网将自己推广出去&#xff0c;最好方式就是建立自己的互联网系统&#xff0c;并对其进行维护和管理。在现实运用中&#xff0c;应用软件的工作规则和开发步骤&#xff0c;采用Java技术建设社区医院信息管理系统…

AI的进阶之路:从机器学习到深度学习的演变(四)

AI的进阶之路&#xff1a;从机器学习到深度学习的演变&#xff08;三&#xff09; 五、深度学习的应用领域 深度学习的应用领域广泛&#xff0c;涵盖了计算机视觉、自然语言处理、语音识别和推荐系统等多个方面。以下将详细探讨这些关键应用领域&#xff0c;展示深度学习在不同…

electron-vite【实战】自定义标题栏【组件封装】(含异形标题栏,指定区域拖拽,窗口置顶,窗口最小化,窗口最大化,取消最大化,隐藏窗口到托盘等)

效果预览 技术要点 透明背景 src/main/index.ts 的 new BrowserWindow 中添加 transparent: true, // 设置窗口背景透明frame: false, // 隐藏窗口边框仅图标和标题部分可拖拽 仅图标和标题部分添加样式 drag .drag {-webkit-app-region: drag; }图标与标题栏的融合 标题栏的…

[react] 获取ant组件ref用ts如何定义?

获取ant的轮播图组件, 我用ts如何定义? import React, { ElementRef } from react; const lunboRef useRef<ElementRef<typeof Carousel>>(null); <Carousel autoplay ref{lunboRef}> 这样就行了! ,然后点一下看看.弹出提示了 当然你还可以用ant内置的G…

springboot中Controller内文件上传到本地以及阿里云

上传文件的基本操作 <form action"/upload" method"post" enctype"multipart/form-data"> <h1>登录</h1> 姓名&#xff1a;<input type"text" name"username" required><br> 年龄&#xf…

python 曲线拟合,曲线拟合交点

目录 效果图: 源代码: 效果图: 源代码: import json import os import shutilimport cv2 import numpy as npfrom numpy.polynomial.polynomial import Polynomialdef calculate_distance(x1, y1, x2, y2):return np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)def get_new_g…

java 集合对象

Java 基础之集合_java集合继承关系图-CSDN博客 集合可以有序或无序&#xff0c;重复或不能重复&#xff0c;空或不能空。 List<> 集合&#xff0c;是有序&#xff0c;允许重复元素&#xff0c;允许空元素 1、ArrayList<> 非线程安全 2、LInkedList<> 非线…

【零基础学习UDS诊断测试】——0x27测试用例设计

从0开始学习CANoe使用 从0开始学习车载测试 相信时间的力量 星光不负赶路者,时光不负有心人。 目录 1.概述 2.响应情况 3.测试点解析 4.0x27诊断调查表 5.详细用例展示 1.概述 UDS(统一诊断服务)中的0x27服务,即安全访问服务(Security Access Service),其主要作用…