第一次写博客也不知怎么写,反正就按照我自己的想法来吧!怎么说呢?还是不要扯那些多余的话了,直接上正题吧! 第一次用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写一个简单的游戏--别踩白块儿的更多相关文章

  1. 手摸手带你实现 小游戏<别踩白块儿 -- 内有游戏链接>

    别踩白块儿 使用(白鹭引擎)Egret编写的游戏 游戏地址 准备工作 了解白鹭引擎 并安装编写工具 安装游戏引擎 安装Egret Wing3 创建项目 创建项目可以选择不同版本的引擎,创建成功之后还可 ...

  2. 怎样用HTML5 Canvas制作一个简单的游戏

    原文连接: How To Make A Simple HTML5 Canvas Game 自从我制作了一些HTML5游戏(例如Crypt Run)后,我收到了很多建议,要求我写一篇关于怎样利用HTML ...

  3. [译]怎样用HTML5 Canvas制作一个简单的游戏

    这是我翻译自LostDecadeGames主页的一篇文章,原文地址:How To Make A Simple HTML5 Canvas Game. 下面是正文: 自从我制作了一些HTML5游戏(例如C ...

  4. 原生js 基于canvas写一个简单的前端 截图工具

    先看效果 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <titl ...

  5. 初学JS——利用JS制作的别踩白块儿(街机模式) 小游戏

    这个是上个星期5写的了,当时是突然想写个游戏,就想到了别踩白块儿,当时的想法是 可能普通模式的别踩白块儿因为他的“块儿”是滚动的向上这种,以我目前会的技术想不出怎么写, 但是如果是街机模式,通过你每按 ...

  6. canvas写个简单的小游戏

    之前在HTML5 Canvas属性和方法汇总一文中,介绍过Canvas的各种属性以及方法的说明,并列举了自己写的一些Canvas demo,接下来开始写一个简单的小游戏吧,有多简单,这么说吧,代码不到 ...

  7. 分享:计算机图形学期末作业!!利用WebGL的第三方库three.js写一个简单的网页版“我的世界小游戏”

    这几天一直在忙着期末考试,所以一直没有更新我的博客,今天刚把我的期末作业完成了,心情澎湃,所以晚上不管怎么样,我也要写一篇博客纪念一下我上课都没有听,还是通过强大的度娘完成了我的作业的经历.(当然作业 ...

  8. 用C写一个简单的推箱子游戏(二)

    下面接着上一篇随笔<用C写一个简单的推箱子游戏(一)>来写 tuidong()函数是用来判断游戏人物前方情况的函数,是推箱子游戏中非常重要的一个函数,下面从它开始继续介绍推箱子的小程序怎么 ...

  9. 用C写一个简单的推箱子游戏(一)

    我现在在读大二,我们有一门课程叫<操作系统>,课程考查要求我们可以写一段程序或者写Windows.iOS.Mac的发展历程.后面我结合网上的资料参考,就想用自己之前简单学过的C写一关的推箱 ...

随机推荐

  1. RTlinux3.2安装

    ( 1 ).前言 2003 年以后, fmslabs 的 RTLinux Free 版本为 3.2Pre ,和以前的 RTLinux 3.1 比较,不再需要必须从 2.4.4 的内核上安装. RTLi ...

  2. final、finally和finalize的区别

    final.finally和finalize的区别 这三者的区别可以从两个方面来说 1.意思解释方面 (1)final是修饰符(关键字) (2)finally是异常处理中的程序块 (3)finaliz ...

  3. Struts 有哪些常用标签库

    Struts 有哪些常用标签库 1.html标签库 2.bean标签库 3.logic标签库

  4. TensorFlow MNIST初级学习

    MNIST MNIST 是一个入门级计算机视觉数据集,包含了很多手写数字图片,如图所示: 数据集中包含了图片和对应的标注,在 TensorFlow 中提供了这个数据集,我们可以用如下方法进行导入: f ...

  5. 一道bfs与邻接表应用题

    Problem Description 终于有一天,王元姬用他的劫打上了最强王者.他号称,他从来不会在偶数段位停留,因为他的实力太强会跳段(这个13我给满分).傲娇棠和翔妹觉得他的13装的都比勇哥哥好 ...

  6. jquery 获取上传图片的大小(或者本张图片的其它属性)

    <input type="file" name="upload" style="display:none;" src="${ ...

  7. mybatis中动态update中的isNotEmpty和isNotNull标签

    一,简介 在iBATIS中isNull用于判断参数是否为Null,isNotNull相反isEmpty判断参数是否为Null或者空,满足其中一个条件则其trueisNotEmpty相反,当参数既不为N ...

  8. 关于js中 toFixed()的一个小坑

    作为一名前端,大家都应该知道,toFixed()的作用,toFixed()经常用于前台与后台数据格式的转换,套用下w3c上面的定义: 定义和用法toFixed(n) 方法可把 Number 四舍五入为 ...

  9. 在TextBox中敲击回车执行ASP.NET后台事件

    1.在TextBox中敲击回车执行ASP.NET后台事件   0.说明 页面中有一个用于搜索的TextBox,希望能在输入内容后直接回车开始搜索,而不是手动去点击它旁边的搜索按钮 但因为该TextBo ...

  10. 实现一个简单的订阅与发布模式的代码块,和redux

    /** * Created by Mrzou on 2018/3/11. */ //实现简单的订阅与发布模式的代码块export function pattern() { let currentLis ...