js实现2048小游戏

这是学完javascript基础,编写的入门级web小游戏

游戏规则:在玩法规则也非常的简单,一开始方格内会出现2或者4等这两个小数字,玩家只需要上下左右其中一个方向来移动出现的数字,所有的数字就会想滑动的方向靠拢,而滑出的空白方块就会随机出现一个数字,相同的数字相撞时会叠加靠拢,然后一直这样,不断的叠加最终拼凑出2048这个数字就算成功。但是我们的程序没有终点。

一、HTML部分

 

 1 <body>
 2     <!-- 分数行 -->
 3   <p id="scorePanel">Score:<span id="score"></span></p>
 4   <div id="gridPanel">
 5     <!-- 背景格 -->
 6     <div id="g00" class="grid"></div>
 7     <div id="g01" class="grid"></div>
 8     <div id="g02" class="grid"></div>
 9     <div id="g03" class="grid"></div>
10     
11     <div id="g10" class="grid"></div>
12     <div id="g11" class="grid"></div>
13     <div id="g12" class="grid"></div>
14     <div id="g13" class="grid"></div>
15     
16     <div id="g20" class="grid"></div>
17     <div id="g21" class="grid"></div>
18     <div id="g22" class="grid"></div>
19     <div id="g23" class="grid"></div>
20     
21     <div id="g30" class="grid"></div>
22     <div id="g31" class="grid"></div>
23     <div id="g32" class="grid"></div>
24     <div id="g33" class="grid"></div>
25 
26     <!-- 前景格 -->
27     <div id="c00" class="cell"></div>
28     <div id="c01" class="cell"></div>
29     <div id="c02" class="cell"></div>
30     <div id="c03" class="cell"></div>
31     
32     <div id="c10" class="cell"></div>
33     <div id="c11" class="cell"></div>
34     <div id="c12" class="cell"></div>
35     <div id="c13" class="cell"></div>
36     
37     <div id="c20" class="cell"></div>
38     <div id="c21" class="cell"></div>
39     <div id="c22" class="cell"></div>
40     <div id="c23" class="cell"></div>
41     
42     <div id="c30" class="cell"></div>
43     <div id="c31" class="cell"></div>
44     <div id="c32" class="cell"></div>
45     <div id="c33" class="cell"></div>
46 
47   </div>
48   <!-- 结束容器 -->
49   <div id="gameOver">
50     <!-- 半透明灰色背景 -->
51     <div>
52     </div>
53     <!--居中小窗口-->
54     <p>Game Over!<br />Score:<span id="finalScore"></span><br/><a class="button" onclick="game.start()">Try again!</a></p>    
55   </div>
56  </body>
View Code

 

