用Canvas写一个简单的游戏--别踩白块儿
第一次写博客也不知怎么写,反正就按照我自己的想法来吧!怎么说呢?还是不要扯那些多余的话了,直接上正题吧! 第一次用canvas写游戏,所以挑个简单实现点的来干:别踩白块儿,其他那些怎么操作的那些就不用再扯了,大家应该都懂,不懂的看到黑色的就点就是了,扯多了我打字手也累,大概。链接给你们:http://nowtd.cn/white/
咱不是理论派,所以在这里不会扯多少理论。
首先看看html的结构吧
<header class="container"> <article class="game-info"> <span class="show sorce rounded">分数:<span class="num">0</span></span> <span class="time">时间: <span class="num">0</span>s</span> </article> </header> <main class="container"> <canvas id="game-body">你的浏览器不支持canvas!!!</canvas> </main> <section class="control container"> <button class="btn rounded" id="game-play">开始</button> <button class="btn rounded" id="game-over">结束</button> </section>
很简单,只有一个canvas和一些按钮 and 用作显示分数那些的span
css的内容就不贴了,反正只是给它作一下简单布局而已,重要的还是js怎么去实现它的。
首先先看看怎么调用吧
<script> (function () { let wb = new WhiteBlock({ canvas: '#game-body', play: '#game-play', over: '#game-over', sorce: '.sorce > .num', time: '.time > .num', width: 330, height: 450, col: 4, row: 4, }); })(); </script>
就这么点东西:
canvas: 画布,
play: 开始按钮,
over: 结束按钮,
sorce: 显示成绩,
time: 显示时间,
white: 画布的宽,
height: 画布的高,
col: 一列多少个砖块
row: 一共多少行
内部的一些参数
this.speedLevel = [4, 5, 6, 7, 8]; // 成绩 this.sorceNumber = 0; this.Time = 0; //定时器 this.timer = null; // 是否已开始游戏 this.isPlay = false; // 是否已结束游戏 this.isOver = false; // 画布参数 this.width = null; this.height = null; this.canvas = null; this.ctx = null; // 砖块 this.blockQueue = []; this.blockWidth = null; this.blockHeight = null; this.col = 4; this.row = 6; // 速度 this.offset = this.speedLevel[0];
这里要说的大概就只有speedLevel和this.blockQueue了:
speedLevel: 输入一个规定速度的数组,然后根据成绩的提升来获取相应的速度值
changeSpeed() { if (this.sorceNumber < (this.speedLevel.length * 10)) { let num = Math.floor(this.sorceNumber / 10); if (this.speedLevel[num]) { this.offset = this.speedLevel[num]; } else { this.offset = this.speedLevel[this.speedLevel.length - 1]; } } }
当成绩超过一定值之后,就以speedLevel的最后一个值为速度,不再加速。
this.blockQueue:生成一组队列做点阵来绘制砖块,分别由createBlock, addBlock,removeBlock,fillBlock这四个方法来操控,drawing方法会遍历this.blockQueue
// 创建砖块 createBlock() { let len = this.col, i = 0, list = [], rand = Math.floor(Math.random() * len); // 随机黑块 for (; i < len; i ++) { list.push(rand === i ? 1 : 0); } return list; } // 添加砖块队列 addBlock(i) { this.blockQueue.unshift({top: -((i + 1) * this.blockHeight), data: this.createBlock()}); } // 移除砖块队列 removeBlock() { (this.blockQueue.length > 0) && this.blockQueue.pop(); fillBlock() { let len = this.row + 1, // 多加一队,以免刚好一屏 i = 0; for (; i < len; i ++) { this.addBlock(i); } } drawing() { this.ctx.clearRect(0, 0, this.width, this.height); this.each(this.blockQueue, (index, item) => { let top = item['top'], block = item['data'], ctx = this.ctx; this.each(block, (index, item) => { ctx.fillStyle = parseInt(item) === 1 ? '#000' : '#fff'; ctx.beginPath(); ctx.fillRect(index * this.blockWidth, top, this.blockWidth, this.blockHeight); }); }); }
因为个人喜欢从数组第一个值迭代,所以这里把队列反转过来操作了
而每个砖块的宽高都由col和row决定:
getBlockWidthAndHeight() { this.blockWidth = this.width / this.col; this.blockHeight = this.height / this.row; return this; }
把砖块绘制好了之后就是要让砖块跑起来了,我让每一队砖块都加一个offset值,this,offset为一个速度值
this.offset = this.speedLevel[num];
runPlay() { this.each(this.blockQueue, (index, item) => { item['top'] += this.offset; }); this.drawing(); this.timer = setTimeout(this.runPlay.bind(this), 20); // 修改时间 this.changeTime(); }
runplay方法设定为20毫秒重绘一次,这个20是随便写上去的,至于是否合理就暂时不管了
点击开始按钮触发事件, 开始游戏
playGame(e) { if (this.isOver) { // 重新开始 this.replay(); console.log(this); } // 让砖块跑起来 if (!this.isPlay) { // 检测是否有黑块到底 this.checkState(); // 让砖块跑进来 this.runPlay(); this.play.html('暂停'); this.isPlay = true; } else { // 暂停游戏 this.puaseGame(); this.play.html('继续'); } }
暂停游戏其实就是清除定时器
puaseGame() { clearTimeout(this.timer); this.isPlay = false; }
判断输赢: 怎么个输赢法就不讲了,只说实现。
checkState() { let i = this.blockQueue.length - 1, item = this.blockQueue[i], top = item['top'], data = item['data']; if ((this.height) <= top) { this.checkBlock(data) ? (() => { this.again(); requestAnimationFrame(this.checkState.bind(this)); })() : this.puaseGame(); requestAnimationFrame(this.checkState.bind(this)); } }
每次运行都取出队列中最后的一队,并判断它的top值是否大于或小于画布的高,如果最前面的一队(数组最后一个)的top大于画布高,同时遍历这个队列,如果有一个值为1(黒块),则游戏失败,并调用gameOver方法。
否则,即说明当前队列安全通过,就调用again方法,这个方法里移除队首,并在队尾添加一队,这样不断循环生成砖块。
点击判断输赢,这里有人可能想到了用getImageData来获取颜色值来判断。这样做对于这个只有黒和白的游戏来说的确是很方便的,但是这里我没用获取颜色值来做(最开始是那样打算来着),因为这里不仅要判断输赢,当点击到了黒块之后也要修改黒块的颜色的,最终还是逃不出要判断当前点击的是哪个砖块这一步,所以这里用碰撞检测来做就好了,也节省了一个方法的代码量(但其实在这里一个方法内的代码量也不会很多),下面是代码:
isWin(e) { let x = e.offsetX || e.originalEvent.layerX, y = e.offsetY || e.originalEvent.layerY, data = this.blockQueue; this.each(data, (index, item) => { let top = item['top']; if (y > top && y < (data[index + 1] ? data[index + 1]['top'] : top + this.blockHeight)) { // 判断点击的列中的黑块是否被点中 this.checkCloumn(x, item['data'], index); } }); } checkCloumn(x, data, i) { this.each(data, (index, item) => { let left = index * this.blockWidth; if (x > left && x < (left + this.blockWidth)) { if (item === 1) { this.blockQueue[i]['data'][index] = 0; // 记录成绩 this.addSorce(); this.drawing(); } else { this.gameOver(); this.puaseGame(); } } }); }
点击结束按钮就调用一个replay方法,重置那些配置参数
replay() { // 清空砖块队列 this.blockQueue = []; // 重置数据 this.offset = this.speedLevel[0]; this.sorceNumber = 0; this.Time = 0; this.isPlay = false; this.timer = null; this.play.html('开始'); this.sorce.html(0); this.time.html(0); //重新填充队列 this.fillBlock(); // 重新维制 this.drawing(); this.isOver = false; }
接下来成绩和时间那个就简单了,剩下就是处理游戏结束后的消息提示了,由于无论是有黒块到了底下还是点击了白块,都会调用一个gameOver方法,
gameOver() { this.isOver = true; this.play.html('开始'); // 显示游戏结束提示 this.showMess(); }
我在这个方法里调用了一个showMess方法,而我写到最后不想写这个了,要写的话一开始就会写好这块了,所心只是简单地弹出一个警告框算了
alert('游戏已结束!' }
到哪天想要弄个好看点的提示时,只要重写这个方法就好了。
嘛,反正到这里算是结束了。总感觉有点一直在贴代码的感觉,哈哈!!
这是源码地址: https://gitee.com/nowtd/test/tree/master/white
第一次写博客,不清楚写得怎样?各位请多多指教!!!
补充一点: 这个demo引入了JQ,用来获取dom和绑定事件,修改属性那些,但现在感觉好像不引入JQ所需要写的代码也不会很多,不过嘛,能少写就少写吧。
用Canvas写一个简单的游戏--别踩白块儿的更多相关文章
- 手摸手带你实现 小游戏<别踩白块儿 -- 内有游戏链接>
别踩白块儿 使用(白鹭引擎)Egret编写的游戏 游戏地址 准备工作 了解白鹭引擎 并安装编写工具 安装游戏引擎 安装Egret Wing3 创建项目 创建项目可以选择不同版本的引擎,创建成功之后还可 ...
- 怎样用HTML5 Canvas制作一个简单的游戏
原文连接: How To Make A Simple HTML5 Canvas Game 自从我制作了一些HTML5游戏(例如Crypt Run)后,我收到了很多建议,要求我写一篇关于怎样利用HTML ...
- [译]怎样用HTML5 Canvas制作一个简单的游戏
这是我翻译自LostDecadeGames主页的一篇文章,原文地址:How To Make A Simple HTML5 Canvas Game. 下面是正文: 自从我制作了一些HTML5游戏(例如C ...
- 原生js 基于canvas写一个简单的前端 截图工具
先看效果 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <titl ...
- 初学JS——利用JS制作的别踩白块儿(街机模式) 小游戏
这个是上个星期5写的了,当时是突然想写个游戏,就想到了别踩白块儿,当时的想法是 可能普通模式的别踩白块儿因为他的“块儿”是滚动的向上这种,以我目前会的技术想不出怎么写, 但是如果是街机模式,通过你每按 ...
- canvas写个简单的小游戏
之前在HTML5 Canvas属性和方法汇总一文中,介绍过Canvas的各种属性以及方法的说明,并列举了自己写的一些Canvas demo,接下来开始写一个简单的小游戏吧,有多简单,这么说吧,代码不到 ...
- 分享:计算机图形学期末作业!!利用WebGL的第三方库three.js写一个简单的网页版“我的世界小游戏”
这几天一直在忙着期末考试,所以一直没有更新我的博客,今天刚把我的期末作业完成了,心情澎湃,所以晚上不管怎么样,我也要写一篇博客纪念一下我上课都没有听,还是通过强大的度娘完成了我的作业的经历.(当然作业 ...
- 用C写一个简单的推箱子游戏(二)
下面接着上一篇随笔<用C写一个简单的推箱子游戏(一)>来写 tuidong()函数是用来判断游戏人物前方情况的函数,是推箱子游戏中非常重要的一个函数,下面从它开始继续介绍推箱子的小程序怎么 ...
- 用C写一个简单的推箱子游戏(一)
我现在在读大二,我们有一门课程叫<操作系统>,课程考查要求我们可以写一段程序或者写Windows.iOS.Mac的发展历程.后面我结合网上的资料参考,就想用自己之前简单学过的C写一关的推箱 ...
随机推荐
- RTlinux3.2安装
( 1 ).前言 2003 年以后, fmslabs 的 RTLinux Free 版本为 3.2Pre ,和以前的 RTLinux 3.1 比较,不再需要必须从 2.4.4 的内核上安装. RTLi ...
- final、finally和finalize的区别
final.finally和finalize的区别 这三者的区别可以从两个方面来说 1.意思解释方面 (1)final是修饰符(关键字) (2)finally是异常处理中的程序块 (3)finaliz ...
- Struts 有哪些常用标签库
Struts 有哪些常用标签库 1.html标签库 2.bean标签库 3.logic标签库
- TensorFlow MNIST初级学习
MNIST MNIST 是一个入门级计算机视觉数据集,包含了很多手写数字图片,如图所示: 数据集中包含了图片和对应的标注,在 TensorFlow 中提供了这个数据集,我们可以用如下方法进行导入: f ...
- 一道bfs与邻接表应用题
Problem Description 终于有一天,王元姬用他的劫打上了最强王者.他号称,他从来不会在偶数段位停留,因为他的实力太强会跳段(这个13我给满分).傲娇棠和翔妹觉得他的13装的都比勇哥哥好 ...
- jquery 获取上传图片的大小(或者本张图片的其它属性)
<input type="file" name="upload" style="display:none;" src="${ ...
- mybatis中动态update中的isNotEmpty和isNotNull标签
一,简介 在iBATIS中isNull用于判断参数是否为Null,isNotNull相反isEmpty判断参数是否为Null或者空,满足其中一个条件则其trueisNotEmpty相反,当参数既不为N ...
- 关于js中 toFixed()的一个小坑
作为一名前端,大家都应该知道,toFixed()的作用,toFixed()经常用于前台与后台数据格式的转换,套用下w3c上面的定义: 定义和用法toFixed(n) 方法可把 Number 四舍五入为 ...
- 在TextBox中敲击回车执行ASP.NET后台事件
1.在TextBox中敲击回车执行ASP.NET后台事件 0.说明 页面中有一个用于搜索的TextBox,希望能在输入内容后直接回车开始搜索,而不是手动去点击它旁边的搜索按钮 但因为该TextBo ...
- 实现一个简单的订阅与发布模式的代码块,和redux
/** * Created by Mrzou on 2018/3/11. */ //实现简单的订阅与发布模式的代码块export function pattern() { let currentLis ...