用Canvas写一个简单的游戏--别踩白块儿

  第一次写博客也不知怎么写,反正就按照我自己的想法来吧!怎么说呢?还是不要扯那些多余的话了,直接上正题吧! 第一次用canvas写游戏,所以挑个简单实现点的来干:别踩白块儿,其他那些怎么操作的那些就不用再扯了,大家应该都懂,不懂的看到黑色的就点就是了,扯多了我打字手也累,大概。链接给你们:http://nowtd.cn/white/

  咱不是理论派,所以在这里不会扯多少理论。

  首先看看html的结构

 1 <header class="container">
 2         <article class="game-info">
 3             <span class="show sorce rounded">分数:<span class="num">0</span></span>
 4             <span class="time">时间: <span class="num">0</span>s</span>
 5         </article>
 6     </header>
 7     <main class="container">
 8         <canvas id="game-body">你的浏览器不支持canvas!!!</canvas>
 9     </main>
10     <section class="control container">
11         <button class="btn rounded" id="game-play">开始</button>
12         <button class="btn rounded" id="game-over">结束</button>
13     </section>

 很简单,只有一个canvas和一些按钮 and 用作显示分数那些的span

  css的内容就不贴了,反正只是给它作一下简单布局而已,重要的还是js怎么去实现它的。

  首先先看看怎么调用吧

 1 <script>
 2         (function () {
 3             let wb = new WhiteBlock({
 4                 canvas: ‘#game-body‘,
 5                 play: ‘#game-play‘,
 6                 over: ‘#game-over‘,
 7                 sorce: ‘.sorce > .num‘,
 8                 time: ‘.time > .num‘,
 9                 width: 330,
10                 height: 450,
11                 col: 4,
12                 row: 4,
13             });
14         })();
15     </script>

就这么点东西:

  canvas: 画布,

  play: 开始按钮,

  over: 结束按钮,

  sorce: 显示成绩,

  time: 显示时间,

  white: 画布的宽,

  height: 画布的高,

  col: 一列多少个砖块

  row: 一共多少行

内部的一些参数

 1        this.speedLevel = [4, 5, 6, 7, 8];
 2
 3             // 成绩
 4             this.sorceNumber = 0;
 5             this.Time = 0;
 6
 7             //定时器
 8             this.timer = null;
 9
10             // 是否已开始游戏
11             this.isPlay = false;
12             // 是否已结束游戏
13             this.isOver = false;
14
15             // 画布参数
16             this.width = null;
17             this.height = null;
18             this.canvas = null;
19             this.ctx = null;
20
21             // 砖块
22             this.blockQueue = [];
23             this.blockWidth = null;
24             this.blockHeight = null;
25             this.col = 4;
26             this.row = 6;
27             // 速度
28             this.offset = this.speedLevel[0];

这里要说的大概就只有speedLevel和this.blockQueue了:

  speedLevel: 输入一个规定速度的数组,然后根据成绩的提升来获取相应的速度值

 1             changeSpeed() {
 2             if (this.sorceNumber < (this.speedLevel.length * 10)) {
 3                 let num = Math.floor(this.sorceNumber / 10);
 4                 if (this.speedLevel[num]) {
 5                     this.offset = this.speedLevel[num];
 6                 } else {
 7                     this.offset = this.speedLevel[this.speedLevel.length - 1];
 8                 }
 9
10             }
11         }    

