一直以来对h5游戏这一块很感兴趣,但苦于现在技术渣渣,也没有合适的项目可以给我练练手,机缘巧合之下看到了2048这款游戏,作为风靡一时的小游戏,当初也是觉得这个很有意思。今天就分享一下如何实现2048这个游戏。
话不多说,直接上代码:
<html>
<head>
<title>2048title>
<meta charset="utf-8" />
<link rel="stylesheet" href="2048.css"/>
<script src="2048.js">script>
head>
<body>
<p>Score:<span id="score">span>p>
<div id="gridPanel">
div>
<div id="gameover">
<div>div>
<p>
Game Over!<br>
Score:<span id="finalScore">span><br>
<a class="btn" id="restart" onclick="game.start()">Try again!a>
p>
div>
body>
html>
2048.css
#gridPanel{
width:480px; height:480px;
margin:0 auto;
background-color:#bbada0;
border-radius:10px;
position:relative;
}
.grid,.cell{
width:100px; height:100px;
border-radius:6px;
}
.grid{
background-color:#ccc0b3;
float:left;
margin: 16px 0 0 16px;
}
.cell{
position:absolute;
text-align:center;
line-height:100px;
font-size:60px;
color:#fff;
}
/*同一行拥有统一的top*/
#c00,#c01,#c02,#c03,#c04,#c05{top:16px}
#c10,#c11,#c12,#c13,#c14,#c15{top:132px}
#c20,#c21,#c22,#c23,#c24,#c25{top:248px}
#c30,#c31,#c32,#c33,#c34,#c35{top:364px}
#c40,#c41,#c42,#c43,#c44,#c45{top:480px}
#c50,#c51,#c52,#c53,#c54,#c55{top:596px}
/*同一列拥有统一的left*/
#c00,#c10,#c20,#c30,#c40,#c50{left:16px}
#c01,#c11,#c21,#c31,#c41,#c51{left:132px}
#c02,#c12,#c22,#c32,#c42,#c52{left:248px}
#c03,#c13,#c23,#c33,#c43,#c53{left:364px}
#c04,#c14,#c24,#c34,#c44,#c54{left:480px}
#c05,#c15,#c25,#c35,#c45,#c55{left:596px}
.n2{background-color:#eee3da}
.n4{background-color:#ede0c8}
.n8{background-color:#f2b179}
.n16{background-color:#f59563}
.n32{background-color:#f67c5f}
.n64{background-color:#f65e3b}
.n128{background-color:#edcf72}
.n256{background-color:#edcc61}
.n512{background-color:#9c0}
.n1024{background-color:#33b5e5}
.n2048{background-color:#09c}
.n4096{background-color:#a6c}
.n8192{background-color:#93c}
.n2,.n4{color:#776e65}
.n1024,.n2048,.n4096,.n8192{font-size:40px}
p{
width:480px; margin:0 auto;
font-family:Arial; font-weight:bold;
font-size:40px; padding-top:15px;
}
/*设置游戏结束界面*/
#gameover{width:100%; height:100%;
position:absolute; top:0; left:0;
display:none;
}
#gameover div{width:100%; height:100%;
background-color:#555; opacity:0.5;
}
#gameover p{width:300px; height:200px;
border:1px solid #edcf72;
line-height:1.6em;
text-align:center;
background-color:#fff;
border-radius:10px;
position:absolute;
top:50%; left:50%;
margin-top:-100px;
margin-left:-150px;
}
.btn{padding:10px; border-radius:6px;
background-color:#9f8b77; color:#fff; cursor:pointer;
}
2048.js
var game={
data:[],//启动后是一个二维数组,存储每个格的数字
RN:4, //总行数
CN:4, //总列数
score:0, //保存游戏分数
state:0, //保存游戏的状态
RUNNING:1, //专门表示游戏正在运行
GAMEOVER:0, //专门表示游戏结束
getGridsHtml:function(){//生成所有背景格的html代码
//r从0开始,到
for(var r=0,arr=[];r<this.RN;r++){
// c从0开始,到
for(var c=0;c<this.CN;arr.push(""+r+c++));
}
return ''" class="grid">'" class="grid">';
},
getCellsHtml:function(){//生成所有前景格的html代码
for(var r=0,arr=[];r<this.RN;r++){
for(var c=0;c<this.CN;arr.push(""+r+c++));
}
return ''" class="cell">'" class="cell">';
},
init:function(){//生成所有背景和前景格,并加入到页面
var gp=document.getElementById("gridPanel");
gp.style.width=116*this.CN+16+"px";
gp.style.height=116*this.RN+16+"px";
gp.innerHTML=this.getGridsHtml()
+this.getCellsHtml();
},
start:function(){//游戏启动方法,启动游戏时调用
this.init();
this.state=this.RUNNING; //游戏状态改为启动
//初始化数组为RN行,CN列的二维数组,所有元素为0
//初始化空数组
for(var r=0;r<this.RN;r++){
this.data[r]=[];//初始化每一行为空数组
for(var c=0;c<this.CN;c++){
this.data[r][c]=0;//初始化每个格为0
}
}
this.score=0;//初始化游戏分数为0
//随机生成2个2或4
this.randomNum();
this.randomNum();
this.updateView();//将data的数据,更行到页面div
},
isGameOver:function(){//判断当前游戏是否结束
//遍历data中所有元素
for(var r=0;r<this.data.length;r++){
for(var c=0;c<this.data[r].length;c++){
// 如果当前元素值==0,就返回false
if(this.data[r][c]==0){return false;}
else{//否则 如果当前列不是最右侧列,
// 且当前元素等于右侧元素
if(c!=this.data[r].length-1
&&this.data[r][c]==this.data[r][c+1]){
return false;//返回false
}else if(r!=this.data.length-1
&&this.data[r][c]==this.data[r+1][c]){
//否则 如果当前行不是最后一行
// 且当前元素等于下方元素
return false;//返回false
}
}
}
}//(遍历结束)将游戏状态改为GAMEOVER
this.state=this.GAMEOVER;
return true;//返回true
},
randomNum:function(){//随机挑选一个位置,生成2或4
if(!this.isFull()){//只有不满,才执行以下所有代码
while(true){//反复执行
// 随机生成一个行下标,保存在r中
var r=parseInt(Math.random()*(this.RN));
// 随机生成一个列下标,保存在c中
var c=parseInt(Math.random()*(this.CN));
// 如果data中r行c列位置的值==0
if(this.data[r][c]==0){
// 随机生成2或4,放入r行c列的元素中
// 如果生成一个随机数<0.5,就放入2,否则放4
this.data[r][c]=Math.random()<0.5?2:4;
break;//退出循环
}
}
}
},
isFull:function(){//专门用来判断数组是否已满
//遍历data中每个元素
for(var r=0;r<this.data.length;r++){
for(var c=0;c<this.data[r].length;c++){
// 只要发现当前元素==0
if(this.data[r][c]==0){
return false;//返回false
}
}
}//(遍历结束)返回true;
return true;
},
//负责将data中每个元素刷到页面中
//并修改页面每个div的class属性
updateView:function(){
//遍历data中每个元素
for(var r=0;r<this.data.length;r++){
for(var c=0;c<this.data[r].length;c++){
//找到页面中和当前元素对应位置的div
var div=document.getElementById("c"+r+c);
//只有当前元素指不等于0
if(this.data[r][c]!=0){
//将当前元素值放入div的内容中
div.innerHTML=this.data[r][c];
//给div的class穿上和数值对应的衣服
div.className=
"cell n"+this.data[r][c];
}else{
//否则,重置div的样式为cell,并清空内容
div.className="cell";
div.innerHTML="";
}
}
}
/*将分数写到页面*/
var span=document.getElementById("score");
span.innerHTML=this.score;
//找到#gameover,
var gameover=document.getElementById("gameover");
if(this.state==this.GAMEOVER){//如果游戏结束
var span=document.getElementById("finalScore");
span.innerHTML=this.score;
//修改display为block
gameover.style.display="block";
}else{//否则,修改display为none
gameover.style.display="none";
}
},
moveLeft:function(){//左移所有行
var before=this.data.toString();
for(var r=0;r<this.data.length;r++){
this.moveLeftInRow(r);
}
var after=this.data.toString();
if(before!=after){
this.randomNum();//随机生成一个新数
this.isGameOver();//判断游戏是否结束
this.updateView(); //更新界面
}
},
moveLeftInRow:function(r){//左移第r行
for(var c=0;c<this.data[r].length-1;c++){
//从c开始,找下一个不为0的位置下标next
var next=this.getRightNext(r,c);
//如果next==-1,说明都是0了
if(next==-1){break;}//退出循环
else{
if(this.data[r][c]==0){
this.data[r][c]=this.data[r][next];
this.data[r][next]=0;
c--;
}else if(this.data[r][c]
==this.data[r][next]){
this.data[r][c]*=2;
this.data[r][next]=0;
this.score+=this.data[r][c];
}
}
}
},
getRightNext:function(r,c){//专门找当前位置右侧下一个
//从c+1开始遍历之后所有元素
for(var next=c+1;next<this.data[r].length;next++){
if(this.data[r][next]!=0){//如果找到!=0的
return next;//返回next
}
}//(遍历结束)返回-1
return -1;
},
moveRight:function(){
var before=this.data.toString();
for(var r=0;r<this.data.length;r++){
this.moveRightInRow(r);
}
var after=this.data.toString();
if(before!=after){
this.randomNum();
this.isGameOver();//判断游戏是否结束
this.updateView();
}
},
moveRightInRow:function(r){
/*遍历当前行中每个元素,
c从length-1开始,到1结束,每次递减1*/
for(var c=this.data[r].length-1;c>0;c--){
// 找左侧下一个不为0的数的下标,保存在prev中
var prev=this.getLeftPrev(r,c);
// 如果prev等于-1,就退出循环
if(prev==-1){break;}
else{// 否则
if(this.data[r][c]==0){//如果当前元素是0
// 用prev位置的元素,替换当前元素
this.data[r][c]=this.data[r][prev];
// 将prev位置的元素设置为0
this.data[r][prev]=0;
c++;//c右移一位
}else if(this.data[r][c]
==this.data[r][prev]){
// 否则,如果当前元素和prev位置的元素相等
// 将当前位置的元素乘2
this.data[r][c]*=2;
// 将prev位置的元素设置为0
this.data[r][prev]=0;
this.score+=this.data[r][c];
}
}
}
},
getLeftPrev:function(r,c){
/*遍历c左侧剩余元素,
prevC从c-1开始,到0结束,每次递减1*/
for(var prevC=c-1;prevC>=0;prevC--){
// 如果prevC位置的元素不等于0,就返回prevC
if(this.data[r][prevC]!=0){return prevC;}
}
return -1;//(循环退出)返回-1;
},
moveUp:function(){//遍历每一列
var before=this.data.toString();
for(var c=0;c<this.CN;c++){
this.moveUpInCol(c);
}
var after=this.data.toString();
if(before!=after){
this.randomNum();
this.isGameOver();//判断游戏是否结束
this.updateView();
}
},
moveUpInCol:function(c){
/*遍历当前列中每个元素,
r从0开始,到<整个数组的length-1结束,每次递增1*/
for(var r=0;r<this.data.length-1;r++){
// 找下方下一个不为0的数的下标,保存在down中
var down=this.getDownNext(r,c);
// 如果down等于-1,就退出循环
if(down==-1){break;}
else{// 否则
if(this.data[r][c]==0){//如果当前元素是0
// 用down位置的元素,替换当前元素
this.data[r][c]=this.data[down][c];
// 将down位置的元素设置为0
this.data[down][c]=0;
r--;//r向上退一位
}else if(this.data[r][c]
==this.data[down][c]){
// 否则,如果当前元素和down位置的元素相等
// 将当前位置的元素乘2
this.data[r][c]*=2;
// 将down位置的元素设置为0
this.data[down][c]=0;
this.score+=this.data[r][c];
}
}
}
},
getDownNext:function(r,c){
/*遍历当前列中下方剩余元素
downR从r+1开始,到<整个数组的length结束,downR递增1*/
for(var downR=r+1;downR<this.data.length;downR++){
// 如果downR位置的元素!=0,就返回downR
if(this.data[downR][c]!=0){return downR}
}
return -1;//(遍历结束)返回-1;
},
moveDown:function(){//遍历每一列
var before=this.data.toString();
for(var c=0;c<this.CN;c++){
this.moveDownInCol(c);
}
var after=this.data.toString();
if(before!=after){
this.randomNum();
this.isGameOver();//判断游戏是否结束
this.updateView();
}
},
moveDownInCol:function(c){
/*r从最后一行开始,向上遍历当前列每个元素
到>0结束,每次递减1*/
for(var r=this.data.length-1;r>0;r--){
// 获得当前位置上方不为0的数的位置,保存在up中
var up=this.getUpPrev(r,c);
// 如果up==-1,就退出循环
if(up==-1){break;}
else{// 否则
// 如果当前位置等于0
if(this.data[r][c]==0){
// 用up位置的值替换当前位置的值
this.data[r][c]=this.data[up][c];
// 将up位置设置为0
this.data[up][c]=0;
r++;//r增1
}else if(this.data[r][c]
==this.data[up][c]){
// 否则,如果当前位置等于up位置
// 将当前位置*=2;
this.data[r][c]*=2;
// 将up位置设置为0
this.data[up][c]=0;
this.score+=this.data[r][c];
}
}
}
},
getUpPrev:function(r,c){
//upR从r的上方开始,到0结束,每次递减1
for(var upR=r-1;upR>=0;upR--){
// 如果upR位置的值!=0,就返回upR
if(this.data[upR][c]!=0){return upR}
}
return -1;//(遍历结束)返回-1
}
}
//当页面加载后,启动游戏
window.οnlοad=function(){
game.start();
//当按键按下时
document.οnkeydοwn=function(){
//只有游戏运行时才响应按键操作
if(game.state==game.RUNNING){
var e=window.event||arguments[0];
if(e.keyCode==37){
game.moveLeft();
}else if(e.keyCode==39){
game.moveRight();
}else if(e.keyCode==38){
game.moveUp();
}else if(e.keyCode==40){
game.moveDown();
}
}
}
}
js这一块都详细备注,希望大家多多支持,共同进步,谢谢!