2048小游戏的制作和算法思路讲解

2048的制作源码

点击此处进入游戏
大家可以点上面链接玩玩游戏,制作的主要难点在于对数字运算的逻辑.

  • 难点:
    1. 首先给游戏赋予状态值放在对象中,并添加一个方便用于判断的空数组属性,添加一个启动的函数
    2. 对于游戏开始时创建两个随机数以及后面的游戏过程中空位置产生的随机数
    3. 为有数字的格子添加该数字的样式
    4. 添加一个游戏结束的判断机制和一个游戏结束的遮罩弹框显示
    5. 写键盘事件
    6. 写按键时的触发机制



  • 1. 首先给游戏赋予状态值放在对象中,并添加一个方便用于判断的空数组属性,添加一个启动的函数,里面加一个数组全部值为0的二维数组,在后面每一次操作中都会改变这个数组中的值,并且每次开始时候重置数组。
	//难点1
	data : [],
	gamerunning : 1,
	gameover : 0,
	start : function(){
		this.data = [];
		this.gameover = 0;
		document.getElementById('gamelast').style.display = "none";
		 for (var x = 0; x < 4; x++) {
			 this.data[x] = []
			for (var y = 0; y < 4; y++) {
				this.data[x][y] = 0;
			}
		 }
		 this.color();
		this.random_1();this.random_1();
		this.color();
	},
  • 2. 对于游戏开始时创建两个随机数以及后面的游戏过程中空位置产生的随机数:对于产生随机数就要运用到Math.random()用循环和break语句在调用这个函数的时候一定给空的格子添加数字2或者数字4。
//难点2
random_1 : function(){
		for(;;){	
			 var x =parseInt(Math.random()*4);
			 var y =parseInt(Math.random()*4);
			 if ( this.data[x][y]== 0) {
				this.data[x][y]= Math.random()>0.3 ? 2 : 4;
				break;
			 }
		 }
	},
  • 3. 为有数字的格子添加该数字的样式:先在css中写好每个数字对应的样式,当调用这个改样式的函数时,遍历那个用于判断的二维数组,如果有相应的数字便修改对应位置的className,如果为0的时候就重置该位置的样式,(同样我这里直接也是改积分,我的积分制是该页面数字相加的总和为分数)
//难点3
color : function(){
		var scort = 0;
		for (var x = 0; x < 4; x++) {
			for (var y = 0; y < 4; y++) {
				if(this.data[x][y] != 0){
					document.getElementById("div_"+ x + y).className ="only n" + this.data[x][y];
					document.getElementById("div_"+ x + y).innerHTML = this.data[x][y];
					scort += this.data[x][y];
				}else{
					document.getElementById("div_"+ x + y).className ="only";		
					document.getElementById("div_"+ x + y).innerHTML = '';			
				}
			}
		}
		document.getElementById("socrt_1").innerHTML = scort;
		document.getElementById("socrt_2").innerHTML = scort;
	},
  • 4. 添加一个游戏结束的判断机制和一个游戏结束的遮罩弹框显示:如何游戏会结束呢,当然是首先是所有位置都有数字,其次相邻的数字不相等,如果都满足就返回true,为后续是否结束做判断,当结束时让那个遮罩和弹框block。
//难点4
tryagain : function(){
		if(this.gameover == 1){
			document.getElementById('gamelast').style.display = "block";
		}
	},
	isgameover : function(){
		for (var x = 0; x < 4; x++) {
			for (var y = 0; y < 4; y++) {
				if (this.data[x][y] == 0) {return false;}
				if(y<3){
					//右边的数字相等
					if(this.data[x][y] == this.data[x][y+1]){return false;}
				}
				if(x<3){
					//下面的数字相等
					if(this.data[x][y] == this.data[x+1][y]){return false;}
				}
			}
		} return true;		
	},
  • 5. 写键盘事件:通过操作上下左右键来控制游戏,4个键的keyCode分别为38、40、37、39。
document.onkeydown = function(event){
	if(g2048.gamerunning != g2048.gameover){
		var event = event || e || arguments[0];
		if(event.keyCode == 37){
			console.log('←');
			g2048.moveleft();
		}else if(event.keyCode == 38){
			console.log('↑');
			g2048.moveup();
		}else if(event.keyCode == 39){
			console.log('→');
			g2048.moveright();
		}else if(event.keyCode == 40){
			console.log('↓');
			g2048.movedown();
		}
	}
}

最难部分

  • 6. 写按键时的触发机制:(先对变化之前的数组利用String拍照),这里只写左键的思路其他的键可以根据理解修改,当按下左键的时候所有数字往左,有相加的相加,没有的依次顶到左边,没有数字就不变。首先需要遍历对每个数字作判断,第一个函数为所有层,第二个函数为每层每个,第三个函数为当前数字后续是否有非0的数字。第三层若后续没有数字就返回-1当前层相当于可以跳出循环,若后续有数字便返回数字的下标,回到第二个函数,如果返回的是-1就跳出当前一层循环,如果返回的是数字小标便拿循环数字和返回的数字做对比,如果相等便将循环数字值乘2而返回位置的数字改为0,如果当前循环数字为0,那么将循环数字改为返回下标的数字,循环下标的数字改为0,(并且要利用y- -再进行一次当前的循环,因为改变的循环数字可能会和后面的数字为相同数字而造成相加),最后回到第一个函数,所有的遍历结束,(再对变化之后的数组利用String拍照),如果拍照不一样,需要重新赋样式产生随机数, 并且之后进行是否游戏结束进行判断。按键事件触发结束。
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

moveleft : function(){
		var before = String(this.data);
		for (var x = 0; x < 4; x++) {
			this.moveleft_row(x);
		}
		var after = String(this.data);
		if(after != before){
			this.random_1();
			this.color();
			if(this.isgameover() == true){
				this.gameover = 1;
				this.tryagain();
			}
		}
	},
	moveleft_row : function(x){
		for (var y = 0; y < 3; y++) {
		var ml_result = this.moveleft_two(x,y)
			if(ml_result != -1){
				if(this.data[x][y] == 0){
					this.data[x][y] = this.data[x][ml_result];
					this.data[x][ml_result] = 0;
					y--;
				}else if(this.data[x][y] == this.data[x][ml_result]){
					this.data[x][y] *= 2;
					this.data[x][ml_result] = 0;				
				}
			}else{
				break;
			}
		}
	},
	moveleft_two : function(x,y){
		for (var i = y+1;i < 4;i++){
			if(this.data[x][i] != 0){
				return i;				
			}
		}return -1;
	},

最后稍加完善(如需要给游戏添加触摸事件可以参看上面的代码),制作完成! 如有不懂可以点上面的链接进行游戏理解!


源码:

html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>2048</title>
	<link rel="stylesheet" type="text/css" href="reset.css">
	<link rel="stylesheet" type="text/css" href="2048.css">
</head>
<body>
	<div class="main">
		<div class="m_head">
			SCORE:
			<span id="socrt_1"></span>
			<a href="#" class="out">x</a>
			<a href="javascript:g2048.start()" class="ftry">try again!</a>
		</div>
		<div class="m_main">
			<div class="only" id="div_00"></div>
			<div class="only" id="div_01"></div>
			<div class="only" id="div_02"></div>
			<div class="only" id="div_03"></div>
			<div class="only" id="div_10"></div>
			<div class="only" id="div_11"></div>
			<div class="only" id="div_12"></div>
			<div class="only" id="div_13"></div>
			<div class="only" id="div_20"></div>
			<div class="only" id="div_21"></div>
			<div class="only" id="div_22"></div>
			<div class="only" id="div_23"></div>
			<div class="only" id="div_30"></div>
			<div class="only" id="div_31"></div>
			<div class="only" id="div_32"></div>
			<div class="only" id="div_33"></div>
		</div>
	</div>	
	<div class="last" id = "gamelast">
		<p>
			 GAME OVER!!!<a href="#" class="out2">x</a><br />
			SCORE:<span id="socrt_2"></span><br />
			<a href="javascript:g2048.start()">try again!</a>						
		</p>
	</div>
	<script type="text/javascript" src="2048.js"></script>		
</body>
</html>

css

/*样式初始化*/
html, body, div, span, object, iframe,h1, h2, 
h3, h4, h5, h6, p, blockquote, pre,abbr, address, cite, code,del, dfn, 
em, img, ins,kbd, q, samp,small, strong, sub, sup, var,b, i,dl, dt, dd, 
ol, ul, li,fieldset, form, label, legend,table, caption, tbody, 
tfoot,thead,tr, th, td,article, aside, canvas, details, figcaption, 
figure, footer, header, hgroup, menu, nav, section, summary,time, mark, 
audio, video,a,img,input{
	font-family: Arial;
	margin: 0;
	padding: 0;
	box-sizing: border-box;    /*让盒子不会被padding及border撑大*/
}

ul{
	list-style: none;
}
a{
	text-decoration: none;
}
input,textarea{
	outline: none;
}
img,input,textarea{
	display: block;
}
img{
	border: none;    /* IE7以下的img标签会有边框  */
}
/*修改input中提示文字的颜色*/
input::-webkit-input-placeholder{
    color:#ccc;
}
input::-moz-placeholder{   /* Mozilla Firefox 19+ */
    color:red;
}
input:-ms-input-placeholder{  /* Internet Explorer 10-11 */ 
    color:red;
}
input:-moz-placeholder{    /* Mozilla Firefox 4 to 18 */
    color:red;
}

body{
	background-color:#232F3E ;
}
.main{
	width: 450px;
	height: 600px;
	margin:9% auto;	
}
.m_head{
	width: 450px;
	height: 70px;
	font-size: 40px;
	line-height: 70px;
	color: #0088FF;
}
#socrt_2,#socrt_1{
	color: red;
}
.m_main{
	width: 450px;
	height: 450px;
	background-color: #BBADA0;
	border-radius: 8px;
}
.only{
	width: 100px;
	height: 100px;
	background-color: #CDC1B4;
	float: left;
	margin-left: 10px;
	margin-top: 10px;
	border-radius: 6px;
	text-align: center;
	line-height: 100px;
	font-size: 60px;
}
.last{
	display: none;
	position: absolute;
	left: 0px;
	right: 0px;
	top: 0px;
	bottom: 0px;
	background-color: rgba(250,248,239,0.4);
}
.last p{
	width: 350px;
	height: 250px;
	margin-left: 41%;
	margin-top: 20% ;
	background-color: white;
	text-align: center;
	line-height: 80px;
	font-size: 35px;
	font-weight:bold;
	border-radius: 10px;
}

.last p a,.ftry{
	margin-top: 20px;
	display: inline-block;
	width: 200px;
	height: 50px;
	background-color:#9F8D77;
	color: black;
	line-height: 50px;
	border-radius: 6px;
}
.ftry{
	float: right;
	font-size: 25px;
	font-weight: 200;
	width: 120px;
	height: 35px;
	line-height: 35px;
	text-align: center;
	margin-top: 17px;
	margin-right: 20px;
}
.out{
	float: right;
	width: 35px;
	height: 35px;
	background-color:#9F8D77; 
	color: white;
	text-align: center;
	line-height: 32px;
	font-size: 35px;
	border-radius: 50%;
	margin-top: 17px;
}
.last p .out2{
	float: right;
	width: 35px;
	height: 35px;
	color: white;
	text-align: center;
	line-height: 32px;
	font-size: 35px;
	border-radius: 50%;
	margin-top: 10px;
	margin-right: 5px;
}
.last p a:hover,.ftry:hover,.out:hover{
	background-color:#BCA283;
}

.n2{
	background-color: #EEE3DA;
	color:#776E65;
}
.n4{
	background-color: #ede0c8;
	color:#776E65;
}
.n8{
	background-color: #f2b179;
	color: #fff;
}
.n16{
	background-color: #f59563;
	color: #fff;
}
.n32{
	background-color: #f67c5f;
	color: #fff;
}
.n64{
	background-color: #f65e3b;
	color: #fff;
}
.n128{
	background-color: #edcf72;
	color: #fff;
}
.n256{
	background-color:#edcc61;
	color: #fff;
}
.n512{
	background-color: #9c0;
	color: #fff;
}
.n1024{
	background-color: #33b5e5;
	color: #fff;
	font-size:40px;
}
.n2048{
	background-color: #09c;
	color: #fff;
	font-size:40px;
}
.n4092{
	background-color: #a6c;
	color: #fff;
	font-size:40px;
}
.n8192{
	background-color: #93c;
	color: #fff;
	font-size:40px;
}

javascript

var g2048 = {
	data : [],
	gamerunning : 1,
	gameover : 0,
	 start : function(){
		this.data = [];
		this.gameover = 0;
		document.getElementById('gamelast').style.display = "none";
		 for (var x = 0; x < 4; x++) {
			 this.data[x] = []
			for (var y = 0; y < 4; y++) {
				this.data[x][y] = 0;
			}
		 }
		this.color();
		this.random_1();this.random_1();
		this.color();
	},
	 random_1 : function(){
		for(;;){	
			 var x =parseInt(Math.random()*4);
			 var y =parseInt(Math.random()*4);
			 if ( this.data[x][y]== 0) {
				this.data[x][y]= Math.random()>0.3 ? 2 : 4;
				break;
			 }
		 }
	},
	color : function(){
		var scort = 0;
		for (var x = 0; x < 4; x++) {
			for (var y = 0; y < 4; y++) {
				if(this.data[x][y] != 0){
					document.getElementById("div_"+ x + y).className ="only n" + this.data[x][y];
					document.getElementById("div_"+ x + y).innerHTML = this.data[x][y];
					scort += this.data[x][y];
				}else{
					document.getElementById("div_"+ x + y).className ="only";		
					document.getElementById("div_"+ x + y).innerHTML = '';			
				}
			}
		}
		document.getElementById("socrt_1").innerHTML = scort;
		document.getElementById("socrt_2").innerHTML = scort;
	},
	tryagain : function(){
		if(this.gameover == 1){
			document.getElementById('gamelast').style.display = "block";
		}
	},
	isgameover : function(){
		for (var x = 0; x < 4; x++) {
			for (var y = 0; y < 4; y++) {
				if (this.data[x][y] == 0) {return false;}
				if(y<3){
					if(this.data[x][y] == this.data[x][y+1]){return false;}
				}
				if(x<3){
					if(this.data[x][y] == this.data[x+1][y]){return false;}
				}
			}
		} return true;		
	},
	moveleft : function(){
		var before = String(this.data);
		for (var x = 0; x < 4; x++) {
			this.moveleft_row(x);
		}
		var after = String(this.data);
		if(after != before){
			this.random_1();
			this.color();
			if(this.isgameover() == true){
				this.gameover = 1;
				this.tryagain();
			}
		}
	},
	moveleft_row : function(x){
		for (var y = 0; y < 3; y++) {
		var ml_result = this.moveleft_two(x,y)
			if(ml_result != -1){
				if(this.data[x][y] == 0){
					this.data[x][y] = this.data[x][ml_result];
					this.data[x][ml_result] = 0;
					y--;
				}else if(this.data[x][y] == this.data[x][ml_result]){
					this.data[x][y] *= 2;
					this.data[x][ml_result] = 0;				
				}
			}else{
				break;
			}
		}
	},
	moveleft_two : function(x,y){
		for (var i = y+1;i < 4;i++){
			if(this.data[x][i] != 0){
				return i;				
			}
		}return -1;
	},
	
	
	moveright : function(){
		var before = String(this.data);
		for (var x = 0; x < 4; x++) {
			this.moveright_row(x);
		}
		var after = String(this.data);
		if(after != before){
			this.random_1();
			this.color();
			if(this.isgameover() == true){
				this.gameover = 1;
				this.tryagain();
			}
		}
	},
	moveright_row : function(x){
		for (var y = 3; y > 0; y--) {
		var ml_result = this.moveright_two(x,y);
			if(ml_result != -1){
				if(this.data[x][y] == 0){
					this.data[x][y] = this.data[x][ml_result];
					this.data[x][ml_result] = 0;	
					y++;
				}else if(this.data[x][y] == this.data[x][ml_result]){
					this.data[x][y] *= 2;
					this.data[x][ml_result] = 0;					
				}
			}else{
				break;
			}
		}
	},
	moveright_two : function(x,y){
		for (var i = y-1;i >= 0;i--){
			if(this.data[x][i] != 0){
				return i;
			}
		}return -1;
	},
	
	movedown : function(){
		var before = String(this.data);
		for (var x = 0; x < 4; x++) {
			this.movedown_row(x);
		}
		var after = String(this.data);
		if(after != before){
			this.random_1();
			this.color();
			if(this.isgameover() == true){
				this.gameover = 1;
				this.tryagain();
			}
		}
	},
	movedown_row : function(x){
		for (var y = 3; y > 0; y--) {
		var ml_result = this.movedown_two(x,y);
			if(ml_result != -1){
				if(this.data[y][x] == 0){
					this.data[y][x] = this.data[ml_result][x];
					this.data[ml_result][x] = 0;
					y++;
				}else if(this.data[y][x] == this.data[ml_result][x]){
					this.data[y][x] *= 2;
					this.data[ml_result][x] = 0;
				}
			}else{
				break;
			}
		}
	},
	movedown_two : function(x,y){
		for (var i = y-1;i >= 0;i--){
			if(this.data[i][x] != 0){
				return i;
			}
		}return -1;
	},
	moveup : function(){
		var before = String(this.data);
		for (var x = 0; x < 4; x++) {
			this.moveup_row(x);
		}
		var after = String(this.data);
		if(after != before){
			this.random_1();
			this.color();
			if(this.isgameover() == true){
				this.gameover = 1;
				this.tryagain();
			}
		}
	},
	moveup_row : function(x){
		for (var y = 0; y < 3; y++) {
		var ml_result = this.moveup_two(x,y)
			if(ml_result != -1){
				if(this.data[y][x] == 0){
					this.data[y][x] = this.data[ml_result][x];
					this.data[ml_result][x] = 0;
					y--;
				}else if(this.data[y][x] == this.data[ml_result][x]){
					this.data[y][x] *= 2;
					this.data[ml_result][x] = 0;				
				}
			}else{
				break;
			}
		}
	},
	moveup_two : function(x,y){
		for (var i = y+1;i < 4;i++){
			if(this.data[i][x] != 0){
				return i;				
			}
		}return -1;
	},
}
var startX,startY,endX,endY;
// var event = event || e || arguments[0];
document.addEventListener("touchstart",function(event){	
	startX = event.touches[0].pageX;
	startY = event.touches[0].pageY;
})
document.addEventListener("touchend",function(event){
	endX = event.changedTouches[0].pageX;
	endY = event.changedTouches[0].pageY;
	var x = endX - startX;
	var y = endY - startY;
	var result = Math.abs(x) > Math.abs(y);
	if(result == true && x < 0 ){
		g2048.moveleft();
	}else if(result == true && x > 0 ){
		g2048.moveright();
	}else if(result == false && y > 0 ){
		g2048.movedown();
	}else if(result == false && y < 0 ){
		g2048.moveup();
	}
})

document.onkeydown = function(event){
	if(g2048.gamerunning != g2048.gameover){
		var event = event || e || arguments[0];
		if(event.keyCode == 37){
			console.log('←');
			g2048.moveleft();
		}else if(event.keyCode == 38){
			console.log('↑');
			g2048.moveup();
		}else if(event.keyCode == 39){
			console.log('→');
			g2048.moveright();
		}else if(event.keyCode == 40){
			console.log('↓');
			g2048.movedown();
		}
	}
}
g2048.start();

~~~~~~~~ end ~~~~~~~~~~

发布了23 篇原创文章 · 获赞 49 · 访问量 1484

猜你喜欢

转载自blog.csdn.net/weixin_44701229/article/details/103190052