当成绩超过一定值之后,就以speedLevel的最后一个值为速度,不再加速。

  this.blockQueue:生成一组队列做点阵来绘制砖块,分别由createBlock, addBlock,removeBlock,fillBlock这四个方法来操控,drawing方法会遍历this.blockQueue

 1         // 创建砖块
 2         createBlock() {
 3             let len = this.col,
 4                 i = 0,
 5                 list = [],
 6                 rand = Math.floor(Math.random() * len); // 随机黑块
 7
 8             for (; i < len; i ++) {
 9                 list.push(rand === i ? 1 : 0);
10             }
11             return list;
12         }
13
14         // 添加砖块队列
15         addBlock(i) {
16             this.blockQueue.unshift({top: -((i + 1) * this.blockHeight), data: this.createBlock()});
17         }
18
19         // 移除砖块队列
20         removeBlock() {
21             (this.blockQueue.length > 0) && this.blockQueue.pop();
22         }29
30         fillBlock() {
31             let len = this.row + 1, // 多加一队,以免刚好一屏
32                 i = 0;
33
34             for (; i < len; i ++) {
35                 this.addBlock(i);
36             }
37         }
38
39         drawing() {
40             this.ctx.clearRect(0, 0, this.width, this.height);
41             this.each(this.blockQueue, (index, item) => {
42                 let top = item[‘top‘],
43                     block = item[‘data‘],
44                     ctx = this.ctx;
45
46                 this.each(block, (index, item) => {
47                     ctx.fillStyle = parseInt(item) === 1 ? ‘#000‘ : ‘#fff‘;
48                     ctx.beginPath();
49                     ctx.fillRect(index * this.blockWidth, top, this.blockWidth, this.blockHeight);
50                 });
51             });
52         }    

因为个人喜欢从数组第一个值迭代,所以这里把队列反转过来操作了

  而每个砖块的宽高都由col和row决定:

1         getBlockWidthAndHeight() {
2             this.blockWidth = this.width / this.col;
3             this.blockHeight = this.height / this.row;
4             return this;
5         }

  把砖块绘制好了之后就是要让砖块跑起来了,我让每一队砖块都加一个offset值,this,offset为一个速度值

   1 this.offset = this.speedLevel[num];

1 runPlay() {
2             this.each(this.blockQueue, (index, item) => {
3                 item[‘top‘] += this.offset;
4             });
5             this.drawing();
6             this.timer = setTimeout(this.runPlay.bind(this), 20);
7             // 修改时间
8             this.changeTime();
9         }

runplay方法设定为20毫秒重绘一次,这个20是随便写上去的,至于是否合理就暂时不管了

  点击开始按钮触发事件, 开始游戏

 1 playGame(e) {
 2             if (this.isOver) {
 3                 // 重新开始
 4                 this.replay();
 5                 console.log(this);
 6             }
 7             // 让砖块跑起来
 8             if (!this.isPlay) {
 9                 // 检测是否有黑块到底
10                 this.checkState();
11                 // 让砖块跑进来
12                 this.runPlay();
13                 this.play.html(‘暂停‘);
14                 this.isPlay = true;
15             } else {
16                 // 暂停游戏
17                 this.puaseGame();
18                 this.play.html(‘继续‘);
19             }
20         }

暂停游戏其实就是清除定时器

1     puaseGame() {
2             clearTimeout(this.timer);
3             this.isPlay = false;
4         }

判断输赢: 怎么个输赢法就不讲了,只说实现。

 1 checkState() {
 2             let i = this.blockQueue.length - 1,
 3                 item = this.blockQueue[i],
 4                 top = item[‘top‘],
 5                 data = item[‘data‘];
 6             if ((this.height) <= top) {
 7                 this.checkBlock(data) ? (() => {
 8                     this.again();
 9                     requestAnimationFrame(this.checkState.bind(this));
10                 })() : this.puaseGame();
12                 requestAnimationFrame(this.checkState.bind(this));
13             }
14         }

每次运行都取出队列中最后的一队,并判断它的top值是否大于或小于画布的高,如果最前面的一队(数组最后一个)的top大于画布高,同时遍历这个队列,如果有一个值为1(黒块),则游戏失败,并调用gameOver方法。

否则,即说明当前队列安全通过,就调用again方法,这个方法里移除队首,并在队尾添加一队,这样不断循环生成砖块。

