`
xlcai
  • 浏览: 19062 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

纯JavaScript山寨腾讯手机游戏《天天爱消除》开发过程详细

 
阅读更多

前言

貌似最近腾讯手机游戏天天爱消除挺火的,我也是粉丝之一,最近对javascript一直比较感兴趣然后想用js仿造一个,应该不是太难,本文系边写代码别写博客,详细叙述开放过程,如果最后开发成功就发表,如果最后失败了就删除草稿。废话不多说开始:

写完后重新声明以下均为本人边写边记录思路开发过程,中途走了很多大弯路小弯路。如不想被误导可直接研究最后的源代码

了解需求

消除类游戏很多平台都有,想必大家都玩过,游戏的原理无非就是不同颜色的方块组成的阵列,移动相邻的方块如果能组成三个同颜色则此三个消失,消失后按重力影响上方下落新的方块。同时要加分,游戏时间一定比较谁在特定时间内得分最多,完全是靠眼疾手快和运气的游戏。说起来含含糊糊的下面说几个主要需求:
这里既然要仿造腾讯的天天爱消除就完全照它的格式来。
1.方块的阵列是七乘七的。
2.方块的颜色分类7个。
3.鼠标拖动(原游戏是触控手滑)如可消除就交换位置,否则还原。另一种操作方法:点击一个后点击相邻另一个。
4.移动后横向竖向比较三个及三个以上相同色可消除,同时由上向下补充新方块,如新入方块有组合三色相同自动再次消除。
5.1分钟时间控制进度条
6.可进入super模式下双倍得分
7.计分板
8.道具
大概就是这些

开发思路

以下是炮灰思路:(后来写起来发现不好用)
按需求想开发思路,方块列阵的实现,肯定就是div了,如果想好看点,就往div里面嵌入图片。这里游戏都是受重力感应的,下面消失了上面要往下补充,开始我想到的是7*7=49个div去实现,但如何保证整齐。开始我想到的是先画个tr,td7*7的table,然后让div去嵌入到每个td中。但要考虑到后面的交换,感觉不是很方便,要取parent然后child等等很麻烦,当然可以放到数组里,但不能是一个数组,不然不好去调用上下相邻,左右相邻,那就是7个数组,横向7个或者竖向7个。其实都一样方便,但还要考虑到方块的下滑效果,就是下面有div消失了,上面的滑下来。开始的想法是就49个div不变,只改变颜色,但那样做会很生硬,做不出动画效果。然后我又想到既然有重力感应就用div的float:bottom不就行了。横向上要并列,竖向上还要float:bottom。那就只能大div套小div了。
最终的思路就是先画7个竖的div,假设游戏整体模型的大小是宽w,高h.那这7个竖div就是宽w/7,高h,横向并列。然后再在7个div每个里竖向并列7个方块(w/7,h/7)(float:bottom),这样下面的div消失了上面的自然下滑了(后来实践证明这完全就是错误的,div一般都是竖向排列的,为了并列才设float:left;正常情况下上面的div消失了下面的会补上去,跟需求正好相反。跟float没啥关系。而且滑动效果接近瞬移,此想法作废),而且左右间不会受影响,因为包在7个竖向的div了。
刚才说了数组,既然用了竖向分组就数组也取竖向的,然后比较颜色也没啥难的,在再每个div消失的时候加个补充的事件就行了。积分也没什么,道具就是让更多的div消失,思路差不多就这些,开始做吧。
 
后来实际写起来时,我才学习到div要想拖动实质是改变他的left,top,跟上文的打砖块似的,position必须设为absolute,left,top才有效,而我们所有的小div是分布在7个长条div里的,即这7个长条div的position要设置为relative才行,这样他的child div才会以他的四壁做坐标left,top否则会以body做坐标。具体这方面的知识百度下有很多博文介绍。后来还试过画7*7的table,感觉还是最初的7个长条div套小div的思路比较正确。


然而当我将position设为absolute时float又不起作用了,即竖排上的小div都叠到一起了,默认left,top都是0,所以我想干脆float:bottom去掉算了,加载时就设置好每个的left,top。然后消失时我们也是改变上面的left ,top,他们的值做setTimeout逐渐减小,就可以看出向下滑动效果了,而且都是分布在7个长条div里,只是改变top值就ok了。left都是0.更幸运的是每个div的高都一样,改变时只需top值只需n*height就行了,看来这个思路确实是正确的!

这时数组还需要吗?需要,我们拖动来拖动去的时候很难用兄弟节点去判断,还是保持放在数组里的位置去判断比较好。

加载模型

加载模型就是先创建这49个随机颜色的div,因为游戏里经常有div消失,div创建,所以最好写个创建div的构造函数,7个颜色算了,我们要仿造就直接内嵌图片好了,(这里很抱歉我要截取腾讯原版游戏里的截图了)所以我们div的构造函数其实是构造一个div,div里再嵌入一个img,img的src取随机数,我们把7个图片分别明明为zimg_0,zimg_1,zimg_2....这样直接取随机数就行了。代码如下:
(做到加入控制时突然发现内嵌图片是不行的,因为对图片进行鼠标拖动的话浏览器会默认在新窗口打开该图片,所以后面的代码改为了以图片做div的背景图片)
其下代码实为改了多遍的加载模型代码。。。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>qqqun.21.777.12</title>
<style>
div{margin:0px;padding:0px;}
#mainDiv{width:343px;height:343px;background-color:#BFEFFF;border:5px solid #87CEFA;}
#mainDiv .sDiv{width:49px;height:343px;float:left;position:relative;}
#mainDiv .sDiv div{width:49px;height:49px;float:bottom;position:absolute;}
</style>
<script>
function init(){//加载模型
	var oCons = document.getElementsByName("contentDiv");
	var arrDiv0 = new Array();//7个’竖向‘的数组
	var arrDiv1 = new Array();
	var arrDiv2 = new Array();
	var arrDiv3 = new Array();
	var arrDiv4 = new Array();
	var arrDiv5 = new Array();
	var arrDiv6 = new Array();
	var arrDiv7 = new Array();
	
	var arrAll = [arrDiv0,arrDiv1,arrDiv2,arrDiv3,arrDiv4,arrDiv5,arrDiv6]
	
	for(var i=0;i<7;i++){
		for(var j=0;j<7;j++){
			var oDiv = createDiv(j*49);
			oCons[i].appendChild(oDiv);
			arrAll[i][j] = oDiv;
		}
	}
}

function createDiv(iTop){//构造div
	var oDiv = document.createElement("div");
	oDiv.style.backgroundImage="url(img/zImg_"+Math.round(Math.random()*6)+".jpg)";
	oDiv.style.top = iTop + "px";
	return oDiv;
}

window.onload = init;
</script>
</head>
<body style="background-color:grey;">
<div align="center">
	<div id="mainDiv">
		<div name="contentDiv" class="sDiv"></div>
		<div name="contentDiv" class="sDiv"></div>
		<div name="contentDiv" class="sDiv"></div>
		<div name="contentDiv" class="sDiv"></div>
		<div name="contentDiv" class="sDiv"></div>
		<div name="contentDiv" class="sDiv"></div>
		<div name="contentDiv" class="sDiv"></div>
	</div>
</div>
</body>
</html>
以上代码基本没什么难点就是加载模型的同时分配到7个数组,不再解释。

加入控制

这里的控制有两种方式,一种是鼠标按住不放拖动与相邻交换位置,二种是先点击某个div后再去点击相邻的另一个互换位置,这里先不控制是否移动能消除而控制是否移动,先让其能动起来。先说第一种方式。就是js鼠标拖动div,这个网上有很多例子,如果紧紧是拖动则构造div的函数改为如下:
function createDiv(iTop){//构造div
	var oDiv = document.createElement("div");
	oDiv.style.backgroundImage="url(img/zImg_"+Math.round(Math.random()*6)+".jpg)";
	oDiv.style.top = iTop + "px";
	oDiv.style.MozUserSelect="none"
	var posX;
	var posY; 
	oDiv.onmousedown=function(e){//鼠标按下时
		e = e ? e : (window.event ? window.event : null);
		posX = e.clientX - this.offsetLeft;//先记录按下时与鼠标位置的差值后面移动时再做差值
		posY = e.clientY - this.offsetTop; 
		moveable = true;
		this.style.zIndex = 100;//zindex默认为空,不设置个数的话拖动过程会被其他元素覆盖掉
		iFlag = 1;
    };
	oDiv.onmousemove=function(e){//鼠标拖动时
        e = e ? e : (window.event ? window.event : null); 
        if(moveable){
			this.style.left = (e.clientX - posX) + "px";
			this.style.top = (e.clientY - posY) + "px";
		}
	};
	oDiv.onmouseup=function (e){//鼠标松起时
        if(moveable)  {  
            moveable = false; 
        }
		this.style.zIndex = "";
    };
	return oDiv;
}
但我们这里不是随便拖动,而且只能横向,或者竖向,不能随便拐弯,而且最多移动一个位移。这就有点难了,因为你不知道玩家按下鼠标后是要左右还是上下,而拖动是个持续动作,不能在拖动中设置个boolean参数去控制。改为如下
var moveable = false;
function createDiv(iTop){//构造div
	var oDiv = document.createElement("div");
	oDiv.style.backgroundImage="url(img/zImg_"+Math.round(Math.random()*6)+".jpg)";
	oDiv.style.top = iTop + "px";
	oDiv.style.MozUserSelect="none"
	var posX,posY;
	var posEx,posEy;
	oDiv.onmousedown=function(e){
		e = e ? e : (window.event ? window.event : null);
		posEx = e.clientX;//按下鼠标时同时记住鼠标位置
		posEy = e.clientY;
		
		posX = e.clientX - oDiv.offsetLeft;
		posY = e.clientY - oDiv.offsetTop; 
		moveable = true;
		this.style.zIndex = 100;
		
    };
	oDiv.onmousemove=function(e){
        e = e ? e : (window.event ? window.event : null); 
		if(moveable){
				if(Math.abs(e.clientX - posEx)>Math.abs(e.clientY - posEy))//移动前先比较鼠标新位置与旧位置,即位置方向再去改变,这样如果是向左,则左差值越来越大就会一直像左,同理上下
				oDiv.style.left = (e.clientX - posX) + "px";
				else
				oDiv.style.top = (e.clientY - posY) + "px";
		}
	};	
	oDiv.onmouseup=function (e){
        if(moveable)  {  
            moveable = false; 
        }
		this.style.zIndex = "";
    };
	return oDiv;
}
这样拖动算算解决了冰山一角,还要解决拖动时向哪个方向走,对方的div要顺势向这边移动。有个交换的效果。
首先鼠标点击后要知道我点击的是7个数组里哪个数组里的哪个位置。点击时获取可以获取到点击的div元素,就是按元素找位置,我们可以遍历之前的数组,进行比对就能得到是哪个位置,但我们可以构造时就传入一个参数指明这个div在7个数组的哪个数组里面,这样不用全部遍历,这时按元素找位置的函数我们就可以这样写:
function findIndex(obj){
	for(var i=0,m=arrAll[iArrIndex].length;i<m;i++){
		if(obj===arrAll[iArrIndex][i]){
			return i;
		}
	}
};
************************************************星号间为错误思路不喜勿看可略过,可直接跳至下个星号行处**************************************************************
开头存储7个数组的大叔组arrAll要改为全局变量,同时声明一个变量iArrIndex,标明是哪个数组,这时构造函数要把这个区分数组的参数加进去:
function createDiv(iTop,iArr){//加在了这
	var oDiv = document.createElement("div");
	oDiv.style.backgroundImage="url(img/zImg_"+Math.round(Math.random()*6)+".jpg)";
	oDiv.style.top = iTop + "px";
	oDiv.style.MozUserSelect="none"
	var posX,posY;
	var posEx,posEy;
	oDiv.onmousedown=function(e){
		e = e ? e : (window.event ? window.event : null);
		posEx = e.clientX;
		posEy = e.clientY;
		
		posX = e.clientX - oDiv.offsetLeft;
		posY = e.clientY - oDiv.offsetTop; 
		moveable = true;
		this.style.zIndex = 100;
		iArrIndex = iArr;//加在了这,没此点击动态改变数组的指向
    };
同时加载模型时肯定是这样写了
for(var i=0;i<7;i++){
		for(var j=0;j<7;j++){
			var oDiv = createDiv(j*49,i);
			oCons[i].appendChild(oDiv);
			arrAll[i][j] = oDiv;
		}
	}
有人想,既然i(区分数组的标识)可以传递进去,干脆把j(区分位置的标识)也传递进去得了,省得还得遍历麻烦,这里开始我也是这么想,但考虑消除后数组里的位置j会递增或递减的不好控制,所以还是遍历j比较省事。而我们构造时i就不用考虑那么多,哪排数组消除了元素构造哪排的i,i值不会随便改。我们可以在onmouseup里写一个alert(findIndex(this));测试下返回值是否正确。本人已测。
然后我们按元素取到了鼠标点击div的数组位置,再去找相邻的div,那就又成了按数组位置返回div了,这个好办,对i,j做加减就行了,然后返回写个函数
function findDiv(iArr,jArr){
	return arrAll[iArr][jArr];
}
然后我们鼠标 点击可以获取到div(this)又可以得到他的相邻元素。接下来要解决的就是相互滑动了。这个其实也不难,我们看鼠标的移动方向来判断4个方向哪个div互换,哪个div做反向移动就行了。做到这里时我突然发现其实我们的移动都是整步移动,每次就移动一个宽度或一个高度,所以上面的还得先改动下
我想让
oDiv.style.left = (e.clientX - posX) + "px"
直接改为left直接加减49。但这样就没有拖动效果了,直接瞬移,所以onmousemove处的不能改,只能加个位移长度的控制49,简单加个
if(Math.abs(e.clientX - posEx)<49)
onmouseup鼠标放手时把位置放正确,然而是上下移动还是左右移动我们在onmousemove已经确定了互动对象,但实际这里要做很多事,要把div的嵌套关系互换,数组里的位置互换。这时我明白过来把i传递到构造函数里也是错误麻烦的!
******************************本人乐于分享,但请尊重他人几个晚上的劳动成果,转载请注明出处http://blog.csdn.net/uucai***********************************************
折腾半天49个div还得挨个遍历findIndex改为如下,按元素获取到数组位置i,j.前面的传递参数i作废不用了,
function findIndex(obj){
	for(var i=0;i<7;i++){
		for(var j=0;j<7;j++){
			if(obj===arrAll[i][j]){
				return {ri:i,rj:j};
			}
		}
	}
};
然后我们要单独写一个交换位置的函数,这个函数不仅要交换数组的位置,还要交换页面上div元素的位置,数组的位置好说如下:
function changeIndex(oIdiv,oLdiv){
	var indexIJ_I = findIndex(oIdiv);
	var indexIJ_L = findIndex(oLdiv);
	
	arrAll[indexIJ_L.ri][indexIJ_L.rj] = oIdiv;
	arrAll[indexIJ_I.ri][indexIJ_I.rj] = oLdiv;
}
然后交换div位置,有人说直接取oIdiv,oLdiv的left,top互换不就行了,但这里做到这一步时我们的left,top已经被鼠标拖动了,是没法再用的,我们做交换div位置的目的是放置好left,和top为49的整数倍。但我们实质改变div的位置还必须要用left,top去改变,难道要传参过来原位置、?后来我想了半天7个数组是没错的,但7个长条div就有点死板多余了,我们完全可以去掉这7个长div,没啥用了,当初float:bottom想法作废的时候就该把它作废了。然后加载模型要改,横向纵向都是49的倍数,那交换位置好说了,得到了数组里的位置去乘以49就知道原位置top,left了。
加载模型的代码要重新改为
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>qqqun.21.777.12</title>
<style>
div{margin:0px;padding:0px;}
#mainDiv{width:343px;height:343px;background-color:#BFEFFF;border:5px solid #87CEFA;position:relative;}
#mainDiv div{width:49px;height:49px;float:bottom;position:absolute;}
</style>
<script>
var $ = function (id) {//方便按id提取
	return document.getElementById(id);
};
var arrAll = new Array();
function init(){//加载模型
	var oCon = $("mainDiv");
	var arrDiv0 = new Array();//7个’竖向‘的数组
	var arrDiv1 = new Array();
	var arrDiv2 = new Array();
	var arrDiv3 = new Array();
	var arrDiv4 = new Array();
	var arrDiv5 = new Array();
	var arrDiv6 = new Array();
	var arrDiv7 = new Array();
	
	arrAll = [arrDiv0,arrDiv1,arrDiv2,arrDiv3,arrDiv4,arrDiv5,arrDiv6]
	
	for(var i=0;i<7;i++){
		for(var j=0;j<7;j++){
			var oDiv = createDiv(i*49,j*49);//更改处
			oCon.appendChild(oDiv);
			arrAll[i][j] = oDiv;
		}
	}
}
function createDiv(iLeft,iTop){//构造div
	var oDiv = document.createElement("div");
	oDiv.style.backgroundImage="url(img/zImg_"+Math.round(Math.random()*6)+".jpg)";
	oDiv.style.left = iLeft + "px";//更改处添加了left参数原本都是0
	oDiv.style.top = iTop + "px";
	return oDiv;
}
window.onload = init;
</script>
</head>
<body style="background-color:grey;">
<div align="center">
	<div id="mainDiv"></div>
</div>
</body>
</html>
居然走了个大弯路。其它的函数不用变,继续说刚才的交换页面的div位置,我们刚才已经有思路了,利用数组位置获取原div的left,top。更换位置的参数变为:
function changeIndex(oIdiv,oLdiv){
	var indexIJ_I = findIndex(oIdiv);
	var indexIJ_L = findIndex(oLdiv);
	
	oIdiv.style.left = indexIJ_L.ri*49;//临div的原位置给原div
	oIdiv.style.top = indexIJ_L.rj*49;
	
	oLdiv.style.left = indexIJ_I.ri*49;//原div的原位置给临div
	oLdiv.style.top = indexIJ_I.rj*49;
		
	arrAll[indexIJ_L.ri][indexIJ_L.rj] = oIdiv;
	arrAll[indexIJ_I.ri][indexIJ_I.rj] = oLdiv;
}
突然觉得用7个数组实在是太明智了!折腾一圈后最终代码如下:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>qqqun.21.777.12</title>
<style>
div{margin:0px;padding:0px;}
#mainDiv{width:343px;height:343px;background-color:#BFEFFF;border:5px solid #87CEFA;position:relative;}
#mainDiv div{width:49px;height:49px;float:bottom;position:absolute;border:1px solid red}
</style>
<script>
var $ = function (id) {//方便按id提取
	return document.getElementById(id);
};
var arrAll = new Array();
function init(){//加载模型
	var oCon = $("mainDiv");
	var arrDiv0 = new Array();//7个’竖向‘的数组
	var arrDiv1 = new Array();
	var arrDiv2 = new Array();
	var arrDiv3 = new Array();
	var arrDiv4 = new Array();
	var arrDiv5 = new Array();
	var arrDiv6 = new Array();
	var arrDiv7 = new Array();
	
	arrAll = [arrDiv0,arrDiv1,arrDiv2,arrDiv3,arrDiv4,arrDiv5,arrDiv6]
	
	for(var i=0;i<7;i++){
		for(var j=0;j<7;j++){
			var oDiv = createDiv(i*49,j*49);
			oCon.appendChild(oDiv);
			arrAll[i][j] = oDiv;
		}
	}
}

var moveable = false;
function createDiv(iLeft,iTop){//构造div
	var oDiv = document.createElement("div");
	oDiv.style.backgroundImage="url(img/zImg_"+Math.round(Math.random()*6)+".jpg)";
	oDiv.style.left = iLeft + "px";
	oDiv.style.top = iTop + "px";
	oDiv.style.MozUserSelect="none"
	var posEx,posEy;
	oDiv.onmousedown=function(e){
		e = e ? e : (window.event ? window.event : null);
		posEx = e.clientX;//按下鼠标时同时记住鼠标位置
		posEy = e.clientY;
		
		moveable = true;
		this.style.zIndex = 100;
    };
	var oBro;//相邻div
	oDiv.onmousemove=function(e){
        e = e ? e : (window.event ? window.event : null); 
		
		if(moveable){
			var indexXY = findIndex(this);
			var eMoveLeft = e.clientX - posEx;
			var eMoveTop = e.clientY - posEy;
			var iBroi,iBroj;//临div的位置
			
			if(Math.abs(eMoveLeft)>Math.abs(eMoveTop)){
				if(Math.abs(eMoveLeft)<49){
					iBroi = eMoveLeft>0?indexXY.ri+1:indexXY.ri-1;//看鼠标的位移正负判断是左右是哪个div互动
					iBroj = indexXY.rj;
					if(iBroi>=0&&iBroi<7){//防止边缘时取不到相邻元素
						oBro = findDiv(iBroi,iBroj);
						oDiv.style.left = indexXY.ri*49 + eMoveLeft + "px";
						oBro.style.left = iBroi*49 - eMoveLeft + "px";
					}
				}
			}else{
				if(Math.abs(eMoveTop)<49){
					iBroj = eMoveTop>0?indexXY.rj+1:indexXY.rj-1;
					iBroi = indexXY.ri;
					if(iBroj>=0&&iBroj<7){
						oBro = findDiv(iBroi,iBroj);
						
						oDiv.style.top = indexXY.rj*49 + eMoveTop + "px";
						oBro.style.top = iBroj*49 - eMoveTop + "px";
					}
				}
			}
		}
	};	
	oDiv.onmouseup=function (e){
        if(moveable)  {
            moveable = false; 
        }
		this.style.zIndex = "";
		if(oBro!=null)
		changeIndex(oDiv,oBro);
    };
	return oDiv;
}

function changeIndex(oIdiv,oLdiv){
	var indexIJ_I = findIndex(oIdiv);
	var indexIJ_L = findIndex(oLdiv);
	
	oIdiv.style.left = indexIJ_L.ri*49;//临div的原位置给原div
	oIdiv.style.top = indexIJ_L.rj*49;
	
	oLdiv.style.left = indexIJ_I.ri*49;//原div的原位置给临div
	oLdiv.style.top = indexIJ_I.rj*49;
		
	arrAll[indexIJ_L.ri][indexIJ_L.rj] = oIdiv;//更改数组位置
	arrAll[indexIJ_I.ri][indexIJ_I.rj] = oLdiv;
}

function findIndex(obj){
	for(var i=0;i<7;i++){
		for(var j=0;j<7;j++){
			if(obj===arrAll[i][j]){
				return {ri:i,rj:j};
			}
		}
	}
};

function findDiv(iArr,jArr){
	return arrAll[iArr][jArr];
}
window.onload = init;
</script>
</head>
<body style="background-color:grey;">
<div align="center">
	<div id="mainDiv"></div>
</div>
</body>
</html>
这时的代码已然是一个可拖动换位置的模型了,其中
border:1px solid red是为了没有本地图片时区分各个div,如果有图片可以去掉此句。
小有成就,然后就是加入逻辑关系,有消除机会才换位置,没有则不换。这里说的消除机会,无非就是比较互换后的两个div,新的位置是否有机会消除,注意这里是两个div,线看一个。

如上图中黄色和绿色交换位置后变为右图,先看黄色变位置后我们要比较他的横向和纵向是否有相同(实际比较的是背景图片this.style.backgroundImage),我们很容易按数组位置取到相邻甚至再相邻的元素,例如黄色左边,我们取到了白色背景图片,那再往左就没意义取了,左侧不会有机会消除了,再看右侧,如果右侧1个颜色相同,我们就要再去比较右侧第二个,如果还相同那就是3个了,满足机会。
这里我的想法是横向上做一个数组去存储相同的元素,纵向上同理,比如横向上,往左对比,如果第一个同色,存入数组,再比较左二,同色继续存入。然后是右侧,最终如果这个数组的大小大于2个,那就满足3个消除(包括本身),有人说没准是存的是左第二个右第二个呢,这里是按1到2去对比的,2能存进去的前提是1能存进去,所有1没存进去2颜色相同中间隔着1个不同色也不行。如果横向大于2,纵向也大于2,则数组取横纵数组的合并。因为我们只需要判断得到的数组是否大于等于2来判断是否移动,而返回这个数组,里面包括了相同可消除的元素,方面我们下一步对其进行删除用!
还是刚才那句话注意这里是两个div。等于是我们互换位置后,div1要比较横向,纵向两个数组,div2同样两个。写个函数,传入的参数为div对象,返回的是div数组如下:
function ifCanDelete(obj){//查看某个div的当前位置是否可消除,返回的是可消除的div数组
	var colorI = obj.style.backgroundImage;
	var ifx = findIndex(obj).ri;
	var ify = findIndex(obj).rj;

	var arrLeft = new Array();
	var arrTop = new Array();
	
	if(ifx-1>=0){//取左侧第一个
		var l1 = findColor(ifx-1,ify);
		if(ifx-2>=0&&l1==colorI){//如果左侧第一个相同再取第二个,当然不过能边界0
			arrLeft[arrLeft.length] = arrAll[ifx-1][ify];
			var l2 = findColor(ifx-2,ify);
			if(l1==l2)
				arrLeft[arrLeft.length] = arrAll[ifx-2][ify];
			}
	}
	
	if(ifx+1<7){//右侧同理
		var r1 = findColor(ifx+1,ify)
		if(ifx+2<7&&r1==colorI){
			arrLeft[arrLeft.length] = arrAll[ifx+1][ify];
			var r2 = findColor(ifx+2,ify);
			if(r1==r2)
				arrLeft[arrLeft.length] = arrAll[ifx+2][ify];
			}
	}
	
	if(ify-1>=0){//上侧
		var t1 = findColor(ifx,ify-1);
		if(ify-2>=0&&t1==colorI){
			arrTop[arrTop.length] = arrAll[ifx][ify-1];
			var t2 = findColor(ifx,ify-2);
			if(t1==t2)
				arrTop[arrTop.length] = arrAll[ifx][ify-2];
			}
	}
	
	if(ify+1<7){//下侧
		var b1 = findColor(ifx,ify+1);
		if(ify+2<7&&b1==colorI){
			arrTop[arrTop.length] = arrAll[ifx][ify+1];
			var b2 = findColor(ifx,ify+2);
			if(b1==b2)
				arrTop[arrTop.length] = arrAll[ifx][ify+2];
			}
	}
	
	if(arrLeft.length>1&&arrTop.length>1){
		for(var k=0;k<arrTop.length;k++){
			arrLeft[arrLeft.length] = arrTop[k];
		}
		arrLeft[arrLeft.length] = obj;//同时别忘了加上原来的div
		return arrLeft;
	}else if(arrLeft.length>1||arrTop.length>1){
		if(arrLeft.length>1){
				arrLeft[arrLeft.length] = obj;
				return arrLeft;
			}else{
				arrTop[arrTop.length] = obj;
				return arrTop;
			}
	}else{
		return [];
	}
}

function findColor(ix,iy){//按位置得到背景色
	return arrAll[ix][iy].style.backgroundImage;
}
相应用到这个函数在交互位置的函数里,如下
function changeIndex(oIdiv,oLdiv){
	var indexIJ_I = findIndex(oIdiv);
	var indexIJ_L = findIndex(oLdiv);
		
	arrAll[indexIJ_L.ri][indexIJ_L.rj] = oIdiv;//更改数组位置
	arrAll[indexIJ_I.ri][indexIJ_I.rj] = oLdiv;
	
	var arryI = ifCanDelete(oIdiv);
	var arryL = ifCanDelete(oLdiv);
	
	if(arryI.length==0&&arryL.length==0){//如果没有消除机会返回位置
		oIdiv.style.left = indexIJ_I.ri*49;//回原位置
		oIdiv.style.top = indexIJ_I.rj*49;
	
		oLdiv.style.left = indexIJ_L.ri*49;//v
		oLdiv.style.top = indexIJ_L.rj*49;
		
		arrAll[indexIJ_L.ri][indexIJ_L.rj] = oLdiv;
		arrAll[indexIJ_I.ri][indexIJ_I.rj] = oIdiv;
	}else{
		oIdiv.style.left = indexIJ_L.ri*49;//临div的原位置给原div
		oIdiv.style.top = indexIJ_L.rj*49;
	
		oLdiv.style.left = indexIJ_I.ri*49;//原div的原位置给临div
		oLdiv.style.top = indexIJ_I.rj*49;
	}
	
	oIdiv.style.backgroundImage;
}
问题解决了,然后是消除了,这里已经有了返回的div数组,就去删除这些div就行了,同时数组里也要删除掉,加在changeIndex后面
else{
		oIdiv.style.left = indexIJ_L.ri*49;//临div的原位置给原div
		oIdiv.style.top = indexIJ_L.rj*49;
	
		oLdiv.style.left = indexIJ_I.ri*49;//原div的原位置给临div
		oLdiv.style.top = indexIJ_I.rj*49;
		
		
		for(var m=0;m<arryI.length;m++){
			var iDexy = findIndex(arryI[m]);
			arrAll[iDexy.ri].splice(iDexy.rj,1);//先剔除数组
			arryI[m].parentNode.removeChild(arryI[m]);//后删除div元素
		}
		
		for(var n=0;n<arryL.length;n++){
			var iDexy = findIndex(arryL[n]);
			arrAll[iDexy.ri].splice(iDexy.rj,1);
			arryL[n].parentNode.removeChild(arryL[n]);
		}
	}
这样就做到了移动消除,但消除了我们还要再补进来。空缺处上面的要补下来。这里想了想,其实还是数组立功了,我们移动消除完后看7个数组,哪个数组的元素个数小于7,就是哪个竖排有空缺,然后我们想让他们有重力感应,就按照当前元素在数组里的位置重新设定top值就行了,就是index * 49.然后我们再调用创建div的函数把数组补齐,貌似这次数组用的真的是用得非常值!再写个填充div函数:
function fillAllArr(){//补齐所有缺失数组
	for(var i=0;i<7;i++){
		if(arrAll[i].length<7){//是否缺失
			for(var k=0;k<arrAll[i].length;k++){//先对齐剩余的div
				arrAll[i][k].style.top = k*49 +"px";
			}
			while(arrAll[i].length<7){//再补齐div
				var j = arrAll[i].length;
				var oDiv = createDiv(i*49,j*49);
				$("mainDiv").appendChild(oDiv);
				arrAll[i][j] = oDiv;
			}
		}
	}
}
这样在消除后调用这个函数,就补充上了。后来实际运行时我发现我犯了一个巨大的错误!这里加载时每个数组里的顺序是0-6从上往下加载的,而按我们消除后上面的部到下面来的顺序是完全相反的,按数组里的顺序是里面有元素剔除,后面的会补上来,这样搞完全乱套了!从一开始的数组顺序就用错了,导致后面都跟着错了,崩溃中!
后来我改了半天想到一个巧妙的办法,我们把以top做坐标改为以bottom做坐标不就刚好反过来了嘛,有点太机智了,我用的开发工具是Notepad++;直接用ctrl+F将所有的top替换为bottom,再试运行游戏,正常了!谢天谢地。好的开始是成功的一半啊!同时测了测发现点小问题,改了bottom,相应的上下交换位置出的加号要改成减号,减号还要改成加号,这里自己去逻辑去吧。然后还测出个小bug,就是这里。在判断是否有消除机会的函数里,判断是否添加到数组有小问题,
if(ifx-1>=0){//取左侧第一个
		var l1 = findColor(ifx-1,ify);
		if(ifx-2>=0&&l1==colorI){//如果左侧第一个相同再取第二个,当然不过能边界0
			arrLeft[arrLeft.length] = arrAll[ifx-1][ify];
			var l2 = findColor(ifx-2,ify);
			if(l1==l2)
				arrLeft[arrLeft.length] = arrAll[ifx-2][ify];
			}
	}
应该这样写才行
	if(ifx-1>=0){//取左侧第一个
		var l1 = findColor(ifx-1,ify);
		if(l1==colorI){
			arrLeft[arrLeft.length] = arrAll[ifx-1][ify];
			if(ifx-2>=0){//如果左侧第一个相同再取第二个,当然不过能边界0
				var l2 = findColor(ifx-2,ify);
				if(l1==l2)
					arrLeft[arrLeft.length] = arrAll[ifx-2][ify];
			}
		}
	}
是分两部判断而不是一棒子打死,所以其它的右,下,上都要改。略
基本游戏可以玩儿了都,然后再加个进度条时间控制,分数累计。再在上下画两个div而已。body改为:
<body style="background-color:black;">
<div align="center">
	<div style = "width:343px;height:60px;background-color:black;">
		<span style="color:white;font-weight:bold;font-size:30;">得分:
			<span id="iMark">0</span>
		</span>
	</div>
	<div id="mainDiv"></div>
	<div style = "width:343px;height:35px;background-color:black;">
		<div style = "width:342;height:20px;background-color:green;margin-top:15px;">
			<span id="iTime">60'</span>
		</div>
	</div>
</div>
</body>
然后我们要有个预备开始,原版的手机游戏是现有开始游戏按钮,进入游戏后有个萌宠喊了一声ready go!就开始了。这里不打算弄多余的按钮,但就一个网页也不能一打开还没准备就开始游戏。所以我这里打算做个3秒的倒数准备,页面显示3,2,1,go!开始。这些倒数显示在一个半透明的div上,页面打开时加载此div,遮住后面的游戏不让点击,显示完3,2,1go后再删除这个div.代码如下:
function beforeBegin() {//显示3,2,1预备倒计时
	var oBeginDiv = document.createElement("div");
	oBeginDiv.className = "beforeBegin";
	$("pa").appendChild(oBeginDiv);
	oBeginDiv.innerHTML ="<span style='width: 343px;height: 438px;color: white;font-size: 100;font-weight: bold;line-height: 438px;'>Three!</span>";

	setTimeout(function(){
			oBeginDiv.innerHTML ="<span style='width: 343px;height: 438px;color: white;font-size: 100;font-weight: bold;line-height: 438px;'>Two!</span>";
			setTimeout(function(){
				oBeginDiv.innerHTML ="<span style='width: 343px;height: 438px;color: white;font-size: 100;font-weight: bold;line-height: 438px;'>One!</span>";
					setTimeout(function(){
						oBeginDiv.innerHTML ="<span style='width: 343px;height: 438px;color: white;font-size: 100;font-weight: bold;line-height: 438px;'>Go!</span>";
							setTimeout(function(){$("pa").removeChild(oBeginDiv)},500);
				},1000);
			},1000);
		},1000);
}
head 处style添加个样式
.beforeBegin{width: 100%;height: 350px;z-index: 1000;position:absolute;top:60;left: 0;
			text-align: center;background-color: gray;
			filter:alpha(opacity=50);/*IE*/  
			opacity:0.5;/*Mozilla*/ };
效果如下:

将此函数加在init后面,没啥难度,页面加载模型完毕后加开始倒数了。然后是60秒的游戏时间控制。按上面预备实际是用了3.5秒,那再写个控制时间的函数3.5秒后执行
var iTimeE = 60;
function gameHandler(){//60秒倒计时
	if(iTimeE>=0){
		var iWidth = $("handleBar").offsetWidth;
		$("handleBar").style.width = iTimeE/60*90+"%";
		iTimeE-=1;
		$("iTime").innerHTML=iTimeE+"'";
		setTimeout("gameHandler()",1000);
	}
}
body变为
<body style="background-color:black;">
<div align="center" id="pa">
	<div style = "width:343px;height:60px;background-color:black;">
		<span style="color:white;font-weight:bold;font-size:30;">得分:
			<span id="iMark">0</span>
		</span>
	</div>
	<div id="mainDiv"></div>
	<div style = "width:324px;height:35px;background-color:black;margin-top:15px;" id="handler" align="left">
		<div style = "width:90%;height:20px;background-color:green;float:left;"id="handleBar"></div>
		<span id="iTime" style = "height:20px;color:white;">60'</span>
	</div>
</div>
</body>
很显然这个倒计时函数是通过对剩余时间与60秒总时间做百分比,然后赋值给原本的进度条长度(90%)做百分比。这里要好好考虑下,没用100%因为60‘时间显示在进度条后面。然后实际运行起来进度条很生硬的。就分60次去变化,每次减少的长度段较多,这样用户体验就不好,我们又没用其他的一些js类库插件啥的,只能增加变化次数,这样每次减少的长度会相应减少。有渐变的效果。
var iTimeE = 60;
function gameHandler(){//60秒倒计时
	if(iTimeE>=0){
		var iWidth = $("handleBar").offsetWidth;
		$("handleBar").style.width = iTimeE/60*90+"%";
		iTimeE-=0.05;
		$("iTime").innerHTML=Math.ceil(iTimeE)+"'";
		setTimeout("gameHandler()",50);
	}
}
继续顺着游戏进程写下去,倒计时0时再弹出个div遮罩层显示得分
这时只是一个积分模型,并没有真正的累计分。我们要在每次消除时对得分进行累积,这时我发现个问题,就是游戏开始加载时随机分配颜色,游戏开始前可能就有可消除的元素了,游戏结束时也是,还有游戏中移动消除后也可能添加新div进来后可连锁消除的。除了第一种情况消除不计分,后面两种都计。又是3个问题,其实都可以化为一个函数,就是遍历列阵是否有可消除。然后消除掉可消除的。然后来个boolean值去区分计分,还是不计分。
其实就是遍历每个div看是否可删除。这时我觉得应该把删除div单独到一个函数去,这样后期有心情添加个消除的特效可以直接改函数就行了。而我们一般都是按一个数组是删除div,所以函数提取如下:
var bMark = false;//是否可累积分
function deleteDivs(arr){//按数组删除div
	for(var n=0;n<arr.length;n++){
		var iDexy = findIndex(arr[n]);
		if(typeof(iDexy)!="undefined"){
			arrAll[iDexy.ri].splice(iDexy.rj,1);
			arr[n].parentNode.removeChild(arr[n]);
			if(bMark){
				iScore+=10;//加10分
				$("iMark").innerHTML = iScore+"";
			}
		}
	}
}
然后是
function initCanDelete(){//游戏开始前,结束后,和移动消除后新加载。可组合消除的先消除掉通用
	var bInit = false;//是否还有可消除的
	for(var i=0;i<7;i++){
		for(var j=0;j<7;j++){
			var deDivs = ifCanDelete(arrAll[i][j]);
			if(deDivs.length>0){
				deleteDivs(deDivs);
				fillAllArr();//删完后填满
				bInit = true;
			}
		}
	}

	if(bInit){//为true则证明刚才消除过。
		initCanDelete();//刚才消除过则有位置变动还得再遍历一次
	}else{
		iMark = true;
	}
}
将此函数放到init()里。
这时突然发现经过了几个晚上的开发后游戏已经基本成型可以玩儿了!乱七八糟的也写了个长篇大论。最初的需求没有写super模式。这个其实非常简单,在换位消除时做累计就行了,累计过5次就进入super模式,super模式下计分翻倍。然后重新归0重新累计5次。就不费神去写了。跟原版相比还缺少道具。这个其实思路也有,就是在换位消除时判断返回的可消除数组个数来分配道具就行了。最终还是消除,补充。。真的累了,不写了。然后还有个消除时的特效。原版是消除时闪现一个心然后消失。这个其实很好做。就是改deleteDivs的代码,在删除某个div前先添加个遮罩层(有个心形的图片)。遮住,再消失,再遮住,再删除整个div。没什么难度。然后最难的是补充div那,简直就是瞬移,完全没有滑下来的效果,用户体验太差。得改改,还得用延迟。这里也不想多写了,留着以后有空再改吧,就这样吧
试玩了一把还蛮有意思,得分如下:



声明:本游戏仅供学习交流,请勿用作它用,发生任何纠纷与本人无关!
另本人乐于分享,但请尊重他人几个晚上的劳动成果,转载请注明出处http://blog.csdn.net/uucai
另附由于游戏包含图片,本人已将源代码和图片打包分享至csdn资源频道,可0积分下载交流。地址在此
 
不知道为什么csdn删除了我的资源?
云盘地址:http://pan.baidu.com/share/link?shareid=457816773&uk=2450514224
 
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics