欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于JS的2048小游戏详解

程序员文章站 2024-03-18 22:51:04
...

本来是想练习刚学的jQuery来写的,但是好像并不会用,所以打算先用javaScript写好,再用jQuery来改。因为之前在实验楼参考着做了一个拼图小游戏,所以这个想试试看能不能自己做。现在网上随便找了个2048的小游戏了解了网页版2048的玩法,然后开始构思怎么实现。

【游戏背景】

游戏背景是一个4*4的方格图,在整个游戏过程中没有任何变化,所以我直接用PS画了一个当作背景图。

基于JS的2048小游戏详解

【随机产生的方块】

<需求分析>:每次移动合并后会在空格子里随机产生一个新方块,且新方块以一定比例出现2或4两个数字,2的概率明显高于4。

<逐步实现>

1)产生方块:方块随机产生在背景图对应的16个格子里,所以定义一个数组确定好16个格子的坐标,方便对新产生的方块进行定位。

var distribute = [
	[10,10],[120,10],[230,10],[340,10],
	[10,120],[120,120],[230,120],[340,120],
	[10,230],[120,230],[230,230],[340,230],
	[10,340],[120,340],[230,340],[340,340]
];

提前在CSS里写好方块的样式,这样用javaScript直接产生一个新的div元素,为其添加对应的类即可显示出想要的方块样式,我给新方块赋予d0、d1等类名,d用来标识这是一个方块,后面的0、1等代表其坐标定位,即从左到右从上到下的16个方格中的第一个和第二个。

[class *= d ]{                            //类名以d开头的元素都会应该这个样式
	height: 100px;
	width: 100px;
	border: none;
	border-radius: 5px;
	background-color: #39b372;
	color: white;
	font:40px/100px "微软雅黑"; 
	text-align: center;
	vertical-align: middle;
	position: absolute;
}

然后根据刚才命名类中的坐标定位索引distribute这个定位数组,提取其中方块的坐标信息,然后用left和top进行定位,一个方块就产生了。

function create(pos,x){
	var classname = 'd'+pos ;
	var newNode = document.createElement("div") ;
	newNode.innerHTML = x ;
	newNode.setAttribute("class",classname);
	var parent = document.getElementsByClassName("container")[0];
	parent.appendChild(newNode);
	newNode.style.left = distribute[pos][0] +'px'
	newNode.style.top = distribute[pos][1] +'px';
	flage[pos]=1;
}

2)方块内数字:我想让2和4两个数字以7:3的概率出现,所以产生0—9之间的随机数,该随机数小于3就返回4,大于等于3就返回2。

function random(){
	var num = Math.floor(Math.random()*10);
	if(num<3) return 4;
	else return 2;
}

然后用innerHTML将其写入新产生的方块内,也就是create函数的参数x。

3)每次产生的新方块必须在空格子里,所以设置一个标识数组flage,用来标识16个格子中哪些格子有方块哪些没有(有即为1无即为0),每次产生新方块时对这个标识数组进行遍历,将有方块的索引位置从0-15这16个可选位子从去掉,遍历结束后根据剩下的可选位置的数量产生一个随机数(比如剩下是6个空格子的位置,就产生一个0—5的随机数),以该随机数索引刚才的可选位置数组,即可在空格子里产生一个新方块。

var flage=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
function createRandom(k){
	var posIndexArray = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
	for (var i = 0; i < flage.length; i++) {
		if(flage[i]==1){
			var index = posIndexArray.indexOf(i);
			posIndexArray.splice(index,1);
		}
	}
	if(posIndexArray.length==0) return 0;
	var x = Math.floor(Math.random()*posIndexArray.length);
	var posIndex = posIndexArray[x] ;
	if(k) create(position[posIndex],k);
	else create(position[posIndex],random());
	return 1;
}

【方块的上下左右移动及合并】

<需求分析>:根据键盘的上下左右键实现已有方格的上下左右移动及相同内容元素的合并。

<逐步实现>

1)左右移动:设置step变量记录每个方块移动的步数,向左移动就从前向后索引flage数组,若flage数组该位置为0(即没有方块),步数加1,若flage数组该位置为1(即有方块),则获取该方块修改其left,让其左移step个步长,循环逢4清空一次step。右移原理与其相同,只不过是从后向前索引flage数组。

2)上下移动:与左右移动原理相同,改成索引转置后的flage数组,修改元素的top值。

3)设置sign变量,初始值为0,检索到flage为1即对sign加1并存储该元素,sign为2时进行判断存储的两个元素的值是否相等,若相等则进行合并(把一个值翻倍并移出另一个元素),然后把sign值重置为1step值加1,若不相等把sign值重置为0,sign值也是逢4清空。

case 37: {  //左移及合并操作
step=0;stepsum=0;
for (var i = 0 ; i < flage.length; i++) {
	if(i%4==0) {
		step=0;
		sign=0;
	}
	if( flage[i] == 0 ) step++;
	else{
		stepsum+=step;
		sign++;
		console.log('left'+" "+'d'+position[i][0]+position[i][1]);
		var d = document.getElementsByClassName('d'+position[i][0]+position[i][1])[0];
		var left = parseInt(window.getComputedStyle(d,null).left);
		d.style.left = left - 110*step+'px' ;
		d.setAttribute("class",'d'+position[i][0]+(position[i][1]-step));
		d0class=d1class;
		d1class='d'+position[i][0]+(position[i][1]-step);
		flage[i]=0;
		flage[i-step]=1;
	}
	if( sign==2 ){
		var parent = document.getElementsByClassName("container")[0];
		var d0 = document.getElementsByClassName(d0class)[0];
		var d1 = document.getElementsByClassName(d1class)[0];
		var d0text = d0.lastChild.nodeValue;
		var d1text = d1.lastChild.nodeValue;
		if( d0text == d1text ){
		    d0text *=2;
			d0.innerHTML = d0text ;
			score += d0text; 
			parent.removeChild(d1);
			var newi = (d1class.substr(1,1)-1)*4+(d1class.substr(2)-1);
			flage[newi]=0;
			step++;
			stepsum++;
			sign = 0;
		}else{
			sign = 1;
		}
	}
	}
break;
}


