原文地址:canvas实现俄罗斯方块

好久没使用canvas了,于是通过写小游戏“俄罗斯方块”再次熟悉下canvas,如果有一定的canvas基础,要实现还是不难的。实际完成的Demo请看:canvas俄罗斯方块

原理详解

看游戏最终界面,可知需要实现以下关键功能:

  1. 游戏面板,也就是12 * 20的方格,以及是否填充了方块信息;
  2. 运动方块,方块需要实现移动,变形的功能。

界面的实现

整个面板就是以左上角(0,0)为原点的坐标系,右上角(12,0)左下角(0,20)右下角(12,20),每个点的坐标位置都可以确定。是否已经填充方块,我们可以将每个方格看成一个数组元素,0表示没有,1表示已经填充。12 * 20 的面板使用两层数组,即用20个长度为12的数组实现。

var maps = [[0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,1,0,1,0], ...];

画出面板的代码,用最基础的canvas的api就能实现

	//格子
for(var i=0;i<12;i++){
for(var j=0;j<20;j++){
ctx.fillRect(i*40,j*40,40,40);
ctx.strokeRect(i*40,j*40,40,40);
if(this.maps[j][i]==1){//方格已经有填充内容
ctx.save();
ctx.lineWidth=4;
ctx.fillStyle='hsla(200,100%,50%,.5)';
ctx.strokeStyle='hsla(200,100%,50%,.9)';
ctx.fillRect(i*40,j*40,40,40);
ctx.strokeRect(i*40+2,j*40+2,38,38);
ctx.restore();
}
}
} //边框
ctx.lineWidth=4;
ctx.strokeStyle='hsla(0,100%,0%,.3)';
ctx.moveTo(0,0);
ctx.lineTo(0,20*40);
ctx.lineTo(12*40,20*40);
ctx.lineTo(12*40,0);
ctx.stroke();
ctx.restore();

方块的实现

游戏中用到以下 7 种图形

结合上面介绍的坐标系,数组 [x1, y1, x2, y2, x3, y3, x4, y4] 就是上面图形中4个点坐标的数据表现形式,7 种图形的坐标分别如下:

var Arr = [[4,0,4,1,5,1,6,1],[4,1,5,1,6,1,6,0],[4,0,5,0,5,1,6,1],[4,1,5,0,5,1,6,0],
[5,0,4,1,5,1,6,1],[4,0,5,0,6,0,7,0],[5,0,6,0,5,1,6,1]];

方块的移动,遍历整个数组,加上位移向量就行,非常简单

class Shape {
constructor(m){
this.m = Object.assign([],m);
}
move(x,y){ // 位移
var m = this.m,
l = m.length;
y = y||0; for (var i=0;i<l;i=i+2){
m[i]+=x;
m[i+1]+=y;
}
return this;
}

方块的旋转,俄罗斯方块里面方块除了左右和上下运动,还会旋转,不是吗?稍微思考下就知道,这不过就是矩阵变换而已,也就是每次图形绕中心点旋转90度。我这里用数组第三个点作为图形变换的中心点,当然这样处理不够完善。

class Shape {
transform(){//二维矩阵变换
var m =this.m,
l = m.length,
c = Math.ceil(l/2),
x = m[c],
y = m[c+1],
cos = Math.cos(Math.PI/2),
sin = Math.sin(Math.PI/2); for (var i=0;i<l;i=i+2){
if(i == c) continue;
var mx = m[i]- x,
my = m[i+1] - y,
nx = mx*cos - my*sin,
ny = my*cos + mx*sin;
m[i]=x+nx;
m[i+1]=y+ny;
}
return this;
}

边界条件

主要包括如下三个方面