点击判断输赢,这里有人可能想到了用getImageData来获取颜色值来判断。这样做对于这个只有黒和白的游戏来说的确是很方便的,但是这里我没用获取颜色值来做(最开始是那样打算来着),因为这里不仅要判断输赢,当点击到了黒块之后也要修改黒块的颜色的,最终还是逃不出要判断当前点击的是哪个砖块这一步,所以这里用碰撞检测来做就好了,也节省了一个方法的代码量(但其实在这里一个方法内的代码量也不会很多),下面是代码:

 1 isWin(e) {
 2             let x = e.offsetX || e.originalEvent.layerX,
 3                 y = e.offsetY || e.originalEvent.layerY,
 4                 data = this.blockQueue;
 5
 6             this.each(data, (index, item) => {
 7                 let top = item[‘top‘];
 8                 if (y > top && y < (data[index + 1] ? data[index + 1][‘top‘] : top + this.blockHeight)) {
 9                     // 判断点击的列中的黑块是否被点中
10                     this.checkCloumn(x, item[‘data‘], index);
11                 }
12             });
13         }
14
15         checkCloumn(x, data, i) {
16             this.each(data, (index, item) => {
17                 let left = index * this.blockWidth;
18
19                 if (x > left && x < (left + this.blockWidth)) {
20                     if (item === 1) {
21                         this.blockQueue[i][‘data‘][index] = 0;
22                         // 记录成绩
23                         this.addSorce();
24                         this.drawing();
25                     } else {
26                         this.gameOver();
27                         this.puaseGame();
28                     }
29                 }
30             });
31         }

点击结束按钮就调用一个replay方法,重置那些配置参数

 1 replay() {
 2             // 清空砖块队列
 3             this.blockQueue = [];
 4             // 重置数据
 5             this.offset = this.speedLevel[0];
 6             this.sorceNumber = 0;
 7             this.Time = 0;
 8             this.isPlay = false;
 9             this.timer = null;
10             this.play.html(‘开始‘);
11             this.sorce.html(0);
12             this.time.html(0);
13             //重新填充队列
14             this.fillBlock();
15             // 重新维制
16             this.drawing();
17
18             this.isOver = false;
19         }

接下来成绩和时间那个就简单了,剩下就是处理游戏结束后的消息提示了,由于无论是有黒块到了底下还是点击了白块,都会调用一个gameOver方法,

1 gameOver() {
2             this.isOver = true;
3             this.play.html(‘开始‘);
4             // 显示游戏结束提示
5             this.showMess();
6         }

我在这个方法里调用了一个showMess方法,而我写到最后不想写这个了,要写的话一开始就会写好这块了,所心只是简单地弹出一个警告框算了

1 showMess() { 2 alert(‘游戏已结束!‘); 3 }

到哪天想要弄个好看点的提示时,只要重写这个方法就好了。

嘛,反正到这里算是结束了。总感觉有点一直在贴代码的感觉,哈哈!!

这是源码地址:  https://gitee.com/nowtd/test/tree/master/white

第一次写博客,不清楚写得怎样?各位请多多指教!!!

补充一点:  这个demo引入了JQ,用来获取dom和绑定事件,修改属性那些,但现在感觉好像不引入JQ所需要写的代码也不会很多,不过嘛,能少写就少写吧。

原文地址:https://www.cnblogs.com/LiQingsong/p/8558988.html

时间: 2024-10-19 20:58:00

用Canvas写一个简单的游戏--别踩白块儿的相关文章

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

为了让大家清楚HTML5制作游戏的简单流程,所以先了制作一个非常简单的游戏,来看一看这个过程.   游戏非常简单,无非就是英雄抓住怪物就得分,然后游戏重新开始,怪物出现在地图的随机位置,英雄初始化在地图的中间.点击[这里](../simple_canvas_game-master/index.html "simple_canvas_game"),我们可以直接先玩玩这个游戏 ![simple_game](Figure/1_simple_game.png) ## 1. 创建一个Canvas

用canvas写一个h5小游戏