嗯,先写这么多,功能是实现了,但是第一次写代码乱七八糟的,等用jQuery改好了再放完整代码。(ps:我第一次实现的时候类都是用d11,d12这样命名类名的,所以左移那段代码可能优点乱)


[class *= d ]{                            //类名以d开头的元素都会应该这个样式
	height: 100px;
	width: 100px;
	border: none;
	border-radius: 5px;
	background-color: #39b372;
	color: white;
	font:40px/100px "微软雅黑"; 
	text-align: center;
	vertical-align: middle;
	position: absolute;
}

然后根据刚才命名类中的坐标定位索引distribute这个定位数组,提取其中方块的坐标信息,然后用left和top进行定位,一个方块就产生了。

function create(pos,x){
	var classname = 'd'+pos ;
	var newNode = document.createElement("div") ;
	newNode.innerHTML = x ;
	newNode.setAttribute("class",classname);
	var parent = document.getElementsByClassName("container")[0];
	parent.appendChild(newNode);
	newNode.style.left = distribute[pos][0] +'px'
	newNode.style.top = distribute[pos][1] +'px';
	flage[pos]=1;
}

2)方块内数字:我想让2和4两个数字以7:3的概率出现,所以产生0—9之间的随机数,该随机数小于3就返回4,大于等于3就返回2。

function random(){
	var num = Math.floor(Math.random()*10);
	if(num<3) return 4;
	else return 2;
}

然后用innerHTML将其写入新产生的方块内,也就是create函数的参数x。

3)每次产生的新方块必须在空格子里,所以设置一个标识数组flage,用来标识16个格子中哪些格子有方块哪些没有(有即为1无即为0),每次产生新方块时对这个标识数组进行遍历,将有方块的索引位置从0-15这16个可选位子从去掉,遍历结束后根据剩下的可选位置的数量产生一个随机数(比如剩下是6个空格子的位置,就产生一个0—5的随机数),以该随机数索引刚才的可选位置数组,即可在空格子里产生一个新方块。

var flage=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
function createRandom(k){
	var posIndexArray = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
	for (var i = 0; i < flage.length; i++) {
		if(flage[i]==1){
			var index = posIndexArray.indexOf(i);
			posIndexArray.splice(index,1);
		}
	}
	if(posIndexArray.length==0) return 0;
	var x = Math.floor(Math.random()*posIndexArray.length);
	var posIndex = posIndexArray[x] ;
	if(k) create(position[posIndex],k);
	else create(position[posIndex],random());
	return 1;
}

【方块的上下左右移动及合并】

<需求分析>:根据键盘的上下左右键实现已有方格的上下左右移动及相同内容元素的合并。

<逐步实现>

1)左右移动:设置step变量记录每个方块移动的步数,向左移动就从前向后索引flage数组,若flage数组该位置为0(即没有方块),步数加1,若flage数组该位置为1(即有方块),则获取该方块修改其left,让其左移step个步长,循环逢4清空一次step。右移原理与其相同,只不过是从后向前索引flage数组。

2)上下移动:与左右移动原理相同,改成索引转置后的flage数组,修改元素的top值。

3)设置sign变量,初始值为0,检索到flage为1即对sign加1并存储该元素,sign为2时进行判断存储的两个元素的值是否相等,若相等则进行合并(把一个值翻倍并移出另一个元素),然后把sign值重置为1step值加1,若不相等把sign值重置为0,sign值也是逢4清空。

case 37: {  //左移及合并操作
step=0;stepsum=0;
for (var i = 0 ; i < flage.length; i++) {
	if(i%4==0) {
		step=0;
		sign=0;
	}
	if( flage[i] == 0 ) step++;
	else{
		stepsum+=step;
		sign++;
		console.log('left'+" "+'d'+position[i][0]+position[i][1]);
		var d = document.getElementsByClassName('d'+position[i][0]+position[i][1])[0];
		var left = parseInt(window.getComputedStyle(d,null).left);
		d.style.left = left - 110*step+'px' ;
		d.setAttribute("class",'d'+position[i][0]+(position[i][1]-step));
		d0class=d1class;
		d1class='d'+position[i][0]+(position[i][1]-step);
		flage[i]=0;
		flage[i-step]=1;
	}
	if( sign==2 ){
		var parent = document.getElementsByClassName("container")[0];
		var d0 = document.getElementsByClassName(d0class)[0];
		var d1 = document.getElementsByClassName(d1class)[0];
		var d0text = d0.lastChild.nodeValue;
		var d1text = d1.lastChild.nodeValue;
		if( d0text == d1text ){
		    d0text *=2;
			d0.innerHTML = d0text ;
			score += d0text; 
			parent.removeChild(d1);
			var newi = (d1class.substr(1,1)-1)*4+(d1class.substr(2)-1);
			flage[newi]=0;
			step++;
			stepsum++;
			sign = 0;
		}else{
			sign = 1;
		}
	}
	}
break;
}


嗯,先写这么多,功能是实现了,但是第一次写代码乱七八糟的,等用jQuery改好了再放完整代码。(ps:我第一次实现的时候类都是用d11,d12这样命名类名的,所以左移那段代码可能优点乱)