html5如何实现俄罗斯方块小游戏

html5如何实现俄罗斯方块小游戏

这篇文章将为大家详细讲解有关html5如何实现俄罗斯方块小游戏,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

制作思路

因为书里的俄罗斯方块比较普通,太常规了,不是很好看,所以我在网上找了上面那张图片,打算照着它来做。

然后便是游戏界面和常规的俄罗斯方块游戏逻辑。

接着便是游戏结束界面了。

原本想做个弹出层,但觉得找图片有点麻烦,所以就在网上找了文字特效,套用了一下。

代码实现:

首先是html文件和css文件,主要涉及了布局方面。作为新手,在上面真的是翻来覆去的踩坑。o(╥﹏╥)o

index.html:

<!DOCTYPEhtml><html><head><title>俄罗斯方块</title><metahttp-equiv="Content-Type"content="text/html;charset=utf-8"/><linkrel=stylesheettype="text/css"href="teris.css"><styletype="text/css">/*导入外部的字体文件*/@font-face{font-family:tmb;/*为字体命名为tmb*/src:url("DS-DIGIB.TTF")format("TrueType");/*format为字体文件格式,TrueType为ttf*/}div>span{font-family:tmb;font-size:18pt;color:green;}</style></head><body><divid="container"class="bg"><!--ui--><divclass="ui_bg"><div>速度:<spanid="cur_speed">1</span></div><div>当前分数:<spanid="cur_points">0</span></div><div>最高分数:<spanid="max_points">0</span></div></div><canvasid="text"width="500"height="100"></canvas><canvasid="stage"width="500"height="100"></canvas></div><scriptsrc='EasePack.min.js'></script><scriptsrc='TweenLite.min.js'></script><scriptsrc='easeljs-0.7.1.min.js'></script><scriptsrc='requestAnimationFrame.js'></script><scripttype="text/javascript"src="jquery-3.4.1.min.js"></script><scripttype="text/javascript"src="teris.js"></script></body></html>

teris.css

