用纯JS做俄罗斯方块 - 简要思路介绍(1)
大家都知道俄罗斯方块是一款大众化的游戏了,我很小的时候就玩过,今年已经25岁了,可以说俄罗斯方块确实是历史悠久,做俄罗斯方块是我上个星期开始的想法。也许是由于自己从来没有写过这种东西吧,所以有生疏。代码的话,只完成了一小部分,大概1/5左右吧。今天还是决定先写一部分思路。
至于俄罗斯方块的话,有很多的难点,如果有JS去写的话,要考虑到碰撞啊,边界啊,下落等问题,本文这些问题大部分不会考虑到,只是提供一部分思路而已,开始已经说了,因为自己还没写完这个游戏,但是又出于想写博客记录,所以才有了这一系列的博客。
回到正题,我们首先想一想,俄罗斯方块需要什么?我做一个简单的归纳。如果我简单点说的话,就是一个俄罗斯方块对象,那么这个对象里又有些什么东西呢?我们可以想象一下,有一个平面直角坐标系,这个平面直角坐标系有X轴,有Y轴,也有“每一等分”的距离(unit),而俄罗斯方块就是一个一个的“格子”,这些格子从某一个地方开始下落,某一个地方停止下落,于是我们就规定了俄罗斯方块的“下落区域”(Area)。但是下落是一个“动作”,所以我们还要有一个类(这里定义为operate),来控制动作的下落。
好了,先就介绍到这里,我们再来做一段代码性质的归纳,表示对上面的代码做一段归纳。
/*俄罗斯方块实体类*/
function Tetris()
{
var self =this; //自身 this.area=null;//区域
this.operate=null; //操作 /*初始化X,Y,单元为5或者20*/
this.x=5;
this.y=5;
this.unit=20; this.running=null; //是否在运行中 //俄罗斯方块实体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=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]=el; //确定元素的位置
} } //消掉所有的行
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]){return false;} //不明觉厉
} return true;
}; //去掉行
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; //当前对象
this.area=area;
this.tetris=tetris; this.types=null; //方块的类型;
this.nextType=null; //下一个类型 //初始化X和Y
this.x=null;
this.y=null;
this.position=0; //初始位置 this.board=[]; //用来填充HTML元素的
this.elements=[]; this.running=null; //是否在运行中
this.stopped=null; //是否停止 this.fallDownId=null; //往下掉落的
this.speed=null; //速度
这么一说有点头晕,我们选一个切入点吧,我们的切入点就是如何构造方块。大家应该知道俄罗斯方块的几种形状吧,比如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.speed;
} this.getX=function()
{
return this.x;
} this.getY=function()
{
return this.y;
} this.isRunning=function()
{
return this.running;
} this.isStopped=function()
{
return this.stopped;
}
当然了,我们如果要”重新开始游戏“,肯定是要建立一个方法reset(),说白一点,就是恢复游戏开始的状态。
//重置(初始化)
this.reset=function()
{
this.nextType=random(this.blockComplex.length);
this.types=this.nextType; this.position=0;
this.board=[];
this.elements=[];
this.x=null;
this.y=null; }
如果这个俄罗斯方法触底的话,那么肯定是会触发下一个俄罗斯方块的开始于是我们这里肯定要有一个方法, 内容我还没想好,就给一个架子吧。我直接返回TRUE了。
this.mayPlace=function()
{ return true;
}
下面的是最重要的方法,就是我们的替换方块的方法。先来简单做一个介绍,我也不知道自己能不能讲好,大家想想在一个坐标系中,方块如果下落了,肯定是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);
} }
return elements;
}
我们如果想下落元素的话,肯定是要知道开始下落的坐标,当然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=AreaXStartPos; //把新的位置赋给X;
this.y=AreaYStartPos; //把新的位置赋给y; //构建空对象,并存入BOARD
/*y:行,x:列*/ //alert(operate[0].length+" "+operate.length);
this.board=this.createEmpty(operate[0].length,operate.length); /*线条,往下掉落,初始化*/
var lines=0;
var foundLines=false; //循环遍历,先遍历行,每一行再来遍历列
for(var yAxis=this.board.length-1;yAxis>=0;yAxis--)
{
for(var xAxis=0;xAxis<=this.blockComplex[yAxis].length;xAxis++)
{
if(this.blockComplex[yAxis][xAxis])
{
var el=document.createElement("div");
el.className="block"+this.types; //确定这个元素的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]=el;
this.elements.push(el); //推入elements中 }
} /*个人感觉这个功能应该是加速往下掉落的方法?不明觉厉*/
if(lines)
{
yAxis--;
} if(foundLines)
{
lines++;
} }
需要注意的是,当下一个俄罗斯方块(随机)的形成是随机的,所以我们需要定义一个RANDOM方法。其实每次下落都是一个RESET的循环,只是游戏还没有结束而已。
//随机数,产生1~6的
function random(i)
{
return Math.floor(Math.random()*i);
}
好了,今天只介绍一个思路,当然了,我也没写出来这个游戏,等下一篇出来的时候应该游戏会有一个大的架子了,还有一些代码我都不好意思放出来了,写得太差了。其实这个俄罗斯方块不完全是我自己写的,我也参考了下别人的东西,但不是抄袭,我想通过自己的努力,做一个游戏出来,这是我多年的梦想,努力!
至于全部的代码我就不贴了,因为还没写完,只是对这几天写代码的一个总结而已,高手可以无视我写的代码。
用纯JS做俄罗斯方块 - 简要思路介绍(1)的更多相关文章
- 纯JS实现俄罗斯方块,打造属于你的游戏帝国
纯JS俄罗斯方块,打造属于你的游戏帝国. 本文原始作者博客 http://www.cnblogs.com/toutou 俄罗斯方块(Tetris, 俄文:Тетрис)是一款电视游戏机和掌上游戏机游戏 ...
- [前端 3]纯Js制作俄罗斯方块游戏
导读:在别人文章里看到了,然后写了一遍.结果出错了,然后调出来了,然后理解了一下,加了点注释,有一些想法.忘了在 哪一篇上面看的了,就贴不出来链接地址.原谅.呃,真没自己的东西,权当练打字了吧.其实, ...
- 纯js做的select二级联动
分步阅读 select 联动用到的范围很广,下面介绍一下简单的二级联动 方法/步骤 做一个简单的html页面,用于显示select联动,如图所示: 设置js,点击一级选择项时,创建其下对应的二 ...
- 如何用纯js做一个大富翁游戏
下面这张是效果图: 先立个flag,一个星期内把这个坑填了
- F2工作流引擎之-纯JS Web在线可拖拽的流程设计器(八)
Web纯JS流程设计器无需编程,完全是通过鼠标拖.拉.拽的方式来完成,支持串行.并行.分支.异或分支.M取N路分支.会签.聚合.多重聚合.退回.传阅.转交,都可以非常方便快捷地实现,管理员 ...
- 纯js实现瀑布流布局及ajax动态新增数据
本文用纯js代码手写一个瀑布流网页效果,初步实现一个基本的瀑布流布局,以及滚动到底部后模拟ajax数据加载新图片功能. 缺点: 1. 程序不是响应式,不能实时调整页面宽度: 2. 程序中当新增ajax ...
- 纯JS Web在线可拖拽的流程设计器
F2工作流引擎之-纯JS Web在线可拖拽的流程设计器 Web纯JS流程设计器无需编程,完全是通过鼠标拖.拉.拽的方式来完成,支持串行.并行.分支.异或分支.M取N路分支.会签.聚合.多重聚合.退回. ...
- 纯js实现html转pdf
项目开发中遇到了一个变态需求,需要把一整个页面导出为pdf格式,而且要保留页面上的所有的表格.svg图片和样式.简而言之,就是希望像截图一样,把整个页面截下来,然后保存成pdf.咋不上天呢--查了一下 ...
- Vue.js是什么,vue介绍
Vue.js是什么,vue介绍 Vue.js 是什么Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用. ...
随机推荐
- C#时间戳转换
,,)).ToUniversalTime ().Ticks ) / ;//先取得当前的UTC时间,然后转换成计算用的周期数(简称计时周期数),每个周期为100纳钞(ns)=0.1微秒(us)=0.00 ...
- 通用EF框架
之前我老大去网上找了一个DAL里面操作数据库的通用类: public class DALHelper { public static List<T> Search<T>() w ...
- final 评论ii
按照演讲顺序 1.约跑app 约跑app,从界面的单调,到最后的final发布,实现界面的友好性,有了很大的提高.约跑app,如果在约定地点可以显示出,所在位置,以及约定地址.就可以达 ...
- Autocad 2012 win7(64位)启动时一直卡在acmgd.dll处的解决方案
安装Autocad 2012后,激活成功后,无法正常启动,一直卡在加载acmgd.dll 通过Procmon监控后发现加载C:\Windows\fonts\AdobeFnt11.lst处出错, 通过命 ...
- XMPP iOS客户端实现二:xcode项目配置
1.下载XMPPFramework,下载地址:https://github.com/robbiehanson/XMPPFramework 2.创建项目并将XMPP库引入: 3.添加需要的库文件: 4. ...
- SQL基础--同义词
同义词的概念: 同义词是Oracle对象的别名,使用同义词访问相同的对象 可以为表.视图.存储过程.函数或另一同义词等对象创建同义词 方便访问其它用户的对象,隐藏了对象的身份 缩短对象名字的长度 同义 ...
- Android Support Library
title: Android Support Library tags: Support Library,支持库 grammar_cjkRuby: true --- DATE: 2016-5-13. ...
- DRY(Don't Repeat Yourself )原则
凡是写过一些代码的程序猿都能够意识到应该避免重复的代码和逻辑.我们通过提取方法,提取抽象类等等措施来达到这一目的.我们总能时不时的听到类似这样的话:”把这些公用的类放到shared项目去,别的项目还要 ...
- C语言指针转换为intptr_t类型
1.前言 今天在看代码时,发现将之一个指针赋值给一个intptr_t类型的变量.由于之前没有见过intptr_t这样数据类型,凭感觉认为intptr_t是int类型的指针.感觉很奇怪,为何要将一个指针 ...
- Could not load type 'System.Reflection.AssemblySignatureKeyAttribute' from assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c
错误: Could not load type 'System.Reflection.AssemblySignatureKeyAttribute' from assembly 'mscorlib, V ...