2048网页版html项目报告,jQuery编写网页版2048小游戏

大致介绍

看了一个实现网页版2048小游戏的视频,觉得能做出自己以前喜欢玩的小游戏很有意思便自己动手试了试,真正的验证了这句话-不要以为你以为的就是你以为的,看视频时觉得看懂了,会写了,但是自己实现起来会遇到各种问题。比如,在最后判断游戏是否结束的时候,我写的语句语法是对的,但就是不执行。最后通过对视频源码的分析对比,发现原作者写的一个setTimeout定时器有额外的意思,本来我以为它就是简单的一个延时动画,其实他是在等待另外一个函数执行完毕。-_-||。最后还是很高兴能写出来,也改进了一些源代码的不足。

这篇博客并不是详细的讲解,只是大致介绍函数的作用,其中实现的细节注释中有解释,网上的这个源码有点乱,如果想看比较整齐的源码或者视频的可以QQ联系我(免费)(找共同学习的伙伴)

思路

这个小游戏可以抽象化分为3层(我觉得这样能更好理解)

◆最底下的一层是基本的样式(可见的)

◆中间的层是最主要的,是一个4x4的二维数组,游戏中我们都是对这个二维数组进行操作(不可见的)

◆最上面的一层也是一个4x4的二维数组,它只是根据第二层数组的每个数显示样式(可见的)

我们通过最底下的一层显示最基本的16个小方格,通过键盘的按键或者手指在屏幕的滑动来操作中间层的数组,最后在通过最上面的一层显示出数字

基本结构与样式

基本的结构和样式都挺简单,直接看代码

结构:

样式:

*{

margin: 0;

padding: 0;

}

#test2048{

font-family: Arial;

margin: 0 auto;

text-align: center;

}

#header{

margin: 20px;

}

#header a{

font-family: Arial;

text-decoration: none;

display: block;

color: white;

margin: 20px auto;

width: 125px;

height: 35px;

text-align: center;

line-height: 40px;

background-color: #8f7a66;

border-radius: 10px;

font-size: 15px;

}

#header p{

font-family: Arial;

font-size: 20px;

}

#container{

width: 460px;

height: 460px;

background-color: #bbada0;

margin: 0 auto;

border-radius: 10px;

position: relative;

padding: 20px;

}

.cell{

width: 100px;

height: 100px;

border-radius: 6px;

background-color: #ccc0b3;

position: absolute;

}

从CSS样式可以看出,我们并没有对每个格子的位置进行设置,因为如果用CSS给每个格子设置样式代码量太大,而且他们的位置有一定的规律,所以我们可以用js循环来完成每个格子样式的设置

代码:

// 初始化棋盘格

function initialize(){

for(var i=0;i<4;i++){

for(var j=0;j<4;j++){

// 设置棋盘格的位置

var everyCell = $('#cell-'+ i +'-'+ j);

everyCell.css({top:getPos(i),left:getPos(j)});

}

}

}

// 获取位置

function getPos(num){

return 20 + num*120;

}

这样我们的第一层就好了

效果:

2048网页版html项目报告,jQuery编写网页版2048小游戏_第1张图片

现在构造第二层,即构建一个4x4的值全部为0的数组,由于在构造第二层时,有两层循环,所以我们可以在构造第一层时也能构造第二层

第三层是用js生成16个格子,它和第一层的16个格子一一对应

代码:

// 数字格

function numFormat(){

for(var i=0;i<4;i++){

for(var j=0;j<4;j++){

$('#container').append('

// 设置数字格的位置,样式

var everyNumber = $('#number-'+ i +'-'+ j);

if(checkerboard[i][j] == 0){

everyNumber.css({

width:'0px',

height:'opx',

top:getPos(i) + 50,

left:getPos(j) + 50

})

}else{

everyNumber.css({

width:'100px',

height:'100px',

top:getPos(i),

left:getPos(j),

backgroundColor:getBackgroundColor(checkerboard[i][j]),

color:getColor(checkerboard[i][j])

});

everyNumber.text(checkerboard[i][j]);

}

}

}

}

// 获取相应数字的背景颜色

function getBackgroundColor(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 8192:return "#93c";break;

}

}

// 设置相应数字的文字颜色

function getColor(number){

if (number <= 4) {

return "#776e65"

}

return "white";

}

初始化

在每次游戏重新开始时,都会在随机的位置出现两个随机的数字,我们写一个在随机位置出现一个随机数的函数,只要调用两次就可以实现了

代码:

// 随机的在一个位置上产生一个数字

function randomNum(){

// 随机产生一个坐标值

var randomX = Math.floor(Math.random() * 4);

var randomY = Math.floor(Math.random() * 4);

// 随机产生一个数字(2或4)

var randomValue = Math.random() > 0.5 ? 2 : 4;

// 在数字格不为0的地方生成一个随机数字

while(true){

if(checkerboard[randomX][randomY] == 0){

break;

}else{

var randomX = Math.floor(Math.random() * 4);

var randomY = Math.floor(Math.random() * 4);

}

}

// 将随机产生的数字显示在随机的位置上

checkerboard[randomX][randomY] = randomValue;

// 动画

randomNumAnimate(randomX,randomY,randomValue);

}

// 随机产生数字的动画

function randomNumAnimate(randomX,randomY,randomValue){

var randomnum = $('#number-'+ randomX +'-'+ randomY);

randomnum.css({

backgroundColor:getBackgroundColor(randomValue),

color:getColor(randomValue),

})

.text(randomValue)

.animate({

width:'100px',

height:'100px',

top:getPos(randomX),

left:getPos(randomY)

},50);

}

基本操作

我们通过switch循环,来根据用户不同的输入进行不同的操作

代码:

// 获取键盘事件,检测不同的按键进行不同的操作

$(document).keydown(function(event){

switch(event.keyCode){

case 37://左

if(canMoveLeft(checkerboard)){

// 如果可以向左移动

MoveLeft();

// 向左移动

setTimeout(function(){

randomNum();

},200);

// 随机产生一个数字

}

break;

case 38://上

if(canMoveUp(checkerboard)){

// 如果可以向上移动

MoveUp();

// 向上移动

setTimeout(function(){

randomNum();

},200);

// 随机产生一个数字

}

break;

case 39://右

if(canMoveRight(checkerboard)){

// 如果可以向右移动

MoveRight();

// 向右移动

setTimeout(function(){

randomNum();

},200);

// 随机产生一个数字

}

break;

case 40://下

if(canMoveDown(checkerboard)){

// 如果可以向下移动

MoveDown();

// 向下移动

setTimeout(function(){

randomNum();

},200);

// 随机产生一个数字

}

break;

default:

break;

}

});

由于数字格的移动只有左、上、右、下四种方式,并且他们都是大同小异的,所以就拿向左移动为例,

向左移动,我们首先需要判断它是否能向左移动,能向左移动有两种情况

第一种:当前格子的左边的格子是空的即值为0

第二种:当前格子的值和左边格子的值相同

由于向左移动,所以第一列的格子不可能向左移动,所以不需要判断

代码:

// 判断是否可以向左移动

function canMoveLeft(checkerboard){

for(var i=0;i<4;i++){

for(var j=1;j<4;j++){

if(checkerboard[i][j] != 0){

// 如果这个数字格它左边的数字格为空或者左边的数字格和它相等,则可以向左移动

if(checkerboard[i][j-1] == 0 || checkerboard[i][j] == checkerboard[i][j-1]){

return true;

}

}

}

}

return false;

}

判断能否向左移动后,我们就要对可以移动的格子进行移动,这里需要特别注意,向哪个方向移动就要先从哪个方向开始判断

代码:

// 向左移动

function MoveLeft(){

for(var i=0;i<4;i++){

for(var j=1;j<4;j++){

if(checkerboard[i][j] != 0){

for(var k=0;k

if(checkerboard[i][k] == 0 && noMiddleNumRow(i,k,j,checkerboard)){

moveAnimation(i,j,i,k);

checkerboard[i][k] = checkerboard[i][j];

checkerboard[i][j] = 0;

}else if(checkerboard[i][k] == checkerboard[i][j] && noMiddleNumRow(i,k,j,checkerboard) && !hasConflicted[i][k]){

moveAnimation(i,j,i,k);

checkerboard[i][k] += checkerboard[i][j];

checkerboard[i][j] = 0;

}

}

}

}

}

// 设置刷新的时间是为了让运动的动画走完在进行更新数字格,否则数字格运动的动画将会被打断

setTimeout(function(){

numFormat();

},200);

}

// 判断中间的数字格是否为0(行)

function noMiddleNumRow(row,col1,col2,checkerboard){

for(var i=col1+1;i

if(checkerboard[row][i] != 0){

return false;

}

}

return true;

}

将上、右、下四个方向写完以后,游戏基本的操作就已经完成了。

游戏分数和判断游戏结束

游戏的分数是每个相加的数的和,所以我们在每个数相加的时候更新分数就可以了

代码:

// 更新分数

score += checkerboard[k][j];

updateScore(score);

// 设置分数

function updateScore(num){

$('#score').text(num);

}

判断游戏是否结束很简单,用我们之前定义的方法就可以实现

代码:

// 判断游戏是否结束

function wheGameOver(checkerboard){

if(!canMoveLeft(checkerboard) && !canMoveUp(checkerboard) && !canMoveRight(checkerboard) && !canMoveDown(checkerboard) ){

showGameOver();

}

}

