遊戲技術相關研究

2017年3月3日 星期五

【Arduino】自己的掌機自己作(3)-俄羅斯方塊遊戲

下午11:59 Posted by Channel Chung No comments
    俄羅斯方塊是我小時候最愛的遊戲之一,所以這次我們就來探討一下這款超經典遊戲的製作原理與在Game8x8掌機上實作這款遊戲,首先我們一樣還是先查詢一下維基百科對俄羅斯方塊(點擊可超連接至維基百科)的解說,重點玩法在以下這段:
俄羅斯方塊》是由以上這幾種四格骨牌構成,全部都由四個方塊組成。開始時,一個隨機的方塊會從區域上方開始緩慢繼續落下。落下期間,玩家可以以90度為單位旋轉方塊,以格子為單位左右移動方塊,或讓方塊加速落下。當方塊下落到區域最下方或著落到其他方塊上無法再向下移動時,就會固定在該處,然後一個新的隨機的方塊會出現在區域上方開始落下。當區域中某一橫行()的格子全部由方塊填滿時,則該列會被消除並成為玩家的得分。同時消除的行數越多,得分指數級上升。玩家在遊戲中的目的就是儘量得分。當固定的方塊堆到區域最頂端而無法消除層數時,遊戲就會結束。部分遊戲提供單格方塊,那些單格方塊能穿透固定的方塊到達最下層空位。其他的改版中則出現更多特別的造型。
(以上內容引用自維基百科)
在了解玩法後我們開始來設計這個遊戲,由維基百科中得知俄羅斯方塊有以下七種方塊組成,長方形方塊我們稍作修改由原本四塊組成的改為三塊,這樣我們就可以跟其他方塊一樣採用3x3陣列就可以存放:

遊戲的基本玩法就是繞著這上面七種方塊的組合進行,以下先貼上這次Game8x8掌機版俄羅斯方塊的所有原始碼,後面會再挑重點對程式碼作個別解說:
/*****************************************************************************
* Game8x8-俄羅斯方塊.
****************************************************************************/
#include "Game8x8Tone.h"
#include "Game8x8JoyStick.h"
#include "Game8x8Time.h"
#include "Game8x8Graphics.h"

// 繪圖函數.
Game8x8Graphics game8x8Graphics = Game8x8Graphics();
// 搖桿函數.
Game8x8JoyStick game8x8JoyStick = Game8x8JoyStick();
// 蜂鳴器函數.
Game8x8Tone  game8x8Tone = Game8x8Tone();

// 繪圖更新時脈.
Game8x8Time TimeGraphicsUpdate = Game8x8Time();
// 掉落方塊時脈.
Game8x8Time TimeMoveDown = Game8x8Time();
// 左右移動方塊時脈.
Game8x8Time TimeMoveRL = Game8x8Time();
// 閃爍方塊.
Game8x8Time TimeBrickFlash = Game8x8Time();


// 設定方塊所有狀態.
byte N1[2][4] = { { 0,3,4,7 },{ 4,5,6,7 } };    // ID:1.
byte N2[2][4] = { { 1,3,4,6 },{ 3,4,7,8 } };    // ID:2.
byte L1[4][4] = { { 3,6,7,8 },{ 0,1,3,6 },{ 3,4,5,8 },{ 1,4,6,7 } }; // ID:3.
byte L2[4][4] = { { 5,6,7,8 },{ 0,3,6,7 },{ 3,4,5,6 },{ 0,1,4,7 } }; // ID:4.
byte T[4][4]  = { { 4,6,7,8 },{ 0,3,4,6 },{ 3,4,5,7 },{ 1,3,4,7 } }; // ID:5.
byte O[1][4]  = { { 3,4,6,7 } };     // ID:6.
byte I[2][3]  = { { 6,7,8 },{ 1,4,7 } };     // ID:7.

// 方塊陣列.
byte brick[3][3] = {
 { 0,0,0 },
 { 0,0,0 },
 { 0,0,0 },
};

// 容器.
//  0:無.
// >0:方塊編號.
//255:閃爍方塊(黑).
//254:閃爍方塊(亮).
byte container[8][8] = {
 { 0,0,0,0,0,0,0,0 },
 { 0,0,0,0,0,0,0,0 },
 { 0,0,0,0,0,0,0,0 },
 { 0,0,0,0,0,0,0,0 },
 { 0,0,0,0,0,0,0,0 },
 { 0,0,0,0,0,0,0,0 },
 { 0,0,0,0,0,0,0,0 },
 { 0,0,0,0,0,0,0,0 },
};

