按键机的年代,想必大家都玩过类似雷电,雷霆战机之类的飞行射击类游戏吧,今天我就试着用canvas来还原一下游戏场景。

素材准备

还原场景的第一步是准备素材,首先我们需要一架雷霆战机,经过九牛二虎之力,我在网上找到了心仪的战机,就是下面这架:

战机的子弹也要准备一下,我给我的战机准备了黄色和蓝色两种子弹,如下:

HTML部分

素材准备完成,我们就开始把它们渲染到页面上吧,首先先用3个img标签引入3个素材,并且设置img的display属性为none,使的它们不显示在页面上。同时,我们定义一个canvas元素,用来把我们的飞机渲染在上面。

<img src="../imgs/plane.png" alt="飞机" style="display: none" id="plane">
<img src="../imgs/blue-bullet.png" alt="蓝子弹" style="display: none" id="blue-bullet">
<img src="../imgs/yellow-bullet.png" alt="黄子弹" style="display: none" id="yellow-bullet">
<canvas id="space"></canvas>

4个元素我都设置了id属性,这是为了一会更方便的获取DOM元素。

CSS部分

样式部分,我们只需要使用通配符选择器,将页面自带的margin和padding设为0,防止页面有白边,影响体验感。

* {margin: 0;padding: 0;
}

JS部分

基础准备

首先,我们要先获取到canvas元素,然后设置它的宽高为浏览器页面的宽高,然后我们还要获取到3个img元素,因此img加载图片是异步的,所以我们需要通过轮询3个img元素的complete属性是否都为true来判断3张图片是否都加载好了,只有图片都加载好了,才可以执行我们的动画效果,进行战机的渲染。

