The sand accumulates to form a pagoda

  • ✨ 写在前面
  • ✨ 功能介绍
  • ✨ 页面搭建
  • ✨ 样式设置
  • ✨ 逻辑部分

✨ 写在前面

上周我们实通过前端基础实现了打字通,当然很多伙伴再评论区提出了想法,后续我们会考虑实现的,今天还是继续按照我们原定的节奏来带领大家完成一个小人逃脱游戏,功能也比较简单简单,也是想借助这样一个简单的功能,然后来帮助大家了解我们JavaScript在前端中的作用, 在前面的文章当中我们也提及到我们在本系列的专栏是循序渐进从简单到复杂的过程,后续会带领大家用前端实现翻卡片、扫雷、贪吃蛇等有趣的小游戏,纯前端语言实现,都会陆续带给大家。欢迎大家订阅我们这份前端小游戏的专栏。

✨ 功能介绍

前端搭建小人逃脱游戏(内附源码)

白色方块为我们游戏中的主角,游戏开始后回随机坠落红色方块,我们可以通过键盘的左右键来控制白色方块的移动,来躲避白色方块,被撞击到游戏结束汇算分数,每次成功躲避都加一分,60秒自动结束汇算得分;你可以通过修改游戏的参数来控制难度等级!

✨ 页面搭建

创建文件

首先呢我们创建我们的HTML文件,这里我就直接命名为 小人逃脱.html 了,大家可以随意命名, 文件创建生成后我们通过编辑器打开,这里我用的是VScode, 然后初始化我们的代码结构,那在这里告诉大家一个快捷键,就是我们敲上我们英文的一个 ! 我们敲击回车直接就会给我们生成基础版本的前端代码结构。

前端搭建小人逃脱游戏(内附源码)

文档声明和编码设置: 在HTML文档的头部,使用<!DOCTYPE>声明HTML文档类型,确保浏览器以正确的方式渲染网页内容。同时,设置UTF-8编码,以确保浏览器能够正确地解析和显示中文字符。下面我就开始搭建我们的DOM结构了!

DOM结构搭建

这段代码定义了一个 HTML 游戏页面,包含了三个 <div> 元素。<div id="game"> 表示游戏主界面,游戏中的元素都将显示在这个 <div> 元素中。<div id="player"></div> 表示玩家的角色,即小人,将显示在游戏主界面中央的底部。<div id="score">分数: 0</div> 表示游戏得分,将显示在游戏主界面的顶部。<div id="message"></div> 表示游戏结束时弹出的提示框,将显示在游戏主界面中央。

<div id="game">
  <div id="player"></div>
  <div id="score">分数: 0</div>
  <div id="message"></div>
</div>

前端搭建小人逃脱游戏(内附源码)

✨ 样式设置

我们看到了上面的的DOM已经搭建好了,但是很显然样式比较随意了,我们简单的来配置一下样式吧,其实我们本专栏也是想带领大家掌握一些逻辑所以样式方面我们就一切从简;其中,body 元素的样式设置了外边距、内边距和隐藏滚动条。#game 元素是整个游戏的容器,设置了宽度、高度、背景颜色和相对定位。#player 元素是玩家,设置了宽度、高度、背景颜色、绝对定位、底部对齐、左侧距离容器中心点的距离,并通过 transform 属性将其水平居中。.obstacle 类表示障碍物,设置了宽度、高度、背景颜色、绝对定位、顶部距离为负数(使其从游戏容器顶部开始落下)、左侧距离容器中心点的距离,并通过 transform 属性将其水平居中。#score 元素是得分标签,设置了绝对定位、顶部距离和左侧距离,并设置了颜色和字体大小。#message 元素是游戏结束提示信息,设置了绝对定位、上、左偏移和颜色、字体大小,并通过 display 属性将其隐藏。

body {
  margin: 0;
  padding: 0;
  overflow: hidden;
}
#game {
  width: 100vw;
  height: 100vh;
  background-color: #222;
  position: relative;
  overflow: hidden;
}
#player {
  width: 50px;
  height: 50px;
  background-color: white;
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
}
.obstacle {
  width: 50px;
  height: 50px;
  background-color: red;
  position: absolute;
  top: -50px;
  left: 50%;
  transform: translateX(-50%);
}
#score {
  position: absolute;
  top: 10px;
  left: 10px;
  color: white;
  font-size: 24px;
}
#message {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: white;
  font-size: 48px;
  display: none;
}

前端搭建小人逃脱游戏(内附源码)

✨ 逻辑部分

首先,通过 document.getElementById 方法获取了 game、player、score 和 message 等 DOM 元素对象。这些元素分别对应游戏区域、玩家方块、得分标签和游戏结束提示信息。在游戏参数部分,定义了一些游戏的参数,包括得分、游戏是否结束、障碍物下落间隔、障碍物下落速度、障碍物宽度、障碍物高度和玩家移动速度等。

