zoukankan      html  css  js  c++  java
  • JAVASCRIPT实现网页版:俄罗斯方块

    HTML+CSS+JS实现俄罗斯方块完整版,素材只有图片,想要的下载图片按提示名字保存,css中用的时候注意路径!!主要在JS中!JS附有详细注释

    效果:

    按键提示:[键盘按键]

    素材:图片名字与代码里对应

      1、背景图片:tetris.png

      

      2、失败时候的弹出框图片:game-over.png

      

      3、七种色彩小方块图片:

        I.png:

        J.png:

        L.png:

        O.png:

        S.png:

        T.png:

        Z.png:

    HTML代码

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8" />
            <title>俄罗斯方块 — 经典完整版</title>
            <link rel="stylesheet" href="css/tetris.css"/>
            <script src="js/shapes.js"></script>
            <script src="js/tetris.js"></script>  
        </head>
        <body>
            <div class="playground">
                <p>SCORE:<span>0</span></p>
                <p>LINES:<span>0</span></p>
                <p>LEVEL:<span>1</span></p>
            </div>              
        </body>
    </html>

    CSS代码

    .playground  {
        width: 525px;
        height: 550px;
        margin: 20px auto 0 auto;
        position: relative;
        background-image:url("tetris.png");
    }
    .playground img  { position: absolute;}
    .playground p  {font-size: 30px;
                    font-family: 'SimHei';
                    font-weight: bold;
                    color: #667799;
                    position: absolute;
                    left:305px;
                    top:120px;
    }
    .playground p+p  {  top:176px; }
    .playground p+p+p  { top:232px; }           

    JAVASCRIPT代码:分两段附有详细步骤解释

      1、tetris.js

    window.$=HTMLElement.prototype.$=function(selector){
    	return (this==window?document:this).querySelectorAll(selector);
    }
    var tetris={
    	RN:20,//总行数
    	CN:10,//总列数
    	CSIZE:26,//每个格子的宽高都是26px
    	OFFSET_X:15,//每个单元格的左侧都要加15px
    	OFFSET_y:15,//每个单元格的上面都要加15px
    	pg:null,//保存游戏主界面对象
    	currShape:null,//专门保存正在移动的图形对象
    	nextShape:null,//八、专门保存下一个图形
    	interval:500,//每秒重绘一次==>下落的速度
    	timer:null,
    	wall:[],//六、保存所有停止的下落的方块
    	state:1,//十、保存游戏当前状态
    	STATE_RUNNING:1,//十、游戏正在运行
    	STATE_GAMEOVER:0,//十、游戏结束
    	STATE_PAUSE:2,//十、游戏暂停
    	IMG_GAMEOVER:"img/game-over.png",
    	IMG_PAUSE:"img/pause.png",
    	SCORES:[0,10,50,80,200],//十三,要加的分数档位
    	score:0,//十三、当前总分
    	lines:0,//十三、当前总行数
    	//十、为游戏添加不同状态的图片
    	paintState:function(){//根据当前游戏状态,为游戏添加不同的图片
    		var img=new Image();
    		switch(this.state){
    		//如果当前状态是STATE_GAMEOVER
    		case this.STATE_GAMEOVER:
    		//		img.src设置为IMG_GAMEOVER
    			img.src=this.IMG_GAMEOVER;
    			break;
    		//如果当前状态是STATE_PAUSE
    		case this.STATE_PAUSE:
    		//		img.src设置为IMG_PAUSE
    			img.src=this.IMG_PAUSE;
    		}
    		//将img追加到pg中
    		this.pg.appendChild(img);
    	},
    	init:function(){
    		this.pg=$(".playground")[0];
    		//创建一个随机图形的对象存在currShape中
    		this.currShape=this.randomShape();
    		this.nextShape=this.randomShape();
    		//六、将wall数组初始化为RN的空数组对象
    		for(var i=0;i<this.RN;i++){
    			this.wall[i]=[];
    		}
    		this.score=0;//十六、初始化
    		this.lines=0;//十六、初始化
    		this.state=1;//十六、初始化
    		this.paint();
    		//三、
    		this.timer=setInterval(function(){
    			//调用tetris的drop方法
    			tetris.drop();
    			//再调用tetris的paint方法;
    			tetris.paint();
    		},this.interval);
    		//十一、
    		document.onkeydown=function(){
    			var e=window.event||arguments[0];
    			switch(e.keyCode){
    				case 37: tetris.moveL();break;//左
    				case 39: tetris.moveR();break;//右
    				case 40: tetris.drop();break;//下
    				//十五步、
    				case 38: tetris.rotateR();break;//上键控制右边旋转
    				case 90: tetris.rotateL();break;//字母Z键控制控制左边旋转
    				//十六步
    				case 80: tetris.pause();break;//字母P键:暂停
    				case 81: tetris.gameOver();break;//字母Q:结束游戏
    				case 67: tetris.myContinue();break;//字母C,在暂停后有效:暂停后,继续游戏
    				case 83: //游戏结束后,重新开始
    					if(this.state==this.STATE_GAMEOVER){
    						tetris.init();
    					}//字母S键:重新开始游戏
    			}
    		}
    	},//init的结束
    	//十六、暂停,开始,继续、结束
    	gameOver:function(){
    		this.state=this.STATE_GAMEOVER;
    		clearInterval(this.timer);
    		this.timer=null;
    		this.paint();
    	},
    	pause:function(){
    		if(this.state==this.STATE_RUNNING){
    			this.state=this.STATE_PAUSE;
    		}
    	},
    	myContinue:function(){
    		if(this.state==this.STATE_PAUSE){
    			this.state=this.STATE_RUNNING;
    		}
    	},
    	//十五、变形
    	rotateR:function(){//按键上,向右旋转
    		if(this.state==this.STATE_RUNNING){//十六
    			this.currShape.rotateR();
    			if(this.outOfBounds()||this.hit()){//验证不通过
    				this.currShape.rotateL();
    			}
    		}
    	},
    	rotateL:function(){//按键Z,向左旋转
    		if(this.state==this.STATE_RUNNING){
    			this.currShape.rotateL();
    			if(this.outOfBounds()||this.hit()){//验证不通过
    				this.currShape.rotateR();
    			}
    		}
    	},
    	//十一、
    	moveR:function(){
    		this.currShape.moveR();
    		if(this.outOfBounds()||this.hit()){//验证不通过
    			this.currShape.moveL();
    		}
    	},
    	moveL:function(){
    		this.currShape.moveL();
    		if(this.outOfBounds()||this.hit()){//验证不通过
    			this.currShape.moveR();
    		}
    	},
    	outOfBounds:function(){//检查当前图形是否越界
    		//当前shape中任意一个单元格的col<0或>=CN
    		var cells=this.currShape.cells;
    		for(var i=0;i<cells.length;i++){
    			if(cells[i].col<0||cells[i].col>=this.CN){
    				return true;
    			}
    		}
    		return false;
    	},
    	hit:function(){//检查当前图形是否碰撞
    	//当前shape中任意一个单元格在wall中相同位置有格
    		var cells=this.currShape.cells;
    		for(var i=0;i<cells.length;i++){
    			if(this.wall[cells[i].row][cells[i].col]){
    				return true;
    			}
    		}
    		return false;
    	},
    	//四、重绘所有的格子,分数等的方法
    	paint:function(){
    		//把所有的img格子删除,再重绘
    				/*结尾的4个/<img(.*?){4}>*/
    		this.pg.innerHTML=this.pg.innerHTML.replace(/<img(.*?)>/g,"");
    		this.paintShape();
    		this.paintWall();
    		this.paintNext();
    		//十三
    		this.paintScore();
    		this.paintState();//十、
    	},
    	//十三、计分
    	paintScore:function(){//找到span元素
    		//第一个span中放this.score
    		$("span")[0].innerHTML=this.score;
    		//第二个放this.lines
    		$("span")[1].innerHTML=this.lines;
    	},
    	drop:function(){
    		//判断能否下落
    		if(this.state==this.STATE_RUNNING){//该行是第十六步加的
    			if(this.canDrop()){
    				this.currShape.drop();
    			}else{//六、否则
    				//六、如果不能下落,就将图形中每个cell,放入wall数组中
    				this.landIntoWall();
    				//十二、消行、并计分
    				var ln=this.deleteLines();//消除并返回本次删除的行数
    				//十三、计分
    				this.score+=this.SCORES[ln];
    				this.lines+=ln;
    				//九、如果游戏没有结束才。。
    				if(!this.isGameOver()){
    					//七、将等待的nextShape,换到currShape
    					this.currShape=this.nextShape;
    					//七、
    					this.nextShape=this.randomShape();
    				}else{//十、否则,一级结束
    					clearInterval(this.timer);
    					this.timer=null;
    					this.state=this.STATE_GAMEOVER;
    					this.paint();//手动绘制一次
    				}
    			}
    		}
    	},
    	//十二、消行,并计分
    	deleteLines:function(){//检查wall中每一行是否要消除
    		//遍历wall中每一行,定义lines变量存本次共删除的行数line
    		for(var row=0,lines=0;row<this.RN;row++){
    			//如果当前行是满的:isFull(row)
    			if(this.isFull(row)){
    				//	就删除当前行:
    				this.deleteL(row);
    				//	每删除一行,lines++
    				lines++;
    			}
    		}
    		return lines;
    	},
    	isFull:function(row){//判断指定行是否已满
    		//取出wall中第row行,存在line变量中
    		var line=this.wall[row];		
    			//遍历line中每个cell
    		for(var c=0;c<this.CN;c++){
    			//	只要当前cell无效
    			if(!line[c]){
    				return false;
    			}
    		}//遍历结束后
    		return true;
    	},
    	deleteL:function(row){//删除指定行,并将其之上所有的cell下移
    		this.wall.splice(row,1);//只删除一行
    		this.wall.unshift([]);//顶部压入一个新空行
    		//从row行开始,向上遍历每一行
    		for(var r=row;r>0;r--){
    			//		从0开始遍历当前行每个格
    			for(var c=0;c<this.CN;c++){
    			//			如果当前格有效
    				if(this.wall[r][c]){
    				//				将当前格的row++
    					this.wall[r][c].row++;
    				}
    			}
    		}
    	},
    	//九、判断游戏是否结束
    	isGameOver:function(){
    		//获取nextShape中所有cell,保存在cells中
    		var cells=this.nextShape.cells;	
    		//遍历cells中每个cell
    		for(var i=0;i<cells.length;i++){
    			//取出wall中和当前cell相同row,col位置的格子
    			var cell=this.wall[cells[i].row][cells[i].col];
    			//只要碰到有效的
    			if(cell){
    				return true;
    			}
    		}//for的结束
    		return false;
    	},
    	//八、
    	paintNext:function(){
    		var cells=this.nextShape.cells;
    		for(var i=0;i<cells.length;i++){
    			//先将当前cell的row+1,存在r变量中
    			var r=cells[i].row+1;
    			//再将当前cell的col+11,存在c变量中
    			var c=cells[i].col+11;
    			var x=c*this.CSIZE+this.OFFSET_X;
    			var y=r*this.CSIZE+this.OFFSET_y;
    			var img=new Image();
    			img.src=cells[i].img;
    			img.style.left=x+"px";
    			img.style.top=y+"px";
    			this.pg.appendChild(img);
    		}
    	},
    	//七、
    	paintWall:function(){
    		//七、遍历二维数组wall中每个格
    		for(var r=0;r<this.RN;r++){
    			for(var c=0;c<this.CN;c++){
    				var cell=this.wall[r][c];
    		//		如果当前cell有效
    				if(cell){
    					var x=cell.col*this.CSIZE+this.OFFSET_X;
    					var y=cell.row*this.CSIZE+this.OFFSET_y;
    					var img=new Image();
    					img.src=cell.img;
    					img.style.left=x+"px";
    					img.style.top=y+"px";
    					this.pg.appendChild(img);
    				}
    			}
    		}
    	},
    	//六、把所有停止下落的方块放入wall中
    	landIntoWall:function(){
    		//遍历当前图形中每个cells
    		//	每遍历一个cell
    		//	就将cell放入wall中相同row,col的位置:this.wall[?][?]=?
    		var cells=this.currShape.cells;
    		for(var i=0;i<cells.length;i++){
    			this.wall[cells[i].row][cells[i].col]=cells[i];
    		}
    	},
    	//五、//判断是否继续可以下落
    	canDrop:function(){
    		//遍历当前currShape中的cells
    		//		只要发现任意一个的cell的row==RN-1
    		//		就返回false
    		//		
    		var cells=this.currShape.cells;
    		for(var i=0;i<cells.length;i++){
    			if(cells[i].row==this.RN-1){
    				return false;
    			}//七、wall中,当前cell的下一行位置有效
    			if(this.wall[cells[i].row+1][cells[i].col]){
    				return false
    			}
    		}//遍历结束后
    		//七、currShape中,任意一个cell的下方有wall中的cell
    		return true;
    	},
    	//4、随机生成一种图形--二
    	randomShape:function(){
    		switch(parseInt(Math.random()*7)){
    			case 0: return new O();
    			case 1: return new L();
    			case 2: return new J();
    			case 3: return new S();
    			case 4: return new Z();
    			case 5: return new I();
    			case 6: return new T();
    		}
    	},
    	//3
    	paintShape:function(){//3、专门绘制当前图形的方法
    		var cells=this.currShape.cells;
    		for(var i=0;i<cells.length;i++){
    			var x=cells[i].col*this.CSIZE+this.OFFSET_X;
    			var y=cells[i].row*this.CSIZE+this.OFFSET_y;
    			var img=new Image();
    			img.src=cells[i].img;
    			img.style.left=x+"px";
    			img.style.top=y+"px";
    			this.pg.appendChild(img);
    		}
    	},//paintShape的结束
    }//tetris结束
    window.onload=function(){
    	tetris.init();
    }
    

      2、shapes.js

    function Cell(row,col,img){
    	this.row=row;
    	this.col=col;
    	this.img=img;
    	//三下落
    	if(!Cell.prototype.drop){
    		Cell.prototype.drop=function(){
    			this.row++;
    		}
    	}
    	if(!Cell.prototype.moveR){//十一
    		Cell.prototype.moveR=function(){
    			this.col++;
    		}
    	}
    	if(!Cell.prototype.moveL){//十一
    		Cell.prototype.moveL=function(){
    			this.col--;
    		}
    	}
    }
    //十四、下落的各种变化状态
    function State(r0,c0,r1,c1,r2,c2,r3,c3){
    	//第0个cell相对于参照cell的下标偏移量
    	this.r0=r0;
    	this.c0=c0;
    	//第1个cell相对于参照cell的下标偏移量
    	this.r1=r1;
    	this.c1=c1;
    	//第2个cell相对于参照cell的下标偏移量
    	this.r2=r2;
    	this.c2=c2;
    	//第3个cell相对于参照cell的下标偏移量
    	this.r3=r3;
    	this.c3=c3;
    }
    function Shape(img,orgi){
    	this.img=img;
    	this.states=[];//十四、保存每个图形不同状态的数组
    	this.orgi=orgi;//十四、以它为固定不变的参照点,去旋转变形,就是数组states的下标
    	this.statei=0;//默认所有图形的最初状态都是0
    	//三
    	if(!Shape.prototype.drop){
    		Shape.prototype.drop=function(){
    			//遍历当前对象的cells中的每个cell对象
    			//	  调用当前cell对象的drop方法
    			for(var i=0;i<this.cells.length;i++){
    				this.cells[i].drop();
    			}
    		}
    	}
    	if(!Shape.prototype.moveR){//十一
    		Shape.prototype.moveR=function(){
    			//遍历当前对象的cells中的每个cell对象
    			for(var i=0;i<this.cells.length;i++){
    			//    调用当前cell对象的drop方法
    				this.cells[i].moveR();
    			}
    		}
    	}
    	if(!Shape.prototype.moveL){//十一
    		Shape.prototype.moveL=function(){
    			//遍历当前对象的cells中的每个cell对象
    			for(var i=0;i<this.cells.length;i++){
    			//    调用当前cell对象的drop方法
    				this.cells[i].moveL();
    			}
    		}
    	}
    	//十五
    	if(!Shape.prototype.rotateR){
    		Shape.prototype.rotateR=function(){
    			//if(Object.getPrototypeOf(this)!=O.prototype){
    			if(this.constructor!=O){
    				this.statei++;
    				this.statei>=this.states.length&&(this.statei=0);
    				//获得下一个状态对象
    				var state=this.states[this.statei];
    				var orgr=this.cells[this.orgi].row;
    				var orgc=this.cells[this.orgi].col;
    				//遍历当前图形中的每个cell
    				//按state中偏移量,设置每个cell的新位置
    				for(var i=0;i<this.cells.length;i++){
    					this.cells[i].row=orgr+state["r"+i];
    					this.cells[i].col=orgc+state["c"+i];
    				}//for的结束
    			}//if的结束
    		}//function的结束
    	}//if的结束
    	if(!Shape.prototype.rotateL){
    		Shape.prototype.rotateL=function(){
    			//if(Object.getPrototypeOf(this)!O.prototype){
    			if(this.constructor!=O){
    				this.statei--;
    				this.statei<0&&(this.statei=this.states.length-1);
    				//获得下一个状态对象
    				var state=this.states[this.statei];
    				var orgr=this.cells[this.orgi].row;
    				var orgc=this.cells[this.orgi].col;
    				//遍历当前图形中的每个cell
    				//按照state中偏移量,设置每个cell的心位置
    				for(var i=0;i<this.cells.length;i++){
    					this.cells[i].row=orgr+state["r"+i];
    					this.cells[i].col=orgc+state["c"+i];
    				}//for的结束
    			}//if的结束
    		}//function的结束
    	}//if的结束
    }//function Shape(img,orgi)的结束
    //二
    function O(){//1
    	Shape.call(this,"img/O.png");
    	if(!Shape.prototype.isPrototypeOf(O.prototype)){
    		Object.setPrototypeOf(O.prototype,Shape.prototype);//继承
    	}
    	this.cells=[
    		new Cell(0,4,this.img),new Cell(0,5,this.img),
    		new Cell(1,4,this.img),new Cell(1,5,this.img)
    	];
    }
    function T(){//2
    	Shape.call(this,"img/T.png",1);
    	if(!Shape.prototype.isPrototypeOf(T.prototype)){
    		Object.setPrototypeOf(T.prototype,Shape.prototype);//继承
    	}
    	this.cells=[
    		new Cell(0,3,this.img),new Cell(0,4,this.img),
    		new Cell(0,5,this.img),new Cell(1,4,this.img)
    	];
    	//十四
    	this.states[0]=new State(0,-1, 0,0, 0,1, 1,0);
    	this.states[1]=new State(-1,0, 0,0, 1,0, 0,-1);
    	this.states[2]=new State(0,1, 0,0, 0,-1, -1,0);
    	this.states[3]=new State(1,0, 0,0, -1,0, 0,1);
    						//   [0]   [1]	[2]	 [3]
    }
    
    function I(){//3
    	Shape.call(this,"img/I.png",1);
    	if(!Shape.prototype.isPrototypeOf(I.prototype)){
    		Object.setPrototypeOf(I.prototype,Shape.prototype);//继承
    	}
    	this.cells=[
    		new Cell(0,3,this.img),new Cell(0,4,this.img),
    		new Cell(0,5,this.img),new Cell(0,6,this.img)
    	];
    	this.states[0]=new State(0,-1, 0,0, 0,1, 0,2);
    						//   [0]   [1]	[2]	 [3]
    	this.states[1]=new State(-1,0, 0,0, 1,0, 2,0);
    }
    function S(){//4
    	Shape.call(this,"img/S.png",3);
    	if(!Shape.prototype.isPrototypeOf(S.prototype)){
    		Object.setPrototypeOf(S.prototype,Shape.prototype);//继承
    	}
    	this.cells=[
    		new Cell(0,4,this.img),new Cell(0,5,this.img),
    		new Cell(1,3,this.img),new Cell(1,4,this.img)
    	];
    	//十四
    	this.states[0]=new State(-1,0, -1,1, 0,-1, 0,0);
    	this.states[1]=new State(0,1, 1,1, -1,0, 0,0);
    						//   [0]   [1]	[2]	 [3]
    }
    function Z(){//5
    	Shape.call(this,"img/Z.png",1);
    	if(!Shape.prototype.isPrototypeOf(Z.prototype)){
    		Object.setPrototypeOf(Z.prototype,Shape.prototype);//继承
    	}
    	this.cells=[
    		new Cell(0,3,this.img),new Cell(0,4,this.img),
    		new Cell(1,4,this.img),new Cell(1,5,this.img)
    	];
    	this.states[0]=new State(0,-1, 0,0, 1,0, 1,1);
    	this.states[1]=new State(-1,0, 0,0, 0,-1, 1,-1);
    						//   [0]   [1]	[2]	 [3]
    }
    function L(){//6
    	Shape.call(this,"img/L.png",1);
    	if(!Shape.prototype.isPrototypeOf(L.prototype)){
    		Object.setPrototypeOf(L.prototype,Shape.prototype);//继承
    	}
    	this.cells=[
    		new Cell(0,3,this.img),new Cell(0,4,this.img),
    		new Cell(0,5,this.img),new Cell(1,3,this.img)
    	];
    	this.states[0]=new State(0,-1, 0,0, 0,1, 1,-1);
    	this.states[1]=new State(-1,0, 0,0, 1,0, -1,-1);
    	this.states[2]=new State(0,1, 0,0, 0,-1, -1,1);
    	this.states[3]=new State(1,0, 0,0, -1,0, 1,1);
    						//   [0]   [1]	[2]	 [3]
    }
    function J(){//7
    	Shape.call(this,"img/J.png",1);
    	if(!Shape.prototype.isPrototypeOf(J.prototype)){
    		Object.setPrototypeOf(J.prototype,Shape.prototype);//继承
    	}
    	this.cells=[
    		new Cell(0,3,this.img),new Cell(0,4,this.img),
    		new Cell(0,5,this.img),new Cell(1,5,this.img)
    	];
    	this.states[0]=new State(-1,0, 0,0, 1,-1, 1,0);
    	this.states[1]=new State(0,1, 0,0, -1,-1, 0,-1);
    	this.states[2]=new State(1,0, 0,0, -1,1, -1,0);
    	this.states[3]=new State(0,-1, 0,0, 1,1, 0,1);
    						//   [0]   [1]	[2]	 [3]
    }
    

     效果:

  • 相关阅读:
    NodeJS学习笔记 进阶 (11)Nodejs 进阶:调试日志打印:debug模块
    NodeJS学习笔记 进阶 (10)Nodejs 进阶:log4js入门实例(ok))
    NodeJS学习笔记 进阶 (9)express+cookie-parser:签名机制深入剖析(ok)
    NodeJS学习笔记 进阶 (8)express+morgan实现日志记录(ok)
    NodeJS学习笔记 进阶 (7)express+session实现简易身份认证(ok)
    NodeJS学习笔记 进阶 (6)本地调试远程服务器上的Node代码(ok)
    NodeJS学习笔记 进阶 (5)将图片转成datauri嵌入到html(ok)
    51nod 1287 线段树
    51nod 1043 数位dp
    51nod 1042 数位dp
  • 原文地址:https://www.cnblogs.com/-walker/p/4996442.html
Copyright © 2011-2022 走看看