2048游戏项目
- UI——HTML/CSS
- 游戏主逻辑——js/jquery
- 动画效果逻辑——js/jquery
- 支撑逻辑——js
- 游戏数据
具体步骤:
1.搭建UI,利用相对与绝对定位在div元素框中排布出4*4的div元素
具体定位则需要进行动态计算,具体代码如下:
function init() {
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));
gridCell.css('left', getPosLeft(i, j));
}
}
以及
function getPosTop(i, j) {
return 20 + i * 120;
}
function getPosLeft(i, j) {
return 20 + j * 120;
}
根据元素的宽、高、和内边距进行动态计算来实现。
2.通过append()
方法向主体添加number-cell
属性。当数字元素不为0时则显示方块并覆盖原方块。同时,每一块数字都有自己各自的颜色属性。将格子位置以board[i][j]
数组形式表现,所有数据都存放在二维数组中。
3.随机在格子中生成数字(新游戏开始时随机生成两个数字,玩家每走一步则生成一个数字)
具体代码表现为:
function generateOneNumber() {
//格子全满,无新空间
if (nospace(board))
return false;
//随机一个位置
//生成0、1、2、3中的一位,且为int型
var randx = parseInt(Math.floor(Math.random() * 4));
var randy = parseInt(Math.floor(Math.random() * 4));
while (true) {
if (board[randx][randy] === 0)
break;
var randx = parseInt(Math.floor(Math.random() * 4));
var randy = parseInt(Math.floor(Math.random() * 4));
}
//随机一个数字
var randNumber = Math.random() < 0.5 ? 2 : 4;
//在随机位置显示随机数字
board[randx][randy] = randNumber;
showNumberWithAnimation(randx, randy, randNumber);
}
4.采用jquery中的animate
函数添加数字的生成动画:
function showNumberWithAnimation(i, j, randNumber) {
var numberCell = $('#number-cell-' + i + "-" + j);
numberCell.css('background-color', getNumberBackgroundColor(randNumber));
numberCell.css('color', getNumberColor(randNumber));
numberCell.text(randNumber);
numberCell.animate({
width: "100px",
height: "100px",
top: getPosTop(i, j),
left: getPosLeft(i, j)
}, 50);
}
5.移动功能的实现与具体逻辑
- 判断棋盘是否有空位可以向左移动,若为真则可移动
- 若移动函数返回true,则调用新数字生成函数
- 判断游戏是否结束
函数canMoveLeft()
判断是否可以移动原则:
- 仅需要判断非最左侧的12格即可
- 左边是否没有数字
- 左边数字是否与自己相等
具体代码为:
function canMoveLeft(board) {
for (var i = 0; i < 4; i++)
for (var j = 1; j < 4; j++)
if (board[i][j] != 0)
if (board[i][j - 1] === 0 || board[i][j - 1] === board[i][j])
return true;
return false;
}
函数moveLeft()
对每一个数字的左侧位置进行判断,看是否可能为落脚点:
- 落脚位置是否为空
- 落脚位置数字和待判定元素数字是否相等
- 移动路径中是否有障碍物
function moveLeft() {
if (!canMoveLeft(board))
return false;
//moveLeft
for (var i = 0; i < 4; i++)
for (var j = 1; j < 4; j++) {
if (board[i][j] != 0) {
for (var k = 0; k < j; k++) {
if (board[i][k] == 0 && noBlockHorizontal(i, k, j, board)) {
//move
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)) {
//move
showMoveAnimation(i, j, i, k);
//add
board[i][k] += board[i][j];
board[i][j] = 0;
continue;
}
}
}
}
setTimeout("updateBoardView()", 200);
return true;
}
其中判断移动路径中是否有其他元素的函数为noBlockHorizontal()
,具体代码如下:
function noBlockHorizontal(row, col1, col2, board) {
for (var i = col1 + 1; i < col2; 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);
}
5.计算分数
在moveLeft()
函数中加上分数计算部分,并用
function updateScore(score) {
$('#score').text(score);
}
来实现动态更新分数
6.判断游戏是否结束
- 棋盘上没有空格
- 且剩下元素无法移动
function isGameOver() {
if (nospace(board) && noMove(board)) {
gameover();
}
}
function gameover() {
alert('gameover')
}
其中noMove()
函数调用了之前判断在四个方向上是否可以位移四个函数,如下:
function noMove(board) {
if (canMoveLeft(board) ||
canMoveRight(board) ||
canMoveUp(board) ||
canMoveDown(board))
return false;
return true;
}
7.设置每一步每个小格只能叠加一次
创建二维数组hasConflicted
,在执行数字叠加的条件中加入新的判断条件!hasConflicted[i][k]
,若叠加已经发生,则将hasConflicted[i][k]
置为true。具体代码如下:
else if (board[i][k] === board[i][j] && noBlockHorizontal(i, k, j, board) && !hasConflicted[i][k]) {
//move
showMoveAnimation(i, j, i, k);
//add
board[i][k] += board[i][j];
board[i][j] = 0;
//add score
score += 10 * (board[i][k]);
updateScore(score);
hasConflicted[i][k] = true;
continue;
}
8.个性化定制
创建getNumberName()
函数,利用switch
函数为每一个数字返回一个对应的字符串,达到将每一块的元素内容以文字形式显示,以此实现个性化定制的目的。
Tips:
1.2048属于基于玩家响应的游戏循环(即每次都需要等待玩家操作后才会根据玩家操作来判断游戏状态)
2.由于运算速度很快,很多动画效果都没有来得及播完就运行了整个棋盘的更新函数updateBoardView()
。解决方案就是在调用更新函数时使用setTimeout
函数,根据动画效果的时长来延迟整个棋盘的更新时间,使动画能够完整播放。