// 获取DOM元素
const game = document.getElementById('game');
const player = document.getElementById('player');
const score = document.getElementById('score');
const message = document.getElementById('message');
// 初始化游戏参数
let scoreValue = 0; // 得分
let isGameOver = false; // 游戏是否结束
let obstacleInterval; // 障碍物下落间隔
let gameTimer; // 游戏计时器
const obstacleSpeed = 5; // 障碍物下落速度
const obstacleWidth = 50; // 障碍物宽度
const obstacleHeight = 50; // 障碍物高度
const playerSpeed = 10; // 玩家移动速度

接着,通过 document.addEventListener 监听键盘事件,控制玩家方块左右移动。在 dropObstacle 函数中,创建障碍物并添加到游戏区域中,并设置障碍物下落动画,同时进行碰撞检测,如果玩家方块与障碍物相撞,游戏结束。

// 监听键盘事件,控制玩家左右移动
document.addEventListener('keydown', event => {
  if (event.key === 'ArrowLeft') {
    player.style.left = Math.max(0, player.offsetLeft - playerSpeed) + 'px';
  } else if (event.key === 'ArrowRight') {
    player.style.left = Math.min(game.clientWidth - player.offsetWidth, player.offsetLeft + playerSpeed) + 'px';
  }
});
// 检测碰撞
function detectCollision() {
  // 玩家和每个障碍物都进行碰撞检测
  const obstacles = document.querySelectorAll('.obstacle');
  obstacles.forEach(obstacle => {
    if (isCollided(player, obstacle)) {
      endGame();
    }
  });
}
// 碰撞检测函数
function isCollided(element1, element2) {
  const rect1 = element1.getBoundingClientRect();
  const rect2 = element2.getBoundingClientRect();
  return !(rect1.bottom < rect2.top || rect1.top > rect2.bottom || rect1.right < rect2.left || rect1.left > rect2.right);
}
// 障碍物下落
function dropObstacle() {
  // 创建障碍物
  const obstacle = document.createElement('div');
  obstacle.classList.add('obstacle');
  obstacle.style.width = obstacleWidth + 'px';
  obstacle.style.height = obstacleHeight + 'px';
  obstacle.style.top = -obstacleHeight + 'px';
  obstacle.style.left = Math.floor(Math.random() * (game.clientWidth - obstacleWidth)) + 'px';
  game.appendChild(obstacle);
  // 障碍物下落动画
  const obstacleDrop = setInterval(() => {
    if (!isGameOver) {
      obstacle.style.top = obstacle.offsetTop + obstacleSpeed + 'px';
      if (obstacle.offsetTop >= game.clientHeight) {
        game.removeChild(obstacle);
        clearInterval(obstacleDrop);
        scoreValue++;
        score.innerHTML = 'Score: ' + scoreValue;
      }
      detectCollision();
    }
  }, 10);
}
// 开始游戏
function startGame() {
  scoreValue = 0;
  isGameOver = false;
  obstacleInterval = setInterval(dropObstacle, 1000);
  gameTimer = setTimeout(() => {
    endGame();
  }, 60000);
}
// 结束游戏
function endGame() {
  isGameOver = true;
  clearInterval(obstacleInterval);
  clearTimeout(gameTimer);
  message.innerHTML = 'Game Over! Your score is ' + scoreValue;
  message.style.display = 'block';
}
// 监听重新开始按钮
message.addEventListener('click', () => {
  message.style.display
  'none';
  while (game.firstChild) {
    game.removeChild(game.firstChild);
  }
  startGame();
});
// 开始游戏
startGame();

通过 setInterval 定时器循环调用 dropObstacle 函数,实现障碍物不断下落。同时,通过 setTimeout 定时器设置游戏时间,如果游戏时间到了,则游戏结束。最后,在 endGame 函数中,设置游戏结束的一些行为,包括清除障碍物下落定时器、游戏时间定时器,显示游戏结束信息等。同时,在 message 元素上添加点击事件,用于重新开始游戏。当然这里大家可以通过自己配置参数来增加游戏的难度!你也可以将元素替换成图片让游戏更加生动!

完整代码

<!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>小人逃脱</title>
  <style>
    body {
      margin: 0;
      padding: 0;
      overflow: hidden;
    }
    #game {
      width: 100vw;
      height: 100vh;
      background-color: #222;
      position: relative;
      overflow: hidden;
    }
    #player {
      width: 50px;
      height: 50px;
      background-color: white;
      position: absolute;
      bottom: 0;
      left: 50%;
      transform: translateX(-50%);
    }
    .obstacle {
      width: 50px;
      height: 50px;
      background-color: red;
      position: absolute;
      top: -50px;
      left: 50%;
      transform: translateX(-50%);
    }
    #score {
      position: absolute;
      top: 10px;
      left: 10px;
      color: white;
      font-size: 24px;
    }
    #message {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      color: white;
      font-size: 48px;
      display: none;
    }
  </style>
