用纯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);
}

  好了,今天只介绍一个思路,当然了,我也没写出来这个游戏,等下一篇出来的时候应该游戏会有一个大的架子了,还有一些代码我都不好意思放出来了,写得太差了。其实这个俄罗斯方块不完全是我自己写的,我也参考了下别人的东西,但不是抄袭,我想通过自己的努力,做一个游戏出来,这是我多年的梦想,努力!

  至于全部的代码我就不贴了,因为还没写完,只是对这几天写代码的一个总结而已,高手可以无视我写的代码。

时间: 2024-10-13 00:51:19

用纯JS做俄罗斯方块 - 简要思路介绍(1)的相关文章

纯js实现瀑布流布局及ajax动态新增数据

本文用纯js代码手写一个瀑布流网页效果,初步实现一个基本的瀑布流布局,以及滚动到底部后模拟ajax数据加载新图片功能. 缺点: 1. 程序不是响应式,不能实时调整页面宽度: 2. 程序中当新增ajax模拟数据图片后,是将整个页面的所有图片都重新定位一次. 3. 程序是等所有图片加载完成后再读取图片的尺寸,实际中肯定不能这样做. 4. 实际项目中,应该由后台程序给出图片尺寸值,在js代码中直接使用图片的width属性. 本程序思路: html结构: ? 1 2 3 4 5 6 7 8 9 10 1

纯js实现html转pdf

项目开发中遇到了一个变态需求,需要把一整个页面导出为pdf格式,而且要保留页面上的所有的表格.svg图片和样式.简而言之,就是希望像截图一样,把整个页面截下来,然后保存成pdf.咋不上天呢--查了一下,能够实现html转pdf的方法还是挺多的,大概有以下几种:1.大部分浏览器就有这个功能.然而我们客户要的可不是这个,人家要的是能够在系统中主动触发的导出为pdf功能,所以这种方案pass.2.利用第三方工具.我找到了一种利用wkhtmltopdf这种工具来导出的方案,自己在我们的项目中试了一下,效

[Erl_Question07] Erlang 做图形化编程的尝试:纯Erlang做2048游戏

用Erlang久了,以为erlang做类似于As3,JS的图形化界面是绝对不可能的,多少次,多少次想用erlang做个炫酷的图形游戏.终于:折腾出来了结果:纯Erlang也可以做到! 因为以前接触过WxErlang,网上的资料又少,还好有wx : demo().神器,用了大约20小时的时间.代码在这里: Erlang 2048 github 欢迎大家一起玩! 框架基本流程 使用到的知识: 1. 图形化界面使用了wxErlang; 2.LAN联网使用到Erlang的分布节点互连:net_kerne

socket.io+angular.js+express.js做个聊天应用(三)

接着前面博客文章socket.io+angular.js+express.js做个聊天应用(二) 首先开发之前先介绍下bower.它是用来管理前端类库的(详细介绍,安装可看http://blog.csdn.net/edagarli/article/details/26359535) [email protected]:~/projects/nodejs/chattingnode$ bower Usage: bower <command> [<args>] [<options&

纯js手动分页

昨天让做个页面,后台提供所有数据,没有做好分页,需要前端js手动分页. 我参考了 http://www.cnblogs.com/jiechn/p/4095029.html 做了些许改动让分页效果更加完善. 最终效果图 js代码 /** * 分页函数 * pno--页数 * psize--每页显示记录数 * 分页部分是从真实数据行开始,因而存在加减某个常数,以确定真正的记录数 * 纯js分页实质是数据行全部加载,通过是否显示属性完成分页功能 **/ function goPage(pno){ va

Json数据如果作为配置文件比较难读懂,XML文件作为配置文件有先天的优势,容易读懂和配置,因此不考虑效率时,在页面中宁可用XML文件作为配置文件再用JS做一次转化把XML转成JSON使用

比如如下相对比较复杂的XML <myobjects> <!--object 1--> <myobject> <id>yourID_1</id> <name>your name</name> <description> <![CDATA[Merck Biologics Pilot Plant ]]> </description> <locations> <location

jQuery+AJAX实现纯js分页功能

使用jQuery的AJAX技术,在bootstrap的框架下搭建的纯js分页 bootstrap作为Twitter推的一款前端框架,效果个人还是觉得很不错的.这次只是拿来作为网页元素的css样式表使用,比较省力,效果也会比自己做要漂亮多了. 使用数据为单独的json文件data.json [plain] view plain copy [ { "name": "bootstrap-table", "stargazers_count": "

JS做深度学习1——偶然发现与入门

JS做深度学习1--偶然发现与入门 不久前,我初次涉猎了Node.js,并且使用它开发了毕业设计的WEB模块,然后通过在Node中调用系统命令执行Python文件方式实现了深度学习功能模块的对接,Python代码的介入,让JS代码显得很累赘,我说过我很爱ES6以后的JS并且很讨厌Python的代码风格,无奈,我在写毕设那会Google还没有正式发布基于JS的深度学习框架,好吧,其实我对这事已经抱怨了很久,但是我的"呼声"仿佛很快就被Google"认同了"(滑稽),就

纯 JS 设置文本框的默认提示

HTML5 中有个新特性叫 placeholder,一般用它来描述输入字段的预期值,适用于 text.search.password 等类型的 input 以及 textarea.示例如下: <input type="text" placeholder="请输入文本"><br> <input type="search" placeholder="请输入查询关键字"><br> &l