Tetris complete code

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Russian Block</title>
    <link rel="stylesheet" href="./css/index.css">
</head>
<body>
    <!--游戏活动区域-->
<article class="container" id="container">
    <!--开始游戏按钮-->
       
    <button id="start">开始游戏</button>
    </article>
    
    <script src="./js/index.js"></script>
    <script src="./js/jquery-3.5.1.min.js"></script>
</body>
</html>

index.css

/* 初始化页面样式 */
* {
    margin: 0;
    padding: 0;
    list-style: none;
}

/* 设置活动区域container样式 */
.container {
    width: 540px;
    height: 600px;
    background-color: black;
    margin: 30px auto 0;
    position: relative;
}

/*设置游戏活动区域创建块元素 actvity_model 样式*/
.actvity_model{
    background-color: cadetblue;
    border: 1px solid #eee;
    box-sizing: border-box;
    position: absolute;
}

/*按钮样式*/
#start{
    top: -27px;
    right: 1px;
    position: absolute;
    color: #fff;
    width: 100px;
    border: 1px solid cadetblue;
    background-color: cadetblue;
    height: 26px;
    text-align: center;
}

/*滑到底部固定游戏快*/
.fixed_model {
    background-color: #FFF;
    border: 1px solid #333;
    box-sizing: border-box;
    position: absolute;
}

/* 提示框 */
#model{
    margin: auto;
    width: 200px;
    border: 3px solid cadetblue;
    padding: 30px 10px;
    position: fixed;
    margin-left: -122px;
    margin-top: -262px;
    text-align: center;
    left: 50%;
    top: 50%;
    background: #fff;
    display: none;
}

index.js

function CreateRandomModel(){
     // 创建所有可能的方块的数组
     var MODELS = [
            {//第一个 L 字型
                 'name': 'L 字型',
                 'model': [[0,0],[1,0],[2,0],[2,1]]
            },
            {//第二个 J 字型
                 'name': 'J 字型',
                 'model': [[0,1],[1,1],[2,0],[2,1]]
            },
            {//第三个凸字型
                 'name': '凸字型',
                 'model': [[1,1],[0,0],[1,0],[2,0]]
            },
            {//第四个田字型
                 'name': '田字型',
                 'model': [[0,0],[0,1],[1,0],[1,1]]
            },
            {//第五个一字型
                 'name': '一字型',
                 'model': [[0,0],[0,1],[0,2],[0,3]]
            },
            {//第六个 Z 字型
                 'name': 'Z 字型',
                 'model': [[0,0],[0,1],[1,1],[1,2]]
            }
     ];
 
     // 随机方块模型下标
     var ramdomNum = Math.floor(Math.random()*MODELS.length);
 
     //方块模型名称
     this.name = MODELS[ramdomNum]['name'];
 
     //方块模型
     this.blockModel = MODELS[ramdomNum]['model'];
 }
 
 CreateRandomModel.prototype.getBlockModle = function(){
     return this.blockModel;
 }
 
 CreateRandomModel.prototype.getName = function(){
     return this.name;
 }
 
 // 显示屏的高度
 var screenWidth = window.screen.width;
 var screenHeight = window.screen.height;
 console.log(screenWidth);
 console.log(screenHeight);
 
 // 根据屏幕分辨率设置步长
 var STEP;
 if(screenWidth >= 1200){
      STEP = 30;
 }else if(screenWidth >= 992){
      STEP = 20;
 }else if(screenWidth >= 768){
      STEP = 10;
 }else{
      STEP = 5;
 }
 
 // 根据步长得到方块的宽高
 var boxWidth = STEP;
 var boxHeight = STEP;
 // 根据游戏界面方块的行列数得到游戏界面的宽高
 var gameHeight = STEP*20;
 var gameWidth = STEP*18;
 console.log(gameWidth);
 console.log(gameHeight);
 console.log(boxWidth);
 console.log(boxHeight);
 
 // 分割容器为多少行多少列(即多少个单个方块的距离)
 var W_COUNT=gameWidth/boxWidth;
 var H_COUNT=gameHeight/boxHeight;
 
 //定义变量设置当前使用的模型
 var currentModel = [];
 //标记初始位置
 var currentX = 0;
 var currentY = 0;
 
 //根据模型的数据创建对应的块元素
 function createModel() {
     //判断游戏是否结束
     if(isGameOver()){
          // console.log("游戏结束!")
     gameOver();
          return;
     }
      //游戏界面宽高样式设置
      document.getElementById('container').style.width = gameWidth + 'px';
      document.getElementById('container').style.height = gameHeight + 'px';
 
      //调用构造函数,实例化一个随机方块对象
      var objectBlocks = new CreateRandomModel();
      //获取随机方块对象的模型
      currentModel = objectBlocks.getBlockModle();
      //重新初始化 16 宫格位置
      currentX = 0;
      currentY = 0;
 
      for (var key in currentModel) {
           //创建 div 子节点
           var divEle = document.createElement('div');
           //给 div 子节点设置 class 属性值
           divEle.className = 'actvity_model';
           //追加方块到游戏界面中
           document.getElementById('container').appendChild(divEle);
           //方块模型中单个方块宽高样式设置
           document.getElementsByClassName('actvity_model')[key].style.width = boxWidth + 'px';
           document.getElementsByClassName('actvity_model')[key].style.height = boxHeight + 'px';
      }
 }
 
 //根据数据源定义移动块元素的位置
 function locationBlocks(){
      //方块移动时需要判断活动边界,在函数顶部调用
      checkBound();
      //获取所有的块元素
      var eles = document.getElementsByClassName('actvity_model');
      for (var i=0; i<eles.length; i++){
           //单个块元素
           var actityModelEle = eles[i];
           //找到每个块元素对应的数据(行,列)
           var blockModel = currentModel[i];
           //根据每个块元素对应的数据来指定块元素的位置
           //每个块元素的位置由两个值来决定 (16 宫格所在的位置/块元素在 16 宫格的位置)(每次移动的距离,为单个方块的宽高度)
           actityModelEle.style.top = (currentY + blockModel[0]) * boxWidth + 'px';
           actityModelEle.style.left = (currentX + blockModel[1]) * boxHeight + "px";
      } 
 }
 //点击页面中“开始游戏”按钮,触发点击事件后游戏开始
