原生JavaScript編寫俄羅斯方塊
首先這里感謝@jdkleo 提出的寶貴建議!
說實在的吧,我這個俄羅斯方塊大家玩起來別罵我就萬歲了,還沒完全完成的,只完成了50%,而且還有很多BUG。
可以實現(xiàn)的功能:
1.掉方塊
2.隨機生成新方塊
3.方塊移動。
目前BUG還很多,由于是第一次寫這么“大”的游戲,有1000多行代碼,所以還請高人指點,BUG太多了。
按START開始游戲。
大家提提建議,我第一次寫JS游戲。
參考了一下網(wǎng)上其他人的代碼,但是不是照抄。
代碼可以直接運行,不用引用JQUERY。
希望大神們能給點建議!?。?!不甚感激!?。?/p>
Ver 0.2版本已經(jīng)出來了哦(2014-12-26),最新的代碼:
此次修正的東西:
1.左邊右邊可以移動。
2.可以旋轉(zhuǎn)
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>無標(biāo)題文檔</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;
}
/*游戲相關(guān)*/
#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維數(shù)組,用來存放俄羅斯方塊的坐標(biāo)
var xYAxis=[];
xYAxis.push([1,2],[3,4]);
//alert(xYAxis[1][0]);
//復(fù)制節(jié)點
/*$(document).ready(function(e) {
for(i=0;i<2;i++)
{
if(i==1)
{
// $("#test").append("<br>"); //加上換行符
}
$(".t").clone().appendTo("#test");
}
//動態(tài)得到test(container)的寬度,包含兩邊的邊框BORDER
$("#test").width(($(".t").width()+2)*2+1);
});
*/
//獲得區(qū)域的橫坐標(biāo)和縱坐標(biāo)
function getArea(x,y,unit,id)
{
this.x=x;
this.y=y;
this.unit=unit; //每個單元的大小,單位為像素
this.el=document.getElementById(id); //得到ID對象
this.board=[]; //面板,即在區(qū)域范圍內(nèi)的元素(俄羅斯方塊)
/*創(chuàng)建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開始坐標(biāo)和Y開始坐標(biāo)的位置(錯誤)
//得到X坐標(biāo)的下落次數(shù),和Y軸的左右移動的次數(shù)
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; //返回線條
}
//和線性有關(guān)的東西(判斷是否滿了)
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--)
{
/*今天暫時寫到這里*/
/*繼續(xù)于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;
}
}
}
};
//活動區(qū)域
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(); //新建狀態(tài)
/*初始化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();
}
}
//游戲結(jié)束
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;
}
//隱藏游戲結(jié)束
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(); //創(chuàng)建鍵盤類實體
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;
//設(shè)置鍵位
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; //當(dāng)前對象
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; //速度
/*方塊的組合方式,用數(shù)組進行組合(二維數(shù)組)
用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開始的坐標(biāo)原點*/
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];
//區(qū)域開始X軸的位置
var AreaXStartPos=parseInt(this.area.x-operate[0].length);
//區(qū)域開始Y軸的位置
//var AreaYStartPos=parseInt(this.area.y-operate[0]);
var AreaYStartPos=1; //因為X軸的位置可能變化,而Y軸總是從最上面下來的,所以是1
this.x=AreaXStartPos; //把新的位置賦給X;
this.y=AreaYStartPos; //把新的位置賦給y;
//構(gòu)建空對象,并存入BOARD
/*y:行,x:列*/
//alert(operate[0].length+" "+operate.length);
this.board=this.createEmpty(operate[0].length,operate.length);
/*線條,往下掉落,初始化*/
var lines=0;
var foundLines=false;
//循環(huán)遍歷,先遍歷行,每一行再來遍歷列
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中
}
}
/*個人感覺這個功能應(yīng)該是加速往下掉落的方法?不明覺厲*/
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++)
{
//創(chuàng)建元素
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); //下一個元素
}
}
}
}
//創(chuàng)建空對象,即所有的都為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;
}
//下落(這是一個最關(guān)鍵的函數(shù),決定了這個游戲的成敗)
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
{
}
}
//是否可以旋轉(zhuǎn)俄羅斯方塊
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;} //獲得區(qū)域
}
}
}
return true;
}
//旋轉(zhuǎn)俄羅斯方塊
this.rotate=function()
{
var puzzle=this.createEmpty(this.board.length,this.board[0].length); //創(chuàng)建一個空的矩陣
for(var y=0;y<this.board.length;y++)
{
for(var x=0;x<this.board[y].length;x++)
{
//旋轉(zhuǎn),X軸和Y軸的坐標(biāo)互換
if(this.board[y][x])
{
var newY=puzzle.length-1-x;
var newX=y;
var el=this.board[y][x]; //旋轉(zhuǎn)前的對象
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()
{
//用一個循環(huán)去控制下落
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();
}
}
/*俄羅斯方塊狀態(tài)*/
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有關(guān)
this.incTime=function()
{
self.time++;
self.el.time.innerHTML=self.time; //設(shè)置時間
self.actions=parseInt(self.actions/self.time)*60;
this.el.apm.innerHTML=self.apm;
}
/*設(shè)置分數(shù)*/
this.setScore=function(i)
{
this.score==i;
this.el.score.innerHTML=this.score;
}
/*設(shè)置等級*/
this.setLevel=function(i)
{
this.level=i;
this.el.level.innerHTML=this.level; //設(shè)置內(nèi)部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;
}
//設(shè)置動作
this.setActions=function(i)
{
this.actions=i;
//this.el.actions.innerHTML=this.actions;
}
this.getActions=function()
{
return this.actions;
}
//設(shè)置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;
}
}
//隨機數(shù),產(chǎn)生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>
<!--游戲結(jié)束-->
<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>
<!-- 功能鍵(繼續(xù))-->
<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
以上所述就是本文的全部內(nèi)容了,希望能夠?qū)Υ蠹覍W(xué)習(xí)javascript有所幫助。
相關(guān)文章
詳解如何在JavaScript中無縫地集成和使用Python代碼
這篇文章主要目標(biāo)是幫助諸位理解如何在JavaScript中無縫地集成和使用Python代碼,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下2023-06-06
js中document.write和document.writeln的區(qū)別
這篇文章主要介紹了js中document.write和document.writeln的區(qū)別,需要的朋友可以參考下2018-03-03
javascript smipleChart 簡單圖標(biāo)類
支持 線性圖 區(qū)域圖 柱狀圖 餅圖 支持多瀏覽器 用到的是svg vml 之后加上 多層餅圖 分段圖 和組合圖2011-01-01
第二次聊一聊JS require.js模塊化工具的基礎(chǔ)知識
第二次聊一聊JS require.js模塊化工具的基礎(chǔ)知識,本文為大家JS require.js模塊化工具的最基本知識點,感興趣的小伙伴們可以參考一下2016-04-04
正則表達式判斷是否存在中文和全角字符和判斷包含中文字符串長度
對于一些更安全的容錯嚴重,需要用到2008-09-09

