仿制 Google Chrome 的恐龙小游戏

通过仿制 Google Chrome 的恐龙小游戏,我们可以掌握如下知识点:

  1. 灵活使用视口单位
  2. 掌握绝对定位
  3. JavaScript 来操作 CSS 变量
  4. requestAnimationFrame 函数的使用
  5. 无缝动画实现

页面结构

在这里插入图片描述

实现页面结构

通过上述的页面结构我们可以知道,此游戏中需要有如下的元素:

  • 游戏世界
  • 小恐龙
  • 分数
  • 游戏开始的信息提示
  • 地面
  • 仙人掌

然后构建对应的页面结构

<div class="world"><div class="score">0</div><div class="start-screen">按任意键开始</div><img src="./image/ground.png" class="ground" /><img src="./image/ground.png" class="ground" /><img src="./image/dino-stationary.png" class="dino" />
</div>

使用绝对定位完成页面元素的布局

定义好元素后,我们可以编写对应的样式:

.world {position: relative;overflow: hidden;/* 这里我们先使用固定值来设置,随后使用JS来动态设置值 */width: 100%;height: 300px;
}.score {position: absolute;top: 1vmin;right: 1vmin;font-size: 3vmin;
}.start-screen {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);font-size: 3vmin;
}.hine {display: none;
}/* 使用CSS变量进行占位,然后使用JS来控制变量计算 */
.ground {--left: 0;position: absolute;width: 300%;bottom: 0;left: calc(var(--left) * 1%);
}.dino {--bottom: 0;position: absolute;left: 0;height: 30%;bottom: calc(var(--bottom) * 1%);
}.cactus {--left: 0;position: absolute;left: calc(var(--left) * 1%);height: 30%;bottom: 0;
}

使用 JS 来监听视口大小改变,从而修改游戏世界元素的宽高

为了能够很好的适配所有的设备,我们需要使用 JS 来监听视口的大小改变,从而动态修改页面的元素大小具体的步骤如下。

首先在游戏世界元素中添加如下属性:

<div class="world" data-world></div>

编写 JS 代码:

const WORLD_WIIDTH = 100;
const WORLD_HEIGHT = 30;const worldElem = document.querySelector("[data-world]");setPixelToWorldScale(); // 初始化游戏世界的大小
window.addEventListener("resize", setPixelToWorldScale);function setPixelToWorldScale() {let worldToPixeScale = 0;/*** 判断视口的大小是否大于我们自定义的常量,这样做主要是保证我们的游戏世界的元素大小在视口的中央** 当视口宽度大于高度时,判断结果为false,就取高度做计算* 当视口宽度小于高度时,判断结果为true,就取宽度做计算*/if (window.innerWidth / window.innerHeight < WORLD_WIIDTH / WORLD_HEIGHT) {worldToPixeScale = window.innerWidth / WORLD_WIIDTH;} else {worldToPixeScale = window.innerHeight / WORLD_HEIGHT;}worldElem.style.width = `${WORLD_WIIDTH * worldToPixeScale}px`;worldElem.style.height = `${WORLD_HEIGHT * worldToPixeScale}px`;
}

使用 requestAnimationFrame 编写对应的更新动画函数

整体的页面布局好以后,我们就可以开始对恐龙、地面、仙人掌和分数进行动画的渲染。但是首先我们先用编写控制动画运行的函数。

我们要编写动画函数的话,可以使用requestAnimationFrame函数来帮我们实现。

window.requestAnimationFrame()函数它会告诉浏览器你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。具体的实现代码如下:

let lastTime; // 上一次动画执行时间
function update(time) {// 动画开始执行时,lastTime是为null,所以需要对齐赋值if (lastTime == null) {lastTime = time;window.requestAnimationFrame(update);return;}const delta = time - lastTime; // 计算上一次动画时间和本次动画时间差console.log(delta);lastTime = time;window.requestAnimationFrame(update);
}

实现地面动画

说明: 以下核心代码中会出现一些辅助函数,为了不让篇幅过长,所以这里就不在展示辅助函数代码,可以在下载完整的代码中进行查看。

实现地面移动的动画其实很简单,因为我们地面的样式是使用绝对定位,并且地面元素有两个,所以只要让两个地面元素交替向左移动就可以实现无缝的动画。