document.getElementById('start').onclick=function(){
     //清除已有模态框元素
     $("#modal").remove();
     //获取根据模型的数据创建对应的块元素
     createModel();
     //根据数据源定义移动块元素的位置
     locationBlocks();
     //监听用户的键盘事件()
     onKeyDown();
}

//创建 onKeyDown()函数监听用户的键盘事件
function onKeyDown(){
     document.onkeydown = function(event){
          console.log(event.keyCode)
     }
}

//监听用户的键盘事件
function onKeyDown() {
     //获取按下的键盘按键 Unicode 值
     document.onkeydown = function (event) {
          switch (event.keyCode) {
               case 38: //(上方向键)
                    console.log("上键")
                    rotate();
                    break;
               case 39: //(右方向键)
                    console.log("右键")
                    move(1,0);
                    break;
               case 40: //(下方向建)
                    console.log("下键")
                    move(0,1);
                    break;
               case 37: //(左方向建)
                    console.log("左键")
                    move(-1,0);
                    break;
          }
     }
}

//移动
function move(x, y) {
     if (isMent(currentX + x, currentY + y, currentModel)) {
          if (y !== 0) {
               // console.log("发生了触碰");
               //模型之间底部发生了触碰
               fixedBottonModel();
          }
     }
          
     //16 宫格移动
     currentX += x;
     currentY += y;
     //根据 16 宫格的位置重新定位块元素
     locationBlocks();
}

// 旋转模型
function rotate() {
     var cloneModels = deepClone(currentModel);
     //测试代码
     console.log(cloneModels);
     //遍历模型数据源
     for (var key in currentModel) {
          //块元素数据源
          var blockModel = currentModel[key];
          var temp = blockModel[0]
          //旋转前的行=旋转后的列
          blockModel[0] = blockModel[1];
          //旋转后的行=3-旋转前的行
          blockModel[1] = 3 - temp;
     }
     if(isMent(currentX, currentY, cloneModels)) {
          return;
     }
     //重新定位块元素
     locationBlocks();
}

