2048小游戏

HTML结构

	<div class="all">
	//头部提示部分
	<p class="first_tr">SCORE:<span id="score01">  0</span></p>
	//中间游戏部分
		<ul class="main">
			<li class="cell" id="n00"></li>
			<li class="cell" id="n01"></li>
			<li class="cell" id="n02"></li>
			<li class="cell" id="n03"></li>
			
			<li class="cell" id="n10"></li>
			<li class="cell" id="n11"></li>
			<li class="cell" id="n12"></li>
			<li class="cell" id="n13"></li>
			
			<li class="cell" id="n20"></li>
			<li class="cell" id="n21"></li>
			<li class="cell" id="n22"></li>
			<li class="cell" id="n23"></li>
			
			<li class="cell" id="n30"></li>
			<li class="cell" id="n31"></li>
			<li class="cell" id="n32"></li>
			<li class="cell" id="n33"></li>
		</ul>
	</div>
	//游戏结束遮罩层部分
	<div id="mark">
		<p id="gameover">
			GAME OVER!<br />
			SCORE:<span id="score02"> 0</span><br />
			<a href="javascript:game.gamestart()">Try Again!</a>
		</p>
	</div>

注意

2048游戏:

  1. 整体的布局,注意分块
  2. 对于命名,设置相同的class名,设置初始样式
  3. 设置不同的ID,由于在JS中采用二维数组的方式进行插入数据,所以,ID的命名具有二维数组的下标的标示性
  4. 命名不能以数字开头

CSS样式

body,ul,li,div,span,p{
    margin:0px; 
    padding: 0px;
    list-style: none;
    font-family: Arial;
    /*box-sizing: border-box;*/
}
.first_tr,.first_tr span{
	font-weight: bold;
	font-size: 40px;
}
.first_tr span{
	color: red;
}
.all{
	width: 500px;
	height: 500px;
	margin:40% auto;
}
.main{
	width: 480px;
	height: 480px;
	background-color: #BBADA0;
	border-radius: 5px;
	text-align: center;
}
.main li{
	float: left;
	width:100px;
	height:100px;
    margin-left: 16px;
    margin-top: 16px;
    text-align: center;
    line-height:100px;
    border-radius: 5px;
}

//初始化样式

.cell{
	color: white;
	font-size: 40px;
    background-color: #CDC1B4;
}

//数据发生改变的样式

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

//遮罩层样式

#mark{
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	background-color: rgba(0,0,0,0.5);
	display: none;
}
#gameover{
	width: 300px;
	height: 200px;
	position: absolute;
	top: 50%;
	left: 50%;
	margin-left: -150px;
	margin-top: -100px;
	box-shadow: 0 0 10px rgba(0,0,0,0.025);
	border-radius: 10px;
	background-color: white;
	font-size: 25px;
	font-weight: bold;
	text-align: center;
	line-height: 60px;
	
}
#gameover>span{
	color: red;
}
#gameover>a{
	color: white;
	text-decoration: none;
	display: inline-block;
	width: 150px;
	height: 35px;
	background-color:#9F8D77;
	line-height: 35px;
	border-radius: 5px;
}

注意

  1. 将初始化样式和其他的样式分开
  2. 将每个不同数据的不同样式分开写,命名的方式同样要有规律
  3. 采用class样式的命名,最后附样式只需根据数据来更换

JS行为

过程

  1. 创建一个game对象,创建game的属性:游戏状态,游戏开始方法,游戏结束方法,赋值样式方法,随机产生2或者4的数据的方法,消除相加数据方法,数据移动方法,记录分数方法

  2. 开始game方法:初始化数据,随机产生两个数据,初始化分数

    (1)创建二维数组,将数据初始化为0,
    (2)开始游戏将分数清0 ,游戏状态初始化为游戏开始
    (3)执行两次产生随机数2或者4
    (4)执行样式方法的函数

gamestart: function() {
		//创建二维数组,初始化数据为0
		this.score = 0; //游戏开始,分数清零
		this.data = [];
		this.status = 1;
		for(var r = 0; r < 4; r++) {
			this.data[r] = [];
			for(var c = 0; c < 4; c++) {
				this.data[r][c] = 0;
			}
		}
		this.randomNum();
		this.randomNum();
		this.boxStyle();
		console.log(this.data);
	},
  1. 界面样式的改
    (1)遍历数据数组,根据产生的数据,同步到页面
    (2)同时将相应数据的样式同步到页面
    【之前在css和html上的命名可以在此时直接做相应的拼接,利于赋className】
    (3)根据规则将页面上的分数做相应的赋值
    (4)页面视图中,游戏结束时遮罩层的显示,及最总分数的显示