// 方塊在容器的位置.
int containerX =  3;  // ( 0~6)(  為6的時候不能旋轉方塊).
int containerY = -2;  // (-2~5)(-2表示在上邊界外慢慢往下掉).

// 方塊編號(1~7).
byte brickId = 1;
// 方塊狀態(0~3).
int  brickState = 0;

// 清除方塊數量.
byte clearBrickFlag = 0;
// 清除方塊閃爍次數.
byte flashNum = 0;

// 方塊下降速度.
int downSpeed = 1000;
int downSpeedTemp = 0;

// 遊戲狀態.
// 0:準備開始中.
// 1:遊戲中.
byte gameStatus = 0;

// 處理變換方塊方向旗標.
bool JoyUp = false;
bool JoyDown = false;

// 音效開關.
bool soundSwitch = true;

//---------------------------------------------------------------------------
// 初始.
//---------------------------------------------------------------------------
void setup()
{
 Serial.begin(9600);
 // 產生隨機的seed.
 randomSeed(analogRead(0));
 // 重新開始遊戲.
 resetGame();
}

//---------------------------------------------------------------------------
// 主迴圈.
//---------------------------------------------------------------------------
void loop()
{
 int posX = 0;
 int posY = 0;

 // 0:準備開始中.
 if (gameStatus == 0) {
  // 搖桿往下-開始遊戲.
  if (game8x8JoyStick.getJoyY() < 3) {
   // 清除方塊數量.
   clearBrickFlag = 0;
   // 設定遊戲中.
   gameStatus = 1;
  }

 // 1:遊戲中.
 }else if (gameStatus == 1) {
  // 方塊掉落時脈.
  if (TimeMoveDown.update(downSpeed) && clearBrickFlag == 0) {
   // 播放音效.
   if (soundSwitch) {
    // 發出聲音.
    game8x8Tone.playTone(262, 33);
   }
   // 方塊往下掉.
   containerY++;
   // 判斷是否碰到其他方塊.
   if (!ifCopyToContainer()) {
    // 往上移一格.
    containerY--;
    // 遊戲結束.
    if (containerY < 0) {
     // 重新開始遊戲.
     resetGame();
     return;
    }
    // 播放音效.
    if (soundSwitch) {
     // 發出聲音.
     game8x8Tone.playTone(494, 33);
    }
    // 產生新塊.
    brickNew();
   }
   // 已到底.
   else if (containerY >= 5) {
    // 播放音效.
    if (soundSwitch) {
     // 發出聲音.
     game8x8Tone.playTone(494, 33);
    }
    // 產生新塊.
    brickNew();
   }
   // 判斷與設定要清除的方塊.
   clearBrickFlag = ifClearBrick();
  }

  //--------------------------------------------------------------------  
  // 處理閃爍和清除方塊.
  if (clearBrickFlag > 0)
  {
   // 閃爍方塊.
   if (TimeBrickFlash.update(100)) {
    // 播放音效.
    if (soundSwitch) {
     // 發出聲音.
     game8x8Tone.playTone(440, 33);
    }
    // 閃爍次數.
    flashNum++;
    // 閃爍方塊.
    brickFlash();
    // 清除方塊繼續遊戲.
    if (flashNum >= 3) {
     // 清除的方塊.
     clearBrick();
     // 初始變數.
     clearBrickFlag = 0;
     flashNum = 0;
    }
   }
  }
 }
 
 //--------------------------------------------------------------------
 // 繪圖更新時脈-FPS 30.
 if (TimeGraphicsUpdate.update(33)) {
 
  // 清除畫面.
  game8x8Graphics.clearDisplay(0);

  //--------------------------------------------------------------------
  // 搖桿往上操作變換方塊.
  if (game8x8JoyStick.getJoyY() > 9 && !JoyUp && containerX != -1 && containerX != 6 && clearBrickFlag == 0) {
   // 播放音效.
   if (soundSwitch) {
    // 發出聲音.
    game8x8Tone.playTone(294, 33);
   }
   // N1、N2、I(2種狀態).
   if ( brickId == 1 || brickId == 2 || brickId == 7) {
    brickState++;
    if (brickState > 1) { brickState = 0; }

    // 處理判斷是否可以旋轉方塊.
    processTransformToBrickArray();
    if (!ifCopyToContainer()) {
     brickState--;
     if (brickState < 0) { brickState = 1; }
    }
   }
   // L1、L2、T(4種狀態)
   else if (brickId == 3 || brickId == 4 || brickId == 5) {
    brickState++;
    if (brickState > 3) { brickState = 0; }

    // 處理判斷是否可以旋轉方塊.
    processTransformToBrickArray();
    if (!ifCopyToContainer()) {
     brickState--;
     if (brickState < 0) { brickState = 3; }
    }
   }
   // O(1種狀態)
   else if (brickId == 6) {
    brickState = 0;
   }
   JoyUp = true;
  }
  else if (game8x8JoyStick.getJoyY() <= 9) {
   JoyUp = false;
  }

  // 搖桿往下-快速下降方塊.
  if (game8x8JoyStick.getJoyY() < 3 && clearBrickFlag == 0) {
   // 紀錄原本下降速度.
   if (downSpeedTemp == 0) {
    downSpeedTemp = downSpeed;
   }
   // 改變速度.
   downSpeed = 100;
  }
  else if (game8x8JoyStick.getJoyY() >= 3 && clearBrickFlag == 0) {   
   // 恢復原本速度.
   if (downSpeedTemp != 0) {
    downSpeed = downSpeedTemp;
    downSpeedTemp = 0;
   }
  }

  //--------------------------------------------------------------------
  // 處理轉換方塊到方塊陣列.
  processTransformToBrickArray();

  //--------------------------------------------------------------------
  // 操作左右移動方塊.
  if (TimeMoveRL.update(100) && clearBrickFlag == 0) {
   int mx = game8x8JoyStick.getJoyX();
   if (mx != 5) {
    // 右移.
    if (mx < 5) {
     containerX++;
     // 判斷可否右移.
     if (ifCopyToContainer()) {
      if (brick[2][0] == 0 && brick[2][1] == 0 && brick[2][2] == 0) {
       if (containerX>6) { containerX = 6; }
      }
      else {
       if (containerX>5) { containerX = 5; }
      }
     }
     else {
      containerX--;
     }
    }
    // 左移.
    else
    {
     containerX--;
     // 判斷可否左移.
     if (ifCopyToContainer()) {
      if (containerX < 0) { 
       // 處理直立長條(特例).
       if (brick[0][0] == 0 && brick[0][1] == 0 && brick[0][2] == 0) {
        containerX = -1;
       }
       else {
        containerX = 0;
       }
      }
     }
     else {
      containerX++;
     }     
    }
   }
  }

  //--------------------------------------------------------------------  
  // 畫容器.
  for (int iy = 0; iy<8; iy++) {
   for (int ix = 0; ix<8; ix++) {
    if (container[ix][iy] > 0 && container[ix][iy] != 255) {
     game8x8Graphics.setPixcls(ix, iy, 1);
    }
    else {
     game8x8Graphics.setPixcls(ix, iy, 0);
    }    
   }
  }

  // 畫方塊.  
  for (int iy = 0; iy < 3; iy++) {
   for (int ix = 0; ix < 3; ix++) {
    posX = containerX + ix;
    posY = containerY + iy;
    if( posX >= 0 && posY >= 0){
     if (brick[ix][iy] > 0) {
      game8x8Graphics.setPixcls( posX, posY, 1);
     }
     else {
      // 處理超出邊界不要畫.
      if (posX <= 5) {
       // 容器位是空的才畫.
       if(container[posX][posY] == 0){
        game8x8Graphics.setPixcls(posX, posY, 0);
       }       
      }      
     }
    }
   }
  }
  // 更新.
  game8x8Graphics.update();
 }

}