function checkBound() {
     //定义模型可以活动边界
     var leftBound = 0;
     var rightBound = W_COUNT;
     var boottonBound = H_COUNT;
     //当块元素超出了边界后,让 16 宫格后退
     for (var key in currentModel) {
          var blockModel = currentModel[key];
          //左侧越界
          if ((blockModel[1] + currentX) < leftBound ) {
               currentX++;
          }
          //右侧越界
          if ((blockModel[1] + currentX) >= rightBound) {
               currentX--;
          }
          // 底部越界
          if ((blockModel[0] + currentY) >= boottonBound) {
               currentY--;
               //把模型固定在底部
               fixedBottonModel();
          }
     }
}

// 记录所有块元素的位置
var fixedBlocks = [];

function reStore(){
     //遍历行
     for(var i=0;i<=H_COUNT;i++){
          fixedBlocks[i] = [];
     //遍历列
     for(var j=0;j<=W_COUNT;j++){
          fixedBlocks[i][j] = null;
          }
     }
}



function fixedBottonModel() {
     var activityModelEles = $(".actvity_model");
     //遍历获取的类元素
     for(var i = activityModelEles.length - 1; i >= 0; i--) {
     //获取当前的模型     
          var activityModelEle = activityModelEles[i];
          //更改块元素的类名     
          $(activityModelEle).attr("class","fixed_model");
          var blockModel = currentModel[i];
          //把该元素放入变量中
          fixedBlocks[currentY + blockModel[0]][currentX + blockModel[1]] =activityModelEle;
     }
     //判断是否清理
     isRemoveLine();
     //创建新的模型
     createModel();
}
//判断是否碰撞
function isMent(x, y, model) {
     for (var k in model) {
          var blockModel = model[k];
          //先判断一维数组是否存在元素
          if(!fixedBlocks[y + blockModel[0]]) {
               return true;
          }
          //再判断二维数组里面是否存在元素
          if (fixedBlocks[y +blockModel[0]][x + blockModel[1]]) {
               return true;
          }
     }
     return false;
}

function deepClone(obj) {
     var _obj = JSON.stringify(obj), 
     cloneObj = JSON.parse(_obj);
     return cloneObj;
}

function isRemoveLine() {
     // 遍历所有行中的列
     for (var i = 0; i < H_COUNT; i++) {
         // 标记符,假设当前行被铺满
         var flag = true;
         // 遍历当前行中的所有列
         for (var j = 0; j < W_COUNT; j++) {
             // 如果当前行中有一列没有数据那就说明没有被铺满
             if (!fixedBlocks[i] || !fixedBlocks[i][j]) {
                 flag = false;
                 break;
             }
         }
         if (flag) {
             // 该行已经被铺满
             // console.log("该行已经被铺满");
             // 清理被铺满的该行
             removeLine(i);
         }
     }
     // 判断 flag 值是否为 true
     if (flag) {
         // 该行已经被铺满
         console.log("该行已经被铺满");
     }
 }
 
 
 function removeLine(line) {
     //遍历该行中的所有行
     for (var i = 0; i < W_COUNT; i++) {
         //删除该行中的所有块元素
         if(!fixedBlocks[line]) fixedBlocks[line] = [];
         $(fixedBlocks[line][i]).remove();
         //删除该行中的所有块元素的数据源
         fixedBlocks[line][i] = null;
     }
     downLine(line);
     }
 
 
 function downLine(line) {
     //遍历被清理行之上的所有行
     for (var i = line - 1; i >= 0; i--) {
     //该行中所有列
         for (var j = 0; j < H_COUNT; j++) {
             if(!fixedBlocks[i]) fixedBlocks[i] = [];
             //假如不存在
             if (!fixedBlocks[i][j]) continue;
             //存在数据
             //1.被清理行之上的所有块元素数据源所在的行数+1
             if(!fixedBlocks[i+1]) fixedBlocks[i+1] = [];
             fixedBlocks[i+1][j] = fixedBlocks[i][j];
             //2.让块元素在容器中的位置下落
             fixedBlocks[i+1][j].style.top = (i + 1) * STEP + "px";
             //3.清理掉之前的块元素
             fixedBlocks[i][j] = null;
         }
     }
 }

