//定义游戏数据 宽度10格 高度20格
blocks =[[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[2,2,2,0,0,0,0,0,0,0],];//封装场地类
class Map {//设置格子大小 30
size =30;//设置格子的间距
margin =5;//创建格子的元素列表
blocks =[];//封装显示场地的方法show(){//在页面中创建一个div 来代表我们的场地
var map = $('<div></div>')//设置宽高.width(10* this.size +9* this.margin).height(20* this.size +19* this.margin)//设置基础样式.css({
backgroundColor:'#ccc',
position:'absolute'})//追加到body中.appendTo('body');//循环20*10次创建小格子for(var y =0; y <20; y++){//每行创建一个
this.blocks.push([]);for(var x =0; x <10; x++){//循环创建小格子
this.blocks[y].push($('<div></div>')//设置宽高.width(this.size).height(this.size)//设置基础样式.css({
backgroundColor:'white',
position:'absolute'})//设置坐标.offset({
left: x *(this.size + this.margin),
top: y *(this.size + this.margin)})//追加到地图中.appendTo(map));}}}//封装砖块的渲染方法render(){//遍历砖块的数据,给对应的砖块div设置颜色//空格子0白色,可移动方块1蓝色,固定方块2黑色for(var y =0; y <20; y++){for(var x =0; x <10; x++){switch(blocks[y][x]){case0:
this.blocks[y][x].css('backgroundColor','white');break;case1:
this.blocks[y][x].css('backgroundColor','blue');break;case2:
this.blocks[y][x].css('background-color','black');break;}}}}//封装砖块的改变方法//@param1 prev num 旧的砖块数据//@param2 next num 新的砖块数据change(prev, next){for(var y =0; y <20; y++){for(var x =0; x <10; x++){//判断当前砖块数据是否为prev 将其转变为nextif(blocks[y][x]== prev){
blocks[y][x]= next;}}}}//封装玩家移动方法//封装向左移动的方法left(){//要判断左移的前提条件
var flag = true;//循环找到可移动砖块,并观察其x是否为0,为0 则说明移动到了左边界for(var y =0; y <20; y++){for(var x =0; x <10; x++){//如果有任何一个可移动格子移动到边界,则左移全部结束 flag为falseif(blocks[y][x]=='1'&& x ==0){
flag = false;//还需要判断可移动格子的左边是否碰到固定砖块2,碰到则左移也结束}elseif(blocks[y][x]==1&& blocks[y][x -1]==2){
flag = false;}}}//当flag为true时,说明不在边界,当flag为false时,说明在边界,则不移动if(flag){for(var y =0; y <20; y++){for(var x =0; x <10; x++){//判断当前砖块是否为可移动砖块if(blocks[y][x]=='1'){//把这个格子本身设置为0
blocks[y][x]=0;//把这个格子左边设置为1
blocks[y][x -1]=1;}}}}//让形状的原点左移//实例化Shape后shape下的origin属性
shape.origin[1]--;//左移后立刻渲染,放置玩家移动过快导致间隔函数没有渲染
map.render();}//向右移动right(){//要判断右移的前提条件
var flag = true;//循环找到可移动砖块,并观察其x是否为9,为9 则说明移动到了右边界//循环要注意,最先移动的格子是最右边的,所有要从右到左循环遍历for(var y =19; y >=0; y--){for(var x =9; x >=0; x--){//如果有任何一个可移动格子移动到边界,则右移全部结束 flag为falseif(blocks[y][x]=='1'&& x ==9){
flag = false;//还需要判断可移动格子的左边是否碰到固定砖块2,碰到则左移也结束}elseif(blocks[y][x]==1&& blocks[y][x +1]==2){
flag = false;}}}//当flag为true时,说明不在边界,当flag为false时,说明在边界,则不移动if(flag){for(var y =19; y >=0; y--){for(var x =9; x >=0; x--){//判断当前砖块是否为可移动砖块if(blocks[y][x]=='1'){//把这个格子本身设置为0
blocks[y][x]=0;//把这个格子右边设置为1
blocks[y][x +1]=1;}}}}//让形状原点右移动
shape.origin[1]++;//右移后立刻渲染,放置玩家移动过快导致间隔函数没有渲染
map.render();}//向下移动down(){//要判断下移的前提条件
var flag = true;//循环找到可移动砖块,并观察其y是否为19,为19 则说明移动到了下边界//循环要注意,最先移动的格子是最下边的,所有要从下到上循环遍历for(var y =19; y >=0; y--){for(var x =9; x >=0; x--){//如果有任何一个可移动格子移动到边界,则下移全部结束 flag为falseif(blocks[y][x]=='1'&& y ==19){
flag = false;//还需要判断可移动格子的下边是否碰到固定砖块2,碰到则下移也结束}elseif(blocks[y][x]==1&& blocks[y +1][x]==2){
flag = false;}}}//当flag为true时,说明不在边界,当flag为false时,说明在边界,则不移动if(flag){for(var y =19; y >=0; y--){for(var x =9; x >=0; x--){//判断当前砖块是否为可移动砖块if(blocks[y][x]=='1'){//把这个格子本身设置为0
blocks[y][x]=0;//把这个格子下边设置为1
blocks[y +1][x]=1;}}}}//让形状原点下移动
shape.origin[0]++;//右移后立刻渲染,放置玩家移动过快导致间隔函数没有渲染
map.render();}//封装可移动方块的检测方法,如果地图中已经没有可移动砖块,添加一个moveTest(){
var flag = true;for(var y =19; y >=0; y--){for(var x =9; x >=0; x--){if(blocks[y][x]==1){
flag = false;}}}if(flag){
shape.addShape();}}//封装底部的检测bottomTest(){
var flag = false;
shape.getPos(shape.type, shape.angle).forEach(pos =>{//判断当前形状的砖块是否到达底部if(pos[0]>=19){
flag = true;//判断当前形状的方块下方是否为固定方块}elseif(blocks[pos[0]+1][pos[1]]==2){
flag = true;}});if(flag){//把目前场地中所有的可移动方块转变成固定方块
this.change(1,2);}}//消除检测clearTest(){
blocks.forEach((row, y)=>{//检测这一行是否全部为固定方块
var flag = row.every(block =>{return block ==2;});if(flag){//消除这一行
blocks.splice(y,1);//在砖块数据的最上方补一行
blocks.unshift([0,0,0,0,0,0,0,0,0,0]);}});}}//封装形状类shape
class Shape {//定义一个属性,代表形状类型0~6 7种类型
type =0;//形状的角度 4个角度
angle =0;//形状数据
shapes =[//1\定义 一字型 数据[[[1,1,1,1],[0,0,0,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],],[[1,1,1,1],[0,0,0,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],]],//2\定义 T字形 数据[[[0,1,0],[1,1,1],[0,0,0]],[[0,1,0],[0,1,1],[0,1,0]],[[0,0,0],[1,1,1],[0,1,0]],[[0,1,0],[1,1,0],[0,1,0]]],//3\定义Z字形数据[[[0,1,1],[1,1,0],[0,0,0]],[[1,0,0],[1,1,0],[0,1,0]],[[0,0,0],[0,1,1],[1,1,0]],[[1,0,0],[1,1,0],[0,1,0]]],//4\定义7字形数据[[[1,1,1],[1,0,0],[0,0,0]],[[1,1,0],[0,1,0],[0,1,0]],[[0,0,1],[1,1,1],[0,0,0]],[[0,1,0],[0,1,0],[0,1,1]]],//5\定义反7字形数据[[[1,1,1],[0,0,1],[0,0,0]],[[0,0,1],[0,0,1],[0,1,1]],[[0,0,0],[1,0,0],[1,1,1]],[[1,1,0],[1,0,0],[1,0,0]]],//6\定义反Z字形数据[[[1,1,0],[0,1,1],[0,0,0]],[[0,1,0],[1,1,0],[1,0,0]],[[0,0,0],[1,1,0],[0,1,1]],[[0,0,1],[0,1,1],[0,1,0]]],//定义田字类[[[1,1],[1,1]],[[1,1],[1,1]],[[1,1],[1,1]],[[1,1],[1,1]],]];//定义形状的原点
origin =[0,3];//封装形状砖块坐标推导方法的工具getPos(type, angle){//获取相对应形状的数据
var shape = this.shapes[type][angle];//储存形状的坐标
var pos =[];//遍历形状数据,根据原点, 推算出可移动方块的坐标//row即item y 即index
shape.forEach((row, y)=>{
row.forEach((block, x)=>{if(block ==1){
pos.push([this.origin[0]+ y, this.origin[1]+ x]);}});});return pos;}//封装显示形状方法show(){//调用getPos方法,获得可移动砖块的坐标,把对应坐标数据设置为1即可
this.getPos(this.type, this.angle).forEach(pos =>{
blocks[pos[0]][pos[1]]=1;});}//封装新增形状方法addShape(){//重置形状原点坐标
this.origin =[0,3];//重置形状的角度angle
this.angle =0;//生成随机形状 0~6 向下取整
this.type = Math.floor(Math.random()*7);//检测这个新的形状是否会与固定方块重合
var flag = true;
this.getPos(this.type, this.angle).forEach(pos =>{if(blocks[pos[0]][pos[1]]==2){
flag = false;}});if(flag){//显示新的形状
this.show();}else{clearInterval(timer);//游戏结束
console.log('游戏结束');alert('游戏结束');}}//封装形状旋转方法rotate(){//根据旧的角度 得到新的角度//对this.angle+1除4取余数 只剩下0,1,2,3
var newAngle =(this.angle +1)%4;//判断新的方块是否会与固定方块重合//判断方块是否会超出边界 //forEach中的pos相当于item//根据推导坐标方法,推导出下次旋转后的坐标,循环判断
var flag = true;
this.getPos(this.type, newAngle).forEach(pos =>{//判断旋转后的方块是否有出边界的if(pos[0]>19){
flag = false;}elseif(pos[1]<0|| pos[1]>9){
flag = false;}elseif(blocks[pos[0]][pos[1]]==2){
flag = false;}});//当flag为true时 才通过if(flag){//设置新的角度
this.angle = newAngle;//清除旧方块
map.change(1,0);//显示新的方块
this.show();//渲染地图
map.render();}}}//实例化的调用
var map = new Map();
var shape = new Shape();
map.show();
map.render();//创建定时器,每隔一秒帮忙刷新场地数据
var timer =setInterval(function (){
map.bottomTest();
map.clearTest();
map.down();
map.moveTest();
map.render();},1000);//提供玩家操作的接口
$(document).keydown((event)=>{
console.log(event.key);switch(event.key){case'ArrowUp':
shape.rotate();break;case'ArrowDown':
map.down();break;case'ArrowLeft':
map.left();break;case'ArrowRight':
map.right();break;}});