//--------------------------------------------------------------------
// 處理轉換方塊到方塊陣列.
//--------------------------------------------------------------------
void processTransformToBrickArray()
{
 // N1.
 if (brickId == 1) {
  transformToBrickArray((byte *)N1, 4, brickState, brickId);
 }
 // N2.
 else if (brickId == 2) {
  transformToBrickArray((byte *)N2, 4, brickState, brickId);
 }
 // L1.
 else if (brickId == 3) {
  transformToBrickArray((byte *)L1, 4, brickState, brickId);
 }
 // L2.
 else if (brickId == 4) {
  transformToBrickArray((byte *)L2, 4, brickState, brickId);
 }
 // T.
 else if (brickId == 5) {
  transformToBrickArray((byte *)T, 4, brickState, brickId);
 }
 // O.
 else if (brickId == 6) {
  transformToBrickArray((byte *)O, 4, brickState, brickId);
 }
 // I.
 else if (brickId == 7) {
  transformToBrickArray((byte *)I, 3, brickState, brickId);
 }
}

//---------------------------------------------------------------------------
// 轉換方塊到方塊陣列.
// pBrick : 方塊陣列.
// bLengthY: 方塊大小.
// bState : 方塊狀態.
//  bBrickId: 方塊編號.
//---------------------------------------------------------------------------
void transformToBrickArray(byte *pBrick, byte bLengthY, byte bState, byte bBrickId)
{
 byte bx, by;

 // 清除方塊陣列.
 for (int iy = 0; iy < 3; iy++) {
  for (int ix = 0; ix < 3; ix++) {
   brick[ix][iy] = 0;
  }  
 }

 // 轉換方塊到方塊陣列.
 for (int i = 0; i < bLengthY; i++) {
  bx = (*(pBrick + bState * bLengthY + i)) % 3;
  by = (*(pBrick + bState * bLengthY + i)) / 3;
  brick[bx][by] = bBrickId;
 }
}