  1. 方块位置不能超出界面的判断;
  2. 方块到达底部或放置完成的判断;
  3. 游戏结束的判断。

遍历数组 (1)任意一个点y坐标为19时表示到达了底部;(2)获取该坐标的y+1位置在maps的信息,如果为1表示已经填充。这两种情况下,运动方块的周期结束,将该方块的坐标填充到maps对应的数组里面即可。

如果坐标的y+1已经有填充,同时当前坐标小于1,即已经在界面的顶部了,那么表示游戏结束。

var isEnd = false,isOver=false,x,y;
for(var i=0,sl=that.shape.m.length;i<sl;i=i+2){
x=that.shape.m[i];
y=that.shape.m[i+1];
if(y >= 19){ // 到了底部
isEnd = true;break;
}
if(that.maps[y+1][x]==1){ // y+1位置已经填充
isEnd = true;
if(y <= 1){isOver=true;} // 游戏结束
break;
}
}

方块运动周期结束时检测每一层是否满格,以及满格后的处理。某项数组全部元素都为1则表示已经满格,那么删除该项数组,同时列表头再压入一项每个元素都为0的数组即可。

checkPoint(){
var that = this,
maps = that.maps; for(var i=0,l=maps.length;i<l;i++){
if(Math.min.apply(null,maps[i]) == 1){// 表示该层已经满格
that.maps.splice(i,1);
that.score+=10; // 增加分数
that.maps.unshift([0,0,0,0,0,0,0,0,0,0,0,0]);
}
}
return this;
}

绑定事件

主要就是绑定keydown事件,要注意的是左移和右移事件包括了边界判断

bindEvent(){
var that = this;
document.addEventListener('keydown',function(e){
switch(e.keyCode){
case 13: //enter
cancelAnimationFrame(that.timer);
that.init().update();
break;
case 80: //p
that.pause = !that.pause;
break;
case 40: //down
that.d = 0.5;
break;
case 37: //left
var over = false,
maps = that.maps,
shape = that.shape,
m = shape.m;
for(var i=0,l=m.length;i<l;i=i+2){
if(m[i]<=0 || maps[m[i+1]][m[i]-1] == 1){
over = true;break;
}
}
if(!over) shape.move(-1,0);
break;
case 39: //right
var over = false,
shape = that.shape,
maps = that.maps,
m = shape.m;
for(var i=0,l=m.length;i<l;i=i+2){
if(m[i]>=11 || maps[m[i+1]][m[i]+1] == 1){
over = true;break;
}
}
if(!over) shape.move(1,0);
break;
case 32: //space
that.shape.transform();
break;
}
},false);
}

总结

这里面实现了俄罗斯方块的最基本功能,还有关卡等功能点并没有实现,同时该demo仍然有不完善的地方需要修正。

canvas实现俄罗斯方块的更多相关文章

  1. canvas版《俄罗斯方块》

    试玩(没有考虑兼容低版本浏览器): See the Pen Canvas俄罗斯方块 by 王美建 (@wangmeijian) on CodePen. ************************ ...

  2. Canvas俄罗斯方块

    写在前面 潜水博客园多年,从未写过博客.最近才注册博客,遂将很久前写的俄罗斯方块分享出来.第一次写博客,不喜勿喷... 游戏说明 游戏操作:J向左,L向右,I旋转,K快速下降 游戏基于HTML can ...

  3. canvas 俄罗斯方块

    <!doctype html> <html> <body> <canvas id="can" width="360px" ...

  4. x01.Tetris: 俄罗斯方块

    最强大脑有个小孩玩俄罗斯方块游戏神乎其技,那么,就写一个吧,玩玩而已. 由于逻辑简单,又作了一些简化,所以代码并不多. using System; using System.Collections.G ...

  5. 【自己给自己题目做】:如何在Canvas上实现魔方效果

    最终demo -> 3d魔方 体验方法: 浮动鼠标找到合适的位置,按空格键暂停 选择要翻转的3*3模块,找到相邻两个正方体,鼠标点击第一个正方体,并且一直保持鼠标按下的状态直到移到第二个正方体后 ...

  6. H5版俄罗斯方块(2)---游戏的基本框架和实现

