Handwriting a tetris game with h5 canvas

Posted May 28, 20207 min read

Start writing a fun Tetris yourself, with the functions of up-transformation, left-right movement, down acceleration, space teleportation, etc. When you are bored, learn canvas, f12 modify the score, and experience the joy of gold fingers


WeChat Picture_20200528103937.png

  1. Define the interface, and buttons

<div id = "by">
        <div id = "title">
            <!-//Define the game interface->
            <canvas id = "myCanvas" height = "600" width = "400" style = "border:2px solid # 3c763d"> </canvas>
            <!-//Define the prediction box of the next block->
            <div id = "title2">
                <canvas id = "Canvas" height = "150" width = "150" style = "border:2px solid # 3c763d"> </canvas>
            </div>
        </div>
        <!-Background->
        <div id = "">
            <img src = "http://cnd.yinglingxuan.cn/75Z58PICq67_1024.jpg" id = "img" width = "405px" height = "602px" />
        </div>
    </div>
    <!-Button->
    <div class = "aj">
        <span onclick = "tops()"> Up </span>
        <span onclick = "under()"> Next </span>
        <span onclick = "lefts()"> Left </span>
        <span onclick = "rights()"> right </span>
    </div>
  1. The js part

1. First define the shape of each figure and the changed shape. Here is the way to save the position of each square of the figure using a multi-dimensional array.

        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. Define the position of the graph and the falling speed of each frame

        var newX = 120; //Record the left and right position of this graph
        var count = 0; //Record the falling speed
        var shape = 0; //Record shape
        var finallys = new Array(); //Record where to stop after falling
        var re = 6; //Get random number square
        var fat = 1; //Speed   per frame
        var xyg = 0; //Next block
        var dfen = 0; //Score
           //Define the game interface
        var c = document.getElementById("myCanvas");
        var ctx = c.getContext("2d");
        //The canvas of the next square
        var c2 = document.getElementById("Canvas");
        var ctx2 = c2.getContext("2d");

3. Define the method of forming the graph

        //Method of forming graphics
          function constitute() {
              var Arr = new Array(); //store the current block position
              var y = 0; //Record the small square y subscript of each small square to form a graph-add
              for(var i = 0; i <data [re][shape].length; i ++) {
                  var x = 0; //Record the subscript x subscript of each small square
                  for(var j = 0; j <data [re][shape][i].length; j ++) {
                      x + = 20;
                      if(data [re][shape][i][j]== 1 && Arr.length <4) {//Each graph is composed of four small squares
                           ctx.strokeStyle = "# 000";
                           ctx.strokeRect(x + newX, y + count, 20,20);
                           ctx.fillStyle = "# 000000"; //Define the color
                           ctx.fillRect(x + newX, y + count, 19,19); //Draw
                           var a = {
                               xs:x + newX,
                               ys:y + count
                           };
                           Arr.push(a); //Record the graphic position
                           if(Arr.length == 4) {//Call after the graph is formed
                               anew(Arr);
                               return Arr;
                           }
                      }
                  }
                  y + = 20;
              };
          }

