B站学习---js面向对象应用(俄罗斯方块游戏的实现)

这是本人在B站学习上跟着老师做的一个小游戏—俄罗斯方块,里面的内容跟老师的视频几乎一样。

视频来源

https://www.bilibili.com/video/BV1Rv41147GP?p=10

采用的技术主要是JavaScript。
全部代码,本人也放在了gitee上了,这是国内的GitHub,速度比较快。
仓库地址

https://gitee.com/jjm1/tetris

1.1 初始化布局

  • 代码
//采用这样的形式是为了防止全局污染
(function () {
  window.Game = function () {
    //设置行和列
    this.row = 20;
    this.col = 12;
    //初始化
    this.init();
  };
  Game.prototype.init = function () {
    var $table = $("<table></table>");
    for (let i = 0; i < this.row; i++) {
      var $tr = $("<tr></tr>");
      for (let j = 0; j < this.col; j++) {
        var $td = $("<td></td>");
        $td.appendTo($tr);
      }
      $tr.appendTo($table);
    }
    $("body").append($table);
  };
})();

1.2 认识方块

俄罗斯方块中有 7 种状态,每一种又可以旋转成不同的状态

  • S 型
  • Z 类
  • J 类
  • L 类
  • O 类
  • T 类
  • I 类
    每一种形态用 4*4 表格表示
    在这里插入图片描述

1.3 方块的表示

使用二维数组去表示一个俄罗斯方块
比如:

[
    [0,0,0,0],
    [1,1,1,0],
    [0,1,0,0],
    [0,0,0,0]
]

我们将所有的俄罗斯方块都放在一个 JSON 中
方块渲染

window.Block = function () {
  //罗列所有类型
  var allType = ["S", "Z", "J", "L", "O", "I", "T"];
  //得到一种类型
  this.type = allType[parseInt(Math.random() * allType.length)];
  //得到一种类型中的总数量
  this.allDir = fangkuai[this.type].length;
  //随机得到一种
  this.dir = parseInt(Math.random() * this.allDir);
  //得到随机方块
  this.code = fangkuai[this.type][this.dir];
  //初始行
  this.row = 0;
  //初始列
  this.col = 4;
};
Block.prototype.render = function () {
  for (let i = 0; i < 4; i++) {
    for (let j = 0; j < 4; j++) {
      if (this.code[i][j] != 0) {
        game.setColor(i + this.row, j + this.col, this.code[i][j]);
      }
    }
  }
};

1.4 方块地图

(window.Map = function () {
  this.codeMap = [
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
  ];
}),
  (Map.prototype.render = function (mapGame) {
    for (let i = 0; i < mapGame.row; i++) {
      for (let j = 0; j < mapGame.col; j++) {
        if (this.codeMap[i][j] != 0) {
          game.setColor(i, j, this.codeMap[i][j]);
        }
      }
    }
  });

1.5 方块运动

方块进行下落之前需要进行预判断,判断方块下落的下一个位置是否有方块存在,如果没有方块存在就直接 row++,否则停止下落

//预判断方法,判断当前方块与对应位置的方块是否有重合
Block.prototype.check = function (row, col) {
  for (let i = 0; i < 4; i++) {
    for (let j = 0; j < 4; j++) {
      if (this.code[i][j] != 0 && game.map.codeMap[row + i][col + j] != 0) {
        return false;
      }
    }
  }
  return true;
};
//方块进行下落之前需要进行预判断,判断方块下落的下一个位置是否有方块存在,如果没有方块存在就直接row++,否则停止下落
Block.prototype.checkDown = function () {
  //下落之前,先进行预判断
  if (this.check(this.row + 1, this.col)) {
    this.row++;
  } else {
    game.block = new Block();
  }
};

1.6 渲染地图

//将下落的方块渲染到地图中
Block.prototype.renderMap = function () {
  for (let i = 0; i < 4; i++) {
    for (let j = 0; j < 4; j++) {
      if (this.code[i][j] != 0) {
        game.map.codeMap[this.row + i][this.col + j] = this.code[i][j];
      }
    }
  }
};

1.7 方块左右移动

//事件监听
Game.prototype.bindEvent = function () {
  var self = this;
  $(document).keydown(function (event) {
    if (event.keyCode == 37) {
      self.block.checkLeft();
    } else if (event.keyCode == 39) {
      self.block.checkRight();
    } else if (event.keyCode == 32) {
      //按空格一键到底
      self.block.checkBlockEnd();
    }
  });
};