*{margin:0;padding:0;}html,body{width:100%;height:100%;}.bg{font-size:13pt;background-color:rgb(239,239,227);/*好看的渐变色*/background-image:radial-gradient(rgb(239,239,227),rgb(230,220,212));/*阴影*/box-shadow:#cdc8c1-1px-1px7px0px;padding-bottom:4px;}.ui_bg{border-bottom:1px#a69e9ea3solid;padding-bottom:2px;overflow:hidden;/*没有这句的话因为子div都设置了float,所以是浮在网页上的,所以父div就没有高度,这句清除了浮动,让父div有了子div的高度*/}

然后是重头戏,teris.js

游戏变量:

//游戏设定varTETRIS_ROWS=20;varTETRIS_COLS=14;varCELL_SIZE=24;varNO_BLOCK=0;varHAVE_BLOCK=1;//定义几种可能出现的方块组合varblockArr=[//Z[{x:TETRIS_COLS/2-1,y:0},{x:TETRIS_COLS/2,y:0},{x:TETRIS_COLS/2,y:1},{x:TETRIS_COLS/2+1,y:1}],//反Z[{x:TETRIS_COLS/2+1,y:0},{x:TETRIS_COLS/2,y:0},{x:TETRIS_COLS/2,y:1},{x:TETRIS_COLS/2-1,y:1}],//田[{x:TETRIS_COLS/2-1,y:0},{x:TETRIS_COLS/2,y:0},{x:TETRIS_COLS/2-1,y:1},{x:TETRIS_COLS/2,y:1}],//L[{x:TETRIS_COLS/2-1,y:0},{x:TETRIS_COLS/2-1,y:1},{x:TETRIS_COLS/2-1,y:2},{x:TETRIS_COLS/2,y:2}],//J[{x:TETRIS_COLS/2,y:0},{x:TETRIS_COLS/2,y:1},{x:TETRIS_COLS/2,y:2},{x:TETRIS_COLS/2-1,y:2}],//□□□□[{x:TETRIS_COLS/2,y:0},{x:TETRIS_COLS/2,y:1},{x:TETRIS_COLS/2,y:2},{x:TETRIS_COLS/2,y:3}],//┴[{x:TETRIS_COLS/2,y:0},{x:TETRIS_COLS/2-1,y:1},{x:TETRIS_COLS/2,y:1},{x:TETRIS_COLS/2+1,y:1}]];//记录当前积分varcurScore=0;//记录曾经的最高积分varmaxScore=1;varcurSpeed=1;//ui元素varcurSpeedEle=document.getElementById("cur_speed");varcurScoreEle=document.getElementById("cur_points");varmaxScoreEle=document.getElementById("max_points");vartimer;//方块下落控制varmyCanvas;varcanvasCtx;vartetris_status;//地图数据varcurrentFall;//当前下落的block

游戏界面的完善

//createcanvasfunctioncreateCanvas(){myCanvas=document.createElement("canvas");myCanvas.width=TETRIS_COLS*CELL_SIZE;myCanvas.height=TETRIS_ROWS*CELL_SIZE;//绘制背景canvasCtx=myCanvas.getContext("2d");canvasCtx.beginPath();//TETRIS_COSfor(leti=1;i<TETRIS_COLS;i++){canvasCtx.moveTo(i*CELL_SIZE,0);canvasCtx.lineTo(i*CELL_SIZE,myCanvas.height);}for(leti=1;i<TETRIS_ROWS;i++){canvasCtx.moveTo(0,i*CELL_SIZE);canvasCtx.lineTo(myCanvas.width,i*CELL_SIZE);}canvasCtx.closePath();canvasCtx.strokeStyle="#b4a79d";canvasCtx.lineWidth=0.6;canvasCtx.stroke();//第一行,最后一行,第一列,最后一列粗一点。canvasCtx.beginPath();canvasCtx.moveTo(0,0);canvasCtx.lineTo(myCanvas.width,0);canvasCtx.moveTo(0,myCanvas.height);canvasCtx.lineTo(myCanvas.width,myCanvas.height);canvasCtx.moveTo(0,0);canvasCtx.lineTo(0,myCanvas.height);canvasCtx.moveTo(myCanvas.width,0);canvasCtx.lineTo(myCanvas.width,myCanvas.height);canvasCtx.closePath();canvasCtx.strokeStyle="#b4a79d";canvasCtx.lineWidth=4;canvasCtx.stroke();//设置绘制block时的stylecanvasCtx.fillStyle="#201a14";}drawcanvasfunctionchangeWidthAndHeight(w,h){//通过jquery设置cssh+=$("ui_bg").css("height")+$("ui_bg").css("margin-rop")+$("ui_bg").css("margin-bottom")+$("ui_bg").css("padding-top")+$("ui_bg").css("padding-bottom");$(".bg").css({"width":w,"height":h,"top":0,"bottom":0,"right":0,"left":0,"margin":"auto"});}changewidthandheight//drawblocksfunctiondrawBlocks(){//清空地图for(leti=0;i<TETRIS_ROWS;i++){for(letj=0;j<TETRIS_COLS;j++)canvasCtx.clearRect(j*CELL_SIZE+1,i*CELL_SIZE+1,CELL_SIZE-2,CELL_SIZE-2);}//绘制地图for(leti=0;i<TETRIS_ROWS;i++){for(letj=0;j<TETRIS_COLS;j++){if(tetris_status[i][j]!=NO_BLOCK)canvasCtx.fillRect(j*CELL_SIZE+1,i*CELL_SIZE+1,CELL_SIZE-2,CELL_SIZE-2);//中间留点缝隙}}//绘制currentFallfor(leti=0;i<currentFall.length;i++)canvasCtx.fillRect(currentFall[i].x*CELL_SIZE+1,currentFall[i].y*CELL_SIZE+1,CELL_SIZE-2,CELL_SIZE-2);}drawblock

游戏逻辑

functionrotate(){//定义记录能否旋转的旗标varcanRotate=true;for(vari=0;i<currentFall.length;i++){varpreX=currentFall[i].x;varpreY=currentFall[i].y;//始终以第三个方块作为旋转的中心,//i==2时,说明是旋转的中心if(i!=2){//计算方块旋转后的x、y坐标varafterRotateX=currentFall[2].x+preY-currentFall[2].y;varafterRotateY=currentFall[2].y+currentFall[2].x-preX;//如果旋转后所在位置已有方块,表明不能旋转if(tetris_status[afterRotateY][afterRotateX+1]!=NO_BLOCK){canRotate=false;break;}//如果旋转后的坐标已经超出了最左边边界if(afterRotateX<0||tetris_status[afterRotateY-1][afterRotateX]!=NO_BLOCK){moveRight();afterRotateX=currentFall[2].x+preY-currentFall[2].y;afterRotateY=currentFall[2].y+currentFall[2].x-preX;break;}if(afterRotateX<0||tetris_status[afterRotateY-1][afterRotateX]!=NO_BLOCK){moveRight();break;}//如果旋转后的坐标已经超出了最右边边界if(afterRotateX>=TETRIS_COLS-1||tetris_status[afterRotateY][afterRotateX+1]!=NO_BLOCK){moveLeft();afterRotateX=currentFall[2].x+preY-currentFall[2].y;afterRotateY=currentFall[2].y+currentFall[2].x-preX;break;}if(afterRotateX>=TETRIS_COLS-1||tetris_status[afterRotateY][afterRotateX+1]!=NO_BLOCK){moveLeft();break;}}}if(canRotate){for(vari=0;i<currentFall.length;i++){varpreX=currentFall[i].x;varpreY=currentFall[i].y;if(i!=2){currentFall[i].x=currentFall[2].x+preY-currentFall[2].y;currentFall[i].y=currentFall[2].y+currentFall[2].x-preX;}}localStorage.setItem("currentFall",JSON.stringify(currentFall));}}旋转//按下下或interval到了functionnext(){if(moveDown()){//记录blockfor(leti=0;i<currentFall.length;i++)tetris_status[currentFall[i].y][currentFall[i].x]=HAVE_BLOCK;//判断有没有满行的for(letj=0;j<currentFall.length;j++){for(leti=0;i<TETRIS_COLS;i++){if(tetris_status[currentFall[j].y][i]==NO_BLOCK)break;//最后一行满了if(i==TETRIS_COLS-1){//消除最后一行for(leti=currentFall[j].y;i>0;i--){for(letj=0;j<TETRIS_COLS;j++)tetris_status[i][j]=tetris_status[i-1][j];}//分数增加curScore+=5;localStorage.setItem("curScore",curScore);if(curScore>maxScore){//超越最高分maxScore=curScore;localStorage.setItem("maxScore",maxScore);}//加速curSpeed+=0.1;localStorage.setItem("curSpeed",curSpeed);//ui输出curScoreEle.innerHTML=""+curScore;maxScoreEle.innerHTML=""+maxScore;curSpeedEle.innerHTML=curSpeed.toFixed(1);//保留两位小数clearInterval(timer);timer=setInterval(function(){next();},500/curSpeed);}}}//判断是否触顶for(leti=0;i<currentFall.length;i++){if(currentFall[i].y==0){gameEnd();return;}}localStorage.setItem("tetris_status",JSON.stringify(tetris_status));//新的blockcreateBlock();localStorage.setItem("currentFall",JSON.stringify(currentFall));}drawBlocks();}//右移functionmoveRight(){for(leti=0;i<currentFall.length;i++){if(currentFall[i].x+1>=TETRIS_ROWS||tetris_status[currentFall[i].y][currentFall[i].x+1]!=NO_BLOCK)return;}for(leti=0;i<currentFall.length;i++)currentFall[i].x++;localStorage.setItem("currentFall",JSON.stringify(currentFall));return;}//左移functionmoveLeft(){for(leti=0;i<currentFall.length;i++){if(currentFall[i].x-1<0||tetris_status[currentFall[i].y][currentFall[i].x-1]!=NO_BLOCK)return;}for(leti=0;i<currentFall.length;i++)currentFall[i].x--;localStorage.setItem("currentFall",JSON.stringify(currentFall));return;}//judgecanmovedownandifarriveatendreturn1,iftouchotherblocksreturn2,else,return0functionmoveDown(){for(leti=0;i<currentFall.length;i++){if(currentFall[i].y>=TETRIS_ROWS-1||tetris_status[currentFall[i].y+1][currentFall[i].x]!=NO_BLOCK)returntrue;}for(leti=0;i<currentFall.length;i++)currentFall[i].y+=1;returnfalse;}上下左右移动functiongameKeyEvent(evt){switch(evt.keyCode){//向下case40://↓case83://Snext();drawBlocks();break;//向左case37://←case65://AmoveLeft();drawBlocks();break;//向右case39://→case68://DmoveRight();drawBlocks();break;//旋转case38://↑case87://Wrotate();drawBlocks();break;}}keydown事件监听

keydown事件监听

其他的详细情况可以看源代码,我就不整理了。

接下来我们看游戏结束时的特效。因为我也不是很懂,所以在这里整理的会比较详细。当做学习。

//gameendfunctiongameEnd(){clearInterval(timer);//键盘输入监听结束window.onkeydown=function(){//按任意键重新开始游戏window.onkeydown=gameKeyEvent;//初始化游戏数据initData();createBlock();localStorage.setItem("currentFall",JSON.stringify(currentFall));localStorage.setItem("tetris_status",JSON.stringify(tetris_status));localStorage.setItem("curScore",curScore);localStorage.setItem("curSpeed",curSpeed);//绘制curScoreEle.innerHTML=""+curScore;curSpeedEle.innerHTML=curSpeed.toFixed(1);//保留两位小数drawBlocks();timer=setInterval(function(){next();},500/curSpeed);//清除特效this.stage.removeAllChildren();this.textStage.removeAllChildren();};//特效,游戏结束setTimeout(function(){initAnim();//擦除黑色方块for(leti=0;i<TETRIS_ROWS;i++){for(letj=0;j<TETRIS_COLS;j++)canvasCtx.clearRect(j*CELL_SIZE+1,i*CELL_SIZE+1,CELL_SIZE-2,CELL_SIZE-2);}},200);//推迟显示FailedsetTimeout(function(){if(textFormed){explode();setTimeout(function(){createText("FAILED");},810);}else{createText("FAILED");}},800);}

上面代码里的localstorage是html5的本地数据存储。因为不是运用很难,所以具体看代码。

整个特效是运用了createjs插件。要引入几个文件。

easeljs-0.7.1.min.js,EasePacj.min.js,requestAnimationFrame.js和TweenLite.min.js 游戏重新开始就要清除特效。我看api里我第一眼望过去最明显的就是removeAllChildren(),所以就选了这个。其他的改进日后再说。

//清除特效this.stage.removeAllChildren();this.textStage.removeAllChildren();functioninitAnim(){initStages();initText();initCircles();//在stage下方添加文字——按任意键重新开始游戏.tmp=newcreatejs.Text("t","12px'SourceSansPro'","#54555C");tmp.textAlign='center';tmp.x=180;tmp.y=350;tmp.text="按任意键重新开始游戏";stage.addChild(tmp);animate();}initAnim

上面初始化了一个stage,用于存放特效,一个textstage,用于形成“FAILED”的像素图片。还有一个按任意键重新游戏的提示。同时开始每隔一段时间就刷新stage。

根据block的位置来初始化小圆点。

functioninitCircles(){circles=[];varp=[];varcount=0;for(leti=0;i<TETRIS_ROWS;i++)for(letj=0;j<TETRIS_COLS;j++)if(tetris_status[i][j]!=NO_BLOCK)p.push({'x':j*CELL_SIZE+2,'y':i*CELL_SIZE+2,'w':CELL_SIZE-3,'h':CELL_SIZE-4});for(vari=0;i<250;i++){varcircle=newcreatejs.Shape();varr=7;//x和y范围限定在黑色block内varx=p[count]['x']+p[count]['w']*Math.random();vary=p[count]['y']+p[count]['h']*Math.random();count++;if(count>=p.length)count=0;varcolor=colors[Math.floor(i%colors.length)];varalpha=0.2+Math.random()*0.5;circle.alpha=alpha;circle.radius=r;circle.graphics.beginFill(color).drawCircle(0,0,r);circle.x=x;circle.y=y;circles.push(circle);stage.addChild(circle);circle.movement='float';tweenCircle(circle);}}initCircles

然后再讲显示特效Failed的createText()。先将FAILED的text显示在textstage里,然后ctx.getImageData.data获取像素数据,并以此来为每个小圆点定义位置。

functioncreateText(t){curText=t;varfontSize=500/(t.length);if(fontSize>80)fontSize=80;text.text=t;text.font="900"+fontSize+"px'SourceSansPro'";text.textAlign='center';text.x=TETRIS_COLS*CELL_SIZE/2;text.y=0;textStage.addChild(text);textStage.update();varctx=document.getElementById('text').getContext('2d');varpix=ctx.getImageData(0,0,600,200).data;textPixels=[];for(vari=pix.length;i>=0;i-=4){if(pix[i]!=0){varx=(i/4)%600;vary=Math.floor(Math.floor(i/600)/4);if((x&&x%8==0)&&(y&&y%8==0))textPixels.push({x:x,y:y});}}formText();textStage.clear();//清楚text的显示}CreateText

跟着代码的节奏走,我们现在来到了formtext.

functionformText(){for(vari=0,l=textPixels.length;i<l;i++){circles[i].originX=offsetX+textPixels[i].x;circles[i].originY=offsetY+textPixels[i].y;tweenCircle(circles[i],'in');}textFormed=true;if(textPixels.length<circles.length){for(varj=textPixels.length;j<circles.length;j++){circles[j].tween=TweenLite.to(circles[j],0.4,{alpha:0.1});}}}formtext

explode()就是讲已组成字的小圆点给重新遣散。

动画实现是使用了tweenlite.

functiontweenCircle(c,dir){if(c.tween)c.tween.kill();if(dir=='in'){/*TweenLite.to改变c实例的x坐标,y坐标,使用easeInOut弹性函数,透明度提到1,改变大小,radius,总用时0.4s*/c.tween=TweenLite.to(c,0.4,{x:c.originX,y:c.originY,ease:Quad.easeInOut,alpha:1,radius:5,scaleX:0.4,scaleY:0.4,onComplete:function(){c.movement='jiggle';/*轻摇*/tweenCircle(c);}});}elseif(dir=='out'){c.tween=TweenLite.to(c,0.8,{x:window.innerWidth*Math.random(),y:window.innerHeight*Math.random(),ease:Quad.easeInOut,alpha:0.2+Math.random()*0.5,scaleX:1,scaleY:1,onComplete:function(){c.movement='float';tweenCircle(c);}});}else{if(c.movement=='float'){c.tween=TweenLite.to(c,5+Math.random()*3.5,{x:c.x+-100+Math.random()*200,y:c.y+-100+Math.random()*200,ease:Quad.easeInOut,alpha:0.2+Math.random()*0.5,onComplete:function(){tweenCircle(c);}});}else{c.tween=TweenLite.to(c,0.05,{x:c.originX+Math.random()*3,y:c.originY+Math.random()*3,ease:Quad.easeInOut,onComplete:function(){tweenCircle(c);}});}}}

TweenLite.to函数第一个参数,要做动画的实例,第二个参数,事件,第三个参数,动画改变参数。

Quad.easeInOut()意思是在动画开始和结束时缓动。onComplete动画完成时调用的函数。易得,在我们的应用中,我们将开始下一次动画。

关于“html5如何实现俄罗斯方块小游戏”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

发布于 2022-03-18 22:50:51
收藏
分享
海报
0 条评论
27
上一篇:js如何删除数组的重复项 下一篇:在html5中表单验证特性的示例分析
目录

    0 条评论

    本站已关闭游客评论,请登录或者注册后再评论吧~

    忘记密码?

    图形验证码