//---------------------------------------------------------------------------
// 判斷是否可以複製到容器內.
// true:可以.  false:不可以.
//---------------------------------------------------------------------------
bool ifCopyToContainer() {
 int posX = 0;
 int posY = 0;
 for (int iy = 0; iy < 3; iy++) {
  for (int ix = 0; ix < 3; ix++) {
   if (brick[ix][iy] != 0) {
    posX = containerX + ix;
    posY = containerY + iy;
    if (posX >= 0 && posY >= 0) {
     if (container[posX][posY] != 0) {
      return false;
     }
    }
   }
  }
 }
 return true;
}

//---------------------------------------------------------------------------
// 複製方塊到容器內.
//---------------------------------------------------------------------------
void copyToContainer() {
 int posX = 0;
 int posY = 0;
 for (int iy = 0; iy < 3; iy++) {
  for (int ix = 0; ix < 3; ix++) {
   if (brick[ix][iy] != 0) {
    posX = containerX + ix;
    posY = containerY + iy;
    if (posX >= 0 && posY >= 0) {
     container[posX][posY] = brick[ix][iy];
    }
    
   }
  }
 }
}

//---------------------------------------------------------------------------
// 判斷與設定要清除的方塊.
//---------------------------------------------------------------------------
byte ifClearBrick() {
 int pointNum = 0; 
 int lineNum = 0;
 for (int iy = 0; iy < 8; iy++) {
  for (int ix = 0; ix < 8; ix++) {
   if (container[ix][iy] > 0) {
    pointNum++;
   }
   if (pointNum == 8) {
    for (int i = 0; i < 8; i++) {
     lineNum++;
     container[i][iy] = 255;
    }
   }
  }
  pointNum = 0;
 }
 return lineNum;
}

//---------------------------------------------------------------------------
// 清除的方塊.
//---------------------------------------------------------------------------
void clearBrick() {
 byte temp = 0;

 // 一列一列判斷清除方塊.
 for (int ix = 0; ix < 8; ix++) {
  for (int iu = 0; iu < 7; iu++) {
   for (int iy = 0; iy < 8; iy++) {
    if (container[ix][iy] == 255 || container[ix][iy] == 254) {
     if (iy > 0) {
      temp = container[ix][iy - 1];
      container[ix][iy - 1] = container[ix][iy];
      container[ix][iy] = temp;
      iy--;
     }
    }
   }
   container[ix][0] = 0;
  }
 }
}

//---------------------------------------------------------------------------
// 閃爍方塊.
//---------------------------------------------------------------------------
void brickFlash() {
 for (int iy = 0; iy < 8; iy++) {
  for (int ix = 0; ix < 8; ix++) {
   if (container[ix][iy] == 255) {
    container[ix][iy] = 254;
   }
   else if (container[ix][iy] == 254) {
    container[ix][iy] = 255;
   }
  }
 }
}