boxStyle: function() {
		//页面视图
		for(var r = 0; r < 4; r++) {
			for(var c = 0; c < 4; c++) {
				//添加不同数据的样式
				var li = document.getElementById("n" + r + c);
				if(this.data[r][c] != 0) {
					li.innerHTML = this.data[r][c];
					li.className = "cell n" + this.data[r][c];
				} else {
					li.innerHTML = "";
					li.className = "cell";
				}
			}
		}
		document.getElementById("score01").innerHTML = this.score; //游戏过程中分数的变化
     //判断游戏是否结束
		if(this.status != 1) {
			document.getElementById("mark").style.display = "block";
			document.getElementById("score02").innerHTML = this.score; //游戏结束的分数
		} else {
			document.getElementById("mark").style.display = "none";
		}
	},
  1. 游戏结束function
    (1)首先遍历整个数组,根据游戏规则,判断游戏是否结束
    (2)若不满足游戏结束规则,则,返回false,反之,true
    (3)返回值在之后的函数中做判断,因此,此时让游戏是否结束的返回值为boolean,也可以在此时改变游戏的状态 status ,将游戏状态改变为结束游戏的状态,在之后的判断中,判断游戏的状态,也可
    【在游戏开始前,重置了游戏的状态,为开始状态】
    (4) 游戏的结束规则:当游戏的所有格子填满,且上下左右都没有相同的数据。
isgameOver: function() {
		//游戏结束
		for(var r = 0; r < 4; r++) {
			for(var c = 0; c < 4; c++) {
				//游戏结束标准,所有格子填满,且上下左右的没有相同的数据
				if(this.data[r][c] == 0) {
					return false;
				}
				if(c < 3) {
					//两两相邻比较 c  c+1
					if(this.data[r][c] == this.data[r][c + 1]) {
						return false;
					}
				}
				if(r < 3) {
					if(this.data[r][c] == this.data[r + 1][c]) {
						return false;
					}
				}
			}
		}
		return true;
	},
  1. 界面数据的变化
    【向左移动代码】
    (1)首先对数据做一个拍照的记录,在之后判断是否移动或者变化
    【String()操作,将数组进行拍照动作,记录当前数组内的元素的位置,数据的大小,转换为字符串类型。数组为引用类型,不能进行==的比较操作,而原始类型可以】
    (2)左右的移动,是在列内的操作,因此每一行可有相同的列的操作
    (3)在对列的操作后,记录此时的数据,在进行比较。
    (4)若发生了改变,则按照规则产生一个随机的数据,并且赋予相应的样式
    (5)同时也要进行游戏是否结束的判断,每一次移动都要进行是否结束的判断。
moveLeft: function(){
		//先对数据做拍照,判断是否移动,引用类型的数据无法用 ==进行判断
		var before = String(this.data);
		//向左移动 是列的移动    每一行中的列的行为  且 第一列不动,
		for(var r = 0; r < 4; r++) {
			this.moveleft_col(r)
		}
		var after = String(this.data);
		if(before != after) {
			//移动了  生成一个随机数 
			this.randomNum();
		console.log(1);
			//首先判断是否结束游戏
			if(this.isgameOver()) {
				//isgameOver返回值为true 和 false 
				this.status = this.gameover;
			}
				this.boxStyle();
		}
	},

  1. 数据的移动—交换位置
    (1)首先对列的数据的遍历,进行左右的比较判断,判断除了第一个数据之后的数据若不为空(不为零),则进行下一步的判断,若都为零,则跳出此行的循环遍历
    (2)若有不为零的数据,则判断列中的第一个数据是否为零,若为零,则交换位置,反之比较是否相同,相同则相加(或者乘以2),并且,将后面的数据清0
    (3)若不相同,则不交换位置
    (4)c 的开始为0,结束为2,比较的数据是前一个c,同c+1相比较。所以,列的遍历,从0,开始,到倒数第二位结束。
    (5)左右的移动,区别的对列的遍历,一个正序,一个倒序,比较的第一个数据,一个是第一位,一个是最后一位
	moveleft_col:function(r){
		for(var c = 0; c < 3; c++) {
			var i = this.getNEXTinRow(r,c);
				if(i != -1){
					//如果一行中除了第一个之外有不为0的数
					if(this.data[r][c] == 0) {
						//如果左侧有为0 的则,交换位置
						this.data[r][c] = this.data[r][i];
						this.data[r][i] = 0;
						c--;
					} //如果相邻有相同的数据  则相加 
					else if(this.data[r][c] == this.data[r][i]) {
						this.data[r][c] *= 2;
						this.data[r][i] = 0;
						this.score += this.data[r][c];
						//这里相邻有不相同的也不做移动,不跳出 ,执行下一次的列循环
					}
				} else {
					//如果一行中都是0 跳出列循环,执行下一行循环    不移动
					break;
				}
			}
	},
	getNEXTinRow:function(r,c){
		for(var i = c+1;i<4;i++){
			if(this.data[r][i] != 0){
				return i;
			}
		}
				return -1;
	},
  1. 上下的数据移动和变化同左右的移动是相同的,只是作用的是在每一行上,每一列重复动作
    【 向上移动代码】
