最近在学习H5的相关知识,为了检验下学习效果,尝试着对小游戏2048进行了实现,本来还觉得学的已经不错了,但是真是做的时候真是应了那句话–书到用时方恨少,绝知此事要躬行!?
为了测试下对基础知识的掌握程度,没有使用现成的框架,如jQuery等,这个小游戏只用了基础的html、css和js。
实现该游戏的思路是,首先将静态页面画出来,我只做了最基础的4 x 4版本的。然后对单元格的左移、右移、上移和下移添加响应事件。如下图所示:
2048
2048 Games
SCORE:0
总共16个单元格,每个单元格定义自己的id号,id号是唯一的。这16个单元格属于同一个class
建一个样式表,如my2048.css文件,里面设置如下:
body{
margin:0;
padding:20px;
background-color:#5c5956;
}
header{
width:100%;
margin:0 auto;
display:block;
text-align:center;
}
.title{
font-family: Arial, Helvetica, sans-serif;
font-size: 30px;
font-weight: bold;
}
#game2048{
width: 460px;
height: 460px;
padding: 20px;
margin: 30px auto;
background-color: #d1ae8c;
border-radius: 10px;
position: relative;
}
.grid-cell{
width: 100px;
height: 100px;
border-radius: 12px;
background-color: #ccc0b3;
font-size:60px;
text-align:center;
position: absolute;
}
[id^="grid-cell-0"]{top:16px}
[id^="grid-cell-1"]{top:132px}
[id^="grid-cell-2"]{top:248px}
[id^="grid-cell-3"]{top:364px}
[id$="-0"]{left:16px}
[id$="-1"]{left:132px}
[id$="-2"]{left:248px}
[id$="-3"]{left:364px}
这样,打开上面的html文件,则可以看到2048小游戏的静态页面了。
对单元格的移动添加响应事件,在my2048.js中,如下图所示:
//keyCode 37 = Left keyCode 38 = Up keyCode 39 = Right keyCode 40 = Down
document.onkeydown = function (e){
if(this.state == this.RUNNING){
switch (e.keyCode){
case 37:
this.moveLeft();
break;
case 38:
this.moveUp();
break;
case 39:
this.moveRight();
break;
case 40:
this.moveDown();
break;
}
}
}.bind(this);
然后就是最关键的移动代码了。对于这16个单元格,有个初始化操作,将所有单元格中的值设置为0(注意单元格的值是0时不显示在页面上),比如对于左移,分两步,首先判断这一行中是否还有为0的单元格,若是有的话,需要将它右边的单元格往左移动一位,让这行单元格从左到右看,为0的单元格在最右边。如一行的值为
那么移动后
第二步,若是相邻单元格的值相等,则相加,左边的单元格的值是它俩的和,右边的单元格县赋值为0,然后对它右边的单元格进行判断,若是存在非0的单元格,则继续左移,直到该行中,若是存在为0的单元格,那么它一定是出现在最右边的单元格。代码如下:
moveLeft(){
//按行判断,若是有为0的单元格,则表示可以往左移动
//若是相邻的单元格的值有相等的,则相加
var cellValue = null;
//var cell_div = null;
for(var row = 0; row < this.RN; row++){
for(var col = 0; col
其他方向的移动与此类似.
后记:这个只是实现了基本的功能,游戏结束的功能还有问题,各个方向移动的代码也还有优化的空间,后续有时间了,发现满足结束条件后,不能将页面刷新,重新来一局,因此有需要的话,这个代码可以参考,但是不能直接拿来用,需要做二次开发.
附:3个文件的源代码:
my2048.html
2048
2048 Games
SCORE:0
TRY AGAIN!
GAME OVER!
SCORE:0
TRY AGAIN!
my2048.css
body{
margin:0;
padding:20px;
background-color:#5c5956;
}
header{
width:100%;
margin:0 auto;
display:block;
text-align:center;
}
.title{
font-family: Arial, Helvetica, sans-serif;
font-size: 30px;
font-weight: bold;
}
#game2048{
width: 460px;
height: 460px;
padding: 20px;
margin: 30px auto;
background-color: #d1ae8c;
border-radius: 10px;
position: relative;
}
.grid-cell{
width: 100px;
height: 100px;
border-radius: 12px;
background-color: #ccc0b3;
font-size:60px;
text-align:center;
position: absolute;
.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}
.number-cell{
border-radius: 4px;
font-family: Arial, Helvetica, sans-serif;
font-weight: bold;
font-size: 60px;
line-height: 100px;
text-align: center;
position: absolute;
}
my2048.js
var game = {
data: null, RN:4, CN:4,//行数和列数都是4
score:0,//保存得分
state:1,//1为运行,2是结束
RUNNING:1,//运行状态
GAMEOVER:0,//表示游戏结束
//对象自己的方法要使用自己的属性,必须this
start(){
this.init();//随机初始化两个单元格,数字为2或者4,其他单元格赋值0
//处理键盘按下的事件
//keyCode 37 = Left keyCode 38 = Up keyCode 39 = Right keyCode 40 = Down
document.onkeydown = function (e){
if(this.state == this.RUNNING){
switch (e.keyCode){
case 37:
this.moveLeft();
break;
case 38:
this.moveUp();
break;
case 39:
this.moveRight();
break;
case 40:
this.moveDown();
break;
}
}
}.bind(this);
},
init(){
this.state = this.RUNNING;
this.data = [];
for (var row = 0; row < this.RN;row++){
this.data[row]=[];
for(var col = 0; col < this.CN;col++){
this.data[row][col]=0;
}
}
this.randomNum();
this.randomNum();
this.updateView();//将data中的数据更新到页面中的每个div中
},
randomNum(){
while(true){
var randomRow = Math.floor(Math.random()*this.RN);
var randomCol = Math.floor(Math.random()*this.CN);
console.log('randomRow is:',randomRow);
console.log('randomRow is:',randomCol);
if(this.data[randomRow][randomCol]==0){
this.data[randomRow][randomCol] = Math.random()<0.5 ? 2: 4;
break;
}
}
},
updateView(){
for (var row = 0; row < this.RN; row++){
for (var col = 0; col < this.CN; col++){
var n = this.data[row][col];
var div = document.getElementById("grid-cell-" + row + "-" + col);
if(n != 0){
div.innerHTML = n;//设置div的内容为n
div.className = "grid-cell n" + n;
}else {
div.innerHTML = "";//清除div中的内容
//恢复div的class为grid-cell
div.className = "grid-cell";
}
}
}
//找到id为score的span,设置其内容为score属性
document.getElementById("score").innerHTML = this.score;
//当游戏结束时,显示gameover的div
var div= document.getElementById("gameOVER");
if(this.state == this.GAMEOVER){
div.style.display = "block";
document.getElementById("final").innerHTML = this.score;//找到id为final的span,设置其内容为score
}else{
//游戏还在运行中
div.style.display = "none";
}
},
moveLeft(){
//按行判断,若是有为0的单元格,则表示可以往左移动
//若是相邻的单元格的值有相等的,则相加
var cellValue = null;
//var cell_div = null;
for(var row = 0; row < this.RN; row++){
for(var col = 0; col =0; col--){
cellValue = this.data[row][col];
if(cellValue == 0){
this.moveRightInOneRow(row,col);
}
}
//每行中值都非0,需要判断是否有可以相加的
for(var col = this.CN - 1; col >=0; col--){
this.addTwoCellInColFromRight(row,col);
}
}
//随机选择一个单元格,然后随机生成2或者4
this.randomNum();
this.updateView();
},
moveRightInOneRow(row,col){
this.moveCellToRight(row,col);
//判断每列中相邻单元格中的值是否相等,若是相等,则加起来,row_index从最大值开始
this.addTwoCellInColFromRight(row,col);
},
addTwoCellInColFromRight(row,col){
for(var col_index = this.CN - 1; col_index > 0; col_index--){
if(this.data[row][col_index]!= 0 &&(this.data[row][col_index] == this.data[row][col_index- 1])){
this.data[row][col_index] = this.data[row][col_index] + this.data[row][col_index - 1];
this.score += this.data[row][col_index];
for(j = col_index - 1;j>=0;j--){
if(this.data[row][j]===0){
this.moveCellToRight(row,j);
}
}
// this.data[row][col_index - 1] = 0;
// this.moveCellToRight(row, col_index - 1);
}
}
},
moveCellToRight(row,col){
for (var temp_col = col - 1;temp_col >=0; temp_col--){
if(this.data[row][temp_col] !=0){
//对于一列中,上面的单元格中有不为0的数
temp = this.data[row][temp_col];
this.data[row][temp_col] = this.data[row][col];
this.data[row][col] = temp;
break;
}
}
},
moveUp(){
//按列判断,若是有为0的单元格,则表示可以往左移动
//若是相邻的单元格的值有相等的,则相加
var cellValue = null;
//var cell_div = null;
for(var col = 0; col < this.CN; col++){
for(var row = 0; row =0; row--){
cellValue = this.data[row][col];
if(cellValue == 0){
this.moveDownInOneCol(row,col);
}
}
//当一列中虽然没有为0的数,但是若是有相同的数,还是需要相加
for (var row = this.RN - 1; row >=0; row--){
this.addTwoCellInColFromBelow(row,col);
}
}
//随机选择一个单元格,然后随机生成2或者4
this.randomNum();
this.updateView();
},
moveDownInOneCol(row,col){
this.moveCellToBelow(row,col);
//判断每列中相邻单元格中的值是否相等,若是相等,则加起来,row_index从最大值开始
this.addTwoCellInColFromBelow(row,col);
},
addTwoCellInColFromBelow(row,col){
for(var row_index = this.RN - 1; row_index > 0; row_index--){
if(this.data[row_index][col]!= 0 &&(this.data[row_index][col] == this.data[row_index - 1][col])){
this.data[row_index][col] = this.data[row_index][col] + this.data[row_index - 1][col];
this.data[row_index - 1][col] = 0;
this.score += this.data[row_index][col];
for(j = row_index - 1;j>=0;j--){
if(this.data[j][col]===0){
this.moveCellToBelow(j,col);
}
}
// this.data[row_index - 1][col] = 0;
// this.moveCellToBelow(row_index - 1, col);
}
}
},
moveCellToBelow(row,col){
for (var temp_row = row - 1;temp_row >=0; temp_row--){
if(this.data[temp_row][col] !=0){
//对于一列中,上面的单元格中有不为0的数
temp = this.data[temp_row][col];
this.data[temp_row][col] = this.data[row][col];
this.data[row][col] = temp;
break;
}
}
},
isGameOver(){
//若是全部格子都被填满,且相邻单元格没有相同的元素,则结束游戏
for(var row = 0;row < this.RN; row++){
for(var col=0; col < this.CN; col++){
if (this.data[row][col] ==0) return false;
else if(col < this.CN -1 && (this.data[row][col] == this.data[row][col + 1])){
return false;
}
else if (row < this.RN - 1 && (this.data[row + 1][col] == this.data[row][col])){
return false;
}
}
//遍历结束
return true;
}
},
}
function tryAgain() {
this.init();
},
game.start();