    前言: 上文中谈到了H5版俄罗斯方块的需求和目标, 这次要实现一个可玩的版本. 但饭要一口一口吃, 很多东西并非一蹴而就. 本文将简单实现一个可玩的俄罗斯方块版本. 下一步会引入AI, 最终采用coc ...

  7. [原创]html5_PC游戏_图片俄罗斯方块

    PC游戏_图片俄罗斯方块 以前的了,快一年了... 使用了离线canvas复制的方法,启动预览效果需要服务器支持 另外,AC娘图片可以自己做加载功能,这样游戏图片显示更顺畅 效果: --- 代码: h ...

  8. 自己写了个H5版本的俄罗斯方块

    在实习公司做完项目后,实在无聊.就用H5写了几个游戏出来玩一下.从简单的做起,就搞了个经典的俄罗斯方块游戏. 先上效果: 上面的数字是得分,游戏没有考虑兼容性,只在chrome上测试过,不过大部分现代 ...

  9. H5实现俄罗斯方块(一)

    这几天一直忙于公司的项目,涉及到流程问题,(有时间会写成博客的)...一直没有更新... 为了更加巩固js的基础,自己封装类似于JQuery的操作库来对dom进行操作. 一:前度页面的绘制. < ...

随机推荐

  1. Day5_协程函数_面向过程

    def func(count): while True: yield count count +=1 #这是一个生成器,需要利用next()来执行. func(10) #yield: #1.把函数的执 ...

  2. (转) windows下 安装 rabbitMQ 及操作常用命令

    该博客转载自:https://blog.csdn.net/gy__my/article/details/78295943 原作者:Eric Li  出处:http://www.cnblogs.com/ ...

  3. WebRTC技术调研

    相关网址: 协议:https://www.w3.org/TR/webrtc/ https://apprtc.webrtc.org/ https://apprtc.appspot.com/ https: ...

  4. python笔记:#005#算数运算符

    算数运算符 计算机,顾名思义就是负责进行 数学计算 并且 存储计算结果 的电子设备 目标 算术运算符的基本使用 01. 算数运算符 算数运算符是 运算符的一种 是完成基本的算术运算使用的符号,用来处理 ...

  5. [Java算法分析与设计]--单向链表(List)的实现和应用

    单向链表与顺序表的区别在于单向链表的底层数据结构是节点块,而顺序表的底层数据结构是数组.节点块中除了保存该节点对应的数据之外,还保存这下一个节点的对象地址.这样整个结构就像一条链子,称之为" ...

  6. SpringCloud实战-Hystrix请求熔断与服务降级

    我们知道大量请求会阻塞在Tomcat服务器上,影响其它整个服务.在复杂的分布式架构的应用程序有很多的依赖,都会不可避免地在某些时候失败.高并发的依赖失败时如果没有隔离措施,当前应用服务就有被拖垮的风险 ...

  7. spring boot入门篇,helloworld案例演示

    为什么用spring boot? 嵌入的 Tomcat,无需部署 WAR 文件 简化 Maven 配置 无需 XML 配置,轻松快速地搭建Spring Web应用 开始学习SpringBoot 构建简 ...

  8. Robot framework之元素定位实战

    1.1  id 和name 定位 Web页面都是由许多标签和元素组成的,每个标签或元素都是很多属性,好比一个人   id 和name 可以看作一个人的身份证号和姓名.下面看下教育局招生系统的用户名输入 ...

  9. codeforces 979D Kuro and GCD and XOR and SUM

    题意: 给出两种操作: 1.添加一个数字x到数组. 2.给出s,x,k,从数组中找出一个数v满足gcd(x,k) % v == 0 && x + v <= s && ...

  10. C# 使用 SmtpClient.SendAsync 方法发送邮件失败,总是返回 Cancelled

    问题: 调用 SmtpClient.SendAsync,在 SendCompleted 的回调函数里面总是获取到 e.Cancelled 为 true. 后来测试了一下,相同的代码,只是把 SmtpC ...