1.设置Game为中介者类
2.方块的状态
俄罗斯方块中,下落的方块形态一共有7种,每一种又有不同的状态(方向)
S型、Z型、J型、L型、O型、T型、I型
这些形态都用一个4*4的矩阵表示
3 方块的表示
使用二维数组去表示俄罗斯方
比如上图的T我们使用二维数组
[
[0,0,0,0],
[1,1,1,0],
[1,1,0,0],
[0,0,0,0],
]
块的方块状态
将所有的俄罗斯方块的类型和状态都放到一个JSON中,JSON是一个三维数组
4 渲染方块
5 地图类
需要一个地图,来显示已经到底的方块,此时的表格只是一个显示的dom,没有办法实时进行显示,因为每一帧要清除画布,所以要想持久,就需要让数据持久起来,所以维持一个地图类,用来持久已经到底的方块
6.方块的下落状态
方块下落很简单,就是给Block.row++就可以了,但是怎么停止?
此时提出了一个“预判断”的概念,也就是每一次方块下落的时候需要进行一次预先判断,当前方块的下一次运动位置去和地图的(mapCode)进行位置判断,如果此时mapCode在相同的位置也有方块存在,则停止下落。
7.渲染地图
当前方块到底之后,不会渲染当前的状态,只会消失,因为是mapCode一直在维护底部的持久状态,所以一旦方块到底后,就要将数据给mapCode进行持久更新
8.方块的左右移动
其实就是增加事件监听,然后通过check方法预判断是否有能力移动
9.方块的旋转
按上键让方块按照顺时针的方向旋转
但是此时会发生一个问题,方块在旋转的过程中不会顾及左右是否有已有的颜色格子
此时可以对方块进行一次“备份”,将旧的方块备份一份,然后让新block的和已有的mapCode进行一一比对,如果新的有重合就打回原形
10.方块消行
方块消行本质就是mapCode的每一项数组的遍历,如果某一项数组中有都不为0了,就说明该消行了
11. 判断游戏结束
地图的第一行的mapCode有不为0的了就判定死亡了
每次方块到底的时候进行一次判断
12.设置预览框
俄罗斯方块有一个重要的逻辑就是预览,下一次的方块 会 提前展示,所以我们可以设置一个nextBlock当做下一次出场的方块
13.加分设置不同档 、不同速度、加不同分
index.html
<style>
*{
margin: 0;
padding: 0;
}
html{
height: 100%;
}
body{
background: url(images/bg2.png) repeat-x center bottom, -webkit-linear-gradient(top,skyblue,white);
overflow: hidden;
background-size: 20%;
padding-top: 20px;
}
table{
margin: 0 auto;
border-collapse: collapse;
background: #525252;
float: left;
margin-right: 20px;
}
td{
width: 25px;height: 25px;
border: 1px solid rgb(68, 37, 37);
}
.c1{
background: url(images/1.jpg) no-repeat;
background-size: cover;
}
.c2{
background: url(images/2.jpg) no-repeat;
background-size: cover;
}
.c3{
background: url(images/3.jpg) no-repeat;
background-size: cover;
}
.c4{
background: url(images/4.jpg) no-repeat;
background-size: cover;
}
.c5{
background: url(images/5.jpg) no-repeat;
background-size: cover;
}
.c6{
background: url(images/6.jpg) no-repeat;
background-size: cover;
}
.c7{
background: url(images/7.jpg) no-repeat;
background-size: cover;
}
h2{
width: 100%;
float: left;
}
</style>
<body>
<h2 id="f">帧编号:0</h2>
<h2 id="score">分数:0</h2>
<script src="js/jquery-3.2.1.min.js"></script>
<script src="js/Game.js"></script>
<script src="js/Block_JSON.js"></script>
<script src="js/Block.js"></script>
<script src="js/Map.js"></script>
<script>
var game = new Game()
</script>
</body>
</html>
Game.js
(function () {
window.Game = function() {
//行列
this.row = 20;
this.col = 12;
//实例block
this.block = new Block();
//实例下一个Block
this.nextBlock = new Block();
//定时器
this.start()
//实例地图
this.map = new Map(this);
//初始化
this.init()
//初始化分数
this.score = 0;
//初始化 速度
this.during = 19;
//键盘监听事件
this.bindEvent();
}
Game.prototype.init = function () {
//创建游戏table
$table = $("<table></table>");
$table.addClass("tab1")
for(var i = 0;i<this.row;i++){
$tr = $("<tr></tr>")
for(var j = 0;j<this.col;j++){
$td = $("<td></td>");
$td.appendTo($tr)
}
$tr.appendTo($table)
}
$table2 = $("<table></table>");
//创建 预览框
$table2.addClass("tab2");
for(var i = 0;i<4;i++){
$tr2 = $("<tr></tr>")
for(var j = 0;j<4;j++){
$td2 = $("<td></td>");
$td2.appendTo($tr2)
}
$tr2.appendTo($table2)
}
//上树
$table.appendTo("body");
$table2.appendTo("body");
}
//渲染 矩阵中方块的颜色,不同类型,添加不同类设置不同颜色
Game.prototype.setColor = function(row,col,num){
$(".tab1").find("tr").eq(row).find("td").eq(col).addClass("c"+num);
}
//渲染下一个方块的颜色 预览方块
Game.prototype.setNextColor = function(row,col,num){
for(var i=0;i<4;i++){
for(var 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.clear = function(){
for(var i=0;i<this.row;i++){
for(var j=0;j<this.col;j++){
$(".tab1").find("tr").eq(i).children("td").eq(j).removeClass();
}
}
//预览框擦除
for(var i=0;i<4;i++){
for(var j=0;j<4;j++){
$(".tab2").find("tr").eq(i).children("td").eq(j).removeClass();
}
}
}
//键盘监听事件
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){
//console.log(event.keyCode);
self.block.checkBlockEnd();
}
//旋转 按上键
else if(event.keyCode == 38){
//console.log(event.keyCode);
self.block.checkRot();
}
})
}
//定时器
Game.prototype.start = function(){
var self = this;
//帧编号
this.f = 0
this.timer = setInterval(function () {
self.f++;
//渲染帧编号
$("#f").html("帧编号:"+self.f)
self.clear();
//渲染方块
self.block.render();
//渲染下一个方块
self.setNextColor();
// 渲染地图
self.map.render(self);
//方块下落
self.f % self.during == 0 && self.block.checkDown();
},20)
}
})()
Block.js
(function () {
window.Block = function () {
//console.log(blocks)
//随机7种方块类型中的某一种
//第一:7种类型
var allType = ["S","Z","J","L","I","O","T"];
//第二:从7种类型的方块中选一种
this.type = allType[parseInt(Math.random() * allType.length)];
//第三:7种类型方块中的某一种方块 一共有几种状态;
this.allDir = blocks[this.type].length;
//第四:7种类型方块中的某一种方块的某一种状态的下标
this.dir = parseInt(Math.random() * this.allDir);
//第五:获取到 7种类型方块中的某一种方块的某一种状态
this.code = blocks[this.type][this.dir];
//初始化从第一行还是
this.row = 0;
//初始化居中显示 从第四列开始
this.col = 4;
}
Block.prototype.render = function () {
//渲染4*4矩阵
for(var i=0;i<4;i++){
for(j=0;j<4;j++){
if(this.code[i][j] != 0){
//如果矩阵中 某一项不等于零,则渲染颜色
game.setColor(i+this.row,j+this.col,this.code[i][j]);
}
}
}
}
//判断能否下落
Block.prototype.check = function(row,col){
for(var i=0;i<4;i++){
for(var j=0;j<4;j++){
if(this.code[i][j] !== 0 && game.map.mapCode[i+row][j+col] !== 0){
return false;
}
}
}
return true;
}
//方块下落
Block.prototype.checkDown = function(){
// 判断当前地图的位置和自己方块的位置是否有重合this.row+1指的是预判断
// 预判断就是在下一次方块将要到达的位置是否有对应的地图不为0
if(this.check(this.row + 1, this.col)){
this.row++;
}
else{
// 下落到底的状态,渲染预览框的方块
game.block = game.nextBlock;
// 让预览框的方块再次渲染新的
game.nextBlock = new Block();
//到底后 地图渲染
this.readerMap();
//消行
game.map.checkRemove();
//死亡判断
this.checkOver();
}
}
//死亡判断
Block.prototype.checkOver = function () {
//mapCode 第一行,中有一个不是0则游戏结束
for(var i=0;i<game.col;i++){
if(game.map.mapCode[0][i] != 0){
clearInterval(game.timer);
alert("游戏结束,您当前的得分是"+ game.score);
}
}
}
//判断能否向左
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 ++;
}
}
//一键到底
Block.prototype.checkBlockEnd = function () {
while(this.check(this.row + 1, this.col)){
this.row ++;
}
}
//旋转
Block.prototype.checkRot = function () {
//旋转能力判断
//备份旧的形状方向
var oddDir = this.dir;
//改变新的
this.dir++;
//如果当前的dir大于最后一种方向,回到第一种状态
if(this.dir > this.allDir -1){
this.dir = 0;
}
//改变之后渲染新的code
this.code = blocks[this.type][this.dir];
//渲染之后的新方块判断,是否有能力渲染
if(!this.check(this.row,this.col)){
console.log(this.dir);
//进入判断说明重合了,打回原形再次渲染
this.dir = oddDir;
this.code = blocks[this.type][this.dir];
}
}
//到底的方块让地图进行渲染
Block.prototype.readerMap = function(){
for(var i=0;i<4;i++){
for(var j=0;j<4;j++){
if(this.code[i][j] !== 0){
game.map.mapCode[this.row + i][this.col + j] = this.code[i][j];
}
}
}
}
})()
Map.js
(function () {
window.Map = function () {
this.mapCode = [
[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, 4, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0],
[1, 2, 3, 4, 3, 4, 0, 0, 0, 0, 0, 0],
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
];
}
// 渲染地图
Map.prototype.render = function (mapGame) {
for(var i=0;i<mapGame.row;i++){
for(var j=0;j<mapGame.col;j++){
if(this.mapCode[i][j] != 0){
game.setColor(i,j,this.mapCode[i][j]);
}
//$("tr").eq(i).children("td").eq(j).html(this.mapCode[i][j]);
}
}
}
//消行
Map.prototype.checkRemove = function (mapGame) {
// 消行规则:当前的mapCode数组的每一项如果都不是0了,就说明该消行了
for(var i=0;i<20;i++){
if(this.mapCode[i].indexOf(0) == -1){
this.mapCode.splice(i,1);
this.mapCode.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 >=10 && game.during<20){
game.score += 20;
}else{
game.score+=30;
}
// 渲染分数
$("#score").html("分数:" + game.score)
if(game.score % 100 == 0){
during -= 5;
if(during <=0){
during = 1;
}
}
}
}
}
})()
JSON.js
var blocks = {
"S" : [
[
[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]
]
],
"Z" : [
[
[2, 2, 0, 0],
[0, 2, 2, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 0, 2, 0],
[0, 2, 2, 0],
[0, 2, 0, 0],
[0, 0, 0, 0]
]
],
"J" : [
[
[0, 3, 0, 0],
[0, 3, 0, 0],
[3, 3, 0, 0],
[0, 0, 0, 0]
],
[
[3, 0, 0, 0],
[3, 3, 3, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 3, 3, 0],
[0, 3, 0, 0],
[0, 3, 0, 0],
[0, 0, 0, 0]
],
[
[3, 3, 3, 0],
[0, 0, 3, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
]
],
"L" : [
[
[0, 4, 0, 0],
[0, 4, 0, 0],
[0, 4, 4, 0],
[0, 0, 0, 0]
],
[
[4, 4, 4, 0],
[4, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[4, 4, 0, 0],
[0, 4, 0, 0],
[0, 4, 0, 0],
[0, 0, 0, 0]
],
[
[0, 0, 4, 0],
[4, 4, 4, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
]
],
"I" : [
[
[0, 5, 0, 0],
[0, 5, 0, 0],
[0, 5, 0, 0],
[0, 5, 0, 0]
],
[
[5, 5, 5, 5],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
]
],
"O" : [
[
[0, 6, 6, 0],
[0, 6, 6, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
]
],
"T" : [
[
[7, 7, 7, 0],
[0, 7, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 7, 0, 0],
[7, 7, 0, 0],
[0, 7, 0, 0],
[0, 0, 0, 0]
],
[
[0, 7, 0, 0],
[7, 7, 7, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 7, 0, 0],
[0, 7, 7, 0],
[0, 7, 0, 0],
[0, 0, 0, 0]
]
]
}