</head>
<body>
  <div id="game">
    <div id="player"></div>
    <div id="score">分数: 0</div>
    <div id="message"></div>
  </div>
</body>
<script>
  // 获取DOM元素
  const game = document.getElementById('game');
  const player = document.getElementById('player');
  const score = document.getElementById('score');
  const message = document.getElementById('message');
  // 初始化游戏参数
  let scoreValue = 0; // 得分
  let isGameOver = false; // 游戏是否结束
  let obstacleInterval; // 障碍物下落间隔
  let gameTimer; // 游戏计时器
  const obstacleSpeed = 5; // 障碍物下落速度
  const obstacleWidth = 50; // 障碍物宽度
  const obstacleHeight = 50; // 障碍物高度
  const playerSpeed = 10; // 玩家移动速度
  // 监听键盘事件,控制玩家左右移动
  document.addEventListener('keydown', event => {
    if (event.key === 'ArrowLeft') {
      player.style.left = Math.max(0, player.offsetLeft - playerSpeed) + 'px';
    } else if (event.key === 'ArrowRight') {
      player.style.left = Math.min(game.clientWidth - player.offsetWidth, player.offsetLeft + playerSpeed) + 'px';
    }
  });
  // 检测碰撞
  function detectCollision() {
    // 玩家和每个障碍物都进行碰撞检测
    const obstacles = document.querySelectorAll('.obstacle');
    obstacles.forEach(obstacle => {
      if (isCollided(player, obstacle)) {
        endGame();
      }
    });
  }
  // 碰撞检测函数
  function isCollided(element1, element2) {
    const rect1 = element1.getBoundingClientRect();
    const rect2 = element2.getBoundingClientRect();
    return !(rect1.bottom < rect2.top || rect1.top > rect2.bottom || rect1.right < rect2.left || rect1.left > rect2.right);
  }
  // 障碍物下落
  function dropObstacle() {
    // 创建障碍物
    const obstacle = document.createElement('div');
    obstacle.classList.add('obstacle');
    obstacle.style.width = obstacleWidth + 'px';
    obstacle.style.height = obstacleHeight + 'px';
    obstacle.style.top = -obstacleHeight + 'px';
    obstacle.style.left = Math.floor(Math.random() * (game.clientWidth - obstacleWidth)) + 'px';
    game.appendChild(obstacle);
    // 障碍物下落动画
    const obstacleDrop = setInterval(() => {
      if (!isGameOver) {
        obstacle.style.top = obstacle.offsetTop + obstacleSpeed + 'px';
        if (obstacle.offsetTop >= game.clientHeight) {
          game.removeChild(obstacle);
          clearInterval(obstacleDrop);
          scoreValue++;
          score.innerHTML = 'Score: ' + scoreValue;
        }
        detectCollision();
      }
    }, 10);
  }
  // 开始游戏
  function startGame() {
    scoreValue = 0;
    isGameOver = false;
    obstacleInterval = setInterval(dropObstacle, 1000);
    gameTimer = setTimeout(() => {
      endGame();
    }, 60000);
  }
  // 结束游戏
  function endGame() {
    isGameOver = true;
    clearInterval(obstacleInterval);
    clearTimeout(gameTimer);
    message.innerHTML = 'Game Over! Your score is ' + scoreValue;
    message.style.display = 'block';
  }
  // 监听重新开始按钮
  message.addEventListener('click', () => {
    message.style.display
    'none';
    while (game.firstChild) {
      game.removeChild(game.firstChild);
    }
    startGame();
  });
  // 开始游戏
  startGame();
</script>
</html>

本期推荐

前端搭建小人逃脱游戏(内附源码)


原创不易,还希望各位大佬支持一下
\textcolor{blue}{原创不易,还希望各位大佬支持一下}
原创不易,还希望各位大佬支持一下

👍
点赞,你的认可是我创作的动力!
\textcolor{green}{点赞,你的认可是我创作的动力!}
点赞,你的认可是我创作的动力!

⭐️
收藏,你的青睐是我努力的方向!
\textcolor{green}{收藏,你的青睐是我努力的方向!}
收藏,你的青睐是我努力的方向!

✏️
评论,你的意见是我进步的财富!
\textcolor{green}{评论,你的意见是我进步的财富!}
评论,你的意见是我进步的财富!

发表回复