moveTop: function(){
		//先对数据做拍照,判断是否移动,引用类型的数据无法用 ==进行判断
		var before = String(this.data);
		for(var c = 0;  c< 4; c++) {
			this.movetop_col(c)
		}
		var after = String(this.data);
		if(before != after) {
			//移动了  生成一个随机数 
			this.randomNum();
			//首先判断是否结束游戏
			if(this.isgameOver()) {
				//isgameOver返回值为true 和 false 
				this.status = this.gameover;
			}
				this.boxStyle();
		}
	},
	movetop_col:function(c){
		for(var r = 0; r < 3; r++) {
			var i = this.getNEXTinRow_top(r,c);
				if(i != -1){
					if(this.data[r][c] == 0) {
						this.data[r][c] = this.data[i][c];
						this.data[i][c] = 0;
						r--;
					}
					else if(this.data[r][c] == this.data[i][c]) {
						this.data[r][c] *= 2;
						this.data[i][c] = 0;
						this.score += this.data[r][c];
					}
				} else {
					break;
				}
			}
	},
	getNEXTinRow_top:function(r,c){
		for(var i = r+1;i < 4;i ++){
			if(this.data[i][c] != 0){
				return i;
			}
		}
				return -1;
	},
  1. 键盘事件
    (1)键盘事件 —onkeydown属性
    (2)event event代表事件的状态,例如触发event对象的元素、鼠标的位置及状态、按下的键等等,event对象只在事件发生的过程中才有效。
    (3)keycode是在控制台查询上下左右键对应的Unicode码,相应的键触发相应的事件
    【左:37;上:38;右:38;下:39】
document.onkeydown = function(event) {
	//做兼容性处理
	var event = event || e || arguments[0];
	if(event.keyCode == 37) {
		game.moveLeft();
	}else if(event.keyCode == 39){
		game.moveRight();
	}else if(event.keyCode == 38){
		game.moveTop();
	}else if(event.keyCode == 40){
		game.moveBottom();
	}
}
  1. 移动端的触屏事件
    (1)触屏事件—touchstart 手指按下,touchend手指抬起 ,touchmove 手指移动属性
    (2)使用监听事件来调用触屏事件—addEventListener(“绑定的事件”,执行的动作function)
    (3)触屏事件中,查询触屏开始的坐标,和结束的坐标,判断是左滑,还是右滑,上滑还是下滑
    (4)判断滑动,要注意,滑的过程不是直线,倾斜的滑动,通过比较水平滑动的距离,和垂直滑动的距离,来控制上下滑动,还是左右滑动
//移动版的触屏事件
			var startX ,startY,endX , endY;
			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 = startX - endX;
				var y = endX - endY;
				var ab_x = Math.abs(x) > Math.abs(y)//斜划,判断x轴  y轴 滑动的距离,
				if(x > 0 && ab_x){
					game.moveLeft();
				}else if(x < 0 && ab_x > 0){
					game.moveRight();
				}else if(y > 0 && !ab_x){
					game.moveTop();
				}else if(y < 0 && !ab_x){
					game.moveBottom();
				}
			})

js所有代码