//判断是否可用左移
Block.prototype.checkLeft = function () {
  if (this.check(this.row, this.col - 1)) {
    this.col--;
  }
};
//判断是否可用右移
Block.prototype.checkRight = function () {
  if (this.check(this.row, this.col + 1)) {
    this.col++;
  }
};

1.8 方块的旋转

Block.prototype.blockRot = function () {
  this.dir++;
  if (this.dir > this.allDir - 1) {
    this.dir = 0;
  }
  //改变方向之后渲染心得方向
  this.code = fangkuai[this.type][this.dir];
};

问题:
方块的旋转不会顾及左右的方块是否已存在
解决:
我们可以对方块进行一次备份,判断不能变化之后,再将变化后的方块变回来

Block.prototype.blockRot = function () {
  //保留旧的状态
  var oldDir = this.dir;
  this.dir++;
  if (this.dir > this.allDir - 1) {
    this.dir = 0;
  }
  //改变方向之后渲染新的方向
  this.code = fangkuai[this.type][this.dir];
  //判断当前改变后的方块是否有能力继续往下走
  if (!this.check(this.row, this.col)) {
    this.dir = oldDir;
    this.code = fangkuai[this.type][this.dir];
  }
};

1.9 判断是否可以移动

//判断是否可以消行
Map.prototype.checkRemove = function () {
  for (let i = 0; i < 20; i++) {
    if (this.codeMap[i].indexOf(0) == -1) {
      this.codeMap.splice(i, 1);
      this.codeMap.unshift([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
    }
  }
};

1.10 判断游戏结束

//判断游戏结束
Block.prototype.checkOver = function () {
  for (let i = 0; i < game.col; i++) {
    if (game.map.codeMap[0][i] != 0) {
      clearInterval(game.timer);
      alert("游戏结束了!!!");
    }
  }
};

1.11 设置预览框

设置预览框的方法

Game.prototype.setNextColor = function () {
  for (let i = 0; i < 4; i++) {
    for (let j = 0; j < 4; j++) {
      if (this.nextBlock.code[i][j] != 0) {
        $(".tab2")
          .find("tr")
          .eq(i)
          .children("td")
          .eq(j)
          .addClass("c" + this.nextBlock.code[i][j]);
      }
    }
  }
};

初始化预览框

Game.prototype.init = function () {
  var $table = $("<table></table>");
  var $table2 = $("<table></table>");
  $table.addClass("tab1");
  $table2.addClass("tab2");
  for (let i = 0; i < this.row; i++) {
    var $tr = $("<tr></tr>");
    for (let j = 0; j < this.col; j++) {
      var $td = $("<td></td>");
      $td.appendTo($tr);
    }
    $tr.appendTo($table);
  }
  for (let i = 0; i < 4; i++) {
    var $tr = $("<tr></tr>");
    for (let j = 0; j < 4; j++) {
      var $td = $("<td></td>");
      $td.appendTo($tr);
    }
    $tr.appendTo($table2);
  }
  $("div").append($table);
  $("div").append($table2);
};
Game.prototype.setColor = function (row, col, num) {
  $(".tab1")
    .find("tr")
    .eq(row)
    .children("td")
    .eq(col)
    .addClass("c" + num);
};

方块到底时

//渲染预览框方块
game.block = game.nextBlock;
//渲染新的方块
game.nextBlock = new Block();

1.12 设置帧编号和分数

//分数
this.score = 0;
//速度
this.during = 30;
Game.prototype.start = function () {
  var self = this;
  //帧编号
  this.f = 0;
  this.timer = setInterval(function () {
    self.f++;
    document.getElementById("f").innerHTML = "帧编号:" + self.f;
    //清屏
    self.clear();
    self.block.render();
    //渲染预览方块
    self.setNextColor();
    //渲染地图
    self.map.render(self);
    //方块下落
    self.f % self.during == 0 && self.block.checkDown();
  }, 30);
};
//判断是否可以消行
Map.prototype.checkRemove = function () {
  for (let i = 0; i < 20; i++) {
    if (this.codeMap[i].indexOf(0) == -1) {
      this.codeMap.splice(i, 1);
      this.codeMap.unshift([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
      if (game.during <= 30 && game.during >= 20) {
        game.score += 10;
      } else if (game.during < 20 && game.during >= 10) {
        game.score += 20;
      } else {
        game.score += 30;
      }

      document.getElementById("score").innerHTML = "分数:" + game.score;
      if (game.score % 100 == 0) {
        game.during -= 5;
        if (game.during < 0) {
          game.during = 1;
        }
      }
    }
  }
};
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值