这篇文章我们来讲一讲用canvas画一个躲水果的小游戏.就是通过手指控制一个人物移动来躲避水果,若发生碰撞,则游戏结束. 我们定义一个game_control对象来处理初始化,事件绑定,游戏开始,游戏结果判定,游戏结束等判定. 在游戏中,我们需要一个人物以及三种水果的图片,我们做成了雪碧图. 接下来直接上代码吧~ 首先我们定义一个ship对象,3个水果.一个人物都是基于这个对象的. function ship(options){ if (options) { var width=options.

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

初学JS--利用JS制作的别踩白块儿(街机模式) 小游戏 这个是上个星期5写的了,当时是突然想写个游戏,就想到了别踩白块儿,当时的想法是 可能普通模式的别踩白块儿因为他的"块儿"是滚动的向上这种,以我目前会的技术想不出怎么写, 但是如果是街机模式,通过你每按一次按键之后他像下跳一格这样的就非常好实现了. 通过我目前会的知识,实现的步骤大概是这样的: 建一个4X4的表格,制作2张150X100的图片,一张全白色,一张全黑色,命名为0.JPG,1.JPG 就是说当文件名为0的时候就是白色的

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

这几天一直在忙着期末考试,所以一直没有更新我的博客,今天刚把我的期末作业完成了,心情澎湃,所以晚上不管怎么样,我也要写一篇博客纪念一下我上课都没有听,还是通过强大的度娘完成了我的作业的经历.(当然作业不是百度来的,我只是百度了一些示例代码的意思,怎么用!算了,越解释万一越黑呢!哈哈O(∩_∩)O哈哈~) ----------------------------------------------------------------分界线------------------------------

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

我现在在读大二,我们有一门课程叫<操作系统>,课程考查要求我们可以写一段程序或者写Windows.iOS.Mac的发展历程.后面我结合网上的资料参考,就想用自己之前简单学过的C写一关的推箱子小程序. 这一程序主要用到了C语言中的二维数组,头文件#include<conio.h>(因为要调用getch()函数以记录输入内容),switch函数等. 一.     功能概述 a)   游戏规则概述 玩家通过键盘输入W.S.A.D四键或者“↑”.“↓”.“←”.“→”四个方向键推动箱子,而

A simple libgdx game (一个简单的游戏)

在深入钻研libGDX提供的api之前,让我们创建一个简单的小游戏来初步接触一个每个模块.这里将会主要介绍一些设计思想,而非细节. 我们将会看到如下内容: 1.主要的文件操作 2.清屏 3.绘制图片 4.使用相机 5.主要的输入处理 6.播放声音效果 工程的创建就不在赘述了. The Game (游戏) 游戏的idea很简单: 1.用桶抓住雨滴 2.桶在屏幕的下方 3.雨滴在屏幕的上面随机出现并且垂直下落 4.玩家可以通过鼠标或者触摸或者键盘方向键来水平移动桶 5.游戏没有结束条件... The

5、使用Libgdx设计一个简单的游戏------雨滴

(原文:http://www.libgdx.cn/topic/49/5-%E4%BD%BF%E7%94%A8libgdx%E8%AE%BE%E8%AE%A1%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84%E6%B8%B8%E6%88%8F-%E9%9B%A8%E6%BB%B4) 在深入研究Libgdx提供的API之前,我们先来创建一个简单的游戏来感受一下libgdx各个功能.这里将简单的对一些功能做介绍. 使用的技术: 文件访问 清除屏幕 渲染图片 使

linux设备驱动第三篇:写一个简单的字符设备驱动

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存. 下面就开始学习如何写一个简单的字符设备驱动.首先我们来分解一下字符设备驱动都有那些结构或者方法组成,也就是说实现一个可以使用的字符设备驱动我们必须做些什么工作. 1.主设备号和次设备号 对于字符设备的访问是通过文件系统中的设备名称进行的.他们通常位于/dev目录下.如下: [plain] vie

(2)自己写一个简单的servle容器

自己写一个简单的servlet,能够跑一个简单的servlet,说明一下逻辑. 首先是写一个简单的servlet,这就关联到javax.servlet和javax.servlet.http这两个包的类,其中一个比较重要的接口就是:javax.servlet.Servlet,所有的servlet必须实现实现或者继承实现该接口的类. Servlet接口有五个方法: public void init(ServletConfig config) throws ServletException publi