var game = {
	data: [],
	gameover: 0,
	gamerunning: 1,
	status: 1, //游戏状态
	score: 0, //游戏分数
	gamestart: function() {
		//创建二维数组,初始化数据为0
		this.score = 0; //游戏开始,分数清零
		this.data = [];
		this.status = 1;
		for(var r = 0; r < 4; r++) {
			this.data[r] = [];
			for(var c = 0; c < 4; c++) {
				this.data[r][c] = 0;
			}
		}
		this.randomNum();
		this.randomNum();
		this.boxStyle();
		console.log(this.data);
	},
	randomNum: function() {
		//随机数 
		for(;;) {
			//赋值
			var r = Math.floor(Math.random() * 4);
			var c = Math.floor(Math.random() * 4);
			if(this.data[r][c] == 0) {
				var num = Math.random() > 0.5 ? 2 : 4;
				this.data[r][c] = num;
				break;
			}
		}
	},
	boxStyle: function() {
		//页面视图
		for(var r = 0; r < 4; r++) {
			for(var c = 0; c < 4; c++) {
				//添加不同数据的样式
				var li = document.getElementById("n" + r + c);
				if(this.data[r][c] != 0) {
					li.innerHTML = this.data[r][c];
					li.className = "cell n" + this.data[r][c];
				} else {
					li.innerHTML = "";
					li.className = "cell";
				}
			}
		}
		document.getElementById("score01").innerHTML = this.score; //游戏过程中分数的变化
     //判断游戏是否结束
		if(this.status != 1) {
			document.getElementById("mark").style.display = "block";
			document.getElementById("score02").innerHTML = this.score; //游戏结束的分数
		} else {
			document.getElementById("mark").style.display = "none";
		}
	},
	isgameOver: function() {
		//游戏结束
		for(var r = 0; r < 4; r++) {
			for(var c = 0; c < 4; c++) {
				//游戏结束标准,所有格子填满,且上下左右的没有相同的数据
				if(this.data[r][c] == 0) {
					return false;
				}
				if(c < 3) {
					//两两相邻比较 c  c+1
					if(this.data[r][c] == this.data[r][c + 1]) {
						return false;
					}
				}
				if(r < 3) {
					if(this.data[r][c] == this.data[r + 1][c]) {
						return false;
					}
				}
			}
		}
		return true;
	},
	moveLeft: function(){
		//先对数据做拍照,判断是否移动,引用类型的数据无法用 ==进行判断
		var before = String(this.data);
		//向左移动 是列的移动    每一行中的列的行为  且 第一列不动,
		for(var r = 0; r < 4; r++) {
			this.moveleft_col(r)
		}
		var after = String(this.data);
		if(before != after) {
			//移动了  生成一个随机数 
			this.randomNum();
		console.log(1);
			//首先判断是否结束游戏
			if(this.isgameOver()) {
				//isgameOver返回值为true 和 false 
				this.status = this.gameover;
			}
				this.boxStyle();
		}
	},
	moveleft_col:function(r){
		for(var c = 0; c < 3; c++) {
			var i = this.getNEXTinRow(r,c);
				if(i != -1){
					//如果一行中除了第一个之外有不为0的数
					if(this.data[r][c] == 0) {
						//如果左侧有为0 的则,交换位置
						this.data[r][c] = this.data[r][i];
						this.data[r][i] = 0;
						c--;
					} //如果相邻有相同的数据  则相加 
					else if(this.data[r][c] == this.data[r][i]) {
						this.data[r][c] *= 2;
						this.data[r][i] = 0;
						this.score += this.data[r][c];
						//这里相邻有不相同的也不做移动,不跳出 ,执行下一次的列循环
					}
				} else {
					//如果一行中都是0 跳出列循环,执行下一行循环    不移动
					break;
				}
			}
	},
	getNEXTinRow:function(r,c){
		for(var i = c+1;i<4;i++){
			if(this.data[r][i] != 0){
				return i;
			}
		}
				return -1;
	},
	moveRight: function(){
		//先对数据做拍照,判断是否移动,引用类型的数据无法用 ==进行判断
		var before = String(this.data);
		for(var r = 0; r < 4; r++) {
			this.moveright_col(r)
		}
		var after = String(this.data);
		if(before != after) {
			//移动了  生成一个随机数 
			this.randomNum();
			//首先判断是否结束游戏
			if(this.isgameOver()) {
				//isgameOver返回值为true 和 false 
				this.status = this.gameover;
			}
				this.boxStyle();
		}
	},
	moveright_col:function(r){
		for(var c = 3; c > 0; c--) {
			var i = this.getNEXTinRow_right(r,c);
				if(i != -1){
					if(this.data[r][c] == 0) {
						this.data[r][c] = this.data[r][i];
						this.data[r][i] = 0;
						c++;
					} //如果相邻有相同的数据  则相加 
					else if(this.data[r][c] == this.data[r][i]) {
						this.data[r][c] *= 2;
						this.data[r][i] = 0;
						this.score += this.data[r][c];
						//这里相邻有不相同的也不做移动,不跳出 ,执行下一次的列循环
					}
				} else {
					break;
				}
			}
	},
	getNEXTinRow_right:function(r,c){
		for(var i = c-1;i>=0;i--){
			if(this.data[r][i] != 0){

				return i;
			}
		}
				return -1;
	},
	moveTop: function(){
		//先对数据做拍照,判断是否移动,引用类型的数据无法用 ==进行判断
		var before = String(this.data);
		for(var c = 0;  c< 4; c++) {
			this.movetop_col(c)
		}
		var after = String(this.data);
		if(before != after) {
			//移动了  生成一个随机数 
			this.randomNum();
			//首先判断是否结束游戏
			if(this.isgameOver()) {
				//isgameOver返回值为true 和 false 
				this.status = this.gameover;
			}
				this.boxStyle();
		}
	},
	movetop_col:function(c){
		for(var r = 0; r < 3; r++) {
			var i = this.getNEXTinRow_top(r,c);
				if(i != -1){
					if(this.data[r][c] == 0) {
						this.data[r][c] = this.data[i][c];
						this.data[i][c] = 0;
						r--;
					}
					else if(this.data[r][c] == this.data[i][c]) {
						this.data[r][c] *= 2;
						this.data[i][c] = 0;
						this.score += this.data[r][c];
					}
				} else {
					break;
				}
			}
	},
	getNEXTinRow_top:function(r,c){
		for(var i = r+1;i < 4;i ++){
			if(this.data[i][c] != 0){
				return i;
			}
		}
				return -1;
	},
	moveBottom: function(){
		//先对数据做拍照,判断是否移动,引用类型的数据无法用 ==进行判断
		var before = String(this.data);
		for(var c = 0; c < 4; c++) {
			this.movebottom_col(c)
		}
		var after = String(this.data);
		if(before != after) {
			//移动了  生成一个随机数 
			 this.randomNum();
			//首先判断是否结束游戏
			if(this.isgameOver()) {
				//isgameOver返回值为true 和 false 
			this.status = this.gameover;
			}
				this.boxStyle();
		}
	},
	movebottom_col:function(c){
		for(var r = 3; r > 0; r--) {
			var i = this.getNEXTinRow_bottom(r,c);
				if(i != -1){
					if(this.data[r][c] == 0) {
						this.data[r][c] = this.data[i][c];
						this.data[i][c] = 0;
						r++;
					} 
					else if(this.data[r][c] == this.data[i][c]) {
						this.data[r][c] *= 2;
						this.data[i][c] = 0;
						this.score += this.data[r][c];
					}
				} else {
					break;
				}
			}
	},
	getNEXTinRow_bottom:function(r,c){
		for(var i = r-1;i >= 0;i--){
			if(this.data[i][c] != 0){
				return i;
			}
		}
				return -1;
	},
}
//键盘事件  onkeydown  
document.onkeydown = function(event) {
	//做兼容性处理
	var event = event || e || arguments[0];
	if(event.keyCode == 37) {
		game.moveLeft();
	}else if(event.keyCode == 39){
		game.moveRight();
	}else if(event.keyCode == 38){
		game.moveTop();
	}else if(event.keyCode == 40){
		game.moveBottom();
	}
}
	//监听事件
	
	//移动版的触屏事件
			var startX ,startY,endX , endY;
			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 = startX - endX;
				var y = endX - endY;
				var ab_x = Math.abs(x) > Math.abs(y)//斜划,判断x轴  y轴 滑动的距离,
				if(x > 0 && ab_x){
					game.moveLeft();
				}else if(x < 0 && ab_x > 0){
					game.moveRight();
				}else if(y > 0 && !ab_x){
					game.moveTop();
				}else if(y < 0 && !ab_x){
					game.moveBottom();
				}
			})
game.gamestart()
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值