// 获取canvas - 设置宽高
const space = document.getElementById('space') // 太空
space.width = window.innerWidth
space.height = window.innerHeight
// 获取上下文
const ctx = space.getContext('2d')
// 获取两个img元素
const plane = document.getElementById('plane') // 飞机
const blueBullet = document.getElementById('blue-bullet') // 蓝子弹
const yellowBullet = document.getElementById('yellow-bullet') // 黄子弹
// 判断三张图片是否都已加载完成
const timer = setInterval(() => {if (plane.complete && blueBullet.complete && yellowBullet.complete) {animate() // 执行动画clearInterval(timer)}
}, 50)

因为是在浏览器上,我们是通过移动鼠标来操作我们的战机,所以我们还需要对鼠标的位置进行监控,可以通过监听 mousemove 事件来获取到鼠标的位置,并且定义一个全局对象 mouse,用其中的xy属性来存放当前鼠标的位置,代码如下:

const mouse = {x: 0, y: 0}// 鼠标位置参数
// 监控鼠标位置改变
window.addEventListener('mousemove', (e) => {mouse.x = e.clientXmouse.y = e.clientY
})

构造函数

这一次我们需要两个构造函数,一个是战机子弹的构造函数 Bullet 另一个则是战机的构造函数 Plane,两个构造函数的结构大体相同,都会拥有xy属性,用于表示自身当前所在位置,有draw方法,基于自身的位置进行绘制,有update方法,对自身的位置进行更新并重绘。 。 不一样的是,子弹构造函数还拥有一个dy属性,表示子弹在y轴的速度,因为子弹是沿着一个方向运动的。

战机构造函数 Plane

function Plane(x, y) {this.x = xthis.y = y// 绘制飞机this.draw = () => { }// 更新飞机位置this.update = () => {}
}

在我们使用 drawImage 进行战机绘制的时候,是以图像的左上角为起点的,也就是鼠标的光标会在战机的左上角,那感觉特别违和,如下图:

战机的素材大小为 140 * 96,我们想要让鼠标在战机中心的话,就要把鼠标的x减去战机宽度的一半,鼠标的y减去战机高度的一半。这一步我们直接放在构造函数的update方法中进行即可。

this.update = () => { // 飞机图像宽高 140 * 96 减去一半 鼠标正好在飞机中间 this.x = mouse.x - 70 this.y = mouse.y - 48 this.draw()
}
// 绘制飞机
this.draw = () => { ctx.drawImage(plane, this.x , this.y)
}

这下舒服多了

战机子弹构造函数 Bullet

function Bullet(x, y) {this.x = xthis.y = ythis.dy = 120 // 子弹速度写死// 绘制子弹this.draw = () => {}// 更新子弹位置this.update = () => { }
}

子弹也有和战机一样的问题但又不尽相同,已知子弹素材的宽高为 38 * 90,因为子弹是要飞出去的,所以我们不必理会y,只需对子弹的x坐标进行计算,让子弹在绘制的时候,x坐标减去19,使的子弹看起来刚好是从机头那里发射出去的。

然后因为我希望我的战机可以发射两种颜色的子弹,间隔发射,所以我定义了一个子弹类型变量 bulletType ,每创建一颗子弹就会给它的值加1,如果是奇数就蓝色子弹,如果是偶数就黄色子弹。

有子弹,自然也要又弹夹,因此我还定义了一个全局弹夹 BULLET_POOL,每个创建的子弹都会push进去这里,然后通过遍历这个数组,调用里面每个子弹的update方法进行子弹位置的更新和绘制。

最后要考虑的就是,因为每颗子弹发射的频率相同,间隔也相等,就会导致这些子弹彷佛是静止的,就像下面这样:

所以我们在绘制的时候,要考虑加点随机因素进去,打破它的这种均衡,弹道的效果显得更加真实。

通过上面的分析,接下来将子弹的构造函数补充完整。

// 更新子弹位置
this.update = () => {// 随机发射this.y -= this.dyif (Math.random() > 0.5) {this.draw()}
}
// 绘制子弹
this.draw = () => {// 子弹图像的大小是38* 90if (bulletType % 2 === 0) {ctx.drawImage(blueBullet, this.x - 19, this.y - 80)} else {ctx.drawImage(yellowBullet, this.x - 19, this.y - 80)}bulletType++ // 更新子弹颜色
}

一开始我曾说过不需要理会子弹的y坐标,其实不然,之所以减80,是因为我们需要让子弹看起来是从飞机头发射出去的,而不是从鼠标(飞机中心)发射出去。

场景还原

素材准备好了,构造函数也准备好了,接下来开始进行场景的还原。

第一步当然是先创建一个雷霆战机啦

// 创建一个雷霆战机
const fighter = new Plane(0, 0) // 飞机起始位置在左上角

第二步就是执行 requestAnimationFrame 来绘制飞机和子弹啦

function animate() {requestAnimationFrame(animate)// 清空画布ctx.clearRect(0, 0, space.width, space.height)// 子弹发射间隔if (interval % 1000 === 0) {const newBullet = new Bullet(mouse.x, mouse.y)// 创建一个新的子弹BULLET_POOL.push(newBullet) // 加入子弹池} else {interval++}// 绘制子弹for (let bullet of BULLET_POOL) {bullet.update()}// 绘制飞机fighter.update()
}

这里要注意的是,要先绘制子弹,然后再绘制战机,因此有层级的存在,这个顺序搞乱了,会导致子弹挡住战机。然后子弹发射间隔那里,本来是想减缓子弹发射的速度的,好像没啥子用,我设置到了10w都没见子弹变慢,但是懒得删掉了。

最终效果

下面就是最后实现的效果啦

什么,你说雷霆战机的背景是外太空黑色的?我也想呀,但是我抠不出来图,只能用白色糊弄过去啦。

最后

最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

【Canvas】童年玩过的雷霆战机你还记得吗?相关推荐

  1. 那些年的java游戏_那些年我们曾经玩过的游戏,你还记得几个

    标题:那些年我们曾经玩过的游戏,你还记得几个 随着时间长河的推进,我们已经长大了.你还记得我们那些年一起玩过的游戏么? 弹弓 一般用树枝做弓架,也可以用旱伞的伞骨做弹弓架.要买弹力很大的像皮筋,就和那 ...

  2. JavaSwing多线程小游戏雷霆战机

    在做完连连看以后,想到要做一个多线程游戏,本来是做的一个跳伞的小游戏的.但是做到一半的时候,觉得可玩性太低了.后面想来想去还是打算做一个以前玩过的雷霆战机小游戏,也就是飞机大战. 1.效果展示 2.绘 ...

  3. python 0基础如何做出雷霆战机?【源码送上】

    嗨害大家好鸭!我是小熊猫~ 大家都玩过雷霆战机吧? 这样的 我的qun友 才学三周就做出了雷霆战机这种游戏, 看起来确实不错啊~ 今天就实现一下雷霆战机的运作,开始整活! 一.准备工作 使用的软件版本 ...

  4. 纯c语言打造的雷霆战机,飞机大战。可吃道具加强。

    纯c语言编写的雷霆战机,简单容易上手 本博客将持续更新带给大家日常生活中的简单经典的C语言小项目和生活中的电子DIY.以后会陆续推出讲解视频发布在b站上.所有程序和日常学习资料全都可以在 群文件中免费 ...

  5. java 雷霆战机游戏 飞机大战 全过程教学+免费素材(附全部源代码)

    这个游戏已是我第二次编写了,之前写过一个简易版的飞机大战类似demo.这次在上一次基础上添加了许多元素,增添了可玩性. 游戏效果图如下: ps :完整源码+视频教程+论文文档 :java雷霆战机完整资 ...

  6. Python3雷霆战机2D+双人联机+源码+解压运行(总之啥都有)

    由于最近时间紧迫没有精力来逐一发布,索性将这款游戏直接打包一波带走上传到了博客,感兴趣想玩的博友们可通过链接自行下载哈!!! 作品名称: MultiplayerGameEngine-master 发布 ...

  7. 当我给表弟用python写了个雷霆战机后

    雷霆战机大家都玩过吧,就这种的 才读小学的表弟,一到周末就打扰我上分,别人都是三年高考五年模拟,那我不一样,我直接给他写了个游戏自己到一边玩去,总不能教他爬虫吧,小小年纪不合适~ 一.准备工作 使用的 ...

  8. 雷霆战机源代码c语言,C++实现雷霆战机可视化小游戏

    用C++和easyx实现简单的雷霆战机小游戏 之前在网上看了许多关于c++或者是其他语言实现雷霆战机的帖子,大多不完整,或者是要付费才能阅读,现将源码展示如下,仅作学习交流之用. 基本原理 基本思路 ...

  9. Unity 3D学习之雷霆战机(一)

    雷霆战机(一) 注:本文包括雷霆战机的第一部分,在没书的情况下,慢慢摸索自己真的了解到许多知识,现于初学者分享并求指教,因为自己是初学者中间可能会有许多不成熟的地方或者错误的地方,望指教.再次重申本人 ...

最新文章

  1. tcp retransmission 出现的原因_为什么 TCP 会被 UDP 取代?
  2. 动手实现Kotlin协程同步切换线程,以及Kotlin协程是如何实现线程切换的
  3. ios view 切上部分圆角_ios – 具有圆角的UIView:如何正确剪辑子视图?
  4. 【Linux】一步一步学Linux——Bash常用快捷键(11)
  5. 登录工程:传统 Web 应用中的身份验证技术
  6. php 输出01,php基础01_thinkphp输出Hello World-Go语言中文社区
  7. EntityFramework Core映射关系详解
  8. html点击按钮 重新加载页面或者跳转页面实现
  9. ***编程DIY (Delphi版) - 第2篇 单实例运行
  10. Python股票历史数据下载
  11. python 下载google文件
  12. API函数大全(转载)
  13. Qt实战案例(13)——Qt的界面外观详细介绍
  14. python题目-兔子生育计数
  15. Kvaser、C++、Qt编写监控界面(三)
  16. 一文读懂 12种卷积方法
  17. CentOS安装NTFS-3G读写Windows 10的移动NTFS磁盘
  18. 如何解决simulink控制系统仿真中的代数环
  19. linux中文输入法 2017,ubuntu 16.04 下安装并切换搜狗中文输入法
  20. 计算机本科生需要具备的素养

热门文章

  1. 一个HashMap跟面试官扯了半个小时【云图智联】
  2. 笑话:​计算机系的男同学追班里一女同学,结果此女总是躲躲闪闪。男的看没戏,就另找了一个去追,结果这女的不满意了...
  3. python特训营-**常用设计模式**
  4. Cglib BeanCopier工具类
  5. 什么是数据仓库,什么是ETL
  6. python 项目结构
  7. 短视频Demo模块:魔法相机、拍摄、导入裁剪、导入编辑的差异
  8. Getting Started with Gym
  9. arch Linux下将esc和cap locks调换位置[更改键盘键位-CW向]
  10. Vue中的事件修饰符