写在开头
在这个项目之前,从来没想过可以自己完成一个网页端游戏. 对游戏实现的步骤也是一片迷茫,不过还好听着课,敲着代码,理解老师的思路,最后逐渐弄明白实现步骤,实现原理,可以说是收获颇丰的一次实践.
谨以此篇笔记,记录一下我的2048项目.——教程
一些收获:
2.简单游戏的原理:
让我想到了PS的蒙版,我们先做好背景,然后在上面设置很多看不见的小单元,当游戏需要时,这些小单元会显示出来,作为玩家则会看到生成了新的方块.
其次,当用户按键盘,就通过event
获取用户的操作,然后进行判断,2048小游戏使用的是类似于回合制的机制,不同于一些实时更新的游戏,这种更简单一些.
正文
一.目标
- 正常游戏开始、过程与结束
- 有积分牌
- 不会出现一个操作同行多格都发生变化:如一行:
2 2 4 8
,向左操作直接变成16 0 0 0
项目结构
引用了百度的在线JQuery库
二.项目截图
三.实现步骤
1.初始界面的实现
- 实现header块,包含标题,newgame按钮,分数面板
1
2
3
4
5
6
<header>
<h1>2048</h1>
<input type="button" id="btn-newgame" value="New Game" onclick="newgame()">
<!-- 数字这里用一个span隔出来是为了以后好直接控制数字变化 -->
<p>score: <span id="span-score">0</span></p>
</header>
header
对应的css样式如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
header{
text-align: center;
width: 500px;
/* margin + auto ,目的就是兼容各大浏览器让布局居中。 */
margin: 0 auto;
}
header h1{
font: bold 60px/20px Arial;
}
header #btn-newgame{
margin: 10px auto;
width: 100px;
padding: 10px;
background: #8f7a66;
font:Arial;
color: white;
border-radius: 10px;
}
#btn-newgame:hover{
background: #d3b599;
}
header p {
font: 25px/10px Arial;
margin: 20px auto;
}
- 实现棋盘初始布局
将棋盘以及16个空格初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="grid-container">
<div class="grid-cell" id="grid-cell-0-0"></div>
<div class="grid-cell" id="grid-cell-0-1"></div>
<div class="grid-cell" id="grid-cell-0-2"></div>
<div class="grid-cell" id="grid-cell-0-3"></div>
<div class="grid-cell" id="grid-cell-1-0"></div>
<div class="grid-cell" id="grid-cell-1-1"></div>
<div class="grid-cell" id="grid-cell-1-2"></div>
<div class="grid-cell" id="grid-cell-1-3"></div>
<div class="grid-cell" id="grid-cell-2-0"></div>
<div class="grid-cell" id="grid-cell-2-1"></div>
<div class="grid-cell" id="grid-cell-2-2"></div>
<div class="grid-cell" id="grid-cell-2-3"></div>
<div class="grid-cell" id="grid-cell-3-0"></div>
<div class="grid-cell" id="grid-cell-3-1"></div>
<div class="grid-cell" id="grid-cell-3-2"></div>
<div class="grid-cell" id="grid-cell-3-3"></div>
</div>
grid-container对应的css:
1
2
3
4
5
6
7
8
9
10
#grid-container{
width: 460px;
height: 460px;
padding: 20px;
margin: 50px auto;
background: #bbada0;
border-radius: 10px;
position: relative;
}
空格对应的css:
1
2
3
4
5
6
7
.grid-cell{
width: 100px;
height: 100px;
border-radius: 10px;
background: #ccc0b3;
position: absolute;
}
为什么16个空格只显示一个?因为还没有给空格绑定绝对定位.
通过循环动态给每个小格赋予”top”和”left”属性.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 赋予cell具体位置
function getPosTop(i,j) {
return 20+120*i;
}
function getPosLeft(i,j) {
return 20+120*j;
}
for(var i = 0;i < 4;i++){
for(var j = 0;j < 4;j++){
var gridCell = $('#grid-cell-'+i+'-'+j);
gridCell.css('top',getPosTop(i,j)).css('left',getPosLeft(i,j));
}
}
这样,棋盘初始化就完成了
2.初始化16个number-cell与二维数组board
为什么要设置number-cell?
因为我们刚刚设置好了棋盘的背景,number-cell就相当于棋子,每一个初始为零且透明.当游戏开始生成数字时,则对应显示出来.
简单来说,number-cell就是一一对应罩在grid-cell上,用来显示的格子.
二维数组board有什么用?
二维数组board,存储每个位置对应的数值.如图:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// 棋盘初始化
function init() {
// 把16个cell遍历,然后赋予位置
for(var i = 0;i < 4;i++){
board[i] = new Array();
for(var j = 0;j < 4;j++){
board[i][j]=0;
}
}
// 游戏逻辑:改变数组值,然后通过updateBoardView()反应到界面上.
updateBoardView();
updateScore(score);
}
// 动态创建16个number-cell,赋予对应位置
for(var i = 0 ; i<4;i++){
for(var j =0; j<4; j++){
$('#grid-container').append('<div class="number-cell" id="number-cell-'+i+'-'+j+'"></div>');
var numberCell = $('#number-cell-'+i+'-'+j);
if (!board[i][j]==0) {
numberCell.css('width','100px').css('height','100px')
.css('top',getPosTop(i,j)).css('left',getPosLeft(i,j))
.css('background',getNumberBackgroundColor(board[i][j]))
.css('color',getNumberColor(board[i][j]))
.text(board[i][j]);
}
}
}
// 判断数值确定背景色
function getNumberBackgroundColor(number){
switch (number) {
case 2:
return '#eee4da';
break;
case 4:
return '#ede0c8'
break;
case 8:
return '#f2b179'
break;
case 16:
return '#f59563'
break;
case 32:
return '#f67c5f'
break;
case 64:
return '#f65e3b'
break;
case 128:
return '#edcf72'
break;
case 256:
return '#edcc61'
break;
case 512:
return '#9c0'
break;
default:
break;
}
}
// 判断数值确定字体色
function getNumberColor(number){
if (number<=4) {
return 'black';
}
return 'white';
}
现在,如果设置board[0][0]=2
,那么就可以看到界面第一行第一列就会固定出现一个2
.
准备工作已经完成了,后面开始实现游戏内在逻辑.
3.更新数字显示与生成随机数
在操作过后,数值可能发生一些变化,这时候我们需要再写一个updateBoardView()
函数,将变化后的数值对应的信息更新到网页.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
function updateBoardView() {
// 删除所有之前的元素,这里相当于直接清空旧元素
$('.number-cell').remove();
// 动态创建16个number-cell,赋予对应位置
for(var i = 0 ; i<4;i++){
for(var j =0; j<4; j++){
$('#grid-container').append('<div class="number-cell" id="number-cell-'+i+'-'+j+'"></div>');
var numberCell = $('#number-cell-'+i+'-'+j);
if (!board[i][j]==0) {
numberCell.css('width','100px').css('height','100px')
.css('top',getPosTop(i,j)).css('left',getPosLeft(i,j))
.css('background',getNumberBackgroundColor(board[i][j]))
.css('color',getNumberColor(board[i][j]))
.text(board[i][j]);
}
}
}
}
function generateOneNumber() {
if (nospace(board)) {
return false;
}
// 生成随机数
var randNumber = Math.random()<0.5?2:4;
// 随机生成位置
var randx = Math.floor(Math.random()*4);
var randy = Math.floor(Math.random()*4);
// 判断生成位置是否为空,如果为空则跳出循环,不为空则继续生成随机数
while (true) {
if(board[randx][randy]==0){
board[randx][randy]=randNumber;
setTimeout(() => {
updateBoardView();
}, 300);
break;
}
// 随机生成位置
randx = Math.floor(Math.random()*4);
randy = Math.floor(Math.random()*4);
}
return true;
}
// 判断棋盘有无空格
function nospace(board) {
for(var i = 0;i < 4;i++){
for(var j = 0;j < 4;j++){
if(board[i][j]==0)
return false;
}
}
return true;
}
4.读取键盘操作,执行对应移动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// 读取键盘,对棋盘进行操作
$(document).keydown(function (e) {
switch (e.keyCode) {
case 37://Left
if (moveLeft()) {
generateOneNumber();
}
isgameover();
break;
case 38://Up
if (moveUp()) {
generateOneNumber();
}
isgameover();
break;
case 39://Right
if (moveRight()) {
generateOneNumber();
}
isgameover();
break;
case 40://Down
if (moveDown()) {
generateOneNumber();
}
isgameover();
break;
default:
break;
}
});
// 判断是否能够进行向左操作
function canMoveLeft(board) {
for(var i = 0 ; i<4 ; i++){
for(var j =1 ; j <4 ; j++){
if(board[i][j]!=0){
//除第一列其他三列,必须要有不为零的数,这个数的左边要有0,或跟他数值一样的数,才可以向左
if(board[i][j-1]==0||board[i][j]==board[i][j-1]){
return true;
}
}
}
}
return false;
}
// 判读水平方向中间是否有阻挡物
function noBlockHorizontal(row,colLeft,colRight,board){
for(var i = colLeft+1 ; i < colRight ; i++ ){
if(board[row][i]!=0){
return false;
}
}
return true;
}
// 数字滑动动画
function showMoveAnimation(fromx,fromy,tox,toy) {
var numberCell = $('#number-cell-'+fromx+'-'+fromy);
numberCell.animate({
top:getPosTop(tox,toy),
left:getPosLeft(tox,toy)
},200);
}
function moveLeft() {
//先判断是否能进行向左操作
if(!canMoveLeft(board)){
return false;
}
//移动
for(var i = 0 ; i < 4 ; i++){
for(var j = 1 ; j < 4 ; j++){
// 遍历除第一列所有number-cell
if(board[i][j]!=0){
for(var k = 0 ; k < j ; k++){
// 分别遍历上面获取的number-cell元素的左侧所有元素
if(board[i][k]==0&&noBlockHorizontal(i,k,j,board)){
showMoveAnimation(i,j,i,k);
board[i][k]=board[i][j];
board[i][j]=0;
continue;
}
else if(board[i][k]==board[i][j] && noBlockHorizontal(i,k,j,board) && !hasConflicted[i][k]){
showMoveAnimation(i,j,i,k);
board[i][k]*=2;
board[i][j]=0;
continue;
}
}
}
}
}
// 这里如果不用延迟函数,会看不到动画效果,因为动画还没执行完,新的board数值就已经更新到界面上了
setTimeout(() => {
updateBoardView();
}, 200);
return true;
}
对应向左移动,补全向右,向上,向下,游戏大体就基本完成
5.gameover以及得分
gameover
很好判断,条件是再也不能向任何方向移动,便自动判断结束.
1
2
3
4
5
6
7
function isgameover() {
if(!canMoveLeft(board) && !canMoveRight(board) && !canMoveUp(board) && !canMoveDown(board)){
setTimeout(() => {
alert('Game Over!');
}, 500);
}
}
①得分系统我们需要先设置一个全局变量score.
1
2
// score 记录得分
var score = 0;
②每次newgame
时重新清零.
③新增updateScore(score)方法:
1
2
3
4
// 更新分数到界面
function updateScore(score) {
$('#span-score').text(score);
}
④每次移动后如果发生数字一样,数值相加,则让score增加对应数值,在moveXXX()
方法中增加:
1
2
score+=board[i][k];
updateScore(score);
6.优化代码达到目标3,不会重复碰撞
新建二维数组 hasConflicted
:
1
2
// 设置二维数组,记录每一个小格有没有发生碰撞过
var hasConflicted = new Array();
在init()
方法中与board
一起初始化,初值均为false:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function init() {
// 清零分数
score=0;
// 把16个cell遍历,然后赋予位置
for(var i = 0;i < 4;i++){
board[i] = new Array();
hasConflicted[i] = new Array();
for(var j = 0;j < 4;j++){
board[i][j]=0;
hasConflicted[i][j]=false;
var gridCell = $('#grid-cell-'+i+'-'+j);
gridCell.css('top',getPosTop(i,j)).css('left',getPosLeft(i,j));
}
}
// 游戏逻辑:改变数组值,然后通过updateBoardView()反应到界面上.
updateBoardView();
updateScore(score);
}
在每个moveXXX()
方法中,当将要发生数值叠加,判断将要移向的这个空格是否已经发生过碰撞,并且当完成碰撞后,将对应的空格hasConflicted
为true:
1
2
3
4
5
6
7
8
9
10
11
else if(board[i][k]==board[i][j] && noBlockHorizontal(i,k,j,board) && !hasConflicted[i][k]){
showMoveAnimation(i,j,i,k);
board[i][k]*=2;
board[i][j]=0;
score+=board[i][k];
updateScore(score);
hasConflicted[i][k]=true;
continue;
}
记得在updateBoardView()
中将hasConflicted
重置,不然下次移动会出现错误.
源码
HTML文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Ivens' 2048</title>
<link rel="stylesheet" href="2048.css">
<script src="http://libs.baidu.com/jquery/1.9.1/jquery.js"></script>
<script src="support2048.js"></script>
<script src="showanimation2048.js"></script>
<script src="mian2048.js"></script>
</head>
<body>
<header>
<h1>2048</h1>
<input type="button" id="btn-newgame" value="New Game" onclick="newgame()">
<p>score: <span id="span-score">0</span></p>
</header>
<div id="grid-container">
<div class="grid-cell" id="grid-cell-0-0"></div>
<div class="grid-cell" id="grid-cell-0-1"></div>
<div class="grid-cell" id="grid-cell-0-2"></div>
<div class="grid-cell" id="grid-cell-0-3"></div>
<div class="grid-cell" id="grid-cell-1-0"></div>
<div class="grid-cell" id="grid-cell-1-1"></div>
<div class="grid-cell" id="grid-cell-1-2"></div>
<div class="grid-cell" id="grid-cell-1-3"></div>
<div class="grid-cell" id="grid-cell-2-0"></div>
<div class="grid-cell" id="grid-cell-2-1"></div>
<div class="grid-cell" id="grid-cell-2-2"></div>
<div class="grid-cell" id="grid-cell-2-3"></div>
<div class="grid-cell" id="grid-cell-3-0"></div>
<div class="grid-cell" id="grid-cell-3-1"></div>
<div class="grid-cell" id="grid-cell-3-2"></div>
<div class="grid-cell" id="grid-cell-3-3"></div>
</div>
</body>
</html>
CSS文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
header{
text-align: center;
width: 500px;
/* margin + auto ,目的就是兼容各大浏览器让布局居中。 */
margin: 0 auto;
}
header h1{
font: bold 60px/20px Arial;
}
header #btn-newgame{
margin: 10px auto;
width: 100px;
padding: 10px;
background: #8f7a66;
font:Arial;
color: white;
border-radius: 10px;
}
#btn-newgame:hover{
background: #d3b599;
}
header p {
font: 25px/10px Arial;
margin: 20px auto;
}
#grid-container{
width: 460px;
height: 460px;
padding: 20px;
margin: 50px auto;
background: #bbada0;
border-radius: 10px;
position: relative;
}
.grid-cell{
width: 100px;
height: 100px;
border-radius: 10px;
background: #ccc0b3;
position: absolute;
}
.number-cell{
position: absolute;
text-align: center;
font: bold 60px/100px Arial;
border-radius: 10px;
}
showanimation2048.js文件
1
2
3
4
5
6
7
8
9
// 数字滑动动画
function showMoveAnimation(fromx,fromy,tox,toy) {
var numberCell = $('#number-cell-'+fromx+'-'+fromy);
numberCell.animate({
top:getPosTop(tox,toy),
left:getPosLeft(tox,toy)
},200);
}
support2048.js文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// 赋予cell具体位置
function getPosTop(i,j) {
return 20+120*i;
}
function getPosLeft(i,j) {
return 20+120*j;
}
// 判断棋盘有无空格
function nospace(board) {
for(var i = 0;i < 4;i++){
for(var j = 0;j < 4;j++){
if(board[i][j]==0)
return false;
}
}
return true;
}
// 判断数值确定背景色
function getNumberBackgroundColor(number){
switch (number) {
case 2:
return '#eee4da';
break;
case 4:
return '#ede0c8'
break;
case 8:
return '#f2b179'
break;
case 16:
return '#f59563'
break;
case 32:
return '#f67c5f'
break;
case 64:
return '#f65e3b'
break;
case 128:
return '#edcf72'
break;
case 256:
return '#edcc61'
break;
case 512:
return '#9c0'
break;
default:
break;
}
}
// 判断数值确定字体色
function getNumberColor(number){
if (number<=4) {
return 'black';
}
return 'white';
}
// 判断是否能够进行向左操作
function canMoveLeft(board) {
for(var i = 0 ; i<4 ; i++){
for(var j =1 ; j <4 ; j++){
if(board[i][j]!=0){
//除第一列其他三列,必须要有不为零的数,这个数的左边要有0,或跟他数值一样的数,才可以向左
if(board[i][j-1]==0||board[i][j]==board[i][j-1]){
return true;
}
}
}
}
return false;
}
// 判断是否能够进行向右操作
function canMoveRight(board){
for(var i = 0 ; i < 4 ; i++){
for(var j = 2 ; j >=0 ; j--){
if(board[i][j]!=0){
if(board[i][j]==board[i][j+1] || board[i][j+1]==0){
return true;
}
}
}
}
return false;
}
// 判断是否能够进行向上操作
function canMoveUp(board) {
for(var j = 0 ; j < 4 ; j++){
for(var i = 1 ; i < 4 ; i++){
if(board[i][j]){
if(board[i][j]==board[i-1][j] || board[i-1][j]==0){
return true;
}
}
}
}
return false;
}
// 判断是否能够进行向下操作
function canMoveDown(board) {
for(var j = 0 ; j < 4 ; j++){
for(var i = 2; i >= 0 ; i--){
if(board[i][j]!=0){
if(board[i][j]==board[i+1][j] || board[i+1][j]==0){
return true;
}
}
}
}
return false;
}
// 判读水平方向中间是否有阻挡物
function noBlockHorizontal(row,colLeft,colRight,board){
for(var i = colLeft+1 ; i < colRight ; i++ ){
if(board[row][i]!=0){
return false;
}
}
return true;
}
// 判读垂直方向中间是否有阻挡物
function noBlockVertical(rowUp,rowDown,col,board) {
for(var i = rowUp+1 ; i < rowDown ; i++){
if(board[i][col]!=0){
return false;
}
}
return true;
}
main2048.js文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
// 二维数组 board 记录每个格子的数字
var board = new Array();
// score 记录得分
var score = 0;
// 设置二维数组,记录每一个小格有没有发生碰撞过
var hasConflicted = new Array();
$(function () {
newgame();
});
function newgame() {
// 初始化棋盘
init();
// 在随机位置生成两个随机数字
generateOneNumber();
generateOneNumber();
}
function init() {
// 清零分数
score=0;
// 把16个cell遍历,然后赋予位置
for(var i = 0;i < 4;i++){
board[i] = new Array();
hasConflicted[i] = new Array();
for(var j = 0;j < 4;j++){
board[i][j]=0;
hasConflicted[i][j]=false;
var gridCell = $('#grid-cell-'+i+'-'+j);
gridCell.css('top',getPosTop(i,j)).css('left',getPosLeft(i,j));
}
}
// 游戏逻辑:改变数组值,然后通过updateBoardView()反应到界面上.
updateBoardView();
updateScore(score);
}
function updateBoardView() {
// 删除所有之前的元素
$('.number-cell').remove();
// 动态创建16个number-cell,赋予对应位置
for(var i = 0 ; i<4;i++){
for(var j =0; j<4; j++){
$('#grid-container').append('<div class="number-cell" id="number-cell-'+i+'-'+j+'"></div>');
var numberCell = $('#number-cell-'+i+'-'+j);
if (!board[i][j]==0) {
numberCell.css('width','100px').css('height','100px')
.css('top',getPosTop(i,j)).css('left',getPosLeft(i,j))
.css('background',getNumberBackgroundColor(board[i][j]))
.css('color',getNumberColor(board[i][j]))
.text(board[i][j]);
hasConflicted[i][j]=false;
}
}
}
}
// 更新分数到界面
function updateScore(score) {
$('#span-score').text(score);
}
function generateOneNumber() {
if (nospace(board)) {
return false;
}
// 生成随机数
var randNumber = Math.random()<0.5?2:4;
// 随机生成位置
var randx = Math.floor(Math.random()*4);
var randy = Math.floor(Math.random()*4);
// 判断生成位置是否为空,如果为空则跳出循环,不为空则继续生成随机数
while (true) {
if(board[randx][randy]==0){
board[randx][randy]=randNumber;
setTimeout(() => {
updateBoardView();
}, 300);
// showNumberWithAnimation(randx,randy,randNumber);
break;
}
// 随机生成位置
randx = Math.floor(Math.random()*4);
randy = Math.floor(Math.random()*4);
}
return true;
}
// 读取键盘,对棋盘进行操作
$(document).keydown(function (e) {
switch (e.keyCode) {
case 37://Left
if (moveLeft()) {
generateOneNumber();
}
isgameover();
break;
case 38://Up
if (moveUp()) {
generateOneNumber();
}
isgameover();
break;
case 39://Right
if (moveRight()) {
generateOneNumber();
}
isgameover();
break;
case 40://Down
if (moveDown()) {
generateOneNumber();
}
isgameover();
break;
default:
break;
}
});
function moveLeft() {
//先判断是否能进行向左操作
if(!canMoveLeft(board)){
return false;
}
//移动
for(var i = 0 ; i < 4 ; i++){
for(var j = 1 ; j < 4 ; j++){
// 遍历除第一列所有number-cell
if(board[i][j]!=0){
for(var k = 0 ; k < j ; k++){
// 分别遍历上面获取的number-cell元素的左侧所有元素
if(board[i][k]==0&&noBlockHorizontal(i,k,j,board)){
showMoveAnimation(i,j,i,k);
board[i][k]=board[i][j];
board[i][j]=0;
continue;
}
else if(board[i][k]==board[i][j] && noBlockHorizontal(i,k,j,board) && !hasConflicted[i][k]){
showMoveAnimation(i,j,i,k);
board[i][k]*=2;
board[i][j]=0;
score+=board[i][k];
updateScore(score);
hasConflicted[i][k]=true;
continue;
}
}
}
}
}
// 这里如果不用延迟函数,会看不到动画效果,因为动画还没执行完,新的board数值就已经更新到界面上了
setTimeout(() => {
updateBoardView();
}, 200);
return true;
}
function moveRight() {
if ((!canMoveRight(board))) {
return false
}
for(var i = 0 ; i < 4 ;i++){
for(var j = 2 ; j>=0;j--){
if (board[i][j]!=0) {
for(var k = 3 ; k >j ; k--){
if(board[i][k]==0&&noBlockHorizontal(i,j,k,board)){
showMoveAnimation(i,j,i,k);
board[i][k]=board[i][j];
board[i][j]=0;
continue;
}else if(board[i][j]==board[i][k] && noBlockHorizontal(i,j,k,board) && !hasConflicted[i][k]){
showMoveAnimation(i,j,i,k);
board[i][k]*=2;
board[i][j]=0;
score+=board[i][k];
updateScore(score);
hasConflicted[i][k]=true;
continue;
}
}
}
}
}
setTimeout(() => {
updateBoardView();
}, 200);
return true;
}
function moveUp() {
if(!canMoveUp(board)){
return false;
}
for(var j=0;j<4;j++){
for(var i=1;i<4;i++){
for(var k =0;k<i;k++){
if (board[k][j]==0&&noBlockVertical(i,k,j,board)) {
showMoveAnimation(i,j,k,j);
board[k][j]=board[i][j];
board[i][j]=0;
continue;
}
else if (board[k][j]==board[i][j]&&noBlockVertical(i,k,j,board) && !hasConflicted[k][j]) {
showMoveAnimation(i,j,k,j);
board[k][j]*=2;
board[i][j]=0;
score+=board[k][j];
updateScore(score);
hasConflicted[k][j]=true;
continue;
}
}
}
}
setTimeout("updateBoardView()",200);
return true;
}
function moveDown() {
if (!canMoveDown(board)) {
return false;
}
for(var j=0;j<4;j++){
for(var i=2;i>=0;i--){
if(board[i][j]!=0){
for(var k=3;k>i;k--){
if (board[k][j]==0 && noBlockVertical(i,k,j,board)) {
showMoveAnimation(i,j,k,j);
board[k][j]=board[i][j];
board[i][j]=0;
continue;
}
else if (board[k][j]==board[i][j]&&noBlockVertical(i,k,j,board) && !hasConflicted[k][j]) {
showMoveAnimation(i,j,k,j);
board[k][j]*=2;
board[i][j]=0;
score+=board[k][j];
updateScore(score);
hasConflicted[k][j]=true;
continue;
}
}
}
}
}
setTimeout("updateBoardView()",200);
return true;
}
function isgameover() {
if(!canMoveLeft(board) && !canMoveRight(board) && !canMoveUp(board) && !canMoveDown(board)){
setTimeout(() => {
alert('Game Over!');
}, 500);
}
}