这是学完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>
二、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 }
游戏开始样式

三、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 }
来源:https://www.cnblogs.com/Medeor/p/4903128.html
