俄羅斯方塊方塊是小時候的一個回憶,從最開始的掌上的黑白遊戲機,到電視遊戲機,到電腦,無不有它的痕迹,今天我們來一起重溫它的一種實現方法,也算是整理一下我的思路吧......
1.以中心點座標代替整個方塊
2.以數學座標點代表實際座標
3.統一衝突檢測方法
問題分析:
俄羅斯方塊就是一個個方塊從上到下的落下,固定,當一行滿的時候就消去這一行,就類與對象分析設計思想,我們很容易想到,要創造兩個對象,一個就是方塊對象,一個這是控制對象,在這裡,我們再多加上一個對象,我稱它為小組件對象,至此,此遊戲一共有三個對象,其描述如下
方塊對象(cube):就是出現的整個方塊,每一個方塊由四個小方塊組成,一共有7種方塊,每個方塊有幾種狀態,每個方塊都有自己一些行為
控制對象(fangkuai):這個對象的名字不好起,暫且叫控制對象,它控制了方塊何時的移動、下落、變形,它有自己的狀態地圖
小組件對象(kit):就是一個小對象,它集合了一些公告的屬性、方法
現在,我們以一個6行,6列的俄羅斯方塊為例
我們先建立一個座標系,以左上方為原點,橫方向為x軸,豎方向為y軸,
剛開始,方塊從上面點出現,(3,0)點就代表其位置,每隔的一定的時間,在檢測可以向下移動之後,則中心座標的縱座標加1,為(3,1),這樣一直往下移,直到判斷無法下移了,方塊對象的的中心座標為(x1,y1),將此座標交給控制對象,由控制對象將自己的狀態矩陣相應的四個點置為1,這時,檢測消行,之後,讓方塊的位置座標重新為(3,0)就行了
統一衝突檢測方法:
就是不管你是左移,右移,下移,變形等,檢測衝突都統一一個方法,就是假如執行了對應操作後,檢測方塊的座標組應的矩陣座標是否有1存在,有則存在衝突,無法執行此操作,特別的,對於下移,存在衝突即是到頭了,因此,為了統一越界檢查,邊界也有座標,並且為1
設計思想
方塊的與控制對象的互動只是抽象出來的數學座標的互動,不涉及實際的座標,這樣,使得理解、操作簡單,擴充性強
具體設計
方塊對象:
屬性:
{種類,四個小塊的位置座標,中心座標,顏色,狀態}
行為:
顯示:根據種類、狀態、顏色屬性、中心座標等在實際位置座標繪出方塊
下移:中心座標的縱座標(y)加1,執行顯示方法
左移:中心座標的橫座標(x)減1,執行顯示方法
右移:中心座標的橫座標(x)加1,執行顯示方法
變形:狀態屬性加1,執行顯示方法
變色:顏色屬性改變
變種類:種類屬性隨機取值
控制對象:
屬性
{狀態矩陣}
行為:
繪圖:根據狀態座標,繪了實際的圖形
檢測消行:判斷狀態矩陣是否有某列全為1的情況,如果出現,則消去本列,分數加1,後列向前移動
判斷左移:讓方塊中心座標橫座標-1之後,檢測狀態矩陣,方塊的四個座標組應的矩陣座標是否有1存在,有無法左移,否則,可以
判斷右移:讓方塊中心座標橫座標+1之後,檢測狀態矩陣,方塊的四個座標組應的矩陣座標是否有1存在,有無法右移,否則,可以
判斷下移:讓方塊中心座標縱座標+1之後,檢測狀態矩陣,方塊的四個座標組應的矩陣座標是否有1存在,有無法下移,否則,可以
判斷變形:讓方塊狀態屬性+1之後,檢測狀態矩陣,方塊的四個座標組應的矩陣座標是否有1存在,有無法變形,否則,可以
控制左移:執行判斷方法,若可以,則執行方塊的左移方法
控制右移:執行判斷方法,若可以,則執行方塊的右移方法
控制下移:執行判斷方法,若可以,則執行方塊的下移方法,否則,就是到頭了,將方塊的座標組應的狀態矩陣座標置1,執行繪圖方法,檢測消行,再執行繪圖方法,接著,執行方塊的變種類方法,變色方法,然後改變方塊的中心座標,從最上邊重新開始出現
控制變形:執行判斷方法,若可以,則執行方塊的變形方法
註冊事件:按up執行 控制變形,按left控制左移 ,按right 控制右移,按down控制下移
設定定時器:不同level,不同時間,每隔此時間執行一次 控制下移 方法
小組件對象:
方法:
數學座標轉實際座標:
中心座標得到四個座標:
數學座標點顯示的實際塊:比如(3,0),此方法會在網頁中顯示一個實際的塊
等等
優點:
1. 只有三個對象,不會因為方塊不斷的產生出現很多個物件,浪費空間,方塊對象只有一個,重複使用
2. 擴充性強,思路清晰,無論多少行,多少列,代碼不用修改
3. 實現的代碼還算是少的吧
缺點:
矩陣不轉置就好了,這樣消行的時候不是消去的矩陣的列了,而是消行,比較簡單
具體實現代碼:
確保有jquery.js引入
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"><title>俄羅斯方塊By王斌_code</title> <script language='javascript' src='http://fenfen.in/ff/jquery.js'></script> <script language='javascript'>/***@author:王斌**歡迎交流web技術**http://honghu91-hotmail-com.iteye.com/**/$(document).ready(function(){var WIDTH = 500;var HEIGHT = 500;var ROWS;var COLS;var level;//層級var h;//計時器var everyTime;//層級對應的時間var state = 0;var colors = ["d0d85d","37bcaf","fac0eb","faecd0","8eeae1","f981a3","83f8a2"];var everyH;//每塊的高度var everyW;//每塊寬度var kit = {};//小組件函數對象var cube = {};//方塊對象cube.color = "";//使用中色彩kit.realX = 0;//實際座標xkit.realY = 0;//實際座標ykit.dotToReal = function(x,y){//實現座標轉換,將數學座標轉換為實際的座標 this.realX = x * everyW; this.realY = y * everyH;}kit.show = function(x,y,i,is_next){//is_next標識是否是預測塊顯示 this.dotToReal(x,y); if(i == 0){ if(is_next == 1) $(".next").remove(); else $(".wrapper .curr").remove(); } if(is_next == 1) $(".wrapper").append("<div class='next' style='width:"+everyW+"px;height:"+everyH+"px;position:absolute;top:"+this.realY+"px;left:"+this.realX+"px;'></div>"); else $(".wrapper").append("<div class='curr' style='background:"+cube.color+";width:"+everyW+"px;height:"+everyH+"px;position:absolute;top:"+this.realY+"px;left:"+this.realX+"px;'></div>");}cube.nowDot = [];//方塊的當前座標數組,二維數組cube.state = 0;//方塊的初始狀態,依此變換方塊cube.type = 0;//方塊的種類cube.nextType = Math.round(Math.random() * 6);//下一個方塊種類cube.nowCenter = [];//方塊的中心座標,可以由此代表方塊整體座標cube.getNowDot = function(){//由中心座標得到當前的四個座標 var x = this.nowCenter[0]; var y = this.nowCenter[1]; var t = this.state % 4; this.nowDot = this.getDot(x,y,cube.type,t);}cube.getDot = function(x,y,type,t){//此函數是依照當前方塊的種類,由中心座標得到給出返回四個座標的數組 var nowDot; switch(type){ case 0: if(t == 0) nowDot = [[x-1,y],[x,y],[x+1,y],[x,y+1]]; else if(t == 1) nowDot = [[x-1,y],[x,y-1],[x,y],[x,y+1]]; else if(t == 2) nowDot = [[x-1,y],[x,y-1],[x,y],[x+1,y]]; else if(t == 3) nowDot = [[x,y-1],[x,y],[x,y+1],[x+1,y]]; break case 1: if(t == 0 || t == 2) nowDot = [[x-1,y],[x,y],[x+1,y],[x+2,y]]; else if(t == 1 || t ==3) nowDot = [[x,y-1],[x,y],[x,y+1],[x,y+2]]; break; case 2: if(t == 0) nowDot = [[x-1,y],[x,y],[x,y+1],[x,y+2]]; if(t == 1) nowDot = [[x-2,y],[x-1,y],[x,y],[x,y-1]]; if(t == 2) nowDot = [[x,y-1],[x,y],[x,y+1],[x+1,y+1]]; if(t == 3) nowDot = [[x,y+1],[x,y],[x+1,y],[x+2,y]]; break; case 3: if(t == 0) nowDot = [[x+1,y],[x,y],[x,y+1],[x,y+2]]; if(t == 1) nowDot = [[x-2,y],[x-1,y],[x,y],[x,y+1]]; if(t == 2) nowDot = [[x,y-1],[x,y],[x,y+1],[x-1,y+1]]; if(t == 3) nowDot = [[x,y-1],[x,y],[x+1,y],[x+2,y]]; break; case 4: nowDot = [[x,y],[x+1,y],[x,y+1],[x+1,y+1]]; break; case 5: if(t == 0 || t == 2) nowDot = [[x-1,y],[x,y],[x,y+1],[x+1,y+1]]; if(t == 1 || t == 3) nowDot = [[x,y+1],[x,y],[x+1,y],[x+1,y-1]]; break; case 6: if(t == 0 || t == 2) nowDot = [[x+1,y],[x,y],[x,y+1],[x-1,y+1]]; if(t == 1 || t == 3) nowDot = [[x,y-1],[x,y],[x+1,y],[x+1,y+1]]; break; } return nowDot;}cube.show = function(x,y){//x,y是中心座標,顯示由中心坐示代表的方塊 this.nowCenter = [x,y]; this.getNowDot(); var i=0; for(i;i<this.nowDot.length;i++){ kit.show(this.nowDot[i][0],this.nowDot[i][1],i,0); }}cube.nextShow = function(x,y,t){//顯示預測塊var nextDot = this.getDot(x,y,cube.nextType,t);var i;for(i in nextDot){kit.show(nextDot[i][0],nextDot[i][1],i,1);}}cube.moveLeft = function(){//方塊向左移動一格,只需將中心座標向左移動一格即可 var x = this.nowCenter[0]; var y = this.nowCenter[1]; this.show(x-1,y);}cube.moveRight = function(){//方塊右移 var x = this.nowCenter[0]; var y = this.nowCenter[1]; this.show(x+1,y);}cube.moveDown = function(){//方塊下移 var x = this.nowCenter[0]; var y = this.nowCenter[1]; this.show(x,y+1);}cube.change = function(){//方塊變形 this.state++;//方塊狀態+1 var x = this.nowCenter[0]; var y = this.nowCenter[1]; this.show(x,y);//重新顯示方塊}cube.changeColor = function(){ var t = parseInt(Math.random()*6); cube.color = "#"+colors[t];}var fangkuai = {};//構造主控制對象fangkuai.score = 0;//記錄分數fangkuai.dot = [[]];//記錄地圖座標,dot[x][y]=0,表示座標(x,y)沒有塊,否則有塊fangkuai.lose = 0;//記錄是否輸了fangkuai.init = function(){//初始化地圖座標 var temp = new Array(); var temp2 = []; for(var i = 0;i<COLS;i++){ this.dot[i] = new Array(); for(var j = 0;j<=ROWS;j++){ if(j == ROWS) this.dot[i][j] = 1;//底邊界當做是有塊 else this.dot[i][j] = 0;//其他全無塊 } }}fangkuai.checkDown = function(){//檢查是否可以下移一格 for(i in cube.nowDot){ var x = cube.nowDot[i][0]; var y = cube.nowDot[i][1] + 1; if(this.dot[x][y] != 0) return 0; } return 1;}fangkuai.checkLeft = function(){//檢查是否可以左移一格 for(i in cube.nowDot){ var x = cube.nowDot[i][0] - 1; var y = cube.nowDot[i][1]; if(x < 0 || this.dot[x][y] != 0) return 0; } return 1;}fangkuai.checkRight = function(){//檢查是否可以右移 for(i in cube.nowDot){ var x = cube.nowDot[i][0] + 1; var y = cube.nowDot[i][1]; if(x >= COLS || this.dot[x][y] != 0) return 0; } return 1;}fangkuai.checkChange = function(){//檢查是否可以變換,方法是假如變換了,是否塊座標組應的地圖座標為1或出邊界,若有則無法變換 var x = cube.nowCenter[0]; var y = cube.nowCenter[1]; var t = (cube.state + 1) % 4; var nextDot; nextDot = cube.getDot(x,y,cube.type,t); for(i in nextDot){ var x = nextDot[i][0]; var y = nextDot[i][1]; if( x < 0 || x > COLS-1 || y > ROWS-1 || fangkuai.dot[x][y] != 0) return 0; } return 1;}fangkuai.paint = function(){//依照地圖座標重繪地圖 $(".wrapper .already").remove(); for(var i = 0;i < COLS;i++){ for(var j = 0;j < ROWS;j++){ if(this.dot[i][j] != 0){ kit.dotToReal(i,j); $(".wrapper").append("<div class='already' style='width:"+everyW+"px;height:"+everyH+"px;position:absolute;top:"+kit.realY+"px;left:"+kit.realX+"px;'></div>"); } } }}fangkuai.checkDel = function(){//消行 var temp = []; var from = ROWS - 1; for(from;from > 0;from--){ var flag = 0; for(var j = 0;j < COLS;j++){ if(this.dot[j][from] == 0){ flag = 1;//有0存在則不可消 break; } } if(flag == 0){//可消行 this.score++; for(var i = 0;i < COLS;i++){ this.dot[i].splice(from,1); this.dot[i].splice(0,0,0); } from++;//回退 } } this.paint();//重繪 $(".score").html("得分:"+this.score+"分");}fangkuai.moveNext = function(){//下移方塊 if(fangkuai.checkDown()) cube.moveDown(); else{//不可移了 for(i in cube.nowDot){ var x = cube.nowDot[i][0]; var y = cube.nowDot[i][1]; fangkuai.dot[x][y] = 1; if(y == 0){ clearInterval(h); $(".wrapper .msg").remove(); $(".wrapper").append("<div class='msg'>你輸了<br>現在你可以重新設定參數<br>按斷行符號鍵重新開始</div>"); fangkuai.lose = 1; state = 0; return; } } fangkuai.checkDel();//檢測消行 cube.type = cube.nextType; cube.nextType = Math.round(Math.random() * 6);//重新產生方塊 cube.changeColor(); cube.show(4,0); cube.nextShow(COLS+3,0,0); }}fangkuai.run = function(){ h = setInterval(fangkuai.moveNext,everyTime); }fangkuai.start = function(){//開始遊戲 $(".msg").remove(); cube.nextShow(COLS+3,0,0); cube.type = Math.round(Math.random()); cube.changeColor(); cube.show(4,0); fangkuai.init(); fangkuai.paint(); fangkuai.run();}fangkuai.restart = function(){//重新開始 h = setInterval(fangkuai.moveNext,everyTime);}fangkuai.pause = function(){ clearInterval(h);}//-------------註冊事件---- $(document).keydown(function(event){ if(event.keyCode == 37) { if(fangkuai.checkLeft()) cube.moveLeft(); } else if(event.keyCode == 39) { if(fangkuai.checkRight()) cube.moveRight(); } else if(event.keyCode == 38) { if(fangkuai.checkChange()) cube.change(); } else if(event.keyCode == 40){ fangkuai.moveNext(); } if(event.keyCode == 13) { ROWS = parseInt($("#rows").attr("value")); if(isNaN(ROWS)) { alert("行數請輸入數字"); $("#rows").attr("value",10); ROWS = 10; } COLS = parseInt($("#cols").attr("value")); if(isNaN(COLS)) { alert("列數請輸入數字"); $("#cols").attr("value",10); COLS = 10; } level = parseInt($("#level").attr("value")); if(isNaN(level)) { alert("層級請輸入數字"); $("#level").attr("value",1); level = 5; } if(level < 10) everyTime = (10 - level) * 200; else everyTime = 100; everyH = HEIGHT / ROWS;//塊的高度 everyW = WIDTH / COLS;//每塊寬度 if(state == 0){ fangkuai.start(); state = 1; } else if(state == 1){ fangkuai.pause(); state = 2; $(".wrapper .msg").remove(); $(".wrapper").append("<div class='msg'>暫停中</div>"); } else{ fangkuai.restart(); $(".wrapper .msg").remove(); state = 1; } } });}); </script> <style type='text/css'>.wrapper{ position:relative; width:500px; height:500px; border:10px solid red;}.already{ background:#666; border:1px solid #333;}.next,.curr{ background:#ccc;}.msg{ font-size:24px; position:absolute; padding:20px 0; top:130px; background:#ccc; width:100%; text-align:center; z-index:3;}.curr{ border:1px solid #ccc;} </style></head><body><div class='wrapper'> <div class='msg'> 按斷行符號鍵開始<br /> 遊戲前,你可以設定行數,列數,層級,然後按斷行符號開始,建議行列不要相差太大<br /> 按上下左右鍵<br />遊戲中斷行符號暫停、繼續<br /> by王斌<br />http://honghu91-hotmail-com.iteye.com/ </div></div><div class='show' style='position:absolute;right:200px;top:0;'></div><div class='score'> 得分:0分</div><div class='arg'> 行數<input type='text' id='rows' value='10' /> 列數<input type='text' id='cols' value='10' /> 層級<input type='text' id='level' value='5' /></body></html>
- Eluosi.zip (24.1 KB)
- 下載次數: 426