首先這裡感謝@jdkleo 提出的寶貴建議!
說實在的吧,我這個俄羅斯方塊大家玩起來別罵我就萬歲了,還沒完全完成的,只完成了50%,而且還有很多BUG。
可以實現的功能:
1.掉方塊
2.隨機生成新方塊
3.方塊移動。
目前BUG還很多,由於是第一次寫這麼“大”的游戲,有1000多行代碼,所以還請高人指點,BUG太多了。
按START開始游戲。
大家提提建議,我第一次寫JS游戲。
參考了一下網上其他人的代碼,但是不是照抄。
代碼可以直接運行,不用引用JQUERY。
希望大神們能給點建議!!!!不甚感激!!!
Ver 0.2版本已經出來了哦(2014-12-26),最新的代碼:
此次修正的東西:
1.左邊右邊可以移動。
2.可以旋轉
3.可以消除滿行的方塊。
代碼:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>無標題文檔</title> <style type="text/css"> #test { /*width:25px;*/ } .t { width:10px; height:10px; border:1px solid black; float:left; } body { margin:0 auto; width:1000px; height:600px; } /*游戲相關*/ #startGame { } #lines { } #level { } #time { } /*俄羅斯方塊實體類*/ #tetris-area { width:auto; height:auto; background:blue; } /*JS生成的CLASS,俄羅斯方塊實體*/ #tetris .block0,#tetris .block1, #tetris .block2, #tetris .block3,#tetris .block4,#tetris .block5,#tetris .block6 { z-index:1000; font-size:10px; line-height:1em; position:absolute; width:13px; height:13px; border:0.5px solid red; background:#000; } </style> <script src="jquery.js"></script> <script type="text/javascript"> //2維數組,用來存放俄羅斯方塊的坐標 var xYAxis=[]; xYAxis.push([1,2],[3,4]); //alert(xYAxis[1][0]); //復制節點 /*$(document).ready(function(e) { for(i=0;i<2;i++) { if(i==1) { // $("#test").append("<br>"); //加上換行符 } $(".t").clone().appendTo("#test"); } //動態得到test(container)的寬度,包含兩邊的邊框BORDER $("#test").width(($(".t").width()+2)*2+1); }); */ //獲得區域的橫坐標和縱坐標 function getArea(x,y,unit,id) { this.x=x; this.y=y; this.unit=unit; //每個單元的大小,單位為像素 this.el=document.getElementById(id); //得到ID對象 this.board=[]; //面板,即在區域范圍內的元素(俄羅斯方塊) /*創建2D范圍矩陣*/ for(var y=0;y<this.y;y++) { this.board.push(new Array()); for(var x=0;x<this.x;x++) { this.board[y].push(0); } } /*從2D矩陣中消除元素*/ this.destroy=function() { for(var y=0;y<this.board.length;y++) { for(var x=0;x<this.board[y].length;x++) { if(this.board[y][x]) { this.el.removeChild(this.board[y][x]); this.board[y][x]=0; } } } } //添加元素 this.addElement=function(el) { //得到起始元素的X開始坐標和Y開始坐標的位置(錯誤) //得到X坐標的下落次數,和Y軸的左右移動的次數 var xBegin=parseInt(el.offsetLeft/unit); var yBegin=parseInt(el.offsetTop/unit); if(xBegin>=0&&xBegin<=this.x&&yBegin>=0&&yBegin<=this.y) { this.board[yBegin][xBegin]=el; //確定元素的位置 } } //消掉所有的行 this.removeFullLines=function() { var lines=0; for(var i=this.y-1;i>0;i--) { if(this.linesRelated(i)) { this.removeLines(i); lines++; y++; } } return lines; //返回線條 } //和線性有關的東西(判斷是否滿了) this.linesRelated=function(y) { for(var x=0;x<this.x;x++) { if(!this.board[y][x]){return false;} //如果不為0的話,那麼菜返回FALSE } return true; }; //去掉行 this.removeLines=function(y) { for(var x=0;x<this.x;x++) { this.el.removeChild(this.board[y][x]); this.board[y][x]=0; } y--; for(;y>0;y--) { /*今天暫時寫到這裡*/ /*繼續於2014-12-21*/ for(var x=0;x<this.x;x++) { if(this.board[y][x]) { var el=this.board[y][x]; el.style.top=el.offsetTop+this.unit+"px"; this.board[y+1][x]=el; this.board[y][x]=0; } } } }; //活動區域 this.getBlock=function(y,x) { if(y<0){return 0;} if(y<this.y&&x<this.x) { return this.board[y][x]; } else { throw "Area get failed!"; } } } /*俄羅斯方塊實體類*/ function Tetris() { var self =this; //自身 var operate=null; this.area=null; this.operate=null; //操作 this.status=new State(); //新建狀態 /*初始化X,Y,單元為5或者20*/ this.x=20; this.y=20; this.unit=20; this.running=null; //是否在運行中 //俄羅斯方塊實體ID this.id="tempid"; /*開始的時候暫停是FALSE的*/ this.paused=false; //開始游戲 this.start=function() { self.reset(); //重新開始游戲 self.status.start(); this.area=new getArea(this.x,this.y,this.unit,"tetris-area"); //獲得Area對象 ,其中TEMPID是俄羅斯方塊實體類ID this.operate=new OperateTetris(this.area,self); //是否可以替換 if(this.operate.mayPlace()) { //alert(1); this.operate.place(); } else { self.gameOver(); } } //游戲結束 this.gameOver=function() { self.status.stopGame(); //停止游戲 self.operate.stopGame(); //操作類停止游戲 } /*重置游戲*/ this.reset=function() { if(this.operate) { self.operate.destroy(); //重新開始 self.operate=null; } if(self.area) { self.area.destroy(); self.area=null; } //隱藏游戲結束 document.getElementById("game_over").style.display="none"; document.getElementById("next_operate").style.display="block"; //下一個操作 document.getElementById("keys_Press").style.display="block"; //顯示按鍵 self.status.reset(); self.paused=false; document.getElementById("tetris-pause").style.display="block"; //暫停按鈕 } /*暫停游戲*/ this.pause=function() { if(self.operate==null) { return ; } if(self.paused) { self.operate.running=true; /*這裡還沒寫完2014-12-22*/ } else { } } //上 this.up=function() { if(self.operate&&self.operate.isRunning()&&!self.operate.isStopped()) { if(self.operate.mayRotate()) { self.operate.rotate(); self.status.setActions(self.status.getActions()+1); } } } //下 this.down=function() { if(self.operate&&self.operate.isRunning()&&!self.operate.isStopped()) { if(self.operate.mayMoveDown()) { self.operate.moveDown(); self.status.setActions(self.status.getActions()+1); } } } //左 this.left=function() { if(self.operate&&self.operate.isRunning()&&!self.operate.isStopped()) { if(self.operate.mayMoveLeft()) { self.operate.moveLeft(); self.status.setActions(self.status.getActions()+1); } } } //右 this.right=function() { if(self.operate&&self.operate.isRunning()&&!self.operate.isStopped()) { if(self.operate.mayMoveRight()) { self.operate.moveRight(); self.status.setActions(self.status.getActions()+1); } } } //開始游戲 document.getElementById("startGame").onclick=function(){self.start()}; //} //Tetris是一個整體類,裡面包含了很多方法。 var keyboard=new Keyboard(); //創建鍵盤類實體 keyboard.set(keyboard.n,this.start); keyboard.set(keyboard.up,this.up); keyboard.set(keyboard.down,this.down); keyboard.set(keyboard.left,this.left); keyboard.set(keyboard.right,this.right); document.onkeydown=keyboard.event; //按下按鍵按鈕的事件 /*鍵盤操作方法*/ function Keyboard() { this.up=38; //上 this.down=40; //下 this.left=37; //左 this.right=39; //右 this.n=78; this.p=80; this.r=82; this.space=32; //空格 this.f12=123; this.escape=27; //退格鍵 this.keys=[]; //鍵位集合 this.funcs=[]; var self=this; //設置鍵位 this.set=function(key,func) { this.keys.push(key); this.funcs.push(func); } this.event=function(e) { if(!e){e=window.event;} for(var i=0;i<self.keys.length;i++) { if(e.keyCode==self.keys[i]) { self.funcs[i](); } } } } //具體的操作類 function OperateTetris(area,tetris) { var self=this; //當前對象 this.area=area; this.tetris=tetris; this.types=null; //方塊的類型; this.nextType=null; //下一個類型 //初始化X和Y this.x=null; this.y=null; this.position=0; //初始位置 this.board=[]; //用來填充HTML元素的 this.elements=[]; this.nextElements=[]; //下一個元素 this.running=null; //是否在運行中 this.stopped=null; //是否停止 this.fallDownId=null; //往下掉落的 this.speed=null; //速度 /*方塊的組合方式,用數組進行組合(二維數組) 用0,1表示是否有方塊存在,如果是0:不存在,1:存在, 以下的邏輯就可以非常的清楚了。*/ this.blockComplex=[ [ [0,0,1],[1,1,1],[0,0,0] //_| ], [ [1,0,0],[1,1,1],[0,0,0] //L ], [ [0,1,0],[1,1,1],[0,0,0] //T ], [ [0,0,0],[1,1,1],[0,0,0] //-- ], [ [0,0,0],[0,1,1],[0,1,1] //口 ], [ [0,1,1],[0,1,0],[1,1,0] //Z ] ]; this.stopGame=function() { this.running=false; } /*一連串的GETTER方法 分別是速度,X,Y軸,運行和停止的GETTER方法*/ this.getSpeed=function() { return this.speed; } this.getX=function() { return this.x; } this.getY=function() { return this.y; } this.isRunning=function() { return this.running; } this.isStopped=function() { return this.stopped; } //重置(初始化) this.reset=function() { if(this.fallDownId) { clearTimeout(this.fallDownId); //下落的時候去掉時間間隔 } this.types=this.nextType; this.nextType=random(this.blockComplex.length); this.position=0; this.board=[]; this.elements=[]; this.x=null; this.y=null; this.speed=200; //速度暫定為51 this.running=false; this.stopped=false; //移除下一個元素 for(var i=0;i<this.nextElements.length;i++) { document.getElementById("next_operate").removeChild(this.nextElements[i]); } this.nextElements=[]; //下一個元素 } //下一個類型,隨機抽取 this.nextType=random(this.blockComplex.length); //重置 this.reset(); /*判斷是否替換*/ this.mayPlace=function() { var isOperate=this.blockComplex[this.types]; /*area開始的坐標原點*/ var areaStartX=parseInt(this.area.x-isOperate[0].length); var areaStartY=1; var lineFound=false; var lines=0; for(var y=isOperate.length-1;y>=0;y--) { for(var x=0;x<isOperate[y].length;x++) { if(isOperate[y][x]) { lineFound=true; if(this.area.getBlock(areaStartY,areaStartX+x)) {return false;} } } if(lineFound) { lines++; } if(areaStartY-lines<0) { break; } } return true; } /*替換*/ this.place=function() { //初始化 var operate=this.blockComplex[this.types]; //區域開始X軸的位置 var AreaXStartPos=parseInt(this.area.x-operate[0].length); //區域開始Y軸的位置 //var AreaYStartPos=parseInt(this.area.y-operate[0]); var AreaYStartPos=1; //因為X軸的位置可能變化,而Y軸總是從最上面下來的,所以是1 this.x=AreaXStartPos; //把新的位置賦給X; this.y=AreaYStartPos; //把新的位置賦給y; //構建空對象,並存入BOARD /*y:行,x:列*/ //alert(operate[0].length+" "+operate.length); this.board=this.createEmpty(operate[0].length,operate.length); /*線條,往下掉落,初始化*/ var lines=0; var foundLines=false; //循環遍歷,先遍歷行,每一行再來遍歷列 for(var yAxis=operate.length-1;yAxis>=0;yAxis--) { for(var xAxis=0;xAxis<=operate[yAxis].length;xAxis++) { if(operate[yAxis][xAxis]) { var el=document.createElement("div"); el.className="block"+this.types; //確定這個元素的CLASSNAME //確定左邊距和上邊距 el.style.left=(this.x+xAxis)*this.area.unit+"px"; el.style.top=(this.y+yAxis)*this.area.unit+"px"; this.area.el.appendChild(el); //這個EL去APPEND主要的EL。 this.board[yAxis][xAxis]=el; this.elements.push(el); //推入elements中 } } /*個人感覺這個功能應該是加速往下掉落的方法?不明覺厲*/ if(lines) { yAxis--; } if(foundLines) { lines++; } } this.running=true; this.fallDownId=setTimeout(this.fallDown,this.speed); //間隔時間,掉落下的 var nextOperate=this.blockComplex[this.nextType]; for(var y=0;y<nextOperate.length;y++) { for(var x=0;x<nextOperate[y].length;x++) { //創建元素 if(nextOperate[y][x]) { /*先寫到這裡:2014-12-22*/ var el=document.createElement("div"); el.className="block"+this.nextType; el.style.left=(x*this.area.unit)+"px"; el.style.top=(y*this.area.unit)+"px"; document.getElementById("next_operate").appendChild(el); this.nextElements.push(el); //下一個元素 } } } } //創建空對象,即所有的都為0的對象,並返回對象 this.createEmpty=function(x,y) { var elements=[]; for(var y2=0;y2<y;y2++) { elements.push(new Array()); for(var x2=0;x2<x;x2++) { elements[y2].push(0); } } return elements; } //下落(這是一個最關鍵的函數,決定了這個游戲的成敗) this.fallDown=function() { if(self.isRunning()) { if(self.mayMoveDown()) { self.moveDown(); self.fallDownId=setTimeout(self.fallDown,self.speed); //下落的間隔時間 } else { for(var i=0;i<self.elements.length;i++) { self.area.addElement(self.elements[i]); } var lines=self.area.removeFullLines(); if(lines) { /*這裡到時候再寫*/ self.tetris.status.setLines(self.tetris.status.getLines()+lines); } self.reset(); if(self.mayPlace()) { self.place(); } else { self.tetris.gameOver(); } } } else { } } //是否可以旋轉俄羅斯方塊 this.mayRotate=function() { for(var y=0;y<this.board.length;y++) { for(var x=0;x<this.board[y].length;x++) { if(this.board[y][x]) { /新的X,Y的值/ var newY=this.getY()+this.board.length-1-x; var newX=this.getX()+y; if(newY>this.area.y){return false;} if(newX<0){return false;} if(newX>=this.area.x){return false;} if(this.area.getBlock(newY,newX)){return false;} //獲得區域 } } } return true; } //旋轉俄羅斯方塊 this.rotate=function() { var puzzle=this.createEmpty(this.board.length,this.board[0].length); //創建一個空的矩陣 for(var y=0;y<this.board.length;y++) { for(var x=0;x<this.board[y].length;x++) { //旋轉,X軸和Y軸的坐標互換 if(this.board[y][x]) { var newY=puzzle.length-1-x; var newX=y; var el=this.board[y][x]; //旋轉前的對象 var moveY=newY-y; var moveX=newX-x; //長度是offsetTop或left加上偏移量 el.style.left=el.offsetLeft+(moveX*this.area.unit)+"px"; el.style.top=el.offsetTop+(moveY*this.area.unit)+"px"; puzzle[newY][newX]=el; } } } this.board=puzzle; } //下落方法(進行判斷) this.mayMoveDown=function() { for(var y=0;y<this.board.length;y++) { for(var x=0;x<this.board[y].length;x++) { if(this.board[y][x]) { if(this.getY()+y+1>=this.area.y){this.stopGame=true;return false;} //如果觸底,那麼就停止游戲 if(this.area.getBlock(this.getY()+y+1,this.getX()+x)){this.stopGame=true;return false;} } } } return true; } //下落 this.moveDown=function() { //用一個循環去控制下落 for(var i=0;i<this.elements.length;i++) { this.elements[i].style.top=this.elements[i].offsetTop+this.area.unit+"px"; } this.y++; } this.mayMoveLeft=function() { /*可以向左移動(判斷方法)*/ for(var y=0;y<this.board.length;y++) { for(var x=0;x<this.board[y].length;x++) { if(this.board[y][x]) { if(this.getX()-1+x<0) { return false; } if(this.area.getBlock(this.getY()+y,this.getX()+x-1)){return false;} } } } return true; } //向左移動 this.moveLeft=function() { /*向左移動(判斷方法)*/ for(var i=0;i<this.elements.length;i++) { this.elements[i].style.left=this.elements[i].offsetLeft-this.area.unit+"px"; } this.x--; } /*是否可以向右移動*/ this.mayMoveRight=function() { for(var y=0;y<this.board.length;y++) { for(var x=0;x<this.board[y].length;x++) { if(this.board[y][x]) { if(this.getX()+1+x>=this.area.x){return false;} if(this.area.getBlock(this.getY()+y,this.getX()+x+1)){return false;} } } } return true; } /*向右移動*/ this.moveRight=function() { for(var i=0;i<this.elements.length;i++) { this.elements[i].style.left=this.elements[i].offsetLeft+this.area.unit+"px"; } this.x++; } /*摧毀方法*/ this.destroy=function() { for(var i=0;i<this.elements.length;i++) { this.area.el.removeChild(this.elements[i]); } this.elements=[]; this.board=[]; this.reset(); } } /*俄羅斯方塊狀態*/ function State() { /*初始化*/ this.level; this.time; this.score; this.opeate; this.lines; this.apm; //不明覺厲 this.actions; //動作 this.el= { "lines":document.getElementById("lines"), "level":document.getElementById("level"), "time":document.getElementById("time"), "apm":document.getElementById("apm"), "operate":document.getElementById("operate") } this.timeId=null; var self=this; //開始游戲 this.start=function() { this.reset(); //重置 this.timeId=setInterval(this.incTime,1500); } /*停止游戲*/ this.stopGame=function() { if(this.timeId) { clearInterval(this.timeId); } } //重置 this.reset=function() { this.stopGame(); this.level=1; this.time=0; this.score=0 this.opeate=0; this.lines=0; /*以後可能加INNERHTML*/ this.el.level=this.level; this.el.time=this.time; this.el.score=this.score; this.el.operate=this.opeate; this.el.lines=this.lines; } //和SetInterval有關 this.incTime=function() { self.time++; self.el.time.innerHTML=self.time; //設置時間 self.actions=parseInt(self.actions/self.time)*60; this.el.apm.innerHTML=self.apm; } /*設置分數*/ this.setScore=function(i) { this.score==i; this.el.score.innerHTML=this.score; } /*設置等級*/ this.setLevel=function(i) { this.level=i; this.el.level.innerHTML=this.level; //設置內部HTML }; this.getLevel=function() { return this.level; } //線條的SETTER方法 this.setLines=function(i) { this.lines=i; this.el.lines.innerHTML=this.lines; } this.getLines=function() { return this.lines; } //設置動作 this.setActions=function(i) { this.actions=i; //this.el.actions.innerHTML=this.actions; } this.getActions=function() { return this.actions; } //設置apm this.setApm=function(i) { this.apm=i; this.el.apm.innerHTML=this.apm; } this.getApm=function() { return this.apm; } //控制下落操作的類 this.setOperate=function(i) { this.opeate=i; this.el.operate=this.operate; } this.getOperate=function() { return this.opeate; } } //隨機數,產生1~6的 function random(i) { return Math.floor(Math.random()*i); } } /*Tetris是一個整體類,裡面包含了很多方法*/ </script> </head> <body> <div id="tetris"> <!--正方形 --> <div id="test"> <div class="t"></div> </div> <!--長方形--> <div id="rect"> <div class="r"> </div> </div> <!-- 俄羅斯方塊實體類--> <div id="tetris-area"> </div> <!-- 下一個操作--> <div id="next_operate"></div> <!--游戲結束--> <div id="game_over">Game Over</div> <!-- 按鍵 --> <div id="keys_Press"> </div> <input type="button" id="startGame" value="Start" /> <!--線條 --> <div id="lines"></div> <!--等級 --> <div id="level"></div> <!--apm(不知道有什麼作用) --> <div id="apm"></div> <!--時間 --> <div id="time"></div> <!--控制下落操作 --> <div id="operate"></div> <!-- 功能鍵(暫停)--> <div id="tetris-pause"> </div> <!-- 功能鍵(繼續)--> <div id="tetris-resume" style="display:none"> </div> </div> <script> var tx=new Tetris(); tx.x=12; tx.y=22; tx.unit=14; //tx.start(); //開始游戲 </script> </body> </html>
演示圖:
項目git地址:
http://git.oschina.net/KMSFan/Tetris_Yang
項目演示地址:http://runjs.cn/detail/ggo07ery
以上所述就是本文的全部內容了,希望能夠對大家學習javascript有所幫助。