//---------------------------------------------------------------------------
// 產生新塊.
//---------------------------------------------------------------------------
void brickNew(){
 // 複製方塊到容器內.
 copyToContainer();
 // 初始方塊.
 containerX = 3;
 containerY = -2;
 // 亂數產生方塊.
 brickId = (byte)random(1, 8);
 // 初始方塊狀態.
 brickState = 0;
}

//---------------------------------------------------------------------------
// 重新開始遊戲.
//---------------------------------------------------------------------------
void resetGame()
{
 // 遊戲狀態.
 gameStatus = 0;

 // 下降速度.
 downSpeed = 1000;
 downSpeedTemp = 0;

 // 亂數產生方塊.
 brickId = (byte)random(1, 8);
 // 方塊狀態(0~3).
 brickState = 0;

 // 方塊在容器的位置.
 containerX = 3;
 if (brickId == 1 || brickId == 2) {
  containerY = 0;
 }
 else if (brickId == 7) {
  containerY = -2;
 }
 else {
  containerY = -1;
 }
 
 // 清除方塊數量(暫停).
 clearBrickFlag = 1;

 // 清除容器.
 for (int iy = 0; iy < 8; iy++) {
  for (int ix = 0; ix < 8; ix++) {
   container[ix][iy] = 0;
  }
 } 
}
首先我們需要針對所有的方塊作編碼,請參考以下圖片作方塊編碼:
編碼後的宣告碼如下:
// 設定方塊所有狀態.
byte N1[2][4] = { { 0,3,4,7 },{ 4,5,6,7 } };    // ID:1.
byte N2[2][4] = { { 1,3,4,6 },{ 3,4,7,8 } };    // ID:2.
byte L1[4][4] = { { 3,6,7,8 },{ 0,1,3,6 },{ 3,4,5,8 },{ 1,4,6,7 } }; // ID:3.
byte L2[4][4] = { { 5,6,7,8 },{ 0,3,6,7 },{ 3,4,5,6 },{ 0,1,4,7 } }; // ID:4.
byte T[4][4]  = { { 4,6,7,8 },{ 0,3,4,6 },{ 3,4,5,7 },{ 1,3,4,7 } }; // ID:5.
byte O[1][4]  = { { 3,4,6,7 } };     // ID:6.
byte I[2][3]  = { { 6,7,8 },{ 1,4,7 } };     // ID:7.
再來是宣告方塊陣列,如下:
// 方塊陣列.
byte brick[3][3] = {
 { 0,0,0 },
 { 0,0,0 },
 { 0,0,0 },
};
方塊狀態會透過以下函數將轉換成方塊陣列:
//---------------------------------------------------------------------------
// 轉換方塊到方塊陣列.
// pBrick : 方塊陣列.
// bLengthY: 方塊大小.
// bState : 方塊狀態.
//  bBrickId: 方塊編號.
//---------------------------------------------------------------------------
void transformToBrickArray(byte *pBrick, byte bLengthY, byte bState, byte bBrickId)
{
 byte bx, by;

 // 清除方塊陣列.
 for (int iy = 0; iy < 3; iy++) {
  for (int ix = 0; ix < 3; ix++) {
   brick[ix][iy] = 0;
  }  
 }

 // 轉換方塊到方塊陣列.
 for (int i = 0; i < bLengthY; i++) {
  bx = (*(pBrick + bState * bLengthY + i)) % 3;
  by = (*(pBrick + bState * bLengthY + i)) / 3;
  brick[bx][by] = bBrickId;
 }
}
有了方塊陣列後,我們還需要一個存放方塊陣列的8x8容器,遊戲會在這個容器內進行,容器的宣告碼如下:
// 容器.
//  0:無.
// >0:方塊編號.
//255:閃爍方塊(黑).
//254:閃爍方塊(亮).
byte container[8][8] = {
 { 0,0,0,0,0,0,0,0 },
 { 0,0,0,0,0,0,0,0 },
 { 0,0,0,0,0,0,0,0 },
 { 0,0,0,0,0,0,0,0 },
 { 0,0,0,0,0,0,0,0 },
 { 0,0,0,0,0,0,0,0 },
 { 0,0,0,0,0,0,0,0 },
 { 0,0,0,0,0,0,0,0 },
};
接下來是遊戲主迴圈的部分,在主迴圈內我們主要是處理遊戲GamePlay,首先看到我們以gameStatus變數來判斷狀態,當其為0得時候會持續判斷玩家是否操控搖桿往下,如果是的話就將gameStatus變數設為1並進入遊戲中,程式碼片段如下:
 // 0:準備開始中.
 if (gameStatus == 0) {
  // 搖桿往下-開始遊戲.
  if (game8x8JoyStick.getJoyY() < 3) {
   // 清除方塊數量.
   clearBrickFlag = 0;
   // 設定遊戲中.
   gameStatus = 1;
  }

 // 1:遊戲中.
 }else if (gameStatus == 1) {
           .
           .
           .
        }
