第一次写博客也不知怎么写,反正就按照我自己的想法来吧!怎么说呢?还是不要扯那些多余的话了,直接上正题吧! 第一次用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. 关于服务器的CPU的几个概念学习总结

    物理CPU 物理CPU: 物理CPU是指插在主板上面的CPU芯片.即指在主板上肉眼能看到的CPU的个数.一般而言,个人台式机或笔记本上只会有一个物理CPU芯片.而服务器主板上往往有多个物理CPU. L ...

  2. NLP+VS=>Image Caption︱自动生成图像标题技术论文+相关项目

    读聪明人的笔记,是不是也能变聪明呢? Image Caption是一个融合计算机视觉.自然语言处理和机器学习的综合问题,它类似于翻译一副图片为一段描述文字. Image Caption问题可以定义为二 ...

  3. SPI FLASH与NOR FLASH的区别?

    1.SPI Flash (即SPI Nor Flash)是Nor Flash的一种: 2.NOR Flash根据数据传输的位数可以分为并行(Parallel)NOR Flash和串行(SPI)NOR ...

  4. Linux显示登入系统的帐号名称和总人数

    Linux显示登入系统的帐号名称和总人数 youhaidong@youhaidong-ThinkPad-Edge-E545:~$ who -q youhaidong youhaidong # 用户数= ...

  5. HTML5中的输入框

    HTML5中的输入框 1.双引号 <input type="text"/> 2.单引号 <input type='text'/> 3.无引号 <inp ...

  6. js、css动态压缩页面代码

    1.js.css动态压缩页面代码 <%@ Page Language="C#" AutoEventWireup="true" CodeFile=" ...

  7. windows下键盘常用快捷键整理

    以下快捷键均在win7环境下测试有效: 声明:本博文由多篇博文经实测整理而出. win键相关的快捷键多用于桌面场景,如开起资源管理器.切换任务窗口.最大化最小化窗口等等. 场景一: 1. 任何情况下想 ...

  8. ASP.NET Core 2.0: 二. 开发环境

    macOS:Install Visual Studio for Mac 系统要求: macOS 10.12 Sierra 及更高版本 其他要求: 可能会要求安装xcode或android相关环境, 详 ...

  9. Modbus总结

    1.概念 ①Coil和Register Modbus中定义的两种数据类型.Coil是位(bit)变量:Register是整型(Word,即16-bit)变量. ②Slave和Master与Server ...

  10. 由js深拷贝引起的对内存空间的一些思考

    数据类型 js常用数据类型分为基本类型和引用类型 基本类型:null.undefined.数值型.字符串型.布尔型 引用类型:数组.对象 内存空间 var a = [1, 2, 3]; var b = ...