开始自己手写一个好玩的俄罗斯方块吧,上变形,左右移动,下加速,空格瞬移等功能,无聊的时候学习下canvas,f12 修改分数,体验金手指的快乐吧
1、定义界面,和按钮
<div id="by"> <div id="title"> <!--//定义游戏界面--> <canvas id="myCanvas" height="600" width="400" style="border: 2px solid #3c763d"></canvas> <!--//定义下一个方块的预知框--> <div id="title2"> <canvas id="Canvas" height="150" width="150" style="border: 2px solid #3c763d"></canvas> </div> </div> <!--背景图--> <div id=""> <img src="http://cnd.yinglingxuan.cn/75Z58PICq67_1024.jpg" id="img" width="405px" height="602px" /> </div> </div> <!--按钮--> <div class="aj"> <span onclick="tops()">上</span> <span onclick="under()">下</span> <span onclick="lefts()">左</span> <span onclick="rights()">右</span> </div>
2、js部分
1、先定义每个图形的形状和变化的形状,这里是使用多维数组的方式去保存它图形每个方块的位置(当然这里也可以用循环的方式)
var data=[[[[1,0,0,0],[1,0,0,0],[1,1,0,0],[0,0,0,0]],[[1,1,1,0],[1,0,0,0],[0,0,0,0],[0,0,0,0]],[[1,1,0,0],[0,1,0,0],[0,1,0,0],[0,0,0,0]],[[0,0,1,0],[1,1,1,0],[0,0,0,0],[0,0,0,0]]], [[[1,0,0,0],[1,1,0,0],[1,0,0,0],[0,0,0,0]],[[1,1,1,0],[0,1,0,0],[0,0,0,0],[0,0,0,0]],[[0,0,1,0],[0,1,1,0],[0,0,1,0],[0,0,0,0]],[[0,0,0,0],[0,1,0,0],[1,1,1,0],[0,0,0,0]]], [[[0,1,0,0],[0,1,1,0],[0,0,1,0],[0,0,0,0]],[[0,1,1,0],[1,1,0,0],[0,0,0,0],[0,0,0,0]],[[0,1,0,0],[0,1,1,0],[0,0,1,0],[0,0,0,0]],[[0,0,0,0],[0,1,1,0],[1,1,0,0],[0,0,0,0]]], [[[0,1,1,0],[0,1,1,0],[0,0,0,0],[0,0,0,0]],[[0,1,1,0],[0,1,1,0],[0,0,0,0],[0,0,0,0]],[[0,1,1,0],[0,1,1,0],[0,0,0,0],[0,0,0,0]],[[0,1,1,0],[0,1,1,0],[0,0,0,0],[0,0,0,0]]], [[[0,0,1,0],[0,1,1,0],[0,1,0,0],[0,0,0,0]],[[1,1,0,0],[0,1,1,0],[0,0,0,0],[0,0,0,0]],[[0,0,1,0],[0,1,1,0],[0,1,0,0],[0,0,0,0]],[[1,1,0,0],[0,1,1,0],[0,0,0,0],[0,0,0,0]]], [[[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]],[[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]],[[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]],[[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]]], [[[1,1,0,0],[1,0,0,0],[1,0,0,0],[0,0,0,0]],[[1,1,1,0],[0,0,1,0],[0,0,0,0],[0,0,0,0]],[[0,0,0,0],[0,0,1,0],[0,0,1,0],[0,1,1,0]],[[0,0,0,0],[0,0,0,0],[1,0,0,0],[1,1,1,0]]] ];
2、定义图形的位置和每帧下落的速度
var newX=120;//记录这个图形的左右位置 var count=0; //记录下落的速度 var shape=0; //记录形状 var finallys=new Array(); //记录落下后停止的位置 var re=6; //获取随机数方块 var fat=1; //每帧的速度 var xyg=0; //下一个方块 var dfen=0;//分数 //定义游戏界面 var c=document.getElementById("myCanvas"); var ctx=c.getContext("2d"); //下个方格的画布 var c2=document.getElementById("Canvas"); var ctx2=c2.getContext("2d");
3、定义组成图形的方法
//组成图形的方法 function constitute(){ var Arr=new Array(); // 存储当前下来的方块位置 var y=0; //记录每个小方块组成图形的小方块y下标--相加 for (var i=0;i<data[re][shape].length;i++) { var x=0; //记录每个小方块组成图形的小方块x下标 for (var j=0;j<data[re][shape][i].length;j++) { x+=20; if(data[re][shape][i][j]==1&&Arr.length<4){ //每个图形都是四个小方块组成 ctx.strokeStyle = "#000"; ctx.strokeRect(x+newX,y+count,20,20); ctx.fillStyle="#000000"; //定义颜色 ctx.fillRect(x+newX,y+count,19,19); //绘图 var a={ xs:x+newX, ys:y+count }; Arr.push(a); //记录图形位置 if(Arr.length==4){ //图形形成后调用掉落完成后重新调用 anew(Arr); return Arr; } } } y+=20; }; }
3、判断掉落是否到底或者碰撞到上一个底部的方块
//掉落完成后重新调用 function anew (arr){ var da=bottom(arr); //调用判断是否到底 if(da==1){ //如果返回1 则已经是到底部, finallys.push(arr); // 保存在底部的方块,方便碰撞判断 count=0; //恢复成原来的上下位置 newX=120; //恢复原来的左右位置 re=xyg; //当前的方块变成上一个的下一个 xyg=Math.floor(Math.random()*7); //重新随机获取当前的下一个 ctx2.clearRect(0, 0, c.width,c.height); //清除画布 xygs();//重新绘制预知框的 } } //判断是否碰到下面的障碍物 function bottom(arr){ //判断是否掉落底部//返回1表示可以保存起来 for (var i =0;i<arr.length;i++) { if(arr[i].ys>=c.height-20){//首先判断是否到界面的底部 /*console.log(arr);*/ return 1; } for (var t=0;t<finallys.length;t++) {//再到保存的底部图形里面遍历出来判断是否与其他图形发生了碰撞 for (var j=0;j<finallys[t].length;j++) { if(finallys[t][j].ys==arr[i].ys+20&&finallys[t][j].xs==arr[i].xs){ /*console.log(arr);*/ return 1; } } } } return 0; }
4、下落到底部后的方块要保存起来,定义重新渲染的方法
//用来组成下标已经堆积的格子 function ground(finallys){ deletes();//判断是否到达顶部了, 顶部了就直接清除重新开始 for (var i=0;i<finallys.length;i++) { //新图形开始的时候重新渲染已经到底部保存的图形 for (var j=0;j<finallys[i].length;j++) { ctx.strokeStyle = "#000"; ctx.strokeRect(finallys[i][j].xs,finallys[i][j].ys,20,20); ctx.fillStyle="#000000"; //定义颜色 ctx.fillRect(finallys[i][j].xs,finallys[i][j].ys,19,19); //画图 if(finallys[i][j].ys<=20){ this.finallys=new Array(); count=0; constitute(); //调用组成画布的方法 /*ctx.clearRect(0, 0, c.width,c.height); //清除画布*/ return; } } } }
5、当横向一行满了后就可以清楚并得到对应的分数
//判断是否组满一行达到清除的位置 function deletes(){ var c=600;//当前横向的宽度 var add=new Array;//记录宽度的每个方块 for (var i=0;i<c/20;i++) {//除以当前方块的宽度获得每个横度的方块位置 c=c-20; add.push(c); } for (var a=0;a<add.length;a++) {//判断每个横向的方块是否堆积满了 var cou=0; for (var t=0;t<finallys.length;t++) { for (var j=0;j<finallys[t].length;j++) { if(add[a]==finallys[t][j].ys){ cou++; } } } if(cou>=20){ //如果横向满了调用清除的方法 qc(add[a]);// cou=0; } } } //一行满了后清除和上面的向下增加移动 function qc(add){ for (var t=0;t<finallys.length;t++) { for (var j=0;j<finallys[t].length;j++) { if(add==finallys[t][j].ys){ finallys[t].splice(j,1); dfen=dfen+10; j--; } } } for (var t=0;t<finallys.length;t++) { for (var j=0;j<finallys[t].length;j++) { if(finallys[t][j].ys<add){ finallys[t][j].ys=finallys[t][j].ys+20; } } } }
6、碰撞检测
//判断不能掉出围起来的范围 function crash(count,e){ //count 下落的位置, e 表示当前是什么按钮操作 var newCount=0; //判断是否有下降的数据有就下降 if(count!=0){ newCount=count; } var y=0; //记录组成形状的位置--相加 var r=0; var make=-1; for (var i=0;i<data[re][shape].length;i++) { var x=0; for (var j=0;j<data[re][shape][i].length;j++) { x+=20; if(data[re][shape][i][j]==1){ //防止变形的时候溢出 if(e==38&&x+newX<0||x+newX>c.width-20){ if(x+newX<0){ newX=newX+20; r=1; }else{ newX=newX-20; r=2; } } if(e==38){ if(y+count>c.height-20){ if(shape!=0){ shape=shape-1; }else{ shape=3; } } } ////防止突出右边 if(e==39&&c.width-20<x+newX+20){ make=1; } //防止突出左边 if(e==37&&x+newX-20<0){ make=1; } if(y+newCount>c.height-20){ count=count-20; } var ys=(y+count)%20; //下落的时候可能会一直按着 速度快会直接插到下面,所以这里要求余,当到这个方块的位置的时候取整判断 var es=20-ys; var newCount=y+count+es; for (var t=0;t<finallys.length;t++) { for (var j1=0;j1<finallys[t].length;j1++) { if(finallys[t][j1].ys==newCount&&finallys[t][j1].xs==x+newX+20&&e==39){ newX=newX-20; } if(finallys[t][j1].ys==newCount&&finallys[t][j1].xs==x+newX-20&&e==37){ newX=newX+20; } if(finallys[t][j1].ys+20==newCount&&finallys[t][j1].xs==x+newX&&e==38){ if(shape!=0){ shape=shape-1; }else{ shape=3; } if(r==1){ newX=newX-20; } if(r==2){ newX=newX+20; } } } } } } y+=20; }; if(e==39&&make==-1){ newX=newX+20; } if(e==37&&make==-1){ newX=newX-20; } }
7、按钮操作
$(document).keydown(function(e){ //电脑键盘的 if(e.keyCode==39){ //右 crash(count,e.keyCode);//判断是否可以加到右边 }else if(e.keyCode==37){ //左 //判断是否可以加到左边 crash(count,e.keyCode); }else if(e.keyCode==40){ //下 var i=count%10; count=count-i; fat=10; }else if(e.keyCode==38){ //上 shape++; if(shape>3){ shape=0; } crash(count,e.keyCode); //防止变形溢出 }else if(e.keyCode==32){ var i=count%20; count=count-i; for (var t=0;t<400;t++) { count=count+10; var arr=constitute(); var e=bottom(arr); if(e==1){ break; } } } }); //////////////////////////// 手机端的按钮 function under(){ var i=count%10; count=count-i; fat=10; setTimeout(function(){ fat=1; },100) } document.onkeyup=function(){ fat=1; } function tops(){ shape++; if(shape>3){ shape=0; } crash(count,38); //防止变形溢出 } function lefts(){ //判断是否可以加到左边 crash(count,37); } function rights(){ crash(count,39);//判断是否可以加到右边 }
8、定义绘制预知框的方法,如上面绘制的方法一样,只是对应的canvas不同
//预知框里面的绘制 function xygs(){ var y=0; //记录组成形状的位置--相加 for (var i=0;i<data[xyg][shape].length;i++) { var x=0; for (var j=0;j<data[xyg][shape][i].length;j++) { x+=20; if(data[xyg][shape][i][j]==1){ ctx2.strokeStyle = "#000"; ctx2.strokeRect(x+30,y+50,20,20); ctx2.fillStyle="#000000"; //定义颜色 ctx2.fillRect(x+30,y+50,19,19); //画图 } } y+=20; }; }
9、开始运行的方法
function dy(){ ctx.clearRect(0, 0, c.width,c.height); //清除画布 constitute(); //调用组成画布的方法 count=count+fat; // 下落的速度 ground(finallys); //调用已经在底部的方块的方法 ctx.font="20px 微软雅黑"; ctx.fillText("得分:"+dfen+"",20,30); requestAnimationFrame(dy); //调用循环运行 } requestAnimationFrame(dy); //开始运行每一帧 if(finallys.length==0){ //获取第一个预知框 xyg=Math.floor(Math.random()*7);//随机获取 xygs();//调用预知框的渲染 } constitute()//开始绘制