一.游戏简介
2048是一款休闲益智类的数字叠加小游戏,类似于欢乐消消消,很有意思的一个小游戏。
二.游戏玩法
在 4*4 的16宫格中,您可以选择上、下、左、右四个方向进行操作,数字会按方向移动,相邻的两个数字相同就会合并,组成更大的数字,每次移动或合并后会自动增加一个数字。
当16宫格中没有空格子,且四个方向都无法操作时,游戏结束。
三、游戏目的
目的是合并出 2048 这个数字,获得更高的分数。
四.游戏截图
五.游戏实现的原理
把16个格子看成4*4的二维数组
在HTML中给每个格子添加类名和属性,来记录每个格子的位置
采用三元表达式随机生成0~1的小数,和0.5(可以设置游戏难度)比较,来给出2或4
最后再把数据更新到每个div中
判断游戏结束:
每个格子上的数都不为零,上下左右的数都不相同的时候就判断游戏结束
HTML设置整体的布局
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>2048小游戏</title>
<link rel="stylesheet" type="text/css" href="2048.css">
</head>
<body>
<div class="bag">
<h1>2048</h1>
<button onclick="game.start();">new game</button>
<div class="score">
<span>当前分数:</span>
<span id="scoreValue">0</span>
</div>
<div class="highScore">
<span>最高得分:</span>
<span id="highScoreValue">0</span>
</div>
<div class="box">
<!-- 第一行 -->
<div class="item" id= "c00"></div>
<div class="item" id= "c01"></div>
<div class="item" id= "c02"></div>
<div class="item" id= "c03"></div>
<!-- 第二行 -->
<div class="item" id= "c10"></div>
<div class="item" id= "c11"></div>
<div class="item" id= "c12"></div>
<div class="item" id= "c13"></div>
<!-- 第三行 -->
<div class="item" id= "c20"></div>
<div class="item" id= "c21"></div>
<div class="item" id= "c22"></div>
<div class="item" id= "c23"></div>
<!-- 第四行 -->
<div class="item" id= "c30"></div>
<div class="item" id= "c31"></div>
<div class="item" id= "c32"></div>
<div class="item" id= "c33"></div>
</div>
<header id="false_1">
<h1>GAME OVER</h1>
<p>SCORE:<span id="endScore">0</span></p>
<br>
<button onclick="game.start();">再玩一次</button>
</header>
</div>
<script type="text/javascript" src="2048.js"></script>
</body>
</html>CSS界面设置div样式
body{
background-color: #fff8dc;
}
.bag{
width: 600px;
height: 650px;
background-color: #ffe4e1;
border-radius: 20px;
margin: 0 auto;
position: relative;
}
.bag>h1{
position: absolute;
font-size: 70px;
color:#ff69b4;
left: 75px;
top: 30px;
}
.box{
width: 450px;
height: 450px;
border-radius: 10px;
background-color:#ffb6c1;
position: absolute;
top: 170px;
left: 75px;
}
header{
width: 450px;
height: 450px;
border-radius: 20px;
background-color:rgba(20,20,20,0.5);
z-index: -99;
position: absolute;
top: 170px;
left: 75px;
}
header>h1{
font-size: 50px;
text-align: center;
color: #ffb6c1;
}
header>button{
width: 200px;
height: 60px;
font-size: 30px;
color: #ff69b4;
border-radius: 6px;
border:none;
background-color:#ffb6c1;
margin-left: 125px;
}
header>p{
color: #ff69b4;
font-size: 30px;
font-weight: bold;
text-align: center;
}
.false{
z-index: 99;
}
.item{
width: 100px;
height: 100px;
border-radius: 8px;
background-color:#ff69b4;
float:left;
margin: 10px 0 0 10px;
font-size: 60px;
text-align: center;
line-height: 100px;
}
.bag>button{
width: 100px;
height: 40px;
font-size: 18px;
color: #ff69b4;
border-radius: 6px;
border:none;
background-color:#ffb6c1;
position: absolute;
right: 75px;
top: 110px;
}
.bag>.score{
width: 100px;
height: 60px;
background-color: #ffb6c1;
position: absolute;
border-radius: 6px;
left: 75px;
top:20px;
color: #ff69b4;
font-size: 18px;
text-align: center;
}
.score>span{
display: block;
}
.highScore{
width: 100px;
height: 60px;
background-color: #ffb6c1;
position: absolute;
border-radius: 6px;
right:75px;
top:20px;
color: #ff69b4;
font-size: 18px;
text-align: center;
}
.highScore>span{
display: block;
}
.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}最后JS界面设计算法运算调用函数
var game = {
RN:4,CN:4,
data:null,//保存游戏的二维数组 //总行数,总列数
score:0, //分数初始值为0
highScore:0,//最高分初始值为0
start(){
//清零分数
this.score = 0;
//1.初始化data二维数组 每一个数据都初始化为
//新建空数组存储到data
this.data = [];
//通过总行数和总列数,循环遍历每个数。并且赋值为0
for(var r = 0;r<this.RN;r++){
//空数组中的r行
//新建一个空数组保存在data的r行中
this.data[r] = [];
for(var c = 0;c<this.CN;c++){
//空数组中r行c位置的数值 设置为0
this.data[r][c] = 0;
}
}
//2.随机在二维数组中添加两个数值,2或4
this.randomNum();
this.randomNum();
console.log(this.data.join('\n'));
//3.将data二维数组中的数据展示到页面的div中去
this.updataView();
//4.触发游戏
//当在键盘上按上,下,左,右触发事件
document.onkeydown = function(e){
switch(e.keyCode){
case 37://左移
//this.moveleft(); 这里面的this关键字不在指向game,因此调用game的成员不能使用this关键字
game.moveLeft();
break;
case 39://右移
game.moveRight();
break;
case 38://上移
game.moveTop();
break;
case 40://下移
game.moveBottom();
break;
}
}
//清零游戏结束界面的样式
document.getElementById("false_1").className = '';
},
updataView(){
//将data中的数据更新到div中
//遍历二维数组data
//r从0开始,到RN结束
//c从0开始,到CN结束
for(var r = 0;r<this.RN;r++){
for(var c = 0;c<this.CN;c++){
//找到id为"c"+r+c的div元素
var div = document.getElementById("c"+r+c);
//获取到data r行c列的数据 存储变量n中
var n = this.data[r][c];
//判断n是否为0
//如果是0
//div的内容设置为空,清除div的内容
if(n==0){
div.innerHTML = "";
//恢复div的class为 item
div.className = "item";//否则
}else{
//将div的值设置为n
div.innerHTML = n;
//将div的class设置为 item 'n'+n
div.className ="item n"+n;
}
}//c 的循环结束
//r 的循环结束
}
document.getElementById("scoreValue").innerHTML = this.score;
if(this.score>this.highScore){
this.highScore = this.score;
}
document.getElementById('highScoreValue').innerHTML = this.highScore;
},
randomNum(){
//在一个随机位置生成2或4
//反复:
while(true){
// 在0~RN-1之间生成随机数r
r = Math.floor(Math.random()*this.RN);
// 在0~CN-1之间生成随机数c
c = Math.floor(Math.random()*this.CN);
// 根据data中r行c列的值来处理
// 如果data中r行c列的值为0
if(this.data[r][c] == 0){
// 将data中的r行c列赋值为:
// 2或者4的数字 随机生成一个小数,如果<0.5,就取2,否则就取4
this.data[r][c] = Math.random()>0.6?2:4;
break;
}
// 退出循环
}
},
end(){
for(var r = 0;r<this.RN;r++){
for(var c=0;c<this.CN;c++){
if(this.data[r][c]==0){
return false;
}
}
}
for(var r=1;r<this.RN-1;r++){
for(var c=0;c<this.CN;c++){
if((this.data[r+1][c] == this.data[r][c])||(this.data[r-1][c]==this.data[r][c])){
return false;
}
}
}
for(var r=0;r<this.RN;r++){
for(var c=1;c<this.CN-1;c++){
if((this.data[r][c-1] == this.data[r][c])||(this.data[r][c+1] == this.data[r][c])){
return false;
}
}
}
return true;
},
isend(){
if(this.end()){
document.getElementById("false_1").className = 'false';
document.getElementById("endScore").innerHTML = this.score;
}
},
moveLeft(){
//左移所有行
//给数组拍照 var before = String(arr)
var before = String(this.data);
//r从0开始,到<RN结束
for(var r = 0;r<this.RN;r++){
this.moveLeftInRow(r);
}
//左移r行
//r循环结束
//移动结束以后,再给数组拍照 赋值给after
var after = String(this.data);
//如果data数组中的数据有变化 如果before != after
if(before != after){
//调用updateView更新页面中数据
this.randomNum();
this.updataView();
this.isend();
}
},
moveLeftInRow(r){
//c从0开始,到<CN-1
////查找r行c列 下一个不为0的位置nextc
for(var c = 0;c<this.CN-1;c++){
for(var i = c+1;i<this.CN;i++){
// i从c+1开始,到<CN结束
// 当前i位置的值是否为0
if(this.data[r][i] != 0){
// 值不为0的位置 i赋值nextc
var nextc = i;
break;
}else{
// 否则
// 将nextc赋值为-1
nextc = -1;
}
}// i循环结束
// 如果nextc的值为-1,证明后面的都是0,直接退出
if (nextc == -1) {
break;
}else{// 否则
// 找r行c列位置的值,判断是否为0
if(this.data[r][c] == 0) {// 如果是0 将nextc位置的值赋值给c位置
this.data[r][c] = this.data[r][nextc];
this.data[r][nextc] = 0;// 将nextc位置的值赋值为0
c--;// 将c留在原地
}else if(this.data[r][c] == this.data[r][nextc]){
// 否则 else if(c位置的值等于nextc位置的值)
this.data[r][c] *= 2;// 将c位置的值*2
this.score += this.data[r][c];
this.data[r][nextc] = 0;// 将nextc位置的值赋值为0
// c的循环结束
}
}
}
},
moveRight(){
//右移所有行
//给数组拍照 var before = String(arr)
var before = String(this.data);
//r从0开始,到<RN结束
for(var r = 0;r<this.RN;r++){
this.moveRightInRow(r);
}
//右移r行
//r循环结束
//移动结束以后,再给数组拍照 赋值给after
var after = String(this.data);
//如果data数组中的数据有变化 如果before != after
if(before != after){
//调用updateView更新页面中数据
this.randomNum();
this.updataView();
this.isend();
}
},
moveRightInRow(r){
//c从cn-1开始,到0
////查找r行c列 上一个不为0的位置nextc
for(var c = this.CN-1;c>0;c--){
for(var i = c-1;i>=0;i--){
// i从c-1开始,到>-1结束
// 当前i位置的值是否为0
if(this.data[r][i] != 0){
// 值不为0的位置 i赋值nextc
var nextc = i;
break;
}else{
// 否则
// 将nextc赋值为-1
nextc = -1;
}
}// i循环结束
// 如果nextc的值为-1,证明后面的都是0,直接退出
if (nextc == -1){
break;
}else{// 否则
// 找r行c列位置的值,判断是否为0
if(this.data[r][c] == 0) {// 如果是0 将nextc位置的值赋值给c位置
this.data[r][c] = this.data[r][nextc];
this.data[r][nextc] = 0;// 将nextc位置的值赋值为0
c++;// 将c留在原地
}else if(this.data[r][c] == this.data[r][nextc]){
// 否则 else if(c位置的值等于nextc位置的值)
this.data[r][c] *= 2;// 将c位置的值*2
this.score += this.data[r][c];
this.data[r][nextc] = 0;// 将nextc位置的值赋值为0
// c的循环结束
}
}
}
},
moveTop(){
//上移所有列
//给数组拍照 var before = String(arr)
var before = String(this.data);
//c从0开始,到<CN结束
for(var c = 0;c<this.CN;c++){
this.moveTopInRow(c);
}
//上移c列
//c循环结束
//移动结束以后,再给数组拍照 赋值给after
var after = String(this.data);
//如果data数组中的数据有变化 如果before != after
if(before != after){
//调用updateView更新页面中数据
this.randomNum();
this.updataView();
this.isend();
}
},
moveTopInRow(c){
//r从0开始,到<CN-1
////查找r行c列 下一个不为0的位置nextr
for(var r = 0;r<this.RN-1;r++){
for(var i = r+1;i<this.RN;i++){
// i从r+1开始,到<RN结束
// 当前i位置的值是否为0
if(this.data[i][c] != 0){
// 值不为0的位置 i赋值nextr
var nextr = i;
break;
}else{
// 否则
// 将nextr赋值为-1
nextr = -1;
}
}// i循环结束
// 如果nextr的值为-1,证明后面的都是0,直接退出
if (nextr == -1) {
break;
}else{// 否则
// 找r行c列位置的值,判断是否为0
if(this.data[r][c] == 0) {// 如果是0 将nextr位置的值赋值给r位置
this.data[r][c] = this.data[nextr][c];
this.data[nextr][c] = 0;// 将nextr位置的值赋值为0
r--;// 将r留在原地
}else if(this.data[r][c] == this.data[nextr][c]){
// 否则 else if(c位置的值等于nextc位置的值)
this.data[r][c] *= 2;// 将c位置的值*2
this.score += this.data[r][c];
this.data[nextr][c] = 0;// 将nextc位置的值赋值为0
// c的循环结束
}
}
}
},
moveBottom(){
//下移所有行
//给数组拍照 var before = String(arr)
var before = String(this.data);
//c从0开始,到<CN结束
for(var c = 0;c<this.CN;c++){
this.moveBottomInRow(c);
}
//下移c列
//c循环结束
//移动结束以后,再给数组拍照 赋值给after
var after = String(this.data);
//如果data数组中的数据有变化 如果before != after
if(before != after){
//调用updateView更新页面中数据
this.randomNum();
this.updataView();
this.isend();
}
},
moveBottomInRow(c){
//c从CN-1开始,到>0
////查找r行c列 下一个不为0的位置nextr
for(var r = this.CN-1;r>0;r--){
for(var i = r-1;i>=0;i--){
// i从r-1开始,到>-1结束
// 当前i位置的值是否为0
if(this.data[i][c] != 0){
// 值不为0的位置 i赋值nextr
var nextr = i;
break;
}else{
// 否则
// 将nextr赋值为-1
nextr = -1;
}
}// i循环结束
// 如果nextr的值为-1,证明后面的都是0,直接退出
if (nextr == -1){
break;
}else{// 否则
// 找r行c列位置的值,判断是否为0
if(this.data[r][c] == 0) {// 如果是0 将nextr位置的值赋值给c位置
this.data[r][c] = this.data[nextr][c];
this.data[nextr][c] = 0;// 将nextr位置的值赋值为0
r++;// 将c留在原地
}else if(this.data[r][c] == this.data[nextr][c]){
// 否则 else if(c位置的值等于nextr位置的值)
this.data[r][c] *= 2;// 将c位置的值*2
this.score += this.data[r][c];
this.data[nextr][c] = 0;// 将nextr位置的值赋值为0
// r的循环结束
}
}
}
},
}
六.总结
1.布局,样式比较简单
2.js算法设计比较麻烦,构造函数不熟练,练习较少
3.游戏进行到一半,出现死循环,界面卡死不动,原因是循环里面的break位置没有放对地方