大多数游戏采用的是基于时间的游戏循环
这种循环会随着时间的流逝更新游戏数据
根据时间流逝判断游戏是否结束
比如:星际争霸、魔兽争霸
另一类是基于玩家响应的游戏循环
这种游戏循环是基于玩家响应而更新游戏数据
玩家响应后继续判断游戏是否结束
比如:棋类游戏,2048游戏
那么我们怎么接收玩家的操作呢?也就是说,玩家如何控制游戏的进行?
2048游戏是玩家通过键盘上下左右操作,进而推进游戏的继续,于是,我们需要获取玩家的按键信息
这里,我们需要使用keydown事件
$(document).keydown(function(event){};
我们传入event参数,可以获得按键信息
接着,我们用switch语句来判断玩家按下的是上下左右的哪个键
$(document).keydown(function(event){
switch(event.keyCode){};
case 37: ;break;
case 38: ;break;
case 39: ;break;
case 40: ;break;
}
37、38、39、40 分别表示 左上右下
如果按下左,则会向左移动,创建一个moveleft()函数,让number-cell左移动
但是别忘了,我们移动之前,还得判断一下可不可以移动,如果左边没有空格,则移动不了。
所以我们还需要在moveleft()函数里判断是否能左移。我们将moveleft()放在if语句里,如果左移之后,则产生新的随机数,我们调用generateOneNumber()函数,每次新增一个随机数后,可能都会导致游戏的结束。我们还要判断,此时游戏是否结束了?
我们加上isgameover()函数来判断。
case 37:
if (moveleft()){
generateOneNumber();
isgameover();
};
break;
同理case 38、39、40,请自行补充完整。
重点来了,moveleft(同理另外三个)是这个小游戏的核心。
首先,我们需要判断当前局势是否能够左移,我们使用一个新的函数canMoveLeft(board),如果返回是真,则可以向左移动。如果返回假,则不可以左移,我们return false就可以了。
我们需要把当前局势(board)作为参数传进去。
if(!canMoveLeft(board))
return false;
一起分析一下,如何判断此时是否能向左移动?
其实第一列我们是不用判断的,因为第【0】列的数字不能再向左移了,所以我们只需要考虑绿色的12个格子是否能左移。
判断能否左移,首先得找左边有没有空位,或者根据游戏规则,左边的数字是否和自己相等,相等则可以左移合成更大的数字。
1. 该数字左边是否没有数字?
2. 左边数字是否和自己想等?
只有满足其中之一,则才能左移。
我们把 canMoveLeft(board) 放在support2048.js里,先进行一次双重循环,遍历12个元素。
这里需要注意:j是从1开始,因为第0列不需要考虑
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;
}
判断完是否能向左移动后,我们才真正开始向左移动了。
向左移动的函数 比 判断是否能向左移动的函数 要复杂得多。
落脚位置是否为空,即左边有空格子
如(1,0)(1,1)(1,2)都是空的,(1,3)格子可以移到(1,0)处
落脚位置数字和自身元素数字相等,则可以左移
如(1,2)和(1,3)相等,(1,3)格子可以左移与(1,2)结合成更大的数
移动的路途中不能有障碍物,
如(1,1)(1,2)都是空的,(1,3)格子可以移到(1,1)处
我们回到moveLeft()函数,我们依旧对3列格子进行遍历,依次循环,j仍然从1开始,只要待判断的(i,j)数不为0,则有机会左移。
我们需要对(i,j)左边的数依次考察,则需要引入一个新的索引变量k,再做一次循环,从0开始一直走到<j 的位置
这里是引用for(var i = 0; i < 4; i++)
for(var j = 1; j <4 ; j++){};
左移的第一种方式,我们要(i,k)的值为0,而且 k 到 j 之间没有障碍物。
要判断是否有障碍物,我们又要进入一个新的函数noBlockHorizontal(i,k,j,board), 引入4个参数,表示第 i 行,从 k 到 j 列是否有障碍物,如果没有障碍物,则可以产生一次移动。 如果产生了移动,则可以返回continue。
if(board[i][k] == 0 && noBlockHorizontal(i,k,j,board)){
//move
continue;
}
左移的第二种方式:board[i][k]==board[i][j] ,而且 k 到 j 之间没有障碍物
else if(board[i][k] == board[i][j] && noBlockHorizontal(i,k,j,board)){
//move
//add
continue;
第二种方式左移后,还会产生叠加,这个我们先留着,之后再完善。
我们在support2048.js 里定义noBlockHorizontal(i,k,j,board)函数
noBlockHorizontal ( row , col1 , col2 , board )
表示判断第 row 行, 第 col1 到第 col2 列 之间是否无障碍物。
此时我们需要判断第 k +1 列到第 j 列之间没有障碍物就可以了,一旦有一个数不为0 ,则返回false,否则返回true。
function noBlockHorizontal(row, col1, col2, board){
for(var i = col1 + 1 ; i < col2; i++){
if( board[row][i] != 0 )
return false;
}
return true;
}
下面回到moveLeft()这个函数继续编写,确定能向左移后,我们给移动编写一个动画效果,新增一个函数showMoveAnimation( i, j, i ,k);。
我们把board[i][j]的值赋值给board[i][k],并让board[i][j] 变为0
board[i][k] = board[i][j];
board[i][j] = 0;
第二种方式也是类似,移动时有一个动画效果。但是却还有一个add叠加效果。
我们都知道,叠加的数是两个数相加,相加后board[i][j]也为0
board[ i ] [ k ] += board[ i ] [ j ];
board[ i ] [ j ] = 0;
当然,这样也是可以的
board[ i ] [ k ] *= 2;
board[ i ] [ j ] = 0;
左移的两种情况是这样的:
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] *= 2;
board[i][j] = 0;
continue;
}
我们再去showanimateion2048.js定义showMoveAnimation(i, j, i, k) 函数
function showMoveAnimation(fromx, fromy, tox, toy){};
首先我们要传入原始xy,和目的xy,再引入numberCell。使用animate()动画函数加上getPosTop()和getPosLeft()来实现。
function showMoveAnimation(fromx, fromy, tox, toy){
var numberCell = $('#number-cell-' + fromx + '-' + fromy);
numberCell.animate({
top:getPosTop(tox,toy),
left:getPosLeft(tox,toy)
},200);
}
至此,我们仍遗留有2个小问题
function moveleft(){
if(!canMoveLeft(board))
return false;
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] *= 2;
board[i][j] = 0;
continue;
}
}
}
}
updateBoardView();
return true;
}
function isgameover() { };
我们发现我们设置的showMoveAnimation()动画似乎没有起作用,这是为什么呢?
因为我们虽然设置了200ms的动画,但是对于for循环,计算机在瞬间就可以执行完毕
完成后,将立即执行updateBoardView()函数
动画还没来得及播放,就已经被刷新了。
为了解决这个问题,我们需要执行完动画,再执行updateBoardView()这个函数。
我们使用settimeout()函数,使其等待200ms再执行updateBoardView()。
setTimeout( 'updateBoardView()',200);
到这里,大家可以再细细琢磨一下。如果大家都琢磨透了,就自己尝试写一下
- moveLeft()以外的另外三个函数
- canMoveLeft()以外三个函数
- 在垂直方向的noBlockVertical()函数
特别要分清楚 i 、j 、k 的位置,不清楚的最好画个草稿理清一下思路
如果写好的,现在试着运行一下,我们已经可以上下左右操作游戏了
如果报错的可以拉到文章最后,跟代码比较一下,自己哪里出错了
弄清楚为什么会错,再继续往下
我们现在需要对整个游戏进行收尾工作
还记得我们在 main2048.js 里的**isgameover()**函数吗?
满足游戏结束需要同时满足什么条件呢?
nospace()函数我们之前已经定义过了
我们现在新定义一个nomove函数,用来表示四周的数与本身不能再结合导致不能移动操作
满足两个条件,则游戏真正**gameover()**了
function isgameover(){
if( nospace(board) && nomove(board)){
gameover();
}
}
下面我们来写gameover()
function gameover() {
alert("Game over!")
}
如果游戏结束,就弹出gameover提示
我们对这个函数不做过多的设计,大家可以自己发挥
接下来我们来写nomove(),我们放在support2048.js里
其实有了之前的函数作为基础,nomove()函数非常好写
我们思考一下,移动指的是什么呢?
->>> 即 能 向上 or 向下 or 向左 or 向右 移动
->>> 即 **canMoveLeft(board) || canMoveRight(board) || canMoveUp(board) || canMoveDown(board) **
所以只要判断能移动,函数就return false,指的是可以移动
相反,如果上下左右都不能移动,则return true,指的是不可以移动
function nomove( board ){
if( canMoveLeft( board ) ||
canMoveRight( board ) ||
canMoveUp( board ) ||
canMoveDown( board ) )
return false;
return true;
}
到这里,细心的朋友会发现有点小问题,就是和之前的这个问题一样 ↓
setTimeout(“updateBoardView()”,200);
于是我们把这里的 随机生成一个数的generateOneNumber()函数 和 isgameover()函数
都加上setTimeout(),这样动画就可以流畅播放了
好了,这一小节结束了,下一小节就是最后最后的工作了
大家坚持到这里真的不容易呀
赶紧来检查一下自己的代码吧
由于 2048.html 和 2048.css 两个文件不变
我就不放上来了,需要的朋友可以去上一节查看
/**
* Created by liuyubobobo on 14-4-11.
* my site: http://www.liuyubobobo.com
*/
var board = new Array();
var score = 0;
$(document).ready(function(){
newgame();
});
function newgame(){
//初始化棋盘格
init();
//在随机两个格子生成数字
generateOneNumber();
generateOneNumber();
}
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 ) );
}
for( var i = 0 ; i < 4 ; i ++ ){
board[i] = new Array();
for( var j = 0 ; j < 4 ; j ++ ){
board[i][j] = 0;
}
}
updateBoardView();
}
function updateBoardView(){
$(".number-cell").remove();
for( var i = 0 ; i < 4 ; i ++ )
for( var j = 0 ; j < 4 ; j ++ ){
$("#grid-container").append( '+i+'-'+j+'">' );
var theNumberCell = $('#number-cell-'+i+'-'+j);
if( board[i][j] == 0 ){
theNumberCell.css('width','0px');
theNumberCell.css('height','0px');
theNumberCell.css('top',getPosTop(i,j) + 50 );
theNumberCell.css('left',getPosLeft(i,j) + 50 );
}
else{
theNumberCell.css('width','100px');
theNumberCell.css('height','100px');
theNumberCell.css('top',getPosTop(i,j));
theNumberCell.css('left',getPosLeft(i,j));
theNumberCell.css('background-color',getNumberBackgroundColor( board[i][j] ) );
theNumberCell.css('color',getNumberColor( board[i][j] ) );
theNumberCell.text( board[i][j] );
}
}
}
function generateOneNumber(){
if( nospace( board ) )
return false;
//随机一个位置
var randx = parseInt( Math.floor( Math.random() * 4 ) );
var randy = parseInt( Math.floor( Math.random() * 4 ) );
while( true ){
if( board[randx][randy] == 0 )
break;
randx = parseInt( Math.floor( Math.random() * 4 ) );
randy = parseInt( Math.floor( Math.random() * 4 ) );
}
//随机一个数字
var randNumber = Math.random() < 0.5 ? 2 : 4;
//在随机位置显示随机数字
board[randx][randy] = randNumber;
showNumberWithAnimation( randx , randy , randNumber );
return true;
}
$(document).keydown( function( event ){
switch( event.keyCode ){
case 37: //left
if( moveLeft() ){
setTimeout("generateOneNumber()",210);
setTimeout("isgameover()",300);
}
break;
case 38: //up
if( moveUp() ){
setTimeout("generateOneNumber()",210);
setTimeout("isgameover()",300);
}
break;
case 39: //right
if( moveRight() ){
setTimeout("generateOneNumber()",210);
setTimeout("isgameover()",300);
}
break;
case 40: //down
if( moveDown() ){
setTimeout("generateOneNumber()",210);
setTimeout("isgameover()",300);
}
break;
default: //default
break;
}
});
function isgameover(){
if( nospace(board) && nomove(board)){
gameover();
}
}
function gameover() {
alert("Game over!")
}
function gameover(){
alert('gameover!');
}
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;
}
function moveRight(){
if( !canMoveRight( board ) )
return false;
//moveRight
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][k] == board[i][j] && noBlockHorizontal( i , j , k , board ) ){
showMoveAnimation( i , j , i , k);
board[i][k] *= 2;
board[i][j] = 0;
continue;
}
}
}
}
setTimeout("updateBoardView()",200);
return true;
}
function moveUp(){
if( !canMoveUp( board ) )
return false;
//moveUp
for( var j = 0 ; j < 4 ; j ++ )
for( var i = 1 ; i < 4 ; i ++ ){
if( board[i][j] != 0 ){
for( var k = 0 ; k < i ; k ++ ){
if( board[k][j] == 0 && noBlockVertical( j , k , i , 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( j , k , i , board ) ){
showMoveAnimation( i , j , k , j );
board[k][j] *= 2;
board[i][j] = 0;
continue;
}
}
}
}
setTimeout("updateBoardView()",200);
return true;
}
function moveDown(){
if( !canMoveDown( board ) )
return false;
//moveDown
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( j , i , k , 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( j , i , k , board ) ){
showMoveAnimation( i , j , k , j );
board[k][j] *= 2;
board[i][j] = 0;
continue;
}
}
}
}
setTimeout("updateBoardView()",200);
return true;
}
function getPosTop(i,j){
return 20 + i*120;
}
function getPosLeft(i,j){
return 20 + j*120;
}
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;
case 1024:return "#33b5e5"; break;
case 2048:return "#09c"; break;
case 4096:return "#a6c"; break;
case 28192:return "#93c"; break;
}
return "black";
}
function getNumberColor(number) {
if(number <= 4)
return "#776e65";
return "white";
}
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 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;
}
function canMoveRight( board ){
// 只需要遍历12个元素,因为最右边的元素不能向左移了
// 右边是否没有数字?
// 右边数字是否和自己相等?
for(var i = 0 ; i < 4 ;i ++)
for( var j = 2 ; j >= 0 ; j --) //第0列不需要遍历
if( board[i][j] !=0 )
if( board[i][j+1] == 0 || board[i][j+1] == board[i][j] )
return true;
return false;
}
function canMoveUp( board ){
// 只需要遍历12个元素,因为最上边的元素不能向上移了
// 上边是否没有数字?
// 上边数字是否和自己相等?
for(var j = 0 ; j < 4 ;j ++)
for( var i = 1 ; i < 4 ; i ++) //第0行不需要遍历
if( board[i][j] !=0 )
if( board[i-1][j] == 0 || board[i-1][j] == board[i][j] )
return true;
return false;
}
function canMoveDown( board ){
// 只需要遍历12个元素,因为最下边的元素不能向下移了
// 下边是否没有数字?
// 下边数字是否和自己相等?
for(var j = 0 ; j < 4 ;j ++)
for( var i = 2 ; i >= 0 ; i --) //第0行不需要遍历
if( board[i][j] != 0 )
if( board[i+1][j] == 0 || board[i+1][j] == board[i][j] )
return true;
return false;
}
function noBlockHorizontal(row, col1, col2, board){
for(var i = col1 + 1 ; i < col2; i++)
if( board[row][i] != 0 )
return false;
return true;
}
function noBlockVertical(col, row1, row2, board){
for(var i = row1 + 1 ; i < row2; i++)
if( board[i][col] != 0 )
return false;
return true;
}
function nomove() {
if(canMoveDown(board) || canMoveLeft(board) || canMoveRight(board) || canMoveUp(board) || )
return false;
return true;
}
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);
}
function showMoveAnimation(fromx, fromy, tox, toy){
var numberCell = $('#number-cell-' + fromx + '-' + fromy);
numberCell.animate({
top:getPosTop(tox,toy),
left:getPosLeft(tox,toy)
},200);
}
我们最后一小节再见哦!
html+css+js适合前端小白的实战全解(超详细)——2048小游戏(五)