進入遊戲後我們首先判斷方塊掉落時脈,方塊會每隔downSpeed變數秒持續往下掉,所以如果想要讓方塊掉落速度變快可以調小這個變數,時脈觸發後我們會播放一短聲音效並將方塊往下移動一個同時判斷有無碰撞到其他方塊和是否到容器底端還有最重要的遊戲是否結束等等的GamePlay判斷,程式片段如下:
  // 方塊掉落時脈.
  if (TimeMoveDown.update(downSpeed) && clearBrickFlag == 0) {
   // 播放音效.
   if (soundSwitch) {
    // 發出聲音.
    game8x8Tone.playTone(262, 33);
   }
   // 方塊往下掉.
   containerY++;
   // 判斷是否碰到其他方塊.
   if (!ifCopyToContainer()) {
    // 往上移一格.
    containerY--;
    // 遊戲結束.
    if (containerY < 0) {
     // 重新開始遊戲.
     resetGame();
     return;
    }
    // 播放音效.
    if (soundSwitch) {
     // 發出聲音.
     game8x8Tone.playTone(494, 33);
    }
    // 產生新塊.
    brickNew();
   }
   // 已到底.
   else if (containerY >= 5) {
    // 播放音效.
    if (soundSwitch) {
     // 發出聲音.
     game8x8Tone.playTone(494, 33);
    }
    // 產生新塊.
    brickNew();
   }
   // 判斷與設定要清除的方塊.
   clearBrickFlag = ifClearBrick();
  }
再來就是判斷方塊是否有連線,如果有的話就處理播放一短音並閃爍以連線的方塊三次後並進行移除的動作,程式片段如下:
  // 處理閃爍和清除方塊.
  if (clearBrickFlag > 0)
  {
   // 閃爍方塊.
   if (TimeBrickFlash.update(100)) {
    // 播放音效.
    if (soundSwitch) {
     // 發出聲音.
     game8x8Tone.playTone(440, 33);
    }
    // 閃爍次數.
    flashNum++;
    // 閃爍方塊.
    brickFlash();
    // 清除方塊繼續遊戲.
    if (flashNum >= 3) {
     // 清除的方塊.
     clearBrick();
     // 初始變數.
     clearBrickFlag = 0;
     flashNum = 0;
    }
   }
  }
接下來要進入更新繪圖部分了,繪圖時脈我們設定為每秒更新30次(1000/30=33.33),程式碼片段如下:
 // 繪圖更新時脈-FPS 30.
 if (TimeGraphicsUpdate.update(33)) {
           .
           .
           .
        }