为了提示游戏的难度,游戏会随着时间的推移地面的移动速度会越来越快,具体核心代码如下:

// 地面相关JS
const SPEED = 0.05; // 地面移动量(移动量越大速度越快)
const grounds = document.querySelectorAll("[data-ground]");// 重置地面位置
export function setupGround() {setCustomProperty(grounds[0], "--left", 0);setCustomProperty(grounds[1], "--left", 300);
}/*** 修改每帧地面动画* @param {number} delta 每帧动画的时间差* @param {number} speedScale 难度系数*/
export function updateGround(delta, speedScale) {grounds.forEach((ground) => {incrementCustomProperty(ground, "--left", delta * speedScale * SPEED * -1); // 向左移动地面// 当地面元素左移到-300像素时,需要把对应的地面元素重置到第二个地面元素后if (getCustomProperty(ground, "--left") <= -300) {// 因为有两个地面元素,所以长度为两个地面元素的长度incrementCustomProperty(ground, "--left", 600);}});
}
// 核心游戏JS// 动画执行函数
function update(time) {if (lastTime == null) {lastTime = time;window.requestAnimationFrame(update);return;}const delta = time - lastTime;updateSpeedScale(delta);updateGround(delta, speedScale);lastTime = time;window.requestAnimationFrame(update);
}// 修改游戏难度系数(随着时间的推移难度系数值越大,地面的移动速度越快)
function updateSpeedScale(delta) {speedScale += delta * SPEED_SCALE_INCREASE;
}

实现小恐龙相关动画

实现小恐龙关键帧替换

在这个游戏中我们的小恐龙是由两个关键帧交替实现动画效果的,所以我们需要在一帧动画期间交替替换小恐龙的两个关键帧,从而绘制小恐龙跑步的动画,具体核心代码如下:

/*** 修改小恐龙的动画* @param {number} delta 每帧动画的时间差* @param {number} speedScale 难度系数*/
export function updateDino(delta, speedScale) {handleRun(delta, speedScale);
}/*** 小恐龙运动动画* @param {number} delta 每帧动画的时间差* @param {number} speedScale 难度系数* @returns*/
function handleRun(delta, speedScale) {// 判断小恐龙是否跳起,跳起的话关键帧只能固定一个if (isJumping) {dinoElem.src = `./imgs/dino-stationary.png`;return;}/*** 因为一帧动画可以执行很多次,所以我们需要对每帧执行完成后交替更换小恐龙的关键帧图片* FRAME_TIME用于每帧小恐龙交替拆分的关键值*/if (currentFrameTime >= FRAME_TIME) {dinoFrame = (dinoFrame + 1) % DINO_FRAME_COUNT;dinoElem.src = `./imgs/dino-run-${dinoFrame}.png`;currentFrameTime -= FRAME_TIME;}currentFrameTime += delta * speedScale;
}

实现小恐龙跳起动画

小恐龙的跳起主要是在 Y 轴上进行上下运动,并且为了达到最好的动画效果,我们会声明两个变量用于控制跳起动画的效果。具体核心代码如下:

/*** 跳起动画* @param {number} delta 每帧动画的时间差,* @returns 小恐龙跳起跳起的高度*/
function handleJump(delta) {if (!isJumping) return;incrementCustomProperty(dinoElem, "--bottom", yVelocity * delta);// 接触到地面后重置相关参数if (getCustomProperty(dinoElem, "--bottom") <= 0) {setCustomProperty(dinoElem, "--bottom", 0);isJumping = false;}yVelocity -= GRAVITY * delta;
}// 监听小恐龙跳起事件
function onJump(e) {if (e.code !== "Space" || isJumping) return;yVelocity = JUMP_SPEED;isJumping = true;
}

实现仙人掌动画

仙人掌是在一定时间间隔内在游戏世界中创建出来,并且动画移动效果跟地面一样。所以我们在实现此功能的时候最核心的业务就是在随机间隔内生成对应的仙人掌,并执行相应的动画。具体的核心代码如下:

// 创建仙人掌
function createCactus() {const cactus = document.createElement("img");cactus.dataset.cactus = true;cactus.src = "./imgs/cactus.png";cactus.classList.add("cactus");setCustomProperty(cactus, "--left", 100);worldElem.append(cactus);
}// 生成仙人掌并执行相应动画
export function updateCactus(delta, speedScale) {document.querySelectorAll("[data-cactus]").forEach((cactus) => {incrementCustomProperty(cactus, "--left", delta * speedScale * SPEED * -1);if (getCustomProperty(cactus, "--left") <= -100) {cactus.remove();}});// 判断是否要生成下一个仙人掌if (nextCactusTime <= 0) {createCactus();// 生成下一个仙人掌的时间间隔nextCactusTime =randomNumberBetween(CACTUS_INTERVAL_MIN, CACTUS_INTERVAL_MAX) /speedScale;}nextCactusTime -= delta;
}// 重置仙人掌
export function setupCactus() {nextCactusTime = CACTUS_INTERVAL_MIN;// 移除是有仙人掌document.querySelectorAll("[data-cactus]").forEach((cactus) => {cactus.remove();});
}

游戏结束评定

游戏的结束判断就是小恐龙是否碰到仙人掌,所以我们首先需要添加获取小恐龙和仙人掌的方法,具体函数如下:

// 获取仙人掌
export function getCactusRects() {return [...document.querySelectorAll("[data-cactus]")].map((cactus) => {return cactus.getBoundingClientRect();});
}// 获取小恐龙
export function getDinoRect() {return dinoElem.getBoundingClientRect();
}// 判断是否游戏结束
function checkLose() {const dinoRect = getDinoRect();return getCactusRects().some((rect) => isCollision(rect, dinoRect));
}// 通过判断小恐龙和仙人掌是否碰撞
function isCollision(rect1, rect2) {return (rect1.left < rect2.right &&rect1.top < rect2.bottom &&rect1.right > rect2.left &&rect1.bottom > rect2.top);
}

完整代码下载

完整代码下载

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

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

相关文章

算法通关村第14关【白银】| 堆的经典问题

1.数组中的第k个最大元素 思路&#xff1a; 最直观的就是选择法&#xff0c;遍历一k次找到第k大的数之前使用快速排序的思想每次找出一个位置&#xff0c;会超时这里使用堆&#xff08;优先队列&#xff09;&#xff0c;找最大用小堆&#xff0c;找最小用大堆。 例如找第k大的…

【ArcGIS】土地利用变化分析详解(矢量篇)

土地利用变化分析详解-矢量篇 土地利用类型分类1 统计不同土地利用类型的面积/占比1.1 操作步骤Step1&#xff1a;Step2&#xff1a;计算面积Step3&#xff1a;计算占比 2 统计不同区域各类土地利用类型的面积2.1 操作步骤 3 土地利用变化转移矩阵3.1 研究思路3.2 操作步骤 4 分…

计算机毕业设计 智慧养老中心管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

3、SpringBoot_配置文件

四、配置文件 1.前言 曾经使用SpringMVC的时候是手动修改tomcat配置的端口信息&#xff0c;那现在Springboot如何修改&#xff1f;springboot有一个默认的配置文件 application.properties 2.配置文件分类 常用配置信息官方文档地址 https://docs.spring.io/spring-boot/doc…

【Vue.js】使用Element搭建登入注册界面axios中GET请求与POST请求跨域问题

一&#xff0c;ElementUI是什么&#xff1f; Element UI 是一个基于 Vue.js 的桌面端组件库&#xff0c;它提供了一套丰富的 UI 组件&#xff0c;用于构建用户界面。Element UI 的目标是提供简洁、易用、美观的组件&#xff0c;同时保持灵活性和可定制性 二&#xff0c;Element…

变量、因子、缺失值、类型转换、剔除多余变量、随机抽样、用R使用SQL、trim、na.rm=TRUE、数据标准化应用

变量&#xff1a;名义型、有序型、连续型变量 名义型&#xff1a;普通事件类型&#xff0c;如糖尿病I型和糖尿病II型。 有序型&#xff1a;有顺序的事件类型&#xff0c;如一年级、二年级和三年级。 连续型&#xff1a;表示有顺序的数量&#xff0c;如年龄。 因子&#xff1a;…

【一、虚拟机vmware安装】

安装虚拟机 下载 官方下载地址&#xff1a;https://www.vmware.com/cn.html 大概流程就是&#xff0c;最重要的事最后一步

转转闲鱼交易猫链接源码 支持二维码收款

最新仿二手闲置链接源码 后台一键生成链接&#xff0c;后台管理教程&#xff1a;解压源码&#xff0c;修改数据库config/Congig 不会可以看源码里有教程 下载程序&#xff1a;https://pan.baidu.com/s/16lN3gvRIZm7pqhvVMYYecQ?pwd6zw3

关于DatagridviewComboBox控件的若干技术问题

一&#xff1a;DatagridviewComboBox 选定索引更改时更改 DatagridviewTextBox 文本内容 private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e){if (dataGridView1.CurrentCell.ColumnIndex 1 && e.Contr…

vue3-ts-vite:Google 多语言调试 / 翻译

一、实现目标 二、代码实现 2.1、项目vue3 - ts - vite 2.2、index.html 引入文件 <script>window.onload function () {const script document.createElement(SCRIPT)script.src https://translate.google.com/translate_a/element.js?cbgoogleTranslateElementI…

uniapp解决h5跨域问题

我是用的最简单的方法进行去代理 在配置文件配置manifest.json 文件进行配置 "h5": {"devServer": {"port": 8080, //端口号"disableHostCheck": true,"proxy": {"/dev-api": {"target": "http://…

yum和vim工具的使用

目录 yum工具的使用 yum下载原理 软件的查找&下载&删除操作 查找lrzsz软件&#xff08;文件上传或者下载软件&#xff09; 下载lrzsz软件 删除lrzsz软件 vim工具的使用 vim命令模式 命令模式与光标相关的快捷键&#xff1a; 插入模式 底行模式 在本次的博客当中我们主要…

The 2023 ICPC Asia Regionals Online Contest (1) E. Magical Pair(数论 欧拉函数)

题目 T(T<10)组样例&#xff0c;每次给出一个n(2<n<1e18)&#xff0c; 询问多少对&#xff0c;满足 答案对998244353取模&#xff0c;保证n-1不是998244353倍数 思路来源 OEIS、SSerxhs、官方题解 2023 ICPC 网络赛 第一场简要题解 - 知乎 题解 官方题解还没有…

相机One Shot标定

1 原理说明 原理部分网上其他文章[1][2]也已经说的比较明白了&#xff0c;这里不再赘述。 2 总体流程 参考论文作者开源的Matlab代码[3]和github上的C代码[4]进行说明&#xff08;不得不说还是Matlab代码更优雅&#xff09; 论文方法总体分两部&#xff0c;第一部是在画面中找…

Vue 使用vue-pdf 显示pdf文件 切换页面 缩放 全屏 自动播放等

<template><div id"container"><!-- 上一页、下一页--><div class"right-btn"><div click"toFullOrExit" class"turn-btn"><span>{{ isFull 1 ? "取消全屏" : "全屏" }}&l…

机器学习第十四课--神经网络

总结起来&#xff0c;对于深度学习的发展跟以下几点是离不开的: 大量的数据(大数据)计算资源(如GPU)训练方法(如预训练) 很多时候&#xff0c;我们也可以认为真正让深度学习爆发起来的是数据和算力&#xff0c;这并不是没道理的。 由于神经网络是深度学习的基础&#xff0c;学…

分类预测 | Matlab实现NGO-CNN-SVM北方苍鹰算法优化卷积支持向量机分类预测

分类预测 | Matlab实现NGO-CNN-SVM北方苍鹰算法优化卷积支持向量机分类预测 目录 分类预测 | Matlab实现NGO-CNN-SVM北方苍鹰算法优化卷积支持向量机分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现NGO-CNN-SVM北方苍鹰算法优化卷积支持向量机分类预…

32 随机链表的复制

随机链表的复制 题解1 哈希表题解2 回溯哈希哈希思路精简 题解3 优化迭代 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点…

操作系统权限提升(二十八)之数据库提权-SQL Server 数据库安装

SQL Server 数据库安装 SQL Server介绍 SQL Server 是Microsoft 公司推出的关系型数据库管理系统。具有使用方便可伸缩性好与相关软件集成程度高等优点,可跨越从运行Microsoft Windows 98 的膝上型电脑到运行Microsoft Windows 2012 的大型多处理器的服务器等多种平台使用。…

Linux下git安装及使用

Linux下Git使用 1. git的安装 sudo apt install git安装完&#xff0c;使用git --version查看git版本 2. 配置git git config --global user.name "Your Name“ ##配置用户 git config --global user.email emailexample.com ##配置邮箱git config --global --list …