通过h5的canvas手写一个俄罗斯方块小游戏

开始自己手写一个好玩的俄罗斯方块吧,上变形,左右移动,下加速,空格瞬移等功能,无聊的时候学习下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()//开始绘制

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理