做个营销型网站要多少钱,网站建设课程设计内容,网站中的幻灯片ie6显示 ie7如何兼容,网页模板建站系统写在前面得话#xff1a;这篇文章主要记录了我是怎么一步一步写出俄罗斯方块#xff0c;整个代码用的函数编程#xff0c;主要是为了让一些不熟悉es6, 面向对象写法得 新手能更容易看明白#xff0c;全部得代码中都是一些js的基础知识#xff0c;很容易理解。要说有点麻烦…写在前面得话这篇文章主要记录了我是怎么一步一步写出俄罗斯方块整个代码用的函数编程主要是为了让一些不熟悉es6, 面向对象写法得 新手能更容易看明白全部得代码中都是一些js的基础知识很容易理解。要说有点麻烦的那就是游戏过程中的各种检测。但是只要你多思考你就能理解代码为什么要那样写你也可以实现这个游戏。当然也许你有更好的实现方法。预览地址俄罗斯方块blog.cwlserver.top1先理清游戏逻辑游戏场景场景大小为 10*18下落时间初始方块每隔1秒会下落一格。随着游戏进行时间得增加方块下落时间间隔会缩短。操作方法方向键得 上下左右 分别控制方块得 变形加速下落左移右移。方块类型一共7种类型得方块。每次随机出现一种, 每种方块由数个 1*1大小得小方块组成方块下落当方块落到底 或者下一格已经被占方块停止下落然后会有一个新的方块出现方块左右移动方块左右移动时如果左右是墙或者是已经被占方块将不能移动。方块变形方块逆时针旋转90°变形时需要判断方块是否可以变形。游戏会有下一个方块得提示消行当一行被填满时这一行将被消除计分规则 消1行得2分2行4分3行8分4行16分游戏结束: 当方块下落到底并且方块超出游戏场景时判定游戏结束2分步实现游戏中得功能html结构 div idboxcanvas idcanvas width300 height540/canvasdiv classscoreboxp游戏已进行: span idgame-time00:00:00/span/pbrp当前得分: span idscore0/span/pbrp下一个方块/pbrcanvas idnext width120 height120/canvasbrp classbtnsbutton idpause暂停/buttonbutton idrestart重新开始/button/p/div/div构建场景因为场景大小是10x18所以我决定用一个 10x18得二维数组来模拟场景这样方便和方块做碰撞检测。//定义列数
var ROW 10;
//定义行数
var COL 18;
//游戏得分
var SCORE 0;
//游戏场景
var area new Array(COL);
for(var i0; iarea.length; i){area[i] new Array(ROW).fill(0);
}
/*
最终得到得area是这样得
area [[0,0,0,0....][0,0,0,0....][0,0,0,0....]...
]
*/
构建小方块小方块我同样使用二维数组来构建//定义各种方块得数组, 一共7种不同得方块数组中的1234..这些数字主要是为了每个方块设置不同的颜色
var data {o:[[1, 1],[1, 1]],s:[[2, 0, 0],[2, 2, 0],[0, 2, 0]],5:[[0, 0, 3],[0, 3, 3],[0, 3, 0]], l:[[4, 0, 0],[4, 0, 0],[4, 4, 0]],t:[[5, 5, 5],[0, 5, 0],[0, 0, 0]],j:[[0, 0, 6],[0, 0, 6],[0, 6, 6]],|:[[0, 7, 0, 0],[0, 7, 0, 0],[0, 7, 0, 0],[0, 7, 0, 0]]
};
//定义方块得颜色每个数字对应一种颜色
var aColor [, #fff, #0000FF, #00FF00, #CC00FF, #CCFFFF,#FFFF33,#99FFFF];
//将data中得key放到一个字符串中 方便随机调用
var sKey os5ltj|;
//定义当前方块, 当前方块默认null;
var cur null;
//因为游戏中会有下一个方块得提示, 所以这里要提前声明一下
var next null;
//定义一个生成方块得函数
function createBox(){//首先创建提示方块if(!next){//从skey中随机取出一个键名var rnd Math.floor(Math.random()*sKey.length);//根据key取得方块数组var box data[sKey[rnd]];//每一个方块都有x, y, box 这三个属性next {//方块初始在场景中间位置方块左移 x--, 右移 x;x: Math.floor((ROW-box[0].length)/2), //方块在垂直方向得位置刚好在场景外, y 方块下落y: -box[0].length,//方块得数组box: box};}//当前方块不存在时 创建当前方块if(!cur){//直接下一个方块变成当前这个cur next;//然后再重新生成下一个next {x: Math.floor((ROW-box[0].length)/2),y: -box[0].length,box: data[sKey[Math.floor(Math.random()*sKey.length)]]}}
}
现在想一个问题有了场景和方块的数据之后如何把他们联系起来我的处理方式是这样的在方块下落的过程中方块和场景是分开的方块的位置和场景是分开刷新的。在下落的过程中我会 检测方块和场景是否发生碰撞如果发生了碰撞将当前方块的数组合并到场景的数组中使方块变成场景的一部分同时生成一个新的方块。看下代码如何实现//将当前方块合并到场景
function mergeBoxArea(){//循环当前方块for(var i0; icur.box.length; i){//这里的判断是为了当方块的一部分在场景外的时候将那一部分跳过只计算在场景中的部分if(icur.y0){for(var j0; jcur.box[i].length){//将当前方块数组中不为0的项和 场景中当前位置为0的项合并if(cur.box[i][j] ! 0 area[icur.y][jcur.x] 0){//合并的结果 将场景中当前位置的值设置为方块对应位置的值area[icur.y][jcur.x] cur.box[i][j];}}}}//将方块合并入场景的同时要尝试 消行var arr isRemove(area);if(arr.length ! 0){for(var i0; iarr.length; i){area.splice(arr[i], 1)area.unshift(new Array(ROW).fill(0))}//更新得分SCOREMath.pow(2, arr.length)scoreEle.innerHTML SCORE;};
}
//碰撞检测
//垂直方向的碰撞检测, 需要接受当前方块做为参数
//作用检测方块下落一格之后和场景的碰撞情况如果会碰撞返回true否则返回false;
function collide(cur){var box cur.box;var len box.length;var x box.x;//因为是检测下一个位置所以要1var y box.y 1;for(var i0; ilen; i){//做碰撞检测同样需要将场景外的方块部分排除掉if(iy0){//方块的数组都是n*n的所以都用lenfor(var j0; jlen; j){//将方块为0的项不检测if(box[i][j] ! 0){//第一种碰撞情况当iy大于等于场景的高度时说明方块出界//第二种碰撞情况方块没有出界但是场景中的这个位置被占用了if(iyarea.length || (iyarea.length area[iy][jx] ! 0)){//碰撞了返回 truereturn true;}}}}}//代码执行到这里时说明没有碰撞返回false;return false;
}
//水平方向的移动限制
//当用键盘控制方块左右移动的时候需要检测左右是否是墙或者方块这里检测的也是下一个位置的碰撞情况
//如果没有墙或者方块(不碰撞)返回true
//如果碰撞 返回 false;
//接受参数 当前方块:cur 移动方向: dir -1|0|1
function bMove(cur, dir){//当前位置加上方向 就是 下一个位置var x cur.xdir;for(var i0; icur.box.length; i){for(var j0; jcur.box[i].length; j){if(cur[i][j] ! 0){//这里发生碰撞的情况有3中//1.方块在左边出界了 这时 jx0//2.方块在右边出界了 jx ROW//3.方块没有出界但是场景中的这个位置被占用 area[icur.y][jx]!0// 加上 icur.y0 jx0 area[icur.y] 是为了防止报错if(jx0 || jxROW || ( icur.y0 jx0 area[icur.y] area[icur.y][jx]!0)){return false;}}}}return true;
}
如何处理方块旋转方块的旋转比较容易处理就把二维数组旋转一下就可以了。但是要注意方块旋转的时候也是需要检测 旋转的合理性 可以想象一下一个长条下落的过程中如果他的左右两边都是方块这种情况肯定是不能旋转的其它方块同理。还有一种情况就是方块靠墙下落的时候旋转一下之后有一部分转到墙里面去了这种也是不合理的但是玩游戏的时候这种情况也能旋转所以出现这种情况的时候我们需要修正一下方块的位置。 下面看代码怎么写//此函数用于检测方块是否能够旋转
/*
参数 当前方块 cur
返回值 true //方块可以直接旋转false //方块不能旋转即使是在尝试修正位置之后就是上面说到的左右都是方块的情况cur.x //当返回 一个数值的时候说明 将方块水平移动到这个位置后可以旋转 即上面说的修正位置
*/
function bRotate(cur){//在这里复制一个旋转后的方块出来用于检测var _cur {x: cur.x, y:cur.y, box: rotateBox(cur.box)};//检测方块旋转之后水平和垂直方向的碰撞情况 如果在任意方向会发生碰撞if( collide(_cur) true || bMove(_cur, 0) false ){//尝试水平移动方块移动方向是分别向左向右移动2格for(var i0; i2; i){//方块靠近左边的时候尝试向右移动并且检测移动的合理性if(_cur.x4 bMove(_cur, 1)){_cur.x;}//靠近右侧的时候向左移动并且检测移动的合理性if(_cur.x6 bMove(_cur, -1)){_cur.x--;}//移动之后再检测是否碰撞, 如果不会发生碰撞 返回移动后的位置if(collide(_cur) false bMove(_cur, 0)){return _cur.x;}}//代码执行到这里的时候说明移动了之后仍会碰撞return false;//如果旋转之后不会发生碰撞直接返回true;}else{return true;}
}
//旋转数组的函数
function rotateBox(arr){var res [];for(var i0; iarr.length; i){res.push([]);}//旋转for(var i0; iarr.length; i){for(var j0; jarr[i].length; j){res[arr.length-1-y][x] arr[x][y];}}return res;
}
现在开始处理游戏的刷新 计算游戏的时间游戏用 requestAnimationFrame 更新var timer null;
//记录一个旧的时间这里用于辅助计算, 每次刷新的间隔时间
var oldTime Date.now();
//n 用于累加 raf 的间隔时间
var n 0;
//游戏运行时间 单位 毫秒
var gameTime 0;
//方块下落的间隔时间
var step 1000;
//游戏是否暂停
var bPause false;
//获取dom元素
//主场景canvas
var canvas document.getElementById(canvas);
var ctx canvas.getContext(2d);
//提示下一个方块 canvas
var nextCanvas document.getElementById(next);
var nextctx nextCanvas.getContext(2d);
//游戏得分
var scoreEle document.getElementById(score);
//暂停按钮
var pauseEle document.getElementById(pause);
//重新开始按钮
var restartEle document.getElementById(restart)
//显示游戏时间
var gameTimeEle document.getElementById(game-time);
//开启主循环
timer requestAnimationFrame(animate);
//主循环函数
function animate(){//累加 raf 的间隔时间nDate.now()-oldTime;//累加游戏运行时间gameTimeDate.now()-oldTime;oldTime Date.now();//方块要开始下落了if(nstep){n 0;//每秒钟更新一次游戏时间updateGameTime()//根据游戏进行时间提高游戏难度changeDifficulty();//方块下落之前要先检测是否会发生碰撞//会发生碰撞if(collide(cur)){//会碰撞并且此时如果方块有一部分在外面说明游戏结束if(cur.y0){gameover()//正常的碰撞}else{//将方块合并入游戏场景mergeBoxArea()//并将cur 设置为nullcur null;//产生一个新的方块createBox()}//不会碰撞}else{cur.y;}}//更新游戏场景drawArea();//画提示方块drawNextBox(); timer requestAnimationFrame(animate);
}
//更新游戏场景
function drawArea(){ctx.clearRect(0, 0, 300, 540)ctx.save()ctx.scale(30, 30)//ctx.fillStyle #fff;//画游戏场景drawcube(ctx, area) //画当前方块drawcube(ctx, cur.box, cur.x, cur.y) ctx.restore();
}
//更新提示
function drawNextBox(){nextctx.clearRect(0, 0, 120, 120)nextctx.save()nextctx.scale(30, 30)//画下一个方块nextdrawcube(nextctx, next.box) nextctx.restore();
}
//画方块接受一个ctx对象一个数组 数组的偏移值
function drawcube(ctx, arr, x, y){x x || 0;y y || 0;for(var i0; iarr.length; i){for(var j0; jarr[i].length; j){if(arr[i][j] ! 0){//设置方块的颜色ctx.fillStyle aColor[arr[i][j]];ctx.fillRect(jx, iy, 1, 1)}}}
}
监听键盘事件移动方块//监听键盘事件
document.addEventListener(keydown, function(ev){if(bPause || !cur){return false;}var keycode ev.keyCode;switch(keycode){//左case 37://是否能向左移动if(bMove(cur, -1)){cur.x--;}break;//右case 39://是否能向右移动if(bMove(cur, 1)){cur.x;}break;//下case 40://如果触底或者落到其它方块上面if(collide(cur)){if(cur.y0){gameover()}else{mergeBoxArea()cur null;createBox()}}else{cur.y;}break;//上case 38://是否能旋转 当n为true时可以直接旋转当n为数值时需要将方块x位置移动到此处才能旋转var rotateRes bRotate(cur);//可以直接旋转if(rotateRes true){cur.box rotateBox(cur.box);//不能旋转}else if(rotateRes false){console.log(不能旋转)//需要移动之后才能旋转}else{cur.x rotateRes;cur.box rotateBox(cur.box);}break;}
})
处理游戏结束 游戏暂停 游戏重新开始 消行 更新游戏得分 更新游戏运行时间等等//点击暂停按钮
pauseEle.addEventListener(click, function(){var html this.innerHTML;if(html 暂停){pause();this.innerHTML 继续;}else{start();this.innerHTML 暂停;}
})
//点击重新开始
restartEle.addEventListener(click, function(){restart();
})
//暂停游戏
function pause(){cancelAnimationFrame(timer);bPause true;
}
//继续
function start(){timer requestAnimationFrame(animate);bPause false;
}
//重新开始
function restart(){//重置场景for(var i0; iarea.length; i){for(var j0; jarea[i].length; j){area[i][j] 0;}}cancelAnimationFrame(timer);timer requestAnimationFrame(animate);bPause false;pauseEle.innerHTML 暂停;//重置游戏时间gameTime 0;//更新游戏时间updateGameTime();cur null;//创建第一个方块createBox();
}
//游戏结束
function gameover(){ cancelAnimationFrame(timer);alert(游戏结束 您一共获得:SCORE分)restart()
}
//检测是否可以消行并将可以消除得行 加入结果数组返回出去
function isRemove(area){var arr [];for(var i0; iarea.length; i){var remove true;//如果数组的一行的每一项都不为0说明可以消除for(var j0; jarea[i].length; j){if(area[i][j] 0){remove false;}}//储存消除行的索引if(remove){arr.push(i)}}return arr;
}
//更新游戏运行时间
function updateGameTime(){var n gameTime/1000;var h Math.floor(n/(60*60));n%60*60;var m Math.floor(n/60);n%60;var s Math.floor(n);h h9?0h:h;m m9?0m:m;s s9?0s:s;gameTimeEle.innerHTML h:m:s;
}
//根据游戏时间修改难度方块下落间隔时间
function changeDifficulty(){//游戏进行5分钟 方块下落间隔为300msif(gameTime1000*60*5){step 300;//游戏运行3分钟 方块下落间隔500ms}else if(gameTime1000*60*3){step 500;//游戏运行2分钟 方块下落间隔700ms}else if(gameTime1000*60*2){step 700;}
}
到这里整个游戏差不多就算完了最后 发一个预览地址, 最终的预览demo和现在的代码并某些细节不是完全一样俄罗斯方块blog.cwlserver.top