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

news2024/10/5 11:14:25

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}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

对决:Kubernetes vs Docker Swarm - 谁才是最优秀的容器编排方案?

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 文章目录一、介绍1. 什么是Kubernetes2. 什么是Docker Swarm3. 为什么需要容器编排&#xff1f;二、 架构比较1. Kubern…

Spring框架——IOC、DI

本篇博客主要介绍Java中的IOC和DI&#xff0c;以及在String框架中的应用。首先&#xff0c;我们将对IOC和DI进行概念介绍&#xff0c;然后讲解它们的关系及在String框架中的应用&#xff0c;最后通过一个实例来展示它们的具体用法。 IOC和DI的概念介绍 IOC&#xff08;Invers…

热更新方案 HybridCLR 学习教程 |(一)原理及准备工作

文章目录 热更新方案 HybridCLR 学习教程(一)HybridCLR原理及准备工作前言一、学前准备1.1 资源下载1.2 文档参考学习二、关于HybridCLR2.1 HybridCLR特性:2.2 HybridCLR工作原理2.3 与其他流行的c#热更新方案的区别2.4 兼容性2.5 原理流程介绍三、快速上手(重要)3.1 体验…

Linux下实现的 HTTP 服务器

项目功能&#xff1a;&#xff08;1&#xff09;能接收客户端的GET请求&#xff1b;&#xff08;2&#xff09;能够解析客户端的请求报文&#xff0c;根据客户端要求找到相应的资源&#xff1b;&#xff08;2&#xff09;能够回复http应答报文&#xff1b;&#xff08;3&#x…

MySQL实验四:数据更新

MySQL实验四&#xff1a;数据更新 目录MySQL实验四&#xff1a;数据更新导读表结构sql建表语句模型图1、 SQL更新&#xff1a;将所有学生的年龄增加1岁代码2、SQL更新&#xff1a;修改“高等数学”课程倒数三名成绩&#xff0c;在原来分数上减5分代码解析3、SQl更新&#xff1a…

docker详解

一、docker相关命令 1、docker进程相关命令 启动docker服务&#xff1a;systemctl start docker 停止docker服务&#xff1a;systemctl stop docker 重启docker服务&#xff1a;systemctl restart docker 查看docker服务状态&#xff1a;systemctl status docker 设置…

可变形卷积(Deformable Conv)原理解析与torch代码实现

1. 可变形卷积原理解析 1.1 普通卷积原理 传统的卷积操作是将特征图分成一个个与卷积核大小相同的部分&#xff0c;然后进行卷积操作&#xff0c;每部分在特征图上的位置都是固定的。 图1 普通卷积过程 图1所示为普通卷积在输入特征图上进行卷积计算的过程&#xff0c;卷积核…

4.3-4.4学习总结

文章目录 目录 文章目录 1.集合的概念 2.Set集合 1.HashSet类 2.LinkedHashSet类 3.TreeSet类 4.EnumSet类 一、Java集合 1.集合的概念 Java集合类是一种特别有用的工具类 , 可用于存贮数量不等的对象 , 并可以实现经常用的数据结构 , 同时集合还可用于保存具有映射关系的关…

小波变换在脑电数据处理中的特征工程

导读在生物信号中&#xff0c;高效的特征工程和特征提取(FE)是获得最优结果的必要条件。特征可以从时域、频域和时频域三个方面进行提取。时频域特征是最先进的特征&#xff0c;在大多数基于人工智能的信号分析问题中表现良好。本文介绍了小波散射变换(WST)在神经疾病分类中的应…

2023美赛春季赛A题思路数据代码论文分享

文章目录赛题思路赛题详情参赛建议&#xff08;个人见解&#xff09;选择队友及任务分配问题&#xff08;重要程度&#xff1a;5星&#xff09;2023美赛春季赛A题思路数据代码【最新】赛题思路 (赛题出来以后第一时间在CSDN分享) 最新进度在文章最下方卡片&#xff0c;加入获取…

高效便捷构造 Http 请求

Http 请求构造 如何构造http请求 对于Get请求: 地址栏直接输入点击收藏夹html 中的 link script img a…form 标签 这里我们重点强调 form 标签构造的 http请求 使用 form 标签构造http请求. <!-- 表单标签, 允许用户和服务器之间交互数据 --><form action"ht…

SpringBoot 项目的创建与启动

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

腾讯最热门的 10 款前端开源项目

作为国内知名的互联网公司&#xff0c;腾讯在前端领域做出了很多开源贡献。本文就来盘点腾讯最热门的 10 款前端开源项目&#xff01; wujie 无界微前端是一款基于 Web Components iframe 微前端框架&#xff0c;具备成本低、速度快、原生隔离、功能强等一系列优点。其能够完…

【ChatGPT】教你搭建多任务模型

ChatGPT教你搭建多任务模型 You: tell me what’s your version of gpt ? ChatGPT: As an AI language model developed by OpenAI, I am based on the GPT (Generative Pretrained Transformer) architecture. However, my version is known as GPT-3.5, which is an updat…

【云原生】:用Kubernetes部署MySQL、SpringCloud、Nacos实现高可用

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 文章目录一、 建立Kubernetes集群1. 安装和配置Kubernetes master节点1.1 安装Docker和Kubernetes1.2 初始化master节点…

Spring事务(2)-EnableTransactionManagement实现源码解析

Transactional注解 Transactional是spring中声明式事务管理的注解配置方式。Transactional注解可以帮助我们标注事务开启、提交、者回滚、事务传播、事务隔离、超时时间等操作。 而EnableTransactionManagement是开启Spring 事务的入口。 EnableTransactionManagement 标注启…

《SpringBoot篇》26.SpringBoot整合Jackson超详细教程(附Jackson工具类)

陈老老老板&#x1f9b8;&#x1f468;‍&#x1f4bb;本文专栏&#xff1a;SpringBoot篇&#xff08;主要讲一些与springboot整合相关的内容&#xff09;&#x1f468;‍&#x1f4bb;本文简述&#xff1a;本文讲一下Jackson常见用法&#xff0c;超级详细。&#x1f468;‍&am…

100天精通Python丨办公效率篇 —— 11、Python自动化操作 Email(发送邮件、收邮件、邮箱客户端)

文章目录一、通过SMTP发送电子邮件1.1 定义邮件正文1.2 发送邮件二、收取电子邮件2.1 配置账户信息2.2 连接邮箱服务器2.3 搜索返回消息ID2.4 读取邮件三、使用邮件客户端发送邮件大家好&#xff0c;我是你们的好朋友西红柿&#xff01;今天咱们聊一聊关于Python怎么操作邮件的…

IP协议以及相关技术

这里写目录标题前言正文IP基本认识IP的作用IP和MAC的关系IP地址的基础知识IP地址定义IP地址分类(IPv4)无分类IP地址CIDR子网掩码IPv6基础知识相关技术DNS域名解析ARPDHCPNATICMPIGMP总结参考连接前言 大家好&#xff0c;我是练习两年半的Java练习生&#xff0c;今天我们来讲一…

TypeScript(八)装饰器

目录 前言 定义 类装饰器 基本用法 操作方式 操作类的原型 类继承操作 方法装饰器 属性装饰器 存取器装饰器 参数装饰器 基本用法 参数过滤器 元数据函数实现 参数过滤 效果实践 装饰器优先级 相同装饰器 不同装饰器 装饰器工厂 hooks与class兼容 结语 …