posts - 48,comments - 156,trackbacks - 0

          特別提示:
          本文中的運(yùn)行效果需要 Chrome 瀏覽器或者 Firefox 瀏覽器。

           

          一、從數(shù)據(jù)出發(fā)還是從界面出發(fā)

          要寫一個(gè)俄羅斯方塊小游戲,我們先來一塊考慮一下下面幾個(gè)問題:

          1、用什么表示方塊

          2、怎么設(shè)置或者改變方塊的顏色

          3、怎么移動(dòng)方塊

          4、怎么消除方塊

          請(qǐng)考慮一分鐘后再繼續(xù)向下看。。。。。。

           

          如果你對(duì)上面幾個(gè)問題思考,每一個(gè)答案都和界面、控件、平臺(tái)有關(guān)的話,就是說假如你是用 .Net 的,你的每一個(gè)答案都是圍繞著如何利用控件、如何使用窗體、在控件的哪個(gè)事件里面改變哪個(gè)屬性等等,那么說明你被微軟的 RAD 開發(fā)環(huán)境毒害的不淺,我建議你立刻扔掉 Visual Studio,改用其他輕量級(jí)的編程語言和開發(fā)平臺(tái),這樣你可以更多的關(guān)注問題的本身,而不是控件。

          記住:程序 = 數(shù)據(jù)結(jié)構(gòu) + 算法

          界面只是數(shù)據(jù)的表象,而數(shù)據(jù)才是問題的本質(zhì)。

          下面,我們將一步一步建立一個(gè)俄羅斯方塊小游戲的數(shù)據(jù)模型,當(dāng)整個(gè)模型建立完畢后,我們會(huì)發(fā)現(xiàn),雖然沒有界面,仍然不妨礙這是一個(gè)功能完整的俄羅斯方塊游戲,因?yàn)榘l(fā)生的每一件事情都很清楚,我們只是沒把它畫而已。當(dāng)然,后面我們會(huì)給出一個(gè)操作簡(jiǎn)易的界面,等到下一篇,會(huì)專門探討界面的問題。

           

          二、“形狀”的數(shù)據(jù)模型

          俄羅斯方塊是一個(gè)經(jīng)久不衰的小游戲,最常見的版本中一般有七個(gè)形狀,分別是:

          直線型、S型、Z型、L型、反L型、T型、方形等,如下圖:

          那么我們?cè)诔绦蛑腥绾伪硎具@七個(gè)形狀呢?我們發(fā)現(xiàn)每一形狀都是四個(gè)小方塊組成的,我們完全可以用四個(gè)點(diǎn)表示。

          但是問題又來了,四個(gè)點(diǎn)的坐標(biāo)分別是什么呢?我查到的方法是:每個(gè)形狀都有一個(gè)自己的坐標(biāo)系,比如S型,可以入下圖表示:

          這樣,S型的數(shù)據(jù)模型可以表示為四個(gè)點(diǎn)組成的數(shù)組:[ [ 0, -1 ],  [ 0, 0 ],   [ -1, 0 ],  [ -1, 1 ] ] 。

          我們可以用同樣的方法建立其他形狀的數(shù)組模型,然后再將這七個(gè)形狀的數(shù)組模型合起來組成一個(gè)大的數(shù)組。

          另外,每個(gè)形狀可以是單色,也可以有自己的顏色。增加顏色會(huì)增加編程的復(fù)雜度,但是也增加不了多少,所以我們的模型中也會(huì)考慮顏色。

          最后,我們最好給每個(gè)形狀一個(gè)編號(hào),這樣方便在形狀數(shù)組和顏色數(shù)組中應(yīng)用他們。

          完成上面的分析后,我們就可以給出形狀數(shù)據(jù)模型的代碼了:

          形狀模型的代碼
          //各種形狀的編號(hào),0代表沒有形狀
          NoShape=0;
          ZShape
          =1;
          SShape
          =2;
          LineShape
          =3;
          TShape
          =4;
          SquareShape
          =5;
          LShape
          =6;
          MirroredLShape
          =7

          //各種形狀的顏色
          Colors=["black","fuchsia","#cff","red","orange","aqua","green","yellow"];

          //各種形狀的數(shù)據(jù)描述
          Shapes=[
              [ [ 
          00 ],   [ 00 ],   [ 00 ],   [ 00 ] ],
              [ [ 
          0-1 ],  [ 00 ],   [ -10 ],  [ -11 ] ],
              [ [ 
          0-1 ],  [ 00 ],   [ 10 ],   [ 11 ] ],
              [ [ 
          0-1 ],  [ 00 ],   [ 01 ],   [ 02 ] ],
              [ [ 
          -10 ],  [ 00 ],   [ 10 ],   [ 01 ] ],
              [ [ 
          00 ],   [ 10 ],   [ 01 ],   [ 11 ] ],
              [ [ 
          -1-1 ], [ 0-1 ],  [ 00 ],   [ 01 ] ],
              [ [ 
          1-1 ],  [ 0-1 ],  [ 00 ],   [ 01 ] ]
          ];

           

          三、定位和旋轉(zhuǎn)形狀

           

          1、定位

          我們上面說到每個(gè)形狀都是在自己的坐標(biāo)系里面描述的,另外還有一個(gè)全局坐標(biāo)系,用來給形狀定位,這樣我們就需要一個(gè)方法將形狀的四個(gè)點(diǎn)從自身坐標(biāo)系轉(zhuǎn)換到全局坐標(biāo)系,從而給形狀定位。

          假如S型在自身坐標(biāo)系中四個(gè)點(diǎn)的坐標(biāo)為:[ [ 0, -1 ],  [ 0, 0 ],   [ -1, 0 ],  [ -1, 1 ] ]

          它當(dāng)前在全局坐標(biāo)系位置為:[12,8]

          則,四個(gè)點(diǎn)轉(zhuǎn)換為全局坐標(biāo)系的坐標(biāo)為:[ [ 0+12, -1+8 ],  [ 0+12, 0+8 ],   [ -1+12, 0+8 ],  [ -1+12, 1+8 ] ]

          這樣,我們就完成了 S型 的全局坐標(biāo)轉(zhuǎn)換。

          這里需要注意一個(gè)問題,形狀自身坐標(biāo)系是用 (x,y) 描述的,而全局坐標(biāo)系為了邏輯上更直觀,是用 (row,col) 描述的,所以我們?cè)趯?shí)際編程中并不是向上面那樣轉(zhuǎn)換的,而是:

          [ [ -1+12, 0+8 ],  [ 0+12, 0+8 ],   [ 0+12, -1+8 ],  [ 1+12, -1+8 ] ]

          即:先將 x 變?yōu)?col ,y 變?yōu)?row ,再轉(zhuǎn)換為全局坐標(biāo)系。

           

          2、旋轉(zhuǎn)

          旋轉(zhuǎn)是在形狀的自身坐標(biāo)系中,并圍繞形狀的原點(diǎn)完成的,公式很簡(jiǎn)單,每個(gè)點(diǎn)旋轉(zhuǎn)后的坐標(biāo)與旋轉(zhuǎn)前坐標(biāo)的關(guān)系如下(向右旋轉(zhuǎn)):

          x' = y

          y' = -x 

          注意:方塊形狀不發(fā)生旋轉(zhuǎn)。

          有了上面的分析,我們就可以給出兩個(gè)全局方法,他們用來對(duì)形狀進(jìn)行全局定位和旋轉(zhuǎn):

          全局定位和旋轉(zhuǎn)的代碼
          //將形狀自身的坐標(biāo)系轉(zhuǎn)換為  Map 的坐標(biāo)系,row col 為當(dāng)前形狀原點(diǎn)在 Map 中的位置
          function translate(data,row,col){
              var copy
          =[];
              
          for(var i=0;i<4;i++){
                  var temp
          ={};
                  temp.row
          =data[i][1]+row;
                  temp.col
          =data[i][0]+col;
                  copy.push(temp);
              }
              
          return copy;
          }

          //向右旋轉(zhuǎn)一個(gè)形狀:x'=y, y'=-x
          function rotate(data){
              var copy
          =[[],[],[],[]];
              
          for(var i=0;i<4;i++){
                  copy[i][
          0]=data[i][1];
                  copy[i][
          1]=-data[i][0];
              }
              
          return copy;
          }

           

          四、移動(dòng)空間

          前面我們說過,形狀是由四個(gè)點(diǎn)組成的,而形狀的移動(dòng)空間也是由 m * n 個(gè)點(diǎn)組成的一個(gè)二維數(shù)組。

          這里為了更直觀的描述,我將 n 個(gè)點(diǎn)組成一條線 Line,再將 m 條 Line 組成形狀的移動(dòng)空間,我把它叫做 Map 。

          我們有了這 m * n 個(gè)點(diǎn)有什么用呢?用處很簡(jiǎn)單,就是保存形狀的編號(hào),如果一個(gè)點(diǎn)沒有被形狀占用,則編號(hào)為 NoShape。這就是前面給出形狀編號(hào)的用處,同時(shí)也是為什么要有一個(gè) NoShape 編號(hào)的原因。

          Map 應(yīng)該具有什么功能呢?下面我列舉了一些:

          1、構(gòu)造函數(shù):這不用說了,n 個(gè)點(diǎn)組成一行 Line, m 行 Line 組成Map,每個(gè)點(diǎn)初始化成 NoShape

          2、newLine:生成新的一行。為什么需要這個(gè)方法呢,因?yàn)槌藰?gòu)造函數(shù)中,游戲運(yùn)行過程中我們也需要用到它,當(dāng)一行或者幾行被消除以后,我們需要在頂部假如一行或者幾行新的Line

          3、isFullLine(row):這個(gè)方法用來判斷第 row 行是否滿了,每次一個(gè)形狀落地后,就需要對(duì)每一行進(jìn)行這個(gè)判斷,滿了當(dāng)然是消除了。

          4、isCollide(data): data 是一個(gè)定位后的形狀數(shù)據(jù),這樣我們就可以檢查這些數(shù)據(jù)是否超出移動(dòng)空間的上下左右邊界,另外還檢查數(shù)據(jù)的四個(gè)點(diǎn)是否已經(jīng)被占用,這就是碰撞檢測(cè)。

          5、appendShape(shape_id,data):當(dāng)一個(gè)形狀落地以后,我們就應(yīng)該將運(yùn)行空間中某些點(diǎn)的值改變?yōu)檫@個(gè)形狀的編號(hào),我把這稱為占用。

          6、消除操作:這個(gè)功能沒有單獨(dú)列為一個(gè)方法,我把它放在 appendShape 方法中了。消除操作也很簡(jiǎn)單,發(fā)現(xiàn)某一行 isFullLine 了以后,在 lines 數(shù)組中移除這一行,并在 lines 數(shù)組的頂部加入一個(gè)空行即可。

          有了上面的分析,我們就可以給出移動(dòng)空間的代碼了:

          移動(dòng)空間的代碼
          /*
           * 說明:由 m 行 Line 組成的格子陣
           
          */
          function  Map(w,h){
              
          //游戲區(qū)域的長(zhǎng)度和寬度
              this.width=w;
              
          this.height=h;
              
          //生成 height 個(gè) line 對(duì)象,每個(gè) line 寬度為 width
              this.lines=[];
              
          for(var row=0;row<h;row++)
                  
          this.lines[row]=this.newLine();
          }

          //說明:間由 n 個(gè)格子組成的一行
          Map.prototype.newLine=function(){
              var shapes
          =[];
              
          for(var col=0;col<this.width;col++)
                  shapes[col]
          =NoShape;
              
          return shapes;
          }

          //判斷一行是否全部被占用
          //如果有一個(gè)格子為 NoShape 則返回 false
          Map.prototype.isFullLine=function(row){
              var line
          =this.lines[row];
              
          for(var col=0;col<this.width;col++)
                  
          if(line[col]==NoShape)
                      
          return false
              
          return true;
          }
          /*
           * 預(yù)先移動(dòng)或者旋轉(zhuǎn)形狀,然后分析形狀中的四個(gè)點(diǎn)是否有碰撞情況:
           *      1:col<0 || col>this.width 超出左右邊界
           *      2:row==this.height ,說明形狀已經(jīng)到最底部
           *      3:任意一點(diǎn)的 shape_id 不為 NoShape ,則發(fā)生碰撞
           *  如果發(fā)生碰撞則放棄移動(dòng)或者旋轉(zhuǎn)
           
          */
          Map.prototype.isCollide
          =function(data){
              
          for(var i=0;i<4;i++){
                  var row
          =data[i].row;
                  var col
          =data[i].col;
                  
          if(col<0 || col==this.width) return true;
                  
          if(row==this.height) return true;
                  
          if(row<0continue;
                  
          else
                      
          if(this.lines[row][col]!=NoShape)
                          
          return true;
              }
              
          return false;
          }

          //形狀在向下移動(dòng)過程中發(fā)生碰撞,則將形狀加入到 Map 中
          Map.prototype.appendShape=function(shape_id,data){
              
          //對(duì)于形狀的四個(gè)點(diǎn):
              for(var i=0;i<4;i++){
                  var row
          =data[i].row;
                  var col
          =data[i].col;
                  
          //找到所在的格子,將格子的顏色改為形狀的顏色
                  this.lines[row][col]=shape_id;
              }
              
          //========================================
              
          //形狀被加入到 Map 中后,要進(jìn)行逐行檢測(cè),發(fā)現(xiàn)滿行則消除
              for(var row=0;row<this.height;row++){
                  
          if(this.isFullLine(row)){
                      
          //將滿的那一行替換成新的空,這一步主要是為了顯示效果,可以不要!
                      
          //this.lines[row]=null;
                      
          //重繪 Map 消除效果
                      
          //onClearLine(row);
                      
          //將滿行刪除
                      this.lines.splice(row,1);
                      
          //第一行添加新的一行
                      this.lines.unshift(this.newLine());
                      
          //重繪 Map 整行下落效果
                      onDraw(this.lines);
                  }
              }
          }

           

           

          五、游戲模型

          我們有了游戲的數(shù)據(jù)模型,我們就可以讀寫他們了。所謂讀好理解,所謂寫就是改變他們,改變的方法當(dāng)然是用戶的操作了。

          下面給出 GameModel 類,他維護(hù)三個(gè)主要的數(shù)據(jù):

          1、一個(gè)形狀的編號(hào),就是用戶可以操作移動(dòng)的那個(gè)形狀

          2、形狀的全局位置,用 row col 表示

          3、一個(gè) Map,用它完成碰撞檢測(cè),添加等操作

          另外,還抽象出幾個(gè)用戶的操作動(dòng)作:

          1、left:左移。將形狀的全局坐標(biāo) col  減少 1 。請(qǐng)思考一下,這樣就可以了嗎?當(dāng)然不行,我們還需要進(jìn)行碰撞檢測(cè),如果已經(jīng)在最左邊,則放棄處理。

          2、right:右移。同上。

          3、rotate:旋轉(zhuǎn)。同上。

          4、down:下落。同上。下落過程中的碰撞檢測(cè)有所不同,一旦發(fā)生碰撞,我們不能再放棄處理了,而是要將當(dāng)前形狀加入到空間中。

          5、GameOver:下落過程中還需要進(jìn)行一個(gè)檢測(cè)就是游戲是否結(jié)束。如果當(dāng)前形狀在出生地點(diǎn)剛一下落就發(fā)生碰撞,說明已經(jīng)到頂部了,則游戲結(jié)束。

          有了上面的分析,我們就可以給出 GameModel 的代碼:

          GameModel 代碼
          /*
           * 說明:GameModel 類
           
          */
          function GameModel(w,h){
              
          this.map=new Map(w,h);
              
          this.born();
          }

          //出生一個(gè)新的形狀
          GameModel.prototype.born=function(){
              
          //隨機(jī)選擇一個(gè)形狀
              this.shape_id=Math.floor(Math.random()*7)+1;
              
          this.data=Shapes[this.shape_id];
              
          //重置形狀的位置為出生地點(diǎn)
              this.row=1;
              
          this.col=Math.floor(this.map.width/2);
              
          //通知繪制移動(dòng)效果,傳回?cái)?shù)據(jù)為形狀的四個(gè)點(diǎn)在 Map 中的位置
              onMove(this.shape_id,this.map,translate(this.data,this.row,this.col));
          }

          //向左移動(dòng)
          GameModel.prototype.left=function(){
              
          this.col--;
              var temp
          =translate(this.data,this.row,this.col);
              
          if(this.map.isCollide(temp))
              
          //發(fā)生碰撞則放棄移動(dòng)
                  this.col++;
              
          else
              
          //通知繪制移動(dòng)效果,傳回?cái)?shù)據(jù)為形狀的四個(gè)點(diǎn)在 Map 中的位置
                  onMove(this.shape_id,this.map,temp);
          }

          //向右移動(dòng)
          GameModel.prototype.right=function(){
              
          this.col++;
              var temp
          =translate(this.data,this.row,this.col);
              
          if(this.map.isCollide(temp))
                  
          this.col--;
              
          else
                  onMove(
          this.shape_id,this.map,temp);
          }

          //旋轉(zhuǎn)
          GameModel.prototype.rotate=function(){
              
          //正方形不旋轉(zhuǎn)
              if(this.shape_id==SquareShape) return;
              
          //獲得旋轉(zhuǎn)后的數(shù)據(jù)
              var copy=rotate(this.data);
              
          //轉(zhuǎn)換坐標(biāo)系
              var temp=translate(copy,this.row,this.col);
              
          //發(fā)生碰撞則放棄旋轉(zhuǎn)
              if(this.map.isCollide(temp))
                  
          return;
              
          //將旋轉(zhuǎn)后的數(shù)據(jù)設(shè)為當(dāng)前數(shù)據(jù)
              this.data=copy;
              
          //通知繪制移動(dòng)效果,傳回?cái)?shù)據(jù)為形狀的四個(gè)點(diǎn)在 Map 中的位置
              onMove(this.shape_id,this.map,translate(this.data,this.row,this.col));
          }

          //下落
          GameModel.prototype.down=function(){
              var old
          =translate(this.data,this.row,this.col);
              
          this.row++;
              var temp
          =translate(this.data,this.row,this.col);
              
          if(this.map.isCollide(temp)){
                  
          //發(fā)生碰撞則放棄下落
                  this.row--;
                  
          //如果在 1 也無法下落,說明游戲結(jié)束
                  if(this.row==1) {
                      
          //通知游戲結(jié)束
                      
          //onGameOver();
                      alert("Game Over")
                      
          return;
                  }
                  
          //無法下落則將當(dāng)前形狀加入到 Map 中
                  this.map.appendShape(this.shape_id,old);
                  
          //出生一個(gè)新的形狀
                  this.born();
              }
              
          else
              
          //通知繪制移動(dòng)效果,傳回?cái)?shù)據(jù)為形狀的四個(gè)點(diǎn)在 Map 中的位置
                  onMove(this.shape_id,this.map,temp);
          }

           

           

          六、一個(gè)簡(jiǎn)單的操作界面

          雖然到現(xiàn)在為止,我們沒有給出一行和界面有關(guān)的代碼,但是整個(gè)游戲在邏輯上已經(jīng)完全可以運(yùn)行起來了,只是我們沒有把他畫出來而已,要想把他畫出來也很簡(jiǎn)單。

          注意上面給出的代碼中很多地方調(diào)用了兩個(gè)全局函數(shù):onDraw 和 onMove ,這兩個(gè)函數(shù)就是用來進(jìn)行繪制的。

          繪制的代碼其實(shí)只占很少的一部分,其中一些繪圖函數(shù)我為了方便對(duì) HTML5 的 2D 函數(shù)進(jìn)行了簡(jiǎn)單的封裝,您完全可以用原生的 HTML5 函數(shù),或者用您自己平臺(tái)的繪圖函數(shù),因?yàn)樗麄儽旧聿皇翘珡?fù)雜。

          另外有一個(gè)全局變量 Spacing ,他表示一個(gè)格子的寬度。

          下面給出操作界面的代碼:

          界面操作代碼
          //每一格的間距,也即一個(gè)小方塊的尺寸
          Spacing=20;

          //在內(nèi)存中繪制一個(gè)小方塊
          function drawRect(color){
              var temp
          =new Surface(Spacing,Spacing,"rgba(255,255,255,0.2)");//背景色
              temp.fillRect(11, Spacing-2, Spacing-2, color);//前景色
              return temp;
          }

          var display
          = Display.attach(document.getElementById("html5_09_1"));
          var model 
          = new GameModel(display.width/Spacing,display.height/Spacing);


          function onDraw(map){
              
          //清屏
              display.clear();
              var lines
          =map.lines;
              
          //依次繪制每一個(gè)非空的格子
              for(var row=0;row<map.height;row++)
                  
          for(var col=0;col<map.width;col++){
                      var shape_id
          =lines[row][col];
                      
          if(shape_id!=NoShape){
                          var rect 
          = drawRect(Colors[shape_id]);
                          var y
          =row * Spacing;
                          var x
          =col * Spacing;
                          display.draw(rect, x, y);
                      }
              }
          }

          function onMove(shape_id,map,data){
              onDraw(map);
              
          //繪制當(dāng)前的形狀
              for(var i=0;i<4;i++){
                  var y
          =data[i].row * Spacing;
                  var x
          =data[i].col * Spacing;
                  var rect 
          = drawRect(Colors[shape_id]);
                  display.draw(rect, x, y);
              }
          }

          function down(){
              model.down();
          }

          function left(){
              model.left();
          }

          function right(){
              model.right();
          }

          function rotate_click(){
              model.rotate();
          }

           

           

          HTML 代碼很簡(jiǎn)單,也給出來吧,就一塊畫布和四個(gè)按鈕,如下:

          HTML 代碼
          <canvas id="html5_09_1" width="260" height="400" style=" background-color: black ">
              你的瀏覽器不支持 Canvas 標(biāo)簽,請(qǐng)使用 Chrome 瀏覽器 或者 FireFox 瀏覽器
          </canvas><p/>
          <input type="button" value="向下" onclick="down()"/>
          <input type="button" value="向左" onclick="left()"/>
          <input type="button" value="向右" onclick="right()"/>
          <input type="button" value="旋轉(zhuǎn)" onclick="rotate_click()"/>

           

           

          七、運(yùn)行效果

          {{{{{{

          你的瀏覽器不支持 Canvas 標(biāo)簽,請(qǐng)使用 Chrome 瀏覽器 或者 FireFox 瀏覽器

          }}}}}}

           

          八、如何改進(jìn)

          到現(xiàn)在為止,程序已經(jīng)基本能運(yùn)行起來了,但是還沒有加入鍵盤操作,另外還有一個(gè)很大的問題就是:程序有時(shí)候換“算死”。為什么會(huì)出現(xiàn)這個(gè)現(xiàn)象呢?

          做個(gè)實(shí)驗(yàn),不管你用什么平臺(tái),你用繪圖函數(shù)繪制先清屏,然后隨機(jī)繪制一條直線。你會(huì)發(fā)現(xiàn),前面999次,并看不到清屏和繪制效果,而且程序都會(huì)失去響應(yīng),等到1000次完成后,你才能看到最后一條直線,程序重新接受響應(yīng)。這就是“算死”,解決的方法就是把繪制動(dòng)作放在計(jì)時(shí)器或者線程里面,到下一篇,我們會(huì)解決這個(gè)問題。
          //==========================================

          posted on 2010-10-17 23:21 左洸 閱讀(3208) 評(píng)論(5)  編輯  收藏 所屬分類: HTML5

          FeedBack:
          # re: HTML5邊玩邊學(xué)(9):俄羅斯方塊就是這么簡(jiǎn)單 之 數(shù)據(jù)模型篇
          2010-12-13 16:55 | anywing
          樓主這個(gè)教程很不錯(cuò),但是Display這個(gè)類的代碼是不是沒有貼上來呢?  回復(fù)  更多評(píng)論
            
          # re: HTML5邊玩邊學(xué)(9):俄羅斯方塊就是這么簡(jiǎn)單 之 數(shù)據(jù)模型篇
          2010-12-13 17:19 | 左洸
          @anywing

          Display 是我自己定義的一個(gè)類,在另外一個(gè) JS 文件中,你沒有下載他,當(dāng)然運(yùn)行不起來。

          繪制的代碼其實(shí)只占很少的一部分,其中一些繪圖函數(shù)我為了方便對(duì) HTML5 的 2D 函數(shù)進(jìn)行了簡(jiǎn)單的封裝,您完全可以用原生的 HTML5 函數(shù),或者用您自己平臺(tái)的繪圖函數(shù),因?yàn)樗麄儽旧聿皇翘珡?fù)雜。  回復(fù)  更多評(píng)論
            
          # re: HTML5邊玩邊學(xué)(9):俄羅斯方塊就是這么簡(jiǎn)單 之 數(shù)據(jù)模型篇
          2010-12-14 14:37 | anywing
          嗯,已經(jīng)下載下來試過了,可以正常運(yùn)行了,非常感謝,學(xué)習(xí)了。  回復(fù)  更多評(píng)論
            
          # re: HTML5邊玩邊學(xué)(9):俄羅斯方塊就是這么簡(jiǎn)單 之 數(shù)據(jù)模型篇
          2011-04-05 23:51 | clarkh
          代碼還沒有仔細(xì)看完,但從實(shí)際效果來看……那個(gè)旋轉(zhuǎn)的方法肯定是錯(cuò)了,正常俄羅斯方塊的Z在旋轉(zhuǎn)時(shí)只會(huì)橫豎兩個(gè)狀態(tài)切換,而你的那個(gè)Z旋轉(zhuǎn)的時(shí)候位置會(huì)不斷變化……  回復(fù)  更多評(píng)論
            
          # re: HTML5邊玩邊學(xué)(9):俄羅斯方塊就是這么簡(jiǎn)單 之 數(shù)據(jù)模型篇
          2012-10-15 19:54 | 學(xué)習(xí)中
          @anywing
          問下你的DISPLAY 是在哪下的啊
            回復(fù)  更多評(píng)論
            
          主站蜘蛛池模板: 顺平县| 平遥县| 永州市| 志丹县| 富阳市| 唐海县| 武隆县| 阳城县| 铜陵市| 五台县| 勃利县| 南漳县| 肇源县| 宁化县| 赤峰市| 乡城县| 林州市| 娄底市| 松阳县| 木兰县| 木里| 嘉善县| 普陀区| 陇南市| 明光市| 蓬溪县| 青龙| 潜山县| 临沭县| 黄梅县| 陕西省| 修武县| 宜丰县| 广南县| 泾阳县| 重庆市| 兴仁县| 天等县| 商丘市| 界首市| 图木舒克市|