如何用javascript做一个俄罗斯方块小游戏下载游戏

给跪了,,60行代码实现俄罗斯方块【javascript吧】_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:150,410贴子:
给跪了,,60行代码实现俄罗斯方块收藏
&!doctype html&
&html&&head&&title&俄罗斯方块&/title&
&meta name=&Description& content=&俄罗斯方块Javascript实现&&
&meta name=&Keywords& content=&俄罗斯方块,Javascript,实现,短小精悍,游戏,ithomer,ithomer.net&&
&/head&&body&
&div id=&box& style=&margin: 20 text-align: width:252font:25px/25px 宋体;background:#000;color:#9f9;border:#999 20text-shadow:2px 3px 1px #0f0;&&&/div&
&div id=&footer& style=&margin-top: 200 text-align: font-size: 16&&© 2009 - 2014 All Rights by &a href=&&&ithomer.net&/a&&/div&
var domain=&&;
var author=&zuidaima&;
var map=eval(&[&+Array(23).join(&0x801,&)+&0xfff]&);
var tatris=[[0x6600],[0x],[0xc600,0x2640],[0x6c00,0x4620],[0xe0,0x],[0x,0x0],[0x,0x0]];
var keycom={&38&:&rotate(1)&,&40&:&down()&,&37&:&move(2,1)&,&39&:&move(0.5,-1)&};
var dia, pos, bak,
function start(){
dia=tatris[~~(Math.random()*7)];
bak=pos={fk:[],y:0,x:4,s:~~(Math.random()*4)};
rotate(0);
function over(){
document.onkeydown=
clearInterval(run);
alert(&GAME OVER&);
function update(t){
bak={fk:pos.fk.slice(0),y:pos.y,x:pos.x,s:pos.s};
for(var i=0,a2=&&; i&22; i++)
a2+=map[i].toString(2).slice(1,-1)+&&br/&&;
for(var i=0,n; i&4; i++)
if(/([^0]+)/.test(bak.fk[i].toString(2).replace(/1/g,&\u25a1&)))
a2=a2.substr(0,n=(bak.y+i+1)*15-RegExp.$_.length-4)+RegExp.$1+a2.slice(n+RegExp.$1.length);
document.getElementById(&box&).innerHTML=a2.replace(/1/g,&\u25a0&).replace(/0/g,&\u3000&);
function is(){
for(var i=0; i&4; i++)
if((pos.fk[i]&map[pos.y+i])!=0) return pos=
function rotate(r){
var f=dia[pos.s=(pos.s+r)%dia.length];
for(var i=0; i&4; i++)
pos.fk[i]=(f&&(12-i*4)&15)&&pos.x;
update(is());
function down(){
for(var i=0; i&4 && pos.y+i&22; i++)
if((map[pos.y+i]|=pos.fk[i])==0xfff)
map.splice(pos.y+i,1), map.unshift(0x801);
if(map[1]!=0x801) return over();
function move(t,k){
for(var i=0; i&4; i++)
pos.fk[i]*=t;
update(is());
document.onkeydown=function(e){
eval(keycom[(e?e:event).keyCode]);
run=setInterval(&down()&,400);
&/script&&/body&&/html&
千锋javascript,名企内部推荐就业,入学签订就业协议,2周免费试学!千锋javascript,20周快速成为高级开发工程师,就业“薪”踏板!
。。。c#吧友路过,还不错
登录百度帐号推荐应用标签:至少1个,最多5个
最近在学习JavaScript,想编一些实例练练手,之前编了个贪吃蛇,但是实现时没有注意使用面向对象的思想,实现起来也比较简单所以就不总结了,今天就总结下俄罗斯方块小游戏的思路和实现吧(需要下载代码也是有的),我会说得很详细很详细的所以要看完需要好多耐心的。。。
游戏区域:游戏区域是固定的,这里将它设为宽10单位,高16单位的矩形区域,前端显示用表格来实现,并将每个td保存在一个二维数组中,用于渲染游戏过程。方块:方块有7种形状,用一个4*4的矩阵来保存方块的形状;按方向上键方块可以旋转,可通过矩阵旋转来实现;方块可以左右移动,需要判断是否出界机左右是否已有方块;还需注意判断方块是否已经触底。按方向下键方块可以速降。分数:在每一次方块降落完成后判断能否得分。
这部分下文有用上在回来看就行,现在看可能有点混乱( ???)? 。
block_now, block_ 当前,下一方块对象,将block_next赋值给block_now,再创建新方块后赋值给block_next。
beforeBlock:16*10的矩阵 方块上一时刻的位置,用于擦除上一秒
allBlock: 16*10的矩阵 已完成方块
ground:16*10的游戏区域,获取DOM,渲染游戏区域
实现这个游戏的最核心就在于这个方块类了,我们将创建一个方块类型,然后7种形状的方块继承这个方块类。
方块类的属性
方块的实例属性有:移动方向,状态,形状,当前位置,颜色。
function Block() {
this.dir = 40;//方块当前移动方向
this.end = 0;//状态,是否下落完成
this.shape = new Array();//4*4的方块
for (var i = 0; i & 4; i++) {
this.shape[i] = new Array();
this.pos = [0, 3];//所在行,列
this.color = ["#FFAEC9", "#B5E61D", "#99D9EA", "#C8BFE7", "#B97A57"];
这里先明确一下方块和形状的关系哈,后面经常用上这个概念。如下,4*4的矩阵我把它叫“方块”然后有颜色(绿色)的部分我叫它形状。
7种方块子类
function Block_i() {
Block.call(this);
this.shape = [
[1, 1, 1, 1],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
Block_i.prototype = new Block();
其他形状与上面类似:
s : [[0, 1, 1, 0],[1, 1, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0]];
j : [[0, 1, 0, 0],[0, 1, 0, 0],[1, 1, 0, 0],[0, 0, 0, 0]];
o : [[1, 1, 0, 0],[1, 1, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0]];
z : [[1, 1, 0, 0],[0, 1, 1, 0],[0, 0, 0, 0],[0, 0, 0, 0]];
t : [[0, 1, 0, 0],[1, 1, 1, 0],[0, 0, 0, 0],[0, 0, 0, 0]];
l : [[1, 0, 0, 0],[1, 0, 0, 0],[1, 1, 0, 0],[0, 0, 0, 0]];
方块类的方法
方块类的方法有:旋转方块,移动方块,速降方块,打印方块
1.旋转方块时我们先将矩阵顺时针旋转90度
for(var i = 0,dst = 3;i & 4;i++, dst--){
for(var j = 0;j & 4;j++)
tmp[j][dst] = this.shape[i][j];
2.再将旋转后的图像移到矩阵左上角,这样可以表现出在原地旋转的效果。
for(var i = 0;i & 4;i++){
var flag = 1;
for(var j = 0;j & 4;j++){//判断第一列是否有图像
if (tmp[j][0]) {
if (flag) {//第一列没图像,将第一列移除,并在最后添一列空白
for(var j = 0;j & 4;j++){
tmp[j].shift();
tmp[j].push(0);
3.最后将旋转后的矩阵保存回原来的矩阵
用户按方向下键,方块将直接降落完成。这个步骤我纠结了挺久的,最后用了个比较笨的方法吧,这里简单说下原理,具体可以下载源码看看。
1.先算出方块矩阵中有形状的内容的右边界和下边界(因为我们已经将图案放在左上角了所以不用求左上边界ヾ( ̄▽ ̄)),比如s形的方块右边界是3,下边界是2这样。这个用两个循环就能实现了。
2.判断形状(注意是形状)的正下方有没有方块(检查allBlock)
下方有方块时:
(1)计算当前形状下边界的块对应下方的块的距离y,如图
(2)计算下方最顶方块距离上方块对应位置距离x,如图
(3)取两个距离中较小的距离为方块垂直移动距离,移动方块。并将方块状态修改。
下方没有方块时,方块降至最低,计算距离时,记得得加上方块底部与形状底部的距离。并将方块状态修改。
用户通过键盘方向键来移动方块:左(37) 上(38) 右(39) 下(40)括号内为键码。对键盘事件进行监听:用this.dir记录方块当前移动方向。
当用户按上键时,调用旋转方块函数;
按左右时,将方块所在列(this.pos[1])加或减1;
按下键时,调用速降方块函数。最后打印方块(判断是否出界等问题在打印方块步骤)
判断待打印方块是否超出边界
判断要渲染(给形状上色)的地方是否已经有方块了
擦除上一时刻方块
绘制这一时刻方块
若方块下落完毕(this.end = 1),将方块加入到已下落方块矩阵(allBlock)中
好啦!完成到这步就胜利在望了,撒花ヾ( ̄▽ ̄)~
用两个随机数随机产生方块形状和颜色:
function createBlock(r1, r2) {
// var r = 0;
switch (r1) {
case 0: block_new = new Block_i();
case 1: block_new = new Block_j();
case 2: block_new = new Block_l();
case 3: block_new = new Block_o();
case 4: block_new = new Block_s();
case 5: block_new = new Block_t();
case 6: block_new = new Block_z();
block_new.color = block_new.color[r2];
return block_
2.生产分数在每次打印方块时都判断一下是否可以得分消去。
若可以得分,就将allBlock中该行删除(splice),并在最开始位置加上一行空白行([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
然后在ground中,将该行样式变为上一行样式,以此类推。呈现出消去该行的效果。
3.记录最高分我还弄了个用cookie记录最高分的功能,每次得分时判断下是否为最高分,并显示,具体还是看代码啦。
最后付上代码差不多啦,恩恩去吃饭。
1 收藏&&|&&22
你可能感兴趣的文章
17 收藏,15.5k
9 收藏,2.3k
本作品采用 署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可
大神,我想做贪吃蛇,但是没思路如何实现由按键控制蛇上下左右运动,你能简单说下你的思路吗
大神,我想做贪吃蛇,但是没思路如何实现由按键控制蛇上下左右运动,你能简单说下你的思路吗
有很大的Bug,不知道你玩过没有,在消了之后总会出现看不见的行在下面
有很大的Bug,不知道你玩过没有,在消了之后总会出现看不见的行在下面
我没看见诶,是在哪里有看不见的行啊 是在数组中 还是在HTML中啊0.0
我没看见诶,是在哪里有看不见的行啊 是在数组中 还是在HTML中啊0.0
我是记录蛇头的位置信息(x,y)然后每次移动就将x或者y改变作为新蛇头,然后将新蛇头加入蛇队列的前面,然后把蛇最后一节给扔掉这样
我是记录蛇头的位置信息(x,y)然后每次移动就将x或者y改变作为新蛇头,然后将新蛇头加入蛇队列的前面,然后把蛇最后一节给扔掉这样
我现在也基本实现了,跟你说的差不多了,放进新蛇头在最前面,去掉蛇尾,但是不应该是往蛇尾加的吗?我考虑蛇尾加,蛇头不变,但是我最初的思路设计导致这样做有点困难,能否看下你的代码?
我现在也基本实现了,跟你说的差不多了,放进新蛇头在最前面,去掉蛇尾,但是不应该是往蛇尾加的吗?我考虑蛇尾加,蛇头不变,但是我最初的思路设计导致这样做有点困难,能否看下你的代码?
蛇尾加,蛇头不变那你这是吃到食物后吧 因为这样蛇就变长了 代码这里。。
蛇尾加,蛇头不变那你这是吃到食物后吧 因为这样蛇就变长了 代码这里。。/s/1pLlLj4n
嘻嘻,谢谢
嘻嘻,谢谢
分享到微博?
技术专栏,帮你记录编程中的点滴,提升你对技术的理解收藏感兴趣的文章,丰富自己的知识库
明天提醒我
我要该,理由是:用纯JS做俄罗斯方块 - 简要思路介绍(1)
 大家都知道俄罗斯方块是一款大众化的游戏了,我很小的时候就玩过,今年已经25岁了,可以说俄罗斯方块确实是历史悠久,做俄罗斯方块是我上个星期开始的想法。也许是由于自己从来没有写过这种东西吧,所以有生疏。代码的话,只完成了一小部分,大概1/5左右吧。今天还是决定先写一部分思路。
  至于俄罗斯方块的话,有很多的难点,如果有JS去写的话,要考虑到碰撞啊,边界啊,下落等问题,本文这些问题大部分不会考虑到,只是提供一部分思路而已,开始已经说了,因为自己还没写完这个游戏,但是又出于想写博客记录,所以才有了这一系列的博客。
&  回到正题,我们首先想一想,俄罗斯方块需要什么?我做一个简单的归纳。如果我简单点说的话,就是一个俄罗斯方块对象,那么这个对象里又有些什么东西呢?我们可以想象一下,有一个平面直角坐标系,这个平面直角坐标系有X轴,有Y轴,也有&每一等分&的距离(unit),而俄罗斯方块就是一个一个的&格子&,这些格子从某一个地方开始下落,某一个地方停止下落,于是我们就规定了俄罗斯方块的&下落区域&(Area)。但是下落是一个&动作&,所以我们还要有一个类(这里定义为operate),来控制动作的下落。
  好了,先就介绍到这里,我们再来做一段代码性质的归纳,表示对上面的代码做一段归纳。
/*俄罗斯方块实体类*/
function Tetris()
& & var self = //自身
& & this.area=//区域
& & this.operate= //操作
& & /*初始化X,Y,单元为5或者20*/
& & this.x=5;
& & this.y=5;
& & this.unit=20;
& & this.running= //是否在运行中
& & //俄罗斯方块实体ID
& & this.id=&tempid&;
& & //开始游戏
& & this.start=function()
& & & & this.area=new getArea(this.x,this.y,this.unit,&tempid&); //获得Area对象 ,其中TEMPID是俄罗斯方块实体类ID
& & & & this.operate=new OperateTetris(this.area,self);
& & & & //是否替换俄罗斯方块
& & & & if(this.operate.mayPlace())
& & & & & & //alert(1);
& & & & & & this.operate.place(); &
& & //开始游戏
& & document.getElementById(&startGame&).onclick=function(){self.start()};
  那么,当我们点击StartGame的时候,开始游戏,即运行start()方法。好,我们现在开始考虑Area对象里面到底需要什么东西 function getArea(x,y,unit,id)参数需要带入4个,前面3个刚才已经说了,第四个参数就是Area的ID。我们需要area这个对象,所以通过HTML代码来设置ID。大家玩过俄罗斯方块的都知道,每一次触底,都会新加一个元素,而新的元素是&随机&的,每当一行是满的(这里不考虑颜色不同的情况),就会消掉一行,当然我们一次形成了多行可消掉的方块的时候,那么我们就可以消掉多行。下面的是代码,算是对上面的文字的一个小小的总结,还没有完成的代码。
//获得区域的横坐标和纵坐标
function getArea(x,y,unit,id)
& & this.x=x;
& & this.y=y;
& & this.unit= //每个单元的大小,单位为像素
& & this.el=document.getElementById(id); //得到ID对象
& & this.board=[]; //面板,即在区域范围内的元素(俄罗斯方块)
& & //添加元素
& & this.addElement=function()
& & & & //得到起始元素的X开始坐标和Y开始坐标的位置(错误)
& & & & //得到X坐标的下落次数,和Y轴的左右移动的次数
& & & & var xBegin=parseInt(el.offsetLeft/unit); &&
& & & & var yBegin=parseInt(el.offsetTop/unit);
& & & & if(xBegin&=0&&xBegin&=this.x&&yBegin&=0&&yBegin&=this.y)
& & & & & & & & board[yBegin][xBegin]= //确定元素的位置
& & //消掉所有的行
& & this.removeFullLines=function()
& & & & var lines=0;
& & & & for(var i=this.y-1;y&0;y--)
& & & & & & if(this.linesRelated(y))
& & & & & & {
& & & & & & & & lines++;
& & & & & & & & this.y++;
& & & & & & }
& & & & } &
& & //和线性有关的东西(判断是否满了)
& & this.linesRelated=function(y)
& & & & for(var x=this.x;x&0;x--)
& & & & & & this.removeLines(y);
& & & & & & if(this.board[y][x]){} &//不明觉厉
& & //去掉行
& & this.removeLines=function(y)
& & & & for(var x=0;x&this.x;x++)
& & & & & & this.el.removeChild(this.board[y][x]);
& & & & & & this.board[y][x]=0;
& & & & y--;
& & & & for(;y&0;y--)
& & & & & & /*今天暂时写到这里*/ &&
  需要注意的一点是,俄罗斯方块是&二维性质&的,所以我这里定义了一个board类型的二维数组,即board[行][列](board[y][x]).好了,这里我们当然还需要一个类,这个类就是控制元素下落的&动作&的类,那么这个下落&动作&的类里应该有一些什么东西呢?我们需要考虑边界,于是有了(区域),我们要考虑俄罗斯方块于是有了俄罗斯方块对象(tetris),因为方块的种类不同,有各种不同的形状于是我们必须考虑方块的类别(types),还有下一个类别(NEXTTYPE),因为方块有下一个提示;我们需要考虑方块在AREA中的位置于是有了(position),我们需要判断游戏是否暂停于是有了running,当然了,方块下落的速度SPEED肯定也是要考虑到的,如果GAME OVER了那么就要判断游戏是否停止stopped,当然了,方块是一个一个的元素于是我们要考虑elements,当然了,最重要的还是下落(falldown).下面是定义的代码:
var self= //当前对象
this.area=
this.tetris=
this.types= //方块的类型;
this.nextType= //下一个类型
//初始化X和Y
this.position=0; //初始位置
this.board=[]; //用来填充HTML元素的
this.elements=[];
this.running= //是否在运行中
this.stopped= //是否停止
this.fallDownId= //往下掉落的
this.speed= //速度
  这么一说有点头晕,我们选一个切入点吧,我们的切入点就是如何构造方块。大家应该知道俄罗斯方块的几种形状吧,比如T形,L形,口形等等,那么我们可以想象一下,把俄罗斯方块定义成一个二维数组,然后有元素的地方为1,没元素的地方为0来构造形状,如下面的代码:
/*方块的组合方式,用数组进行组合(二维数组)
用0,1表示是否有方块存在,如果是0:不存在,1:存在,
以下的逻辑就可以非常的清楚了。*/
this.blockComplex=[
[0,0,1],[1,1,1],[0,0,0] &//_|
[1,0,0],[1,1,1],[0,0,0] //L
[0,1,0],[1,1,1],[0,0,0] &//T
[0,0,0],[1,1,1],[0,0,0] //--
[0,0,0],[0,1,1],[0,1,1] //口
[0,1,1],[0,1,0],[1,1,0] //Z
  好了,形状构造好之后,我们当然需要考虑程序的性能方面的问题,于是我创建了如下的GETTER方法,来判断是游戏是否在运行中等。
/*一连串的GETTER方法
& & 分别是速度,X,Y轴,运行和停止的GETTER方法*/
& & this.getSpeed=function()
& & & & return this.&
& & this.getX=function()
& & & & return this.x;&
& & this.getY=function()
& & & & return this.y;&
& & this.isRunning=function()
& & & & return this. &&
& & this.isStopped=function()
& & & & return this.
  当然了,我们如果要&重新开始游戏&,肯定是要建立一个方法reset(),说白一点,就是恢复游戏开始的状态。
//重置(初始化)
this.reset=function()
& & this.nextType=random(this.blockComplex.length);
& & this.types=this.nextT
& & this.position=0;
& & this.board=[];
& & this.elements=[];
& & this.x=
& & this.y=
  如果这个俄罗斯方法触底的话,那么肯定是会触发下一个俄罗斯方块的开始于是我们这里肯定要有一个方法, 内容我还没想好,就给一个架子吧。我直接返回TRUE了。
this.mayPlace=function()
  下面的是最重要的方法,就是我们的替换方块的方法。先来简单做一个介绍,我也不知道自己能不能讲好,大家想想在一个坐标系中,方块如果下落了,肯定是Y--,毕竟方块是向下方下落的,当然,我们还需要有线条,假设我们一直在堆方块的话,这个线肯定是会增加的,还有我们的方块本身就是DIV,肯定是一个掉落DIV的过程,而这些DIV,肯定是在AREA范围内的。我们不妨想一想,第一步,我们来创建一个空的BOARD,就是面板,然后往这个面板里面填充东西呢?
//创建空对象,即所有的都为0的对象,并返回对象
this.createEmpty=function(x,y)
& & & & var elements=[];
& & & & for(var y2=0;y2&y;y2++)
& & & & & & &
& & & & & & elements.push(new Array());
& & & & & & &
& & & & & & for(var x2=0;x2&x;x2++)
& & & & & & {
& & & & & & & & elements[y2].push(0); & & & & & & }
& & & & & & & & &
  我们如果想下落元素的话,肯定是要知道开始下落的坐标,当然Y轴肯定是0,X轴可以依据自己的喜好来设定。当然了,下落的DIV肯定是属于这个AREA下面的子元素的,所以我们等下肯定要把这个APPENDCHILD到这里面去。下面是代码:
this.place=function()
& & //初始化
& & var operate=this.blockComplex[this.types];
& & //区域开始X轴的位置
& & var AreaXStartPos=parseInt(this.area.x-operate[0].length);
& & //区域开始Y轴的位置
& & //var AreaYStartPos=parseInt(this.area.y-operate[0]);
& & var AreaYStartPos=1; //因为X轴的位置可能变化,而Y轴总是从最上面下来的,所以是1
& & this.x=AreaXStartP //把新的位置赋给X;
& & this.y=AreaYStartP //把新的位置赋给y;
& & //构建空对象,并存入BOARD
& & /*y:行,x:列*/
& & //alert(operate[0].length+& &+operate.length);
& & this.board=this.createEmpty(operate[0].length,operate.length);
& & /*线条,往下掉落,初始化*/
& & var lines=0;
& & var foundLines=
& & //循环遍历,先遍历行,每一行再来遍历列
& & for(var yAxis=this.board.length-1;yAxis&=0;yAxis--)
& & & & for(var xAxis=0;xAxis&=this.blockComplex[yAxis].xAxis++)
& & & & & & if(this.blockComplex[yAxis][xAxis])
& & & & & & {
& & & & & & & & var el=document.createElement(&p&);
& & & & & & & & el.className=&block&+this. //确定这个元素的CLASSNAME
& & & & & & & & &
& & & & & & & & //确定左边距和上边距
& & & & & & & & el.style.left=(this.x+xAxis)*this.area.unit+&px&;
& & & & & & & & el.style.top=(this.y+yAxis)*this.area.unit+&px&;
& & & & & & & & this.area.el.appendChild(el); //这个EL去APPEND主要的EL。
& & & & & & & & &
& & & & & & & & this.board[yAxis][xAxis]=
& & & & & & & & this.elements.push(el); //推入elements中
& & & & & & & & &
& & & & & & }
& & & & /*个人感觉这个功能应该是加速往下掉落的方法?不明觉厉*/
& & & & if(lines)
& & & & & & yAxis--; &&
& & & & if(foundLines)
& & & & & & lines++;
& & & & & & &
  需要注意的是,当下一个俄罗斯方块(随机)的形成是随机的,所以我们需要定义一个RANDOM方法。其实每次下落都是一个RESET的循环,只是游戏还没有结束而已。
//随机数,产生1~6的
function random(i)
& & return Math.floor(Math.random()*i);
  好了,今天只介绍一个思路,当然了,我也没写出来这个游戏,等下一篇出来的时候应该游戏会有一个大的架子了,还有一些代码我都不好意思放出来了,写得太差了。其实这个俄罗斯方块不完全是我自己写的,我也参考了下别人的东西,但不是抄袭,我想通过自己的努力,做一个游戏出来,这是我多年的梦想,努力!
  至于全部的代码我就不贴了,因为还没写完,只是对这几天写代码的一个总结而已,高手可以无视我写的代码。HTML5/CSS3简易版俄罗斯方块游戏 | HTML5资源教程用JavaScript写一个俄罗斯方块-js教程-PHP中文网QQ群微信公众号还没有收藏用JavaScript写一个俄罗斯方块曾经用 Turbo C++ 3.0 写过 DOS 下的俄罗斯方块,不久之后又用 VB 写了另一个版本。这次决定用 JavaScript 再写一个并非完全心血来潮,从技术上来说,主要是想尝试使用 webpack + babel 构建的纯 es6 前端项目。项目结构这是一个纯静态项目,而且 HTML 只有一页,就是 index.html。样式表内容不多,还是习惯用 LESS 来写,不喜欢用 sass 的原因其实很直白——不想装逼(Ruby)。重点自然是在脚本上,一个是想尝试完整的 ES6 语法,包括 import/export 的模块管理;二个是想尝试像构建静态语言项目那样,使用构建的思想,通过 webpack + babel 构建出 es5 语法的目标脚本。源(es6语法,模块化)==& 目标(es5语法,打包)项目中使用了 jQuery,但是因为习惯,不想把 jQuery 打包在目标脚本中,也不想手工去下载,所以干脆尝试了一下 bower。相比手工下载,使用 bower 是有好处的,至少 bower install 可以写入构建脚本。一开始对项目目录结构考虑得不是特别清楚,所以建出来的目录结构其实有点乱。整个目录结构如下[root&
|-- index.html
: 构建生成的脚本
: 构建生成的样式表
: bower 引入的库
: 前端源文件
: 样式表源文件
: 脚本(es6)源文件构建配置前端构建脚本部分使用的是 webpack + babel,样式表使用的 less,然后通过 gulp 组织起来。所有前端构建配置和源代码都放在 app 目录下。app 目录下是个 npm 项目,有 gulpfile.js 和 webpack.config.js 等构建配置。因为 gulp 之前用过,fulpfile.js 写起来还比较顺手,但是在配置 webpack 的时候费了点劲。先在网上抄了一个配置const path = require(&path&);module.exports = {
context: path.resolve(__dirname, &src&),
entry: [ &./index& ],
path: path.resolve(__dirname, &../js/&),
filename: &tetris.js&
loaders: [
test: /\.js$/,
exclude: /(node_modules)/,
loader: &babel&,
presets: [&es2015&]
};然后在写的过程中发现需要引入 jQuery,于是又在网上找了半天,抄了一句externals: {
&jquery&: &jQuery&
}不过后来看到说推荐用 ProvidePlugin ,以后再来研究了。在代码初成,初次运行的时候,发现调试非常麻烦,因为编译过,找不到错误在 es6 的源码位置。这时候才发现缺少了非常重要的 source map。于是又在网上搜了半天,加上了devtool: &source-map&程序分析因为以前写过,所以在数据结构上还是有点映像,游戏区就对应着一个二维数组。每个图形就是一组有着相对位置关系的坐标,当然还有颜色定义。所有行为都是通过数据(坐标)的变化来实现的。而障碍物(已固定下来的小方块)判断则是通过当前图形位置及定义中所有小方块的相对位置计算出各小方块坐标之后检查大矩阵对应坐标是否存在小方块数据来判断。这需要提前计算出当前图形在下一个形态所需要占用的坐标列表。方块的自动下落是通过时钟周期控制。如果还要处理消除动画,就可能需要两个时钟周期控制。当然可以取两个时钟周期的了大公约数来合并成一个公共时钟周期,但俄罗斯方块的动画相当简单,似乎没有必要进行这么复杂的处理——可以考虑在消除时暂停下落时钟周期,消除完成之后再重启。交互部分主要靠键盘处理,只需要给 document 绑定 keydown 事件处理就好。方块模型传统的俄罗斯方块只有 7 种图形,加上旋转变形一共也才 19 个图形。所以需要定义的图形不多,懒得去写旋转算法,直接用坐标来定义了。于是先用WPS表格把图形画出来了:然后照此图形,在 JavaScript 中定义结构。设想的数数据结构是这样的SHAPES: [Shape]
// 预定义所有图形Shape: {
// 图形的结构
colorClass: string,
// 用于染色的 css class
forms: [Form]
// 旋转变形的组合}
Form: [Block]
// 图形变形,是一组小方块的坐标Block: {
// 小方块坐标
x: number,
// x 表示横向
// y 表示纵向}其中 SHAPES 、 Form 都直接用数组表示, Block 结构简单,直接使用字面对象表示,只需要定义一个 Shape 类(当时考虑加些方法在里面,但后来发现没必要)class Shape {
constructor(colorIndex, forms) {
this.colorClass = `c${1 + colorIndex % 7}`;
this.forms =
}为了偷懒, SHAPE 是用一个三维数组的数据,通过 Array.prototype.map() 来得到的 Shape 数组class Shape {
constructor(colorIndex, forms) {
this.colorClass = `c${1 + colorIndex % 7}`;
this.forms =
export const SHAPES = [
[[0, 0], [0, 1], [1, 0], [1, 1]]
[[0, 0], [0, 1], [0, 2], [0, 3]],
[[0, 0], [1, 0], [2, 0], [3, 0]]
// .... 省略,请参阅文末附上的源码地址].map((defining, i) =& {
// data 就是上面提到的 forms 了,命名时没想好,后来也没改
const data = defining.map(form =& {
// 计算 right 和 bottom 主要是为了后面的出界判断
let right = 0;
let bottom = 0;
// point 就是 block,当时取名的时候没想好
const points = form.map(point =& {
right = Math.max(right, point[0]);
bottom = Math.max(bottom, point[1]);
x: point[0],
y: point[1]
points.width = right + 1;
points.height = bottom + 1;
return new Shape(i, data);
});游戏区模型虽然游戏区只有一块,但是就画图的这部分行为来说,还有一个预览区的行为与之相仿。游戏区除了显示外还需要处理方块下落、响应键盘操作左、右、下移及变形、堆积、消除等。对于显示,定义了一个 Matrix 类来处理。 Matrix 主要是用来在 HTML 中创建用来显示每一个小方块的 &span& 以及根据数据绘制小方块。当然所谓的“绘制”其实只是设置 &span& 的 css class 而已,让浏览器来处理绘制的事情。Matrix 根据构建传入的 width 和 height 来创建 DOM,每一行是一个 &p&作为容器,但实际需要操作的是每一行中,由 &span& 表示的小方块。所以其实 Matrix 的结构也很简单,这里简单的列出接口,具体代码参考后面的源码链接class Matrix {
constructor(width, height) {}
build(container) {}
render(blockList) {}
}逻辑控制上面提到主游戏区有一些逻辑控制,而 Matrix 只处理了绘制的问题。所以另外定义了一个类: Puzzle 来处理控制和逻辑的问题,这些问题包括预览图形的生成的显示游戏图形和已经固定的方块显示进行中的图形行为(旋转、左移、右移、下移等)边界及障碍判断下落结束后可消除行的判断下落动画处理消除动画处理消除后的数据重算(因为位置改变)Game Over 判断......其实比较关键的问题是图形和固定方块的显示、边界及障碍判断、动画处理。游戏区方块绘制已经确定了 Matrix 用于处理绘制,但绘制需要数据,数据又分两部分。一部分是当前下落中的图形,其位置是动态的;另一部分是之前落下的图形,已经固定在游戏区的。从当前下落中的图形生成一个 blocks 数组,再将已经固定的小方块生成另一个 blocks 数组,合并起来,就是 Matrix.render() 的数据。 Matrix 拿到这个数据之后,先遍历所有 &span& ,清除颜色 class,再遍历得到的数据,根据每一个 block 提供的位置和颜色,去设置对应的 &span& 的 css class。这样就完成了绘制。边界和障碍判断之前提到的 Shape 只是一个形状的定义,而下落中的图形是另一个实体,由于 Shape 命名已经被占用了,所以源代码中用 Block 来对它命名。这个命名确实有点乱,需要这样解理: Shape -& ShapeDefinition ; Block -& Shape 。现在下落中的图形是一个 Block 的实例(对象)。在判断边界和障碍判断的过程中需要用到其位置信息、边界信息(right、bottom)等;另外还需要知道它当前是哪一个旋转形态……所以定义了一些属性。不过关键问题是需要知道它的下个状态(位置、旋转)会占用哪些坐标的位置。所以定义了几个方法fasten() ,不带参数的时候返回当前位置当前形态所占用的坐标,主要是绘图用;带参数时可以返回指定位置和指定形态所需要占用的坐标。fastenOffset() ,因为通常需要的位移坐标数据都相对原来的位置只都有少量的偏移,所以定义这个方法,以简化调用 fasten() 的参数。fastenRotate() ,简化旋转后对 fasten() 的调用。这里有一点需要注意,就是有图形在到在边界之后,旋转可能会造成出界。这种情况下需要对其进行位移,所以 Block 的 rotate() 和 fastenRotate() 都可以输入边界参数,用于计算修正位置。而修正位置则是通过模块中一个局部函数 getRotatePosition() 来实现的。动画控制前面已经提到了,动画时钟分两个,下落动画时钟和消除动画时钟。对于人工操作引起的动画,在操作之后直接重绘,就不需要通过时钟来进行了。考虑到在开始消除动画时需要暂停下落动画,之后又要重新开始。所以为下落动画时钟定义为一个 Timer 类来控制 stop() 和 start() ,内部实现当然是用的 setInterval() 和 clearInterval() 。当然 Timer 也可以用于消除动画,但是因为在写消除动画的时候发现代码比较简单,就直接写 setInterval() 和 clearInterval() 解决了。在 Puzzle 类中,某个图形下图到底的时候,通过 fastenCurent() 为固定它,这个方法里固定了当前图形之后会调用 eraseRows() 来检查和删除已经填满的行。从数据上消除和压缩行都是在这里处理的,同时这里还进行了消除行的动画处理——对需要消除的行从左到右清除数据并立即重绘。let columnIndex = 0;const t = setInterval(() =& {
// fulls 是找出来的需要消除的行
fulls.forEach((rowIndex) =& {
matrix[rowIndex][columnIndex] =
this.render();
// 消除列达到右边界时结束动画
if (++columnIndex &= this.puzzle.width) {
clearInterval(t);
reduceRows();
this.render();
this.process();
}, 10);小结俄罗斯方块的算法并不难,但这个仓促完成的小游戏中仍然存在一些问题需要将来处理掉:没有交互方式的开始和结束,页面一旦打开就会持续运行。还没有引入计分每次绘制都是全部重绘,应该可以优化为局部(变化的部分)重绘更多用JavaScript写一个俄罗斯方块相关文章请关注PHP中文网!共3篇250点赞收藏分享:.&猜你喜欢我在网上找了个,看了下源码,差不多,有些能看懂,有些看不懂,这个不适用在项目里面,但可以用来学习问玩玩!这个不适合在项目中使用,个人觉得看懂就好,没事可以自己写一个自己玩,哈哈!好像我之前在哪里看到了例子,自己还下载了玩了下,之后看了下源码,太多了!代码很多,自己写的话肯定有难度!感觉js什么都能做对我来说JS就是个迷!首页上一页下一页尾页PHP中文网:独家原创,永久免费的在线,php技术学习阵地!
All Rights Reserved | 皖B2-QQ群:关注微信公众号}

我要回帖

更多关于 俄罗斯方块小游戏免费 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信