3. Determine whether the drop is in the end or hits the bottom square

          //Recall after the drop is complete
          function anew(arr) {
              var da = bottom(arr); //Call to judge whether it is in the end
              if(da == 1) {//If it returns 1, it is already at the bottom,
                  finallys.push(arr); //save the square at the bottom to facilitate collision judgment
                  count = 0; //Restore the original up and down position
                  newX = 120; //Restore the original left and right position
                  re = xyg; //The current block becomes the previous one
                  xyg = Math.floor(Math.random() * 7); //Re-randomly get the current next one
                  ctx2.clearRect(0, 0, c.width, c.height); //Clear canvas
                  xygs(); //Redraw the prediction box
              }
          }



          //Determine whether you have encountered the following obstacle
          function bottom(arr) {//Determine whether the bottom is dropped //Return 1 means it can be saved
              for(var i = 0; i <arr.length; i ++) {
                  if(arr [i].ys> = c.height-20) {//First determine whether to the bottom of the interface
                      /*console.log(arr);*/
                      return 1;
                  }
                  for(var t = 0; t <finallys.length; t ++)(//Go to the saved bottom figure to traverse it to determine whether it collides with other figures
                      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. The square that has fallen to the bottom should be saved, and the method of re-rendering should be defined

          //Used to form a grid with subscripts already stacked
          function ground(finallys) {
              deletes(); //Determine whether it has reached the top
              for(var i = 0; i <finallys.length; i ++) {//When the new graphic starts, re-render the graphic that has been saved to the bottom
                  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"; //Define the color
                       ctx.fillRect(finallys [i][j].xs, finallys [i][j].ys, 19,19); //Draw a picture
                       if(finallys [i][j].ys <= 20) {
                           this.finallys = new Array();
                           count = 0;
                           constitute(); //Call the method that makes up the canvas
                           /*ctx.clearRect(0, 0, c.width, c.height); //Clear canvas * /
                           return;
                       }
                  }
              }
          }

5. When the horizontal line is full, you can clear and get the corresponding score

      //Determine whether the group is full to reach the clear position
      function deletes() {
        var c = 600; //The width of the current horizontal
        var add = new Array; //record each block of width
        for(var i = 0; i <c/20; i ++) {//divide the width of the current square to get the square position of each horizontal
            c = c-20;
            add.push(c);
        }
        for(var a = 0; a <add.length; a ++) {//determine whether each horizontal block is full
            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) {//If the horizontal is full, call the clear method
                qc(add [a]); //
                cou = 0;
            }
        }
    }
      //Clear after a line is full and move upwards downward
      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. Collision detection

          //Judgment can't fall out of the enclosing range
          function crash(count, e) {//count falling position, e indicates what button operation is currently
              var newCount = 0; //Decide if there is any data that drops
              if(count! = 0) {
                  newCount = count;
              }
              var y = 0; //Record the position of the shape-add
              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) {
                          //Prevent overflow when deformed
                          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;
                                }
                              }
                          }

                          ////Prevent the right highlight
                          if(e == 39 && c.width-20 <x + newX + 20) {
                              make = 1;
                          }
                          //Prevent highlighting the left
                          if(e == 37 && x + newX-20 <0) {
                              make = 1;
                          }
                          if(y + newCount> c.height-20) {
                              count = count-20;
                          }
                          var ys =(y + count)%20; //When you fall, you may keep pressing it, and it will be inserted directly below, so here is the requirement, and when you reach the position of this block, round it up
                          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. Button operation

        $(document) .keydown(function(e) {//Computer keyboard
            if(e.keyCode == 39) {//right
                crash(count, e.keyCode); //judge whether it can be added to the right
            } else if(e.keyCode == 37) {//left
                //Determine whether it can be added to the left
                crash(count, e.keyCode);
            } else if(e.keyCode == 40) {//Next
                var i = count%10;
                count = count-i;
                fat = 10;

            } else if(e.keyCode == 38) {//Up
               shape ++;
               if(shape> 3) {
                   shape = 0;
               }
               crash(count, e.keyCode); //Prevent deformation overflow
            } 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;
                    }
                }
            }
        });
        /////////////////////////////button on the phone
        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); //Prevent deformation overflow
          }
          function lefts() {
              //Determine whether it can be added to the left
            crash(count, 37);
          }
          function rights() {
              crash(count, 39); //Determine whether it can be added to the right
          }

8. Define the method of drawing the predictive box, just like the method drawn above, but the corresponding canvas is different

          //Drawing in the prediction box
          function xygs() {
              var y = 0; //Record the position of the shape-add
              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"; //define color
                           ctx2.fillRect(x + 30, y + 50,19,19); //Draw a picture
                      }
                  }
                  y + = 20;
              };
          }

9. How to start running

      function dy() {
            ctx.clearRect(0, 0, c.width, c.height); //Clear canvas
            constitute(); //Call the method that makes up the canvas
            count = count + fat; //Falling speed
            ground(finallys); //Call the method of the square that is already at the bottom
            ctx.font = "20px Microsoft Yahei";
            ctx.fillText("Score:" + dfen + "", 20,30);
            requestAnimationFrame(dy); //Call to run in a loop
        }
        requestAnimationFrame(dy); //Start running every frame

        if(finallys.length == 0) {//Get the first prediction box
            xyg = Math.floor(Math.random() * 7); //random acquisition
            xygs(); //Call the rendering of the prediction box
        }
        constitute() //Start drawing