二、CSS代码 

  1 #gridPanel{
  2     width:480px;
  3     height:480px;
  4     margin:0 auto;
  5     background-color:#bbada0;
  6     border-radius:10px;
  7     position:relative;
  8 }
  9 /* 单元格 */
 10 .grid,.cell{
 11     width:100px;
 12     height:100px;
 13     border-radius:6px;
 14 }
 15 /* 背景单元格 */
 16 .grid {
 17     margin:16px 0 0 16px;
 18     float:left;
 19     background-color:#ccc0b3;
 20 }
 21 /* 前景单元格 */
 22 .cell{
 23     /*    top:-464px;*/
 24     position:absolute;
 25     background-color:transparent;
 26     text-align:center;
 27     line-height:100px;
 28     font-size:60px;
 29 }
 30 /*为后续做数字移动动画 而准备*/
 31 /* 行top值 */
 32 #c00,#c01,#c02,#c03{top:16px;}
 33 #c10,#c11,#c12,#c13{top:132px;}
 34 #c20,#c21,#c22,#c23{top:248px;}
 35 #c30,#c31,#c32,#c33{top:364px;}
 36 /* 列left值 */
 37 #c00,#c10,#c20,#c30{left:16px;}
 38 #c01,#c11,#c21,#c31{left:132px;}
 39 #c02,#c12,#c22,#c32{left:248px;}
 40 #c03,#c13,#c23,#c33{left:364px;}
 41 /* 数字单元格分数背景颜色 */
 42 .n2{background-color:#eee3da}
 43 .n4{background-color:#ede0c8}
 44 .n8{background-color:#f2b179}
 45 .n16{background-color:#f59563}
 46 .n32{background-color:#f67c5f}
 47 .n64{background-color:#f65e3b}
 48 .n128{background-color:#edcf72}
 49 .n256{background-color:#edcc61}
 50 .n512{background-color:#9c0}
 51 .n1024{background-color:#33b5e5}
 52 .n2048{background-color:#09c}
 53 .n4096{background-color:#a6c}
 54 .n8192{background-color:#93c}
 55 .n8,.n16,.n32,.n64,.n128,.n256,.n512,.n1024,.n2048,.n4096,.n8192{color:#fff}
 56 .n1024,.n2048,.n4096,.n8192{font-size:40px}
 57 /**/
 58 
 59 /*分数显示*/
 60 p{
 61     width:470px;
 62     margin:15px auto 5px;
 63     font-family:Arial;
 64     font-weight:bold;
 65     font-size:40px;
 66 
 67     padding:5px;
 68     background-color:#ccc;
 69     border-radius:10px;
 70     color:#e4393c;
 71 }
 72 #score{
 73     color:#000;
 74 }
 75 
 76 /*Game Over*/
 77 #gameOver{
 78     width:100%;
 79     height:100%;
 80     position:absolute;
 81     top:0;
 82     left:0;
 83     display:none;
 84 }
 85 #gameOver>div{
 86     width:100%;
 87     height:100%;
 88     background-color:#555;
 89     opacity:.5;    
 90 }
 91 #gameOver>p{
 92     width:300px;
 93     height:200px;
 94     border:1px solid #edcf72;
 95     line-height:1.6em;
 96     text-align:center;
 97     background-color:#fff;
 98     position:absolute;
 99     border-radius:10px;
100     top:50%;
101     left:50%;
102     margin-top:-135px;
103     margin-left:-150px;
104 }
105 /*再来一次按钮*/
106 .button{
107     padding:10px;
108     border-radius:6px;
109     background-color:#9f8b77;
110     color:#fff;
111     cursor:pointer;
112 }
View Code

游戏开始样式

 三、Javascript代码

  1.定义二位数组初始化(保存游戏网格的数据状态)  

 1 data:[],//保存所有数字的二位数组
 2 rn:4,//定义行数
 3 cn:4,//定义列数
 4 
 5 for (var r=0; r<this.rn; r++)
 6 {
 7     this.data[r]=[];//向空数组中添加行
 8     for (var c=0; c<this.cn; c++)
 9     {
10         this.data[r][c]=0;//向当前空数组中加0
11     }
12 }

  2.随机的向界面添加2或4中的一个数

 1 randomNum:function(){//在随机空位置生成一个数
 2         //0-3随机生成一个行号row
 3         //0-3随机生成一个列号col
 4         //如果data[row][col]==0
 5         //    Math.random():
 6         //    随机生成一个数>=0.5?4:2
 7         //    放入data[row][col]
 8         if(this.isFull()) return -1;//判断二位数组中是否已满,满 返回-1
 9         while(true){
10             var row=parseInt(Math.random()*this.rn);
11             var col=parseInt(Math.random()*this.cn);
12             if(!this.data[row][col]){
13                 this.data[row][col]=Math.random()>=0.5?4:2;
14                 break;
15             }
16         }
17     },

  3.更新界面,根据数组中的状态更新界面

 1 updataView:function(){//将二位数组中每个格的数字放入前景格中
 2         //遍历二位素组中每个元素,
 3         for (var row=0; row<this.rn; row++)
 4         {
 5             for (var col=0; col<this.cn; col++)
 6             {
 7                 //获取要div标签
 8                 var div=document.getElementById("c"+row+col);
 9                 //console.log("c"+row+col);
10                 var curr=this.data[row][col];//当前元素
11                 //修改grid的数字内容
12                 div.innerHTML=curr!=0?curr:"";
13                 //修改div的class属性
14                 div.className=curr!=0?"cell n"+curr:"cell";
15             }
16         }
17         document.getElementById("score").innerHTML=this.score;

  4.实现左移动

    第一步:为了实现数据整体做移动,就要先实现一行可以向左移动,例如:如果我们现在移动0行,那么就要先查找当前元素右侧第一个不为0的数字,如果存在则返回不为0元素的下表,否则返回-1;

 1 //找当前位置右侧,下一个不为0的数
 2     getRightNext:function(row,col){
 3         //从col+1 遍历row行中剩余元素,<this.cn
 4         //    遍历到不为0的元素返回nextc-->下一个元素的列下标
 5         //    循环退出返回-1
 6         for ( col+=1; col<this.cn; col++)
 7         {
 8             if (this.data[row][col])
 9             {
10                 return col;
11             }
12         }
13         return -1;
14     },

    第二步:实现一行的做移动。建立循环从左到右判断当前行的每一个元素。如果当前元素值为0,且查找右侧元素的返回值为-1,则判断为这一整行为空;如果当前元素值为0,返回值不为空,则将返回下标元素的值赋给当前元素,并将返回下标元素的值设置为0,继续判断本元素;如果当前元素值==返回下表元素的值,则将当前元素的值*=2,并将返回元素的值设置为0;如果返回值为-1,则跳出循环。

 1 /*判断并向左移动指定行中的每个元素*/
 2     moveLeftInRow:function(row){
 3         //0开始,遍历row行中每一个元素,<cn-1结束
 4         //获得当前元素下一个不为0的元素的下标nextc
 5         //如果nextc==-1,说明右侧么有元素了,退出循环
 6         //否则 
 7         //    如果自己==0 则将下一个位置放入当前位置,下一个位置设置为零,col--重新检查
 8         //    如果当前位置==nextc的位置,将当前位置*=2;下一个位置设置为0;
 9         for (var col=0; col<this.cn-1; col++)
10         {
11             var nextc=this.getRightNext(row,col);
12             if (nextc==-1){return nextc;}
13             else if (this.data[row][col]==0)
14             {
15                 this.data[row][col]=this.data[row][nextc];
16                 this.data[row][nextc]=0;
17                 col--;
18             }
19             else if (this.data[row][col]==this.data[row][nextc])
20             {
21                 this.data[row][col]*=2;
22                 this.data[row][nextc]=0;
23                 this.score+=this.data[row][col];//将当前值累加到score属性上
24             }
25         }
26     },

    第三步:实现所有行的左移动。依次循环所有的行,实现元素左移动。

 1 /*向左移动所有行*/
 2     moveLeft(){
 3         var oldStr=this.data.toString();//判断字符串是否移动
 4         //循环遍历每一行
 5         // 调用moveLeftInRow
 6         //调用 randomNum(),随机生成一个数
 7         //调用 updataView(),更新页面
 8         for (var row=0; row<this.rn; row++)
 9         {
10             this.moveLeftInRow(row);
11         }
       //判断是否有元素左移动,如果没有则不生成数字和更新界面
12 if(oldStr!=this.data.toString()){ 13 this.randomNum(); 14 this.updataView(); 15 } 16 },

  以相应的方式实现右移动和上下移动  

  5.判断数组元素是否都不为零(即判断页面元素已满)

 1 isFull:function(){//判断当前数组是否已满
 2         for (var row=0; row<this.rn; row++)
 3         {
 4             for (var col=0; col<this.cn; col++)
 5             {
 6                 if (!this.data[row][col])
 7                 {
 8                     return false;
 9                 }
10             }
11         }
12         return true;
13     },

  6.判断游戏是否结束

//判断游戏状态是否结束
    isGameOver:function(){
        //如果数组没有满,则返回false
        if (!this.isFull())
        {
            return false;
        }else{
            //从左上角第一个元素开始,遍历二维数组,如果有相等的二个元素则游戏还可以继续,返回false
            for (var row=0; row<this.rn; row++)
            {
                for (var col=0; col<this.cn; col++)
                {
                    //如果当前元素不是最右侧元素
                    if(col<this.cn-1){
                        if(this.data[row][col]==this.data[row][col+1]){return false;}
                    }
                    if(row<this.rn-1){
                        if(this.data[row][col]==this.data[row+1][col]){return false;}
                    }
                }
            }
            return true;
        }
    },

  7.定义键盘接受事件(响应上下左右按键事件)

 1     //当按键盘按键时,触发移动
 2     document.onkeydown=function(){
 3         if(game.state==game.RUNNING){
 4             //获得事件对象
 5             var e=window.event||documents[0];
 6             //获得键盘号:e.keyCode 左37上38右39下40
 7             if (e.keyCode==37)//左键
 8             {
 9                 game.moveLeft();
10             }else if (e.keyCode==38)//上键
11             {
12                 game.moveUp();
13             }else if (e.keyCode==39)//右键
14             {
15                 game.moveRight();
16             }else if (e.keyCode==40)//下键
17             {
18                 game.moveDown();
19             }
20         }
21     }

后记,这个游戏是在学习完js基础 老师的带领下完成的,不足之处请谅解,欢迎提出更好的建议和方案,希望可以多多交流。

附带完整js代码:

  1 var game={
  2     data:[],//保存所有数字的二位数组
  3     rn:4,//定义行数
  4     cn:4,//定义列数
  5     state:0,//游戏当前状态:running|GameOver
  6     RUNNING:1,
  7     GAMEOVER:0,
  8     score:0,//定义总分数
  9     //判断游戏状态是否结束
 10     isGameOver:function(){
 11         //如果没有满,则返回false
 12         if (!this.isFull())
 13         {
 14             return false;
 15         }else{
 16             //从左上角第一个元素开始,遍历二维数组
 17             for (var row=0; row<this.rn; row++)
 18             {
 19                 for (var col=0; col<this.cn; col++)
 20                 {
 21                     //如果当前元素不是最右侧元素
 22                     if(col<this.cn-1){
 23                         if(this.data[row][col]==this.data[row][col+1]){return false;}
 24                     }
 25                     if(row<this.rn-1){
 26                         if(this.data[row][col]==this.data[row+1][col]){return false;}
 27                     }
 28                 }
 29             }
 30             return true;
 31         }
 32     },
 33     start:function(){//游戏开始方法
 34         this.state=this.RUNNING;
 35         //隐藏游戏结束界面
 36         document.getElementById("gameOver").style.display="none";
 37         this.data=[//初始化二位数组
 38             [0,2,2,2],
 39             [0,0,2,2],
 40             [4,2,0,0],
 41             [2,0,0,2]
 42         ];
 43         this.score=0;//重置分数为0
 44         
 45         for (var r=0; r<this.rn; r++)
 46         {
 47             this.data[r]=[];//向空数组中添加行
 48             for (var c=0; c<this.cn; c++)
 49             {
 50                 this.data[r][c]=0;//向当前空数组中加0
 51             }
 52         }
 53         
 54         //在两个随机位置生成2或4
 55         this.randomNum();
 56         this.randomNum();
 57         this.updataView();//将数字写入前景grid中
 58     },
 59     randomNum:function(){//在随机空位置生成一个数
 60         //0-3随机生成一个行号row
 61         //0-3随机生成一个列号col
 62         //如果data[row][col]==0
 63         //    Math.random():
 64         //    随机生成一个数>=0.5?4:2
 65         //    放入data[row][col]
 66         if(this.isFull()) return -1;//判断二位数组中是否已满,满 返回-1
 67         while(true){
 68             var row=parseInt(Math.random()*this.rn);
 69             var col=parseInt(Math.random()*this.cn);
 70             if(!this.data[row][col]){
 71                 this.data[row][col]=Math.random()>=0.5?4:2;
 72                 break;
 73             }
 74         }
 75     },
 76     isFull:function(){//判断当前数组是否已满
 77         for (var row=0; row<this.rn; row++)
 78         {
 79             for (var col=0; col<this.cn; col++)
 80             {
 81                 if (!this.data[row][col])
 82                 {
 83                     return false;
 84                 }
 85             }
 86         }
 87         return true;
 88     },
 89     updataView:function(){//将二位数组中每个格的数字放入前景格中
 90         //遍历二位素组中每个元素,
 91         for (var row=0; row<this.rn; row++)
 92         {
 93             for (var col=0; col<this.cn; col++)
 94             {
 95                 //获取要div标签
 96                 var div=document.getElementById("c"+row+col);
 97                 //console.log("c"+row+col);
 98                 var curr=this.data[row][col];//当前元素
 99                 //修改grid的数字内容
100                 div.innerHTML=curr!=0?curr:"";
101                 //修改div的class属性
102                 div.className=curr!=0?"cell n"+curr:"cell";
103             }
104         }
105         document.getElementById("score").innerHTML=this.score;
106         //判断并修改游戏状态
107         if (this.isGameOver())
108         {
109             this.state=this.GAMEOVER;
110             //修改节点style属性下的display的属性为"block"
111             document.getElementById("finalScore").innerHTML=this.score;
112             document.getElementById("gameOver").style.display="block";
113         }
114     },
115 
116     //实现左移动
117     //找当前位置右侧,下一个不为0的数
118     getRightNext:function(row,col){
119         //从col+1 遍历row行中剩余元素,<this.cn
120         //    遍历到不为0的元素返回nextc-->下一个元素的列下标
121         //    循环退出返回-1
122         for ( col+=1; col<this.cn; col++)
123         {
124             if (this.data[row][col])
125             {
126                 return col;
127             }
128         }
129         return -1;
130     },
131     /*判断并向左移动指定行中的每个元素*/
132     moveLeftInRow:function(row){
133         //0开始,遍历row行中每一个元素,<cn-1结束
134         //获得当前元素下一个不为0的元素的下标nextc
135         //如果nextc==-1,说明右侧么有元素了,退出循环
136         //否则 
137         //    如果自己==0 则将下一个位置放入当前位置,下一个位置设置为零,col--重新检查
138         //    如果当前位置==nextc的位置,将当前位置*=2;下一个位置设置为0;
139         for (var col=0; col<this.cn-1; col++)
140         {
141             var nextc=this.getRightNext(row,col);
142             if (nextc==-1){return nextc;}
143             else if (this.data[row][col]==0)
144             {
145                 this.data[row][col]=this.data[row][nextc];
146                 this.data[row][nextc]=0;
147                 col--;
148             }
149             else if (this.data[row][col]==this.data[row][nextc])
150             {
151                 this.data[row][col]*=2;
152                 this.data[row][nextc]=0;
153                 this.score+=this.data[row][col];//将当前值累加到score属性上
154             }
155         }
156     },
157     /*向左移动所有行*/
158     moveLeft(){
159         var oldStr=this.data.toString();//判断字符串是否移动
160         //循环遍历每一行
161         // 调用moveLeftInRow
162         //调用 randomNum(),随机生成一个数
163         //调用 updataView(),更新页面
164         for (var row=0; row<this.rn; row++)
165         {
166             this.moveLeftInRow(row);
167         }
168         if(oldStr!=this.data.toString()){
169             this.randomNum();
170             this.updataView();
171         }
172     },
173     //实现右移动
174     //找当前位置左侧,下一个不为0的数
175     getLeftNext:function(row,col){
176         //从col-1开始,遍历row行中剩余元素, >=0结束
177         for ( col-=1; col>=0; col--)
178         {
179             if (this.data[row][col])
180             {
181                 return col;
182             }
183         }
184         return -1;
185     },
186     /*判断并向右移动指定行中的每个元素*/
187     moveRightInRow:function(row){
188         for (var col=this.cn; col>0; col--)
189         {
190             var nextc=this.getLeftNext(row,col);
191             if (nextc==-1){return nextc;}
192             else if (this.data[row][col]==0)
193             {
194                 this.data[row][col]=this.data[row][nextc];
195                 this.data[row][nextc]=0;
196                 col++;
197             }
198             else if (this.data[row][col]==this.data[row][nextc])
199             {
200                 this.data[row][col]*=2;
201                 this.data[row][nextc]=0;
202                 this.score+=this.data[row][col];//将当前值累加到score属性上
203             }
204         }
205     },
206     /*向右移动所有行*/
207     moveRight(){
208         var oldStr=this.data.toString();
209         //循环遍历每一行
210         // 调用moveRightInRow()
211         //调用 randomNum(),随机生成一个数
212         //调用 updataView(),更新页面
213         for (var row=0; row<this.rn; row++)
214         {
215             this.moveRightInRow(row);
216         }
217         if(oldStr!=this.data.toString()){
218             this.randomNum();
219             this.updataView();
220         }
221     },
222     //实现向上移动
223     /*找到当前位置下侧,不为0的数*/
224     getDownNext:function(row,col){
225         //nextr从row+1开始,到<this.rn结束
226         for ( row+=1; row<this.rn; row++)
227         {
228             if (this.data[row][col])
229             {
230                 return row;
231             }
232         }
233         return -1;
234     },
235     /*判断并向上移动指定行中的每个元素*/
236     moveUpInCol:function(col){
237         //row从0开始,到 <rn-1,遍历每行元素
238         for (var row=0; row<this.rn-1; row++)
239         {
240             //先获得当前位置下方不为0的行nextr
241             var nextr=this.getDownNext(row,col);
242             if (nextr==-1){return nextr;}//如果next==-1
243             //否则
244             else if (this.data[row][col]==0)//如果当前位置等于0
245             {
246                 //当前位置替换为nextr位置的元素
247                 this.data[row][col]=this.data[nextr][col];
248                 //将nextr位置设置为0
249                 this.data[nextr][col]=0;
250                 row--;
251             }
252             //如果当前位置等于nextr位置
253             else if (this.data[row][col]==this.data[nextr][col])
254             {
255                 //将当前位置值*=2
256                 this.data[row][col]*=2;
257                 //将nextr位置设置为0
258                 this.data[nextr][col]=0;
259                 this.score+=this.data[row][col];//将当前值累加到score属性上
260             }
261         }
262     },
263     /*向上移动所有行*/
264     moveUp(){
265         var oldStr=this.data.toString();//判断字符串是否移动
266         //循环遍历每一行
267         // 调用moveUpInCol()
268         //调用 randomNum(),随机生成一个数
269         //调用 updataView(),更新页面
270         for (var col=0; col<this.cn; col++)
271         {
272             this.moveUpInCol(col);
273         }
274         if(oldStr!=this.data.toString()){
275             this.randomNum();
276             this.updataView();
277         }
278     },
279     //实现向下移动
280     /*找到当前位置上侧,不为0的数*/
281     getUpNext:function(row,col){
282         //nextr从row-1开始,到<this.rn结束
283         for ( row-=1; row>=0; row--)
284         {
285             if (this.data[row][col])
286             {
287                 return row;
288             }
289         }
290         return -1;
291     },
292     /*判断并向下移动指定行中的每个元素*/
293     moveDownInCol:function(col){
294         //row从rn-1开始
295         for (var row=this.rn-1; row>=0; row--)
296         {
297             var nextr=this.getUpNext(row,col);
298             //console.log((row+1)+"行"+(col+1)+"列:"+nextr);
299             if (nextr==-1){return nextr;}
300             else if (this.data[row][col]==0)
301             {
302                 this.data[row][col]=this.data[nextr][col];
303                 this.data[nextr][col]=0;
304                 row++;
305             }
306             else if (this.data[row][col]==this.data[nextr][col])
307             {
308                 this.data[row][col]*=2;
309                 this.data[nextr][col]=0;
310                 this.score+=this.data[row][col];//将当前值累加到score属性上
311             }
312         }
313     },
314     /*向下移动所有行*/
315     moveDown(){
316         var oldStr=this.data.toString();//判断字符串是否移动
317         //循环遍历每一行
318         // 调用moveDownInCol()
319         //调用 randomNum(),随机生成一个数
320         //调用 updataView(),更新页面
321         for (var col=0; col<this.cn; col++)
322         {
323             this.moveDownInCol(col);
324         }
325         if(oldStr!=this.data.toString()){
326             this.randomNum();
327             this.updataView();
328         }
329     }
330 }
331 //onload事件:当页面加载后自动执行
332 window.onload=function(){
333     game.start();//页面加载后,自动启动游戏
334     //console.log(game.getDownNext(0,0));
335     //console.log(game.getUpNext(3,1));
336     //当按键盘按键时,触发移动
337     document.onkeydown=function(){
338         if(game.state==game.RUNNING){
339             //获得事件对象
340             var e=window.event||documents[0];
341             //获得键盘号:e.keyCode 左37上38右39下40
342             if (e.keyCode==37)//左键
343             {
344                 game.moveLeft();
345             }else if (e.keyCode==38)//上键
346             {
347                 game.moveUp();
348             }else if (e.keyCode==39)//右键
349             {
350                 game.moveRight();
351             }else if (e.keyCode==40)//下键
352             {
353                 game.moveDown();
354             }
355         }
356     }
357 }
View Code

 

posted @ 2015-10-22 23:09  Medeor  阅读(905)  评论(0编辑  收藏  举报