// 显示游戏结束

function showGameOver(){

$('#container').append("

最终得分

"+ score +" 重新开始游戏
")

}

// 重新开始游戏

function resert(){

$('#gameover').remove();

newgame();

}

最后优化

1、游戏中会出现一次移动,一个数会被累加很多次

在原游戏中,每个数在每次操作中只能累加一次,所以我们在定义一个4x4的值为false的数组,与中间层的数组一一对应,专门用来防止一个数的多次累加,如果是false则可以累加,并将值改为false,否则不可以累加

2、结束死循环

由于在设置随机数的时候用到了一个死循环,但是在游戏结束后,该循环还在,所以我们在死循环中在添加一个条件,如果游戏结束就跳出循环

3、最后的结束游戏提示不执行

case 37://左

if(canMoveLeft(checkerboard)){

// 如果可以向左移动

MoveLeft();

// 向左移动

setTimeout(function(){

wheGameOver(checkerboard)

},300);

// 判断游戏是否结束,这里设置延时是因为要等到随机产生数字后再进行判断,如果不加

// 延时,则最后一次的判断因为canMoveLeft(checkerboard)为false就不会再执行了

setTimeout(function(){

randomNum();

},200);

// 随机产生一个数字

}

break;

从代码中可以看出,判断游戏是否结束是在随机产生一个数字前执行的,所以在判断游戏结束时,总是有一个空的格子,所以代码执行后认为游戏没有结束,但是当这个随机数字产生后,所有的格子不能移动,当我们按键时,if条件不通过,判断游戏是否结束的函数不能执行。所以我们要给判断游戏结束的函数设置定时器,让他在随机产生一个数字后再进行判断

4、在移动端可以执行

由于原作者没有写有关移动端的操作,所以我在网上找的判断移动端触屏手机滑动位置的代码,加入了游戏的事件就可以执行了

//返回角度

function GetSlideAngle(dx, dy) {

return Math.atan2(dy, dx) * 180 / Math.PI;

}

//根据起点和终点返回方向 1:向上,2:向下,3:向左,4:向右,0:未滑动

function GetSlideDirection(startX, startY, endX, endY) {

var dy = startY - endY;

var dx = endX - startX;

varresult = 0;

//如果滑动距离太短

if(Math.abs(dx) < 2 && Math.abs(dy) < 2) {

returnresult;

}

var angle = GetSlideAngle(dx, dy);

if(angle >= -45 && angle < 45) {

result = 4;

}else if (angle >= 45 && angle < 135) {

result = 1;

}else if (angle >= -135 && angle < -45) {

result = 2;

}

else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) {

result = 3;

}

return result;

}

//滑动处理

var startX, startY;

document.addEventListener('touchstart',function (ev) {

startX = ev.touches[0].pageX;

startY = ev.touches[0].pageY;

}, false);

document.addEventListener('touchend',function (ev) {

var endX, endY;

endX = ev.changedTouches[0].pageX;

endY = ev.changedTouches[0].pageY;

var direction = GetSlideDirection(startX, startY, endX, endY);

switch(direction) {

case 0:

//没滑动

break;

case 1:

if(canMoveUp(checkerboard)){

// 如果可以向上移动

MoveUp();

// 向上移动

setTimeout(function(){

wheGameOver(checkerboard)

},300);

// 判断游戏是否结束

setTimeout(function(){

randomNum();

},200);

// 随机产生一个数字

}

break;

case 2:

if(canMoveDown(checkerboard)){

// 如果可以向下移动

MoveDown();

// 向下移动

setTimeout(function(){

wheGameOver(checkerboard)

},300);

// 判断游戏是否结束

setTimeout(function(){

randomNum();

},200);

// 随机产生一个数字

}

break;

case 3:

if(canMoveLeft(checkerboard)){

// 如果可以向左移动

MoveLeft();

// 向左移动

setTimeout(function(){

wheGameOver(checkerboard)

},300);

// 判断游戏是否结束,这里设置延时是因为要等到随机产生数字后再进行判断,如果不加

// 延时,则最后一次的判断因为canMoveLeft(checkerboard)为false就不会再执行了

setTimeout(function(){

randomNum();

},200);

// 随机产生一个数字

}

break;

case 4:

if(canMoveRight(checkerboard)){

// 如果可以向右移动

MoveRight();

// 向右移动

setTimeout(function(){

wheGameOver(checkerboard)

},300);

// 判断游戏是否结束

setTimeout(function(){

randomNum();

},200);

// 随机产生一个数字

}

break;

default:

}

}, false);

总结

总体来说这个游戏实现起来并不是太难,就是许多小的操作集合起来

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持脚本之家!

你可能感兴趣的:(2048网页版html项目报告)