俄羅斯方塊代碼說明css
/** 名稱:Javascript 俄羅斯方塊! 做者:Gloot 郵箱:glootz@gmail.com QQ:345268267 網站:http://www.cnblogs.com/editor/ */ OLSFK = {};
本俄羅斯方塊代碼採用 JavaScript 腳本代碼寫成,簡單易懂;node
全代碼採用靜態類及靜態變量成員組成;json
全腳本經過實現代碼全局配置 OLSFK.Options = {...}dom
定義方塊起始座標及定義各自的旋轉點;網站
從初始化俄羅斯方塊界面開始,再監聽鍵盤事件;以及左右,向下及旋轉動做判斷,從新渲染方塊位置;編碼
判斷是否消行,以及相應的加級判斷,執行速度,加分操做來執行;spa
最後以判斷是否當前級別大於所定義的最大級別來判斷是否結束;code
代碼說明講解對象
OLSFK.Options = { //相關參數 width:12,//界面橫向方塊數 height:20,//界面縱向方塊數 boxWidth : '16px', curLevel:1, speed : 1000, //setInterval,setTimeout direct : { //能夠設定是A S D W, 仍是← ↓ → Down: 40 , /*run speed*/ Left: 37, Right: 39, Rotate: 38 }, Move:true,//是否正在移動 Eventing:false, Levels: { 1:1000, 2:900, 3:800, 4:700, 5:600, 6:500, 7:400, 8:300, 9:200, 10:100 }, curBlock:4, //當前移動的圖形名稱 nextBlock:0, GampMap:new Object(), Timer:null, deline:0, Score:0, Deling:false, Start:false, lineNum:10, //刪除幾行了,加級 ScoreNum:40 //消一行加分 }
direct 表示 使用鍵盤方位鍵來操做方塊的移動方向;blog
使用哪一種方向鍵按自由喜歡配置,好比字母鍵的A, S, D, W; 或右邊小數字鍵盤的數字鍵各自的鍵盤編碼;
好比 上(旋轉)、下、左、右 方向鍵的編碼分別爲:3八、40、3七、39;
Levels:表示級別配置,本配置共分爲10級,每一個級別所對應的下落速度,即定時執行間隔;
curBlock:表示當前活動的方塊;
nextBlock:表示接下來執行的方塊索引,並顯示界面右上角的預覽框中;
GampMap:用於保存在根據定義行列數造成的遊戲表格中保存每一個格的數據信息;
OLSFK.Options.GampMap[x+'_'+y] = 0;
對象表格爲: id: "box_"+x+"_"+y;
初始化數據爲 ‘0’; 表示該表格還未佔用;當有佔用時,設置值爲 ‘1’;
Timer:爲定時執行器;setTimeout 定時執行方塊下落的的頻率;定時時間越小,速度越快;
Deling:當正在執行消行操做時,下次暫不顯示並下落;
lineNum:表示消超過 10 行,加一級;
ScoreNum:表示每消一行所加的分數;
OLSFK.ReItems = function (cur){ //key旋轉點 switch (cur) { case 1: OLSFK.Items[1] = {//長塊 LongBlock 1:{x:4,y:0}, 2:{x:5,y:0}, 3:{x:6,y:0}, 4:{x:7,y:0}, 5:{x:5,y:0} //旋轉點 }; break; //.... } }
該方法用於恢復方塊的初始設置;
OLSFK.Next = { //key旋轉點 //長塊 LongBlock 1: { 1:{x:0,y:1}, 2:{x:1,y:1}, 3:{x:2,y:1}, 4:{x:3,y:1} }, //... }
爲不了不與遊戲方塊的設置衝突,獨立出來下次隨機方塊的對象配置;
OLSFK.Items = { //key旋轉點 //長塊 LongBlock 1: { 1:{x:4,y:0}, 2:{x:5,y:0}, 3:{x:6,y:0}, 4:{x:7,y:0}, 5:{x:5,y:0} }, //方塊Box 2: { 1:{x:4,y:0}, 2:{x:5,y:0}, 3:{x:4,y:1}, 4:{x:5,y:1}, 5:{x:0,y:0} }, //凸塊 TuBlock 3: { 1:{x:4,y:1}, 2:{x:5,y:0}, 3:{x:5,y:1}, 4:{x:6,y:1}, 5:{x:5,y:1} }, //L塊 LBlock 4: { 1:{x:5,y:0}, 2:{x:5,y:1}, 3:{x:5,y:2}, 4:{x:6,y:2}, 5:{x:5,y:2} }, 5: { //反向L塊 FLBlock 1:{x:5,y:2}, 2:{x:6,y:2}, 3:{x:6,y:1}, 4:{x:6,y:0}, 5:{x:6,y:2} }, //Z塊 ZBlock 6: { 1:{x:4,y:0}, 2:{x:5,y:0}, 3:{x:5,y:1}, 4:{x:6,y:1}, 5:{x:5,y:0} }, 7: {//反向Z塊 FZBlock 1:{x:4,y:1}, 2:{x:5,y:1}, 3:{x:5,y:0}, 4:{x:6,y:0}, 5:{x:5,y:1} } }
方塊共分爲:長條塊,方塊,凸塊(T塊),L塊,反L塊,Z塊,反Z塊幾種;
共7種方塊,以1,2,3,4,5,6,7 索引鍵表示,方塊是四個小塊組成,每塊都有各自的座標,1-4表示組成該塊的初始座標位置,5表示旋轉點;
OLSFK.Init = function() { //初始化界面 //... }
俄羅斯方塊的界面初始化方法;將在 window.onload 中調用執行;
var w = OLSFK.Options.width; var h = OLSFK.Options.height; var total = w * h; var x=0,y=0; for (var i=0; i<total; i++) { OLSFK.Options.GampMap[x+'_'+y] = 0; Lib.Tag('SPAN',{id:"box_"+x+"_"+y,name:"cbox",style:{ width:OLSFK.Options.boxWidth, height:OLSFK.Options.boxWidth, border:"2px outset #669", background:"#ddd", float:"left", overflow:"hidden" },innerHTML:" ",className:"cssbox"},back); var end = i + 1; x++; if (end >= w && end % w == 0) { x=0; y++; Lib.Tag('DIV',{style:{ clear:"both" }},back); } }
經過設置的 Options.width, Options.height 列數與行數,以及設置的小方格寬度,初始化了一個寬:Options.width列,高爲 Options.height 的表格界面出來;
Lib.Tag 用於建立標籤對象;
Lib.Tag = function(TAG,json,pnode) { //... }
TAG爲標籤名,好比: div, span 等;
json爲設置標籤樣式 style;
pnode 是該建立所在的父容器;
OLSFK.Init = function() {} 還建立主遊戲區域旁邊的下次隨機方塊預覽區,當前級別,及分數,以及操做「開始」,「暫停」按鈕等;
遊戲初始入口點
window.onload = function() { if (window.isIE) { document.attachEvent("onkeydown",function(e) { if (OLSFK.Options.Start) { var E = OLSFK.KeyCode(); OLSFK.EventFunc(E); } }); document.attachEvent("onkeyup",function(e) { if (!OLSFK.Options.Move && OLSFK.Options.Start) { OLSFK.Options.Move = true; OLSFK.Options.Eventing = false; OLSFK.Options.Timer = setTimeout(function() { OLSFK.play(); }, OLSFK.Options.Levels[OLSFK.Options.curLevel]); } }); } else { document.addEventListener("keydown",function(e) { if (OLSFK.Options.Start) { var E = OLSFK.KeyCode(); OLSFK.EventFunc(E); } },false); document.addEventListener("keyup",function(e) { if (!OLSFK.Options.Move && OLSFK.Options.Start) { OLSFK.Options.Move = true; OLSFK.Options.Eventing = false; OLSFK.Options.Timer = setTimeout(function() { OLSFK.play(); }, OLSFK.Options.Levels[OLSFK.Options.curLevel]); } },false); } OLSFK.Init(); }
主要是監聽鍵盤事件,根據 鍵盤事件 返回的按鈕編碼與 OLSFK.Options.direct 設置方向鍵匹配來操做方塊的移動,旋轉等;
keydown 用於操做下落方塊的移動方向,旋轉等;並從新繪製方塊位置;
keyup 後繼續按本級速度向下落;
OLSFK.Options.Levels[OLSFK.Options.curLevel]
表示當前級別對應的速度,即定時器間隔執行時間(毫秒);
OLSFK.EventFunc = function(code) { switch (code) { case OLSFK.Options.direct.Left: //LEFT if (!OLSFK.Options.Deling) { clearTimeout(OLSFK.Options.Timer); OLSFK.Options.Eventing = true; OLSFK.Options.Move = false; OLSFK.Left(); } break; //... } }
該方法是 監聽 keydown 事件執行的動做;code 爲按鍵 編碼;
當判斷未在消行動做時,清除定時器,OLSFK.Options.Eventing 設置爲事件中 true,OLSFK.Options.Move 爲 false 表示中止移動;
進入 向左移動方法 OLSFK.Left();
OLSFK.Left = function() { var block = OLSFK.Items[OLSFK.Options.curBlock]; if (block) { var flag = true; for (var i=1; i<=4; i++) { var x = block[i].x; var y = block[i].y; if (x-1 < 0) { flag = false; break; } if (OLSFK.Options.GampMap[(x-1)+'_'+y] == 1 && !OLSFK.isMe(x-1,y)) { flag = false; break; } } if (flag) { for (var i=1; i<=4; i++) //清除圖形 { var itm = block[i]; var box = Lib.Getid('box_'+itm.x+'_'+itm.y); box.style.background = '#ddd'; OLSFK.Options.GampMap[itm.x+'_'+itm.y] = 0; } for (var i=1; i<=5; i++) { var x = block[i].x; var y = block[i].y; OLSFK.Items[OLSFK.Options.curBlock][i] = {x:(x-1),y:y}; } OLSFK.draw(); } } }
var block = OLSFK.Items[OLSFK.Options.curBlock]; 表示獲取當前移動方塊;
if (OLSFK.Options.GampMap[(x-1)+'_'+y] == 1 && !OLSFK.isMe(x-1,y)) { flag = false; break; }
判斷該方塊四個小方塊左邊是否有被佔用的方塊,也即: OLSFK.Options.GampMap[(x-1)+'_'+y] 爲 1; 而且該位置塊不屬於方塊本身的;
當左邊方向無佔用格時,清除當前方塊四個小方塊位置,從新繪製方塊新座標位置;並重置 相應的 OLSFK.Options.GameMap [x+y] 相應格的值;
當按鈕 keyup 時,繼承正常向下落;
OLSFK.isMe 代碼:
OLSFK.isMe = function(x,y) { var block = OLSFK.Items[OLSFK.Options.curBlock]; if (block) { for (var i=1; i<=4; i++) { if (block[i].x == x && block[i].y == y) { return true; } } } return false; }
即指定的 x,y (參數值) 是否還在當前方塊四個座標內;
OLSFK.Right () 與 Left() 同樣;
旋轉方塊代碼;
OLSFK.Rotate = function() { var block = OLSFK.Items[OLSFK.Options.curBlock]; if (block) { var flag = true; var R = block[5]; for (var i=1; i<=4; i++) { var x = block[i].x; var y = block[i].y; if (R.x == x && R.y == y) { } else { var nson = new Object(); nson.x = R.x + R.y - y; nson.y = R.y - R.x + x; if ( nson.x < 0 || nson.y < 0 || nson.x >= OLSFK.Options.width || nson.y >= OLSFK.Options.height ) { flag = false; break; } if (OLSFK.Options.GampMap[nson.x+'_'+nson.y] == 1 && !OLSFK.isMe(nson.x,nson.y)) { flag = false; break; } } } if (flag) { for (var i=1; i<=4; i++) //清除圖形 { var itm = block[i]; var box = Lib.Getid('box_'+itm.x+'_'+itm.y); box.style.background = '#ddd'; OLSFK.Options.GampMap[itm.x+'_'+itm.y] = 0; } var Pnt = 1; for (var i=1; i<=4; i++) { var x = block[i].x; var y = block[i].y; if (R.x == x && R.y == y) { Pnt = i; } else { var nson = new Object(); nson.x = R.x + R.y - y; nson.y = R.y - R.x + x; OLSFK.Items[OLSFK.Options.curBlock][i] = {x:nson.x,y:nson.y}; } } OLSFK.Items[OLSFK.Options.curBlock][5] = OLSFK.Items[OLSFK.Options.curBlock][Pnt]; OLSFK.draw(); } } }
var R = block[5]; 就是獲取旋轉點;
就開始對方塊四個小塊以旋轉點爲中心,逆時針旋轉(並不全是 90 度);噹噹前塊不爲旋轉點時,旋轉公式;
var nson = new Object(); nson.x = R.x + R.y - y; nson.y = R.y - R.x + x;
這個公式要這樣看;
ResultX = RotateX + (RotateY - CurrentY); ResultY = RotateY - (RotateX - CurrentX); //Y的偏移量,就是X的增長值; //反之同
當旋轉四周都無佔用物時;清除當前圖形,重繪旋轉後的圖形位置;
重置 OLSFK.Options.GampMap[itm.x+'_'+itm.y] 各個方塊的佔用值;
OLSFK.Random = function() { if (OLSFK.Options.nextBlock != 0) { OLSFK.Options.curBlock = OLSFK.Options.nextBlock; var block = OLSFK.Next[OLSFK.Options.nextBlock]; if (block) { for (var i=1; i<=4; i++) { var itm = block[i]; var box = Lib.Getid('cur_'+itm.x+'_'+itm.y); box.style.background = '#ddd'; //OLSFK.Options.GampMap[itm.x+'_'+itm.y] = 0; } } } else { OLSFK.Options.curBlock = Math.floor(Math.random() * 7 + 1); } OLSFK.Options.nextBlock = Math.floor(Math.random() * 7 + 1); OLSFK.drawNext(); }
隨機生成下次預下落的方塊;並顯示到右上角的預覽表格裏;
OLSFK.play = function(speed) { var block = OLSFK.Items[OLSFK.Options.curBlock]; if (block && OLSFK.Options.Move) { var flag = true; for (var i=1; i<=4; i++) { var x = block[i].x; var y = block[i].y; if (y+1 >= OLSFK.Options.height) { flag = false; break; } if (OLSFK.Options.GampMap[x+'_'+(y+1)] == 1 && !OLSFK.isMe(x,y+1)) { flag = false; break; } } if (flag) { for (var i=1; i<=4; i++) //清除圖形 { var itm = block[i]; var box = Lib.Getid('box_'+itm.x+'_'+itm.y); box.style.background = '#ddd'; OLSFK.Options.GampMap[itm.x+'_'+itm.y] = 0; } for (var i=1; i<=5; i++) { var x = block[i].x; var y = block[i].y; OLSFK.Items[OLSFK.Options.curBlock][i] = {x:x,y:(y+1)}; } OLSFK.draw(); var S = OLSFK.Options.Levels[OLSFK.Options.curLevel]; if (speed) { S = 50; } OLSFK.Options.Timer = setTimeout(function() { OLSFK.play(); }, S); } else { OLSFK.ReItems(OLSFK.Options.curBlock); OLSFK.newRun(); } } }
OLSFK.play 正常下落的方法,也得判斷下落一格是否有被佔用的格,若是沒有,清除當前方塊,繪製方塊新位置;
當方塊不能再下落時(flag = false),重置當前方塊座標配置; OLSFK.ReItems(OLSFK.Options.curBlock);
進入 OLSFK.newRun(); 新下落方塊下落過程準備;
OLSFK.newRun = function() { clearTimeout(OLSFK.Options.Timer); OLSFK.DelFunc(); if (OLSFK.Options.deline >= 10) { OLSFK.Options.deline = 0; OLSFK.Options.curLevel ++; OLSFK.Element.CurLevel.setHTML("級:"+OLSFK.Options.curLevel); } OLSFK.Element.Score.setHTML("分:"+OLSFK.Options.Score); if (OLSFK.Options.curLevel <= OLSFK.Options.lineNum) { OLSFK.Random(); //判斷是否結束 OLSFK.ChkEnd(); } else { OLSFK.Options.Move = false; OLSFK.Options.Start = false; OLSFK.Options.Eventing = false; OLSFK.Options.Deling = false; Lib.Getid('spn').innerHTML = 'Game Is Over! You Win the Game!'; Lib.Getid('dobtn').innerHTML = ' 開始 '; } }
當下落結束時,清除定時器,暫停新方塊下落,檢測是否有可消除的行;減了多少行;
每減去一行 加分 OLSFK.Options.Score += OLSFK.Options.ScoreNum;
這個方法在 :
OLSFK.DelFunc = function() { OLSFK.Options.Deling = true; OLSFK.Options.Move = false; OLSFK.Options.Eventing = false; var Fn = 0; for (var i=OLSFK.Options.height-1; i>=0; i--) { Fn = 0; for (var j=0; j<OLSFK.Options.width; j++) { if (OLSFK.Options.GampMap[j+'_'+i] == 1) { Fn++; } } if (Fn == OLSFK.Options.width) { OLSFK.Options.deline ++; OLSFK.Options.Score += OLSFK.Options.ScoreNum; OLSFK.DelLine(i); i++; } } OLSFK.Options.Deling = false; OLSFK.Options.Move = true; OLSFK.Options.Eventing = true; }
中執行;
減完一行,就重置該行以上全部行往降低一行;並重置 :
OLSFK.Options.GampMap[x+'_'+y] = OLSFK.Options.GampMap[x+'_'+(y-1)];
該減行爲上行的數據;
if (Fn == OLSFK.Options.width) { OLSFK.Options.deline ++; OLSFK.Options.Score += OLSFK.Options.ScoreNum; OLSFK.DelLine(i); i++; }
該判斷表示該行上全部格都被佔用到;
回到 newRun 上,當判斷消行超過幾行時,即加級;
if (OLSFK.Options.curLevel <= OLSFK.Options.lineNum) { OLSFK.Random(); //判斷是否結束 OLSFK.ChkEnd(); }
若是級數小於配置的總級數,則進入 OLSFK.random();
設置當前下落方塊,並隨機生成下次下落方塊並預覽右上角表格上;
OLSFK.ChkEnd = function() { var block = OLSFK.Items[OLSFK.Options.curBlock]; if (block && OLSFK.Options.Move) { var flag = true; for (var i=1; i<=4; i++) { var x = block[i].x; var y = block[i].y; if (OLSFK.Options.GampMap[x+'_'+y] == 1) { flag = false; break; } } } if (flag ) { OLSFK.draw(); //定時往下掉 OLSFK.Options.Timer = setTimeout(function() { OLSFK.play(); }, OLSFK.Options.Levels[OLSFK.Options.curLevel]); } else { OLSFK.Options.Move = false; OLSFK.Options.Start = false; OLSFK.Options.Eventing = false; OLSFK.Options.Deling = false; Lib.Getid('spn').innerHTML = 'Game Is Over'; Lib.Getid('dobtn').innerHTML = ' 開始 '; } }
噹噹前下落的方塊進入表格上有被佔用的格子,即被卡住,遊戲結束;
反之 則 setTimeout 開始新方塊的下落動做;
其餘方法說明
OLSFK.Event = function() { if (window.isIE) return window.event; func = OLSFK.Event.caller; while(func!=null) { var arg0=func.arguments[0]; if(arg0) { return arg0; } func=func.caller; } return null; } OLSFK.KeyCode = function() { return OLSFK.Event().keyCode || OLSFK.Event().which; }
OLSFK.Event = function();
這是一種獲取當前事件的方法,能夠比較兼容獲取當前的事件;
俄羅斯方塊 JavaScript 代碼