下面繼續說明繪圖時脈啟動後裡面處理的動作,首先會先處理清除畫面,接下來是判斷玩家是否有將搖桿往上,如果有的話就處理變換方塊,搖桿往下會改變速度讓方塊快速往下移動,放開搖桿後速度
就會恢復正常,程式碼片段如下:
  // 清除畫面.
  game8x8Graphics.clearDisplay(0);

  // 搖桿往上操作變換方塊.
  if (game8x8JoyStick.getJoyY() > 9 && !JoyUp && containerX != -1 && containerX != 6 && clearBrickFlag == 0) {
   // 播放音效.
   if (soundSwitch) {
    // 發出聲音.
    game8x8Tone.playTone(294, 33);
   }
   // N1、N2、I(2種狀態).
   if ( brickId == 1 || brickId == 2 || brickId == 7) {
    brickState++;
    if (brickState > 1) { brickState = 0; }

    // 處理判斷是否可以旋轉方塊.
    processTransformToBrickArray();
    if (!ifCopyToContainer()) {
     brickState--;
     if (brickState < 0) { brickState = 1; }
    }
   }
   // L1、L2、T(4種狀態)
   else if (brickId == 3 || brickId == 4 || brickId == 5) {
    brickState++;
    if (brickState > 3) { brickState = 0; }

    // 處理判斷是否可以旋轉方塊.
    processTransformToBrickArray();
    if (!ifCopyToContainer()) {
     brickState--;
     if (brickState < 0) { brickState = 3; }
    }
   }
   // O(1種狀態)
   else if (brickId == 6) {
    brickState = 0;
   }
   JoyUp = true;
  }
  else if (game8x8JoyStick.getJoyY() <= 9) {
   JoyUp = false;
  }

  // 搖桿往下-快速下降方塊.
  if (game8x8JoyStick.getJoyY() < 3 && clearBrickFlag == 0) {
   // 紀錄原本下降速度.
   if (downSpeedTemp == 0) {
    downSpeedTemp = downSpeed;
   }
   // 改變速度.
   downSpeed = 100;
  }
  else if (game8x8JoyStick.getJoyY() >= 3 && clearBrickFlag == 0) {   
   // 恢復原本速度.
   if (downSpeedTemp != 0) {
    downSpeed = downSpeedTemp;
    downSpeedTemp = 0;
   }
  }
再來就是判斷玩家左右晃動搖桿來移動方塊,在方塊移動的時候需要判斷要移動過去的區域是否以有方塊還有 方塊是否已經移動到左右邊界,另外這裡有個特例判斷,就是長條方塊移到左邊邊界的時候要讓他靠齊邊界,所以有處理這個例外狀況,程式碼片段如下:
  // 操作左右移動方塊.
  if (TimeMoveRL.update(100) && clearBrickFlag == 0) {
   int mx = game8x8JoyStick.getJoyX();
   if (mx != 5) {
    // 右移.
    if (mx < 5) {
     containerX++;
     // 判斷可否右移.
     if (ifCopyToContainer()) {
      if (brick[2][0] == 0 && brick[2][1] == 0 && brick[2][2] == 0) {
       if (containerX>6) { containerX = 6; }
      }
      else {
       if (containerX>5) { containerX = 5; }
      }
     }
     else {
      containerX--;
     }
    }
    // 左移.
    else
    {
     containerX--;
     // 判斷可否左移.
     if (ifCopyToContainer()) {
      if (containerX < 0) { 
       // 處理直立長條(特例).
       if (brick[0][0] == 0 && brick[0][1] == 0 && brick[0][2] == 0) {
        containerX = -1;
       }
       else {
        containerX = 0;
       }
      }
     }
     else {
      containerX++;
     }     
    }
   }
  }
最後就是將方塊畫到容器陣列內,並處理超出邊界的方塊不要畫,在呼叫game8x8Graphics.update()函數實際將內容畫到螢幕上,程式碼片段如下:
  // 畫容器.
  for (int iy = 0; iy<8 container="" for="" if="" int="" ix="" iy=""> 0 && container[ix][iy] != 255) {
     game8x8Graphics.setPixcls(ix, iy, 1);
    }
    else {
     game8x8Graphics.setPixcls(ix, iy, 0);
    }    
   }
  }

  // 畫方塊.  
  for (int iy = 0; iy < 3; iy++) {
   for (int ix = 0; ix < 3; ix++) {
    posX = containerX + ix;
    posY = containerY + iy;
    if( posX >= 0 && posY >= 0){
     if (brick[ix][iy] > 0) {
      game8x8Graphics.setPixcls( posX, posY, 1);
     }
     else {
      // 處理超出邊界不要畫.
      if (posX <= 5) {
       // 容器位是空的才畫.
       if(container[posX][posY] == 0){
        game8x8Graphics.setPixcls(posX, posY, 0);
       }       
      }      
     }
    }
   }
  }
  // 更新.
  game8x8Graphics.update();
以下是實際操作影片:
完成,以上就是Game8x8掌機版本俄羅斯方塊,希望大家會喜歡。

PS:掌機接線方式請參考【Arduino】自己的掌機自己作(1)-線路圖與開發套件

0 意見:

張貼留言