function isGameOver(){
     //当第0行存在元素的时候,表示游戏结束
     for (var i =0;i<W_COUNT;i++){
          if(fixedBlocks[0][i]||fixedBlocks[1][0]||fixedBlocks[1][1]||fixedBlocks[1][2]||fixedBlocks[1][3]){
               return true;
          }
     }
     return false;
}

function gameOver(){
     if ($("#model").length <= 0){ //校验页面是否存在提示框元素
          var model = $('#container').append("<div id='model'<p>本轮游戏结束!</p></div>");
     }
     //淡入效果显示提示框
     $('#model').fadeIn(100);
     $('.fixed_model').remove();
}
 
 //点击游戏界面,触发点击事件后游戏开始
 document.getElementById("start").onclick = function () {
     reStore();
     //获取根据模型的数据创建对应的块元素
     createModel();
     //根据数据源定义移动块元素的位置
     locationBlocks();
     //监听用户的键盘事件()
     onKeyDown();
 }

 

About jQury related functions

Step one: Open the index.html file and introduce the jquery file into the page.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Russian Block</title>
    <link rel="stylesheet" href="./css/index.css">
</head>
<body>
    <!--游戏活动区域-->
<article class="container" id="container">
    <!--开始游戏按钮-->
    
    
    <button id="start">开始游戏</button>
    </article>
    

    <script src="./js/index.js"></script>
    <script src="./js/jquery-3.5.1.min.js"></script>
</body>
</html>
 Step 2: In the index.js file, create the fixedBottonModel() function to fix the model.

  • Use jQuery's $(".actvity_model") to get all elements with the class name "actvity_model" in the page and save them in the activityModelEles variable.

  • Traverse the obtained class elements in the fixedBottonModel function, obtain the current traversed element, use jQuery to operate the DOM attributes, and change the element attribute values.

  • Get the current block coordinates (currentX and currentY), calculate their absolute coordinates in the fixedBlocks array based on the relative coordinates of each block in the block model currentModel, and save the corresponding activityModelEle elements in the fixedBlocks array for subsequent processing.

  • Call the isRemoveLine() function to determine whether the line needs to be cleared.

  • Finally, call the createModel() function to create a new model.

 Step 3: In the index.js file, create the isMent() function to determine whether there is contact between models.

  • The function is named isMent and receives three parameters: x, y and model. These three parameters represent the current position of the block and the block model.
  • Use a for...in loop to traverse the block model model. In the loop, save the relative position of the current block in the variable blockModel.
  • First, determine whether the y + blockModel[0]-th element in the one-dimensional array fixedBlocks exists, that is, determine whether there are other blocks below the block. If it does not exist, it means that the block collides and returns true. Secondly, determine whether the elements in the y + blockModel[0] row and x + blockModel[1] column in the two-dimensional array fixedBlocks exist, that is, determine whether there are other blocks at the current position of the block. If it exists, it means that the block collides and returns true.

  • If neither of the above two judgments returns true, it means that the blocks have not collided and continue to traverse the next block. If true is not returned after traversing all the blocks, it means that the block has not collided with other blocks, and false is returned.

Call isMent() at the top of the move() function. If there is contact between currently existing models and the model coordinates are at the bottom, use console.log to test whether it is successful.

Modify the debugging code, call fixedBottonModel() to fix the model, and terminate function execution

 Step 4: In the index.js file, define the deepClone(obj) function, edit the rotate() function, and set the square to rotate and then touch.
  • The function name is deepClone and receives a parameter obj, which represents the object to be copied.
  • Inside the function, use the JSON.stringify method to convert the object obj to be copied into string form, and save the result in the _obj variable.
  • Then, use the JSON.parse method to parse the _obj string into a new object and save it in the cloneObj variable.
  • Finally, cloneObj is returned, which completes the deep copy operation.

Instantiate deepClone() in the rotate() function

In the rotate() function, use the deep-copied data source cloneModels to replace the original data currentModel. Call the isMent() function to set the parameters to the current coordinates currentX, currentY, and clone the data source cloneModels.

Step 5: In the index.js file, create the isRemoveLine() function to determine whether a line is filled.
  1. Use a loop to iterate over the rows of the game area. What is used here is a variable  i, which starts from 0 and gradually increases until it reaches the upper limit of the number of rows  H_COUNT. On each pass through the loop, the code sets a marker variable  flagto its initial value  true. This flag variable is used to record whether the current row is filled.

  2. In the nested loop, the code iterates through all columns of the current row. Again, a variable is used here  j, which starts from 0 and gradually increases until it reaches the upper limit of the number of columns  W_COUNT. On each column loop, the code checks to see if there is a square in each column of the current row. Here, it is determined  fixedBlocks[i][j]whether there is a block in the current column by judging whether it exists. If there are no squares in the current column, it means that the current row is not filled, and will  flag be set to  false.

  3. If it is found in the column loop that the current row is not filled ( flag that is, it is  false), the inner loop will be jumped out and the judgment of the next row will continue to be processed. If after the loop through the column ends, the tag variable  flag is still  true, then the current row is filled. At this point, the code calls a function  removeLine(i) to remove the row and perform the appropriate operations. The implementation of the function  removeLine is not given in the code.

  4. The loop continues to process the judgment and operation of the next line. After the loop ends, the code will judge  flag the value of the marked variable again. If  flag it is still  true, the message "The line has been filled" is output.

Call the isRemoveLine() method in the fixedBottonModel() method

 

 

Step 6: In the index.js file, create the removeLine() function to clean up the filled line. 

Define the removeLine() function, get the row number "i", and traverse all the column numbers in the row. It starts from 0 and gradually increases until it reaches the upper limit of the number of columns  W_COUNT.

In each loop, the code first checks  line whether the deleted row exists  fixedBlocks in the array.

The code then  $(fixedBlocks[line][i]).remove() removes the box element in that row from the DOM. A function is used here  $ , probably to use the selector of the jQuery library to delete the corresponding DOM element.

 At the same time, the code sets the corresponding position of  line the square element in the  deleted row  in the array to , thereby clearing the data source of the square element.fixedBlocksnull

Step 7: In the index.js file, create the downLine() function to drop the block element above the cleaned line.

Traverse all rows above the row to be cleaned, traverse all columns of the row, and determine whether data currently exists. If data exists. 

 Use the traversal sequence number to access the elements in the two-dimensional array fixedBlocks and determine whether the current element exists. If it does not exist, the row number line+1 of the data source of all block elements above the row being cleaned is located. Copies the block element's data source to a new location. Move the position of the block element in the container downward by one unit and clear the data source of the block element at the original position, that is  fixedBlocks[i][j] = null.

Set the css style in this element to let the block element drop in position in the container

 

Step 8: In the index.js file, create the function isGameOver() to determine the end of the game. 

(1) Create the isGameOver() function, use a for loop to traverse the column array, and determine whether there is a block element in row 0 of the two-dimensional array fixedBlocks. If it exists, it means the game is over, and returns true. If there is no element, return false to indicate that the game is not over yet.

(2) Determine whether the game is over before generating the model createModel (isGameOver() returns true). If the game is over, terminate the function.

 

Step 9: The game is over, create the gameOver() function, and generate the game over prompt modal box 

Set the style of the modal box with the ID to #modal in the index.css file, and set it to float vertically in the middle of the page. It is hidden by default.

 

In the index.js file, create the gameOver() function. Verify whether the modal box element exists on the page. If it does not exist, use jQuery Dom operation to add a modal box in the game area of ​​the page. Use jQuery animation to display hidden tooltip elements and delete square elements in the game area.

 

When isGameOver() returns true, it means the game is over, the gameOver() function is called, and the creation module function createModel is terminated.

 

In the index.js file, use the jQuery Dom operation to clear the modal box element in the page when the game is started on the click event.

 

run this code

Start the game:

 

Guess you like

Origin blog.csdn.net/2201_76041915/article/details/135005209