用html、css、js实现2048小游戏

用html、css、js实现2048小游戏

最近在学习H5的相关知识,为了检验下学习效果,尝试着对小游戏2048进行了实现,本来还觉得学的已经不错了,但是真是做的时候真是应了那句话–书到用时方恨少,绝知此事要躬行!?
为了测试下对基础知识的掌握程度,没有使用现成的框架,如jQuery等,这个小游戏只用了基础的html、css和js。
实现该游戏的思路是,首先将静态页面画出来,我只做了最基础的4 x 4版本的。然后对单元格的左移、右移、上移和下移添加响应事件。如下图所示:



    
        
        
        2048
        
    
    
        

2048 Games

SCORE:0

总共16个单元格,每个单元格定义自己的id号,id号是唯一的。这16个单元格属于同一个class
建一个样式表,如my2048.css文件,里面设置如下:

body{
	margin:0;
	padding:20px;
	background-color:#5c5956;
}

header{
	width:100%;
	margin:0 auto;
	display:block;
	text-align:center;	
}
.title{
    font-family: Arial, Helvetica, sans-serif;
    font-size: 30px;
    font-weight: bold;
}

#game2048{
    width: 460px;
    height: 460px;
    padding: 20px;
    margin: 30px auto;
    background-color: #d1ae8c;
    border-radius: 10px;
    position: relative;
}
.grid-cell{
    width: 100px;
    height: 100px;
    border-radius: 12px;
    background-color: #ccc0b3;
	font-size:60px;
	text-align:center;
    position: absolute;
}
[id^="grid-cell-0"]{top:16px}
[id^="grid-cell-1"]{top:132px}
[id^="grid-cell-2"]{top:248px}
[id^="grid-cell-3"]{top:364px}
[id$="-0"]{left:16px}
[id$="-1"]{left:132px}
[id$="-2"]{left:248px}
[id$="-3"]{left:364px}

这样,打开上面的html文件,则可以看到2048小游戏的静态页面了。
对单元格的移动添加响应事件,在my2048.js中,如下图所示:

//keyCode 37 = Left keyCode 38 = Up keyCode 39 = Right keyCode 40 = Down
		document.onkeydown = function (e){
			if(this.state == this.RUNNING){
				switch (e.keyCode){
					case 37:
					this.moveLeft();
					break;
					case 38:
					this.moveUp();
					break;
					case 39:
					this.moveRight();
					break;
					case 40:
					this.moveDown();
					break;
				}
			}
		}.bind(this);

然后就是最关键的移动代码了。对于这16个单元格,有个初始化操作,将所有单元格中的值设置为0(注意单元格的值是0时不显示在页面上),比如对于左移,分两步,首先判断这一行中是否还有为0的单元格,若是有的话,需要将它右边的单元格往左移动一位,让这行单元格从左到右看,为0的单元格在最右边。如一行的值为
在这里插入图片描述
那么移动后
在这里插入图片描述
第二步,若是相邻单元格的值相等,则相加,左边的单元格的值是它俩的和,右边的单元格县赋值为0,然后对它右边的单元格进行判断,若是存在非0的单元格,则继续左移,直到该行中,若是存在为0的单元格,那么它一定是出现在最右边的单元格。代码如下:

moveLeft(){
		//按行判断,若是有为0的单元格,则表示可以往左移动
		//若是相邻的单元格的值有相等的,则相加
		var cellValue = null;
		//var cell_div = null;
		for(var row = 0; row < this.RN; row++){
			for(var col = 0; col 

其他方向的移动与此类似.
后记:这个只是实现了基本的功能,游戏结束的功能还有问题,各个方向移动的代码也还有优化的空间,后续有时间了,发现满足结束条件后,不能将页面刷新,重新来一局,因此有需要的话,这个代码可以参考,但是不能直接拿来用,需要做二次开发.
附:3个文件的源代码:
my2048.html




    
    
	
    2048
    



    

2048 Games

SCORE:0

TRY AGAIN!

GAME OVER!
SCORE:0 TRY AGAIN!

my2048.css

body{
	margin:0;
	padding:20px;
	background-color:#5c5956;
}

header{
	width:100%;
	margin:0 auto;
	display:block;
	text-align:center;	
}
.title{
    font-family: Arial, Helvetica, sans-serif;
    font-size: 30px;
    font-weight: bold;
}

#game2048{
    width: 460px;
    height: 460px;
    padding: 20px;
    margin: 30px auto;
    background-color: #d1ae8c;
    border-radius: 10px;
    position: relative;
}
.grid-cell{
    width: 100px;
    height: 100px;
    border-radius: 12px;
    background-color: #ccc0b3;
	font-size:60px;
	text-align:center;
    position: absolute;

.n2{background-color:#eee3da}
.n4{background-color:#ede0c8}
.n8{background-color:#f2b179}
.n16{background-color:#f59563}
.n32{background-color:#f67c5f}
.n64{background-color:#f65e3b}
.n128{background-color:#edcf72}
.n256{background-color:#edcc61}
.n512{background-color:#9c0}
.n1024{background-color:#33b5e5}
.n2048{background-color:#09c}
.n4096{background-color:#a6c}
.n8192{background-color:#93c}
.n2,.n4{color:#776e65}
.n1024,.n2048,.n4096,.n8192{font-size:40px}
.number-cell{
    border-radius: 4px;
    font-family: Arial, Helvetica, sans-serif;
    font-weight: bold;
    font-size: 60px;
    line-height: 100px;
    text-align: center;
    position: absolute;
}

my2048.js

var game = {
	data: null, RN:4, CN:4,//行数和列数都是4
	score:0,//保存得分
	state:1,//1为运行,2是结束
	RUNNING:1,//运行状态
	GAMEOVER:0,//表示游戏结束
	//对象自己的方法要使用自己的属性,必须this
	
	start(){
		this.init();//随机初始化两个单元格,数字为2或者4,其他单元格赋值0
		//处理键盘按下的事件
		//keyCode 37 = Left keyCode 38 = Up keyCode 39 = Right keyCode 40 = Down
		document.onkeydown = function (e){
			if(this.state == this.RUNNING){
				switch (e.keyCode){
					case 37:
					this.moveLeft();
					break;
					case 38:
					this.moveUp();
					break;
					case 39:
					this.moveRight();
					break;
					case 40:
					this.moveDown();
					break;
				}
			}
		}.bind(this);
	},
	
	init(){
		this.state = this.RUNNING;
		this.data = [];
		for (var row = 0; row < this.RN;row++){
			this.data[row]=[];
			for(var col = 0; col < this.CN;col++){
				this.data[row][col]=0;
			}
		}
		this.randomNum();
		this.randomNum();
		this.updateView();//将data中的数据更新到页面中的每个div中
	},
	
	randomNum(){
		while(true){
			var randomRow = Math.floor(Math.random()*this.RN);
			var randomCol = Math.floor(Math.random()*this.CN);
			console.log('randomRow is:',randomRow);
			console.log('randomRow is:',randomCol);
			if(this.data[randomRow][randomCol]==0){
				this.data[randomRow][randomCol] = Math.random()<0.5 ? 2: 4;
				break;
		}	
		}

	},
	
	updateView(){
		for (var row = 0; row < this.RN; row++){
			for (var col = 0; col < this.CN; col++){
				var n = this.data[row][col];
				var div = document.getElementById("grid-cell-" + row + "-" + col);
				if(n != 0){
					div.innerHTML = n;//设置div的内容为n
					div.className = "grid-cell n" + n;
				}else {
					div.innerHTML = "";//清除div中的内容
					//恢复div的class为grid-cell
					div.className = "grid-cell";
				}
			}
		}
		//找到id为score的span,设置其内容为score属性
		document.getElementById("score").innerHTML = this.score;
		
		//当游戏结束时,显示gameover的div
		var div= document.getElementById("gameOVER");
		if(this.state == this.GAMEOVER){
			div.style.display = "block";
			document.getElementById("final").innerHTML = this.score;//找到id为final的span,设置其内容为score
		}else{
			//游戏还在运行中
			div.style.display = "none";
		}
		
		
	},
	

	moveLeft(){
		//按行判断,若是有为0的单元格,则表示可以往左移动
		//若是相邻的单元格的值有相等的,则相加
		var cellValue = null;
		//var cell_div = null;
		for(var row = 0; row < this.RN; row++){
			for(var col = 0; col =0; col--){
				cellValue = this.data[row][col];
				if(cellValue == 0){
					this.moveRightInOneRow(row,col);
				}
			}
			
			//每行中值都非0,需要判断是否有可以相加的
			for(var col = this.CN - 1; col >=0; col--){
				this.addTwoCellInColFromRight(row,col);
			}
					
			}	
//随机选择一个单元格,然后随机生成2或者4
		this.randomNum();
		this.updateView();
	},
	moveRightInOneRow(row,col){
		this.moveCellToRight(row,col);
		//判断每列中相邻单元格中的值是否相等,若是相等,则加起来,row_index从最大值开始
        this.addTwoCellInColFromRight(row,col);
		
	},
	addTwoCellInColFromRight(row,col){
		for(var col_index = this.CN - 1; col_index > 0; col_index--){
			if(this.data[row][col_index]!= 0 &&(this.data[row][col_index] == this.data[row][col_index- 1])){
				this.data[row][col_index] = this.data[row][col_index] + this.data[row][col_index - 1];
				this.score += this.data[row][col_index];
				
				for(j = col_index - 1;j>=0;j--){
					if(this.data[row][j]===0){
						this.moveCellToRight(row,j);
					}
					
				}
		//		this.data[row][col_index - 1] = 0;
		//		this.moveCellToRight(row, col_index - 1);
			}
		}
	},
    moveCellToRight(row,col){
		for (var temp_col = col - 1;temp_col >=0; temp_col--){
			if(this.data[row][temp_col] !=0){
				//对于一列中,上面的单元格中有不为0的数
				temp = this.data[row][temp_col];
				this.data[row][temp_col] = this.data[row][col];
				this.data[row][col] = temp;
				break;
			}
			
		}
		
	},


	moveUp(){
		//按列判断,若是有为0的单元格,则表示可以往左移动
		//若是相邻的单元格的值有相等的,则相加
		var cellValue = null;
		//var cell_div = null;
		for(var col = 0; col < this.CN; col++){
			for(var row = 0; row =0; row--){
				cellValue = this.data[row][col];
				if(cellValue == 0){
					this.moveDownInOneCol(row,col);
				}
			}
			
			
			//当一列中虽然没有为0的数,但是若是有相同的数,还是需要相加
			for (var row = this.RN - 1; row >=0; row--){
				this.addTwoCellInColFromBelow(row,col);
			}
			
		}
		//随机选择一个单元格,然后随机生成2或者4
		this.randomNum();
		this.updateView();
	},
	
	moveDownInOneCol(row,col){
		this.moveCellToBelow(row,col);
		//判断每列中相邻单元格中的值是否相等,若是相等,则加起来,row_index从最大值开始
		this.addTwoCellInColFromBelow(row,col);

	},
	
	addTwoCellInColFromBelow(row,col){
		for(var row_index = this.RN - 1; row_index > 0; row_index--){
			if(this.data[row_index][col]!= 0 &&(this.data[row_index][col] == this.data[row_index - 1][col])){
				this.data[row_index][col] = this.data[row_index][col] + this.data[row_index - 1][col];
				this.data[row_index - 1][col] = 0;
				this.score += this.data[row_index][col];
				for(j = row_index - 1;j>=0;j--){
					if(this.data[j][col]===0){
						this.moveCellToBelow(j,col);
					}
					
				}
			//	this.data[row_index - 1][col] = 0;
			//	this.moveCellToBelow(row_index - 1, col);
			}
		}
	},
	moveCellToBelow(row,col){
		for (var temp_row = row - 1;temp_row >=0; temp_row--){
			if(this.data[temp_row][col] !=0){
				//对于一列中,上面的单元格中有不为0的数
				temp = this.data[temp_row][col];
				this.data[temp_row][col] = this.data[row][col];
				this.data[row][col] = temp;
				break;
			}
			
		}
	},
	isGameOver(){
		//若是全部格子都被填满,且相邻单元格没有相同的元素,则结束游戏
		for(var row = 0;row < this.RN; row++){
			for(var col=0; col < this.CN; col++){
				if (this.data[row][col] ==0) return false;
				else if(col < this.CN -1 && (this.data[row][col] == this.data[row][col + 1])){
					return false;
				}
				else if (row < this.RN - 1 && (this.data[row + 1][col] == this.data[row][col])){
					return false;
				}
			}
			//遍历结束
			return true;
		}
	},
	
	
}
function tryAgain() {
    this.init();
  },

game.start();

你可能感兴趣的:(技术干货)