在之前文章中我們有用 PixelBit 玩過小恐龍,這次我們換點口味改玩俄羅斯方塊。
此次俄羅斯方塊是參考這位作者的 Code 下去重構:https://www.youtube.com/watch?v=a9YPPsduDNg
下圖是我們的 PixelBit,可以看到除了 500 萬畫素鏡頭、TFT(240x240),還有左右兩顆 A、B 無段按鍵,這兩顆按鍵剛好適合我們玩各種遊戲,我們將會搭配 TFT、AB 按鍵來玩俄羅斯方塊。
引用相關 Library
/* #region  include */
#include "CircusUart.h"
#include "config.h"
#include "tet.h"
#include <SPI.h>
#include <TFT_eSPI.h>
#include <TJpg_Decoder.h>
/* #endregion */
將積木分三個區塊
/* #region  buff */
uint16_t BlockImage[Block_NUM][Block_SIEZ][Block_SIEZ];     // 8 種積 Pixel,包含分隔線
uint16_t backBuffer[Height * Length][Width * Length];       // 遊戲區塊 Pixel,[Height*Length][Width*Length]
int      screen[Width][Height] = {0};                       // 存放積木區塊顏色 index (積木格數)
/* #endregion */
定義 7 種積木各個方向占用座標、可旋轉的方向數量(每個積木最多四個方向),積木顏色
/* #region  建立 7 個積木形狀、各種方向、顏色 index */
Block_t blocks[7] = {
     {{{{-1, 0}, {0, 0}, {1, 0}, {2, 0}}, {{0, -1}, {0, 0}, {0, 1}, {0, 2}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}},       2, 1}, // 長條型
     {{{{0, -1}, {1, -1}, {0, 0}, {1, 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}}},       1, 2}, // 正方形
     {{{{-1, -1}, {-1, 0}, {0, 0}, {1, 0}}, {{-1, 1}, {0, 1}, {0, 0}, {0, -1}}, {{-1, 0}, {0, 0}, {1, 0}, {1, 1}}, {{1, -1}, {0, -1}, {0, 0}, {0, 1}}}, 4, 3}, //
     {{{{-1, 0}, {0, 0}, {0, 1}, {1, 1}}, {{0, -1}, {0, 0}, {-1, 0}, {-1, 1}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}},     2, 4},
     {{{{-1, 0}, {0, 0}, {1, 0}, {1, -1}}, {{-1, -1}, {0, -1}, {0, 0}, {0, 1}}, {{-1, 1}, {-1, 0}, {0, 0}, {1, 0}}, {{0, -1}, {0, 0}, {0, 1}, {1, 1}}}, 4, 5},
     {{{{-1, 1}, {0, 1}, {0, 0}, {1, 0}}, {{0, -1}, {0, 0}, {1, 0}, {1, 1}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}},       2, 6},
     {{{{-1, 0}, {0, 0}, {1, 0}, {0, -1}}, {{0, -1}, {0, 0}, {0, 1}, {-1, 0}}, {{-1, 0}, {0, 0}, {1, 0}, {0, 1}}, {{0, -1}, {0, 0}, {0, 1}, {1, 0}}},   4, 7}
};
/* #endregion */
宣告一些會需要用到的全域變數
/* #region  動態變數 */
Point_t pos;       // 當前積木座標
Block_t block;     // 當前積木
int     rot     = 0;
bool    started = false, gameover = false;
boolean btn_AB     = false;               // 觸發積木 旋轉
boolean btn_LEFT   = false;               // 觸發積木 往左
boolean btn_RIGHT  = false;               // 觸發積木 往右
int     game_speed = GAME_INIT_SPEED;     // 下降速度
// 紀錄按鍵狀態,避免重複觸發
// TODO: 將按鍵觸發狀態細分,並交由 328P 處理
int pom  = 0;
int pom2 = 0;
int pom3 = 0;
int score = 0;
int lvl   = 1;
ATM_BTN_STATE_E btn_b_state = ATM_BTN_REL;
ATM_BTN_STATE_E btn_a_state = ATM_BTN_REL;
/* #endregion */
建立 TFT、CircusUart object
/* #region  Object */
TFT_eSPI   tft = TFT_eSPI();
CircusUart uart(Serial);
/* #endregion */
初始化遊戲
/* #region  初始化遊戲 */
void initGame()
{
    // 清除 screen 內容
    for (int j = 0; j < Height; ++j)
        for (int i = 0; i < Width; ++i)
            screen[i][j] = 0;
    // 變數初始化
    gameover   = false;
    score      = 0;
    game_speed = GAME_INIT_SPEED;
    lvl        = 1;
    // 產生新積木
    PutStartPos();
    /*  根據當前旋轉方向(rot)選擇 Block_t 內其中一種方向積木,
        取得 X 座標(block.square[rot][i].X)加上 X 開始座標(pos.X),
        取得 Y 座標(block.square[rot][i].Y)加上 Y 開始座標(pos.Y),
        設定積木顏色 index 到積木空間 buff(screen)內
    */
    for (int i = 0; i < 4; ++i)
        screen[pos.X + block.square[rot][i].X][pos.Y + block.square[rot][i].Y] = block.color;
    // 繪製分數、難度等級
    tft.drawString("SCORE:" + String(score), 38, 8, 1);
    tft.drawString("LVL:" + String(lvl), 167, 8, 1);
    // 繪製所有積木到 TFT
    Draw();
}
/* #endregion */
初始化遊戲積木
/* #region  初始化遊戲積木 */
void PutStartPos()
{
    game_speed = GAME_INIT_SPEED;
    pos.X      = 7;                           // 初始化積木 X 座標,遊戲區正中間 Width/2
    pos.Y      = 1;                           // 初始化積木 Y 座標,遊戲區最上方
    block      = blocks[random(7)];           // 隨機取積木
    rot        = random(block.numRotate);     // 隨機設定方向
}
/* #endregion */
繪製所有積木到 TFT
/* #region  更新 backBuffer,繪製 backBuffer 到 TFT */
void Draw()
{                                                    // Draw 120x240 in the center
    for (int i = 0; i < Width; ++i)                  // 水平尋訪 square
        for (int j = 0; j < Height; ++j)             // 垂直尋訪 square
            for (int k = 0; k < Length; ++k)         // 垂直尋訪 square 中 Pixel
                for (int l = 0; l < Length; ++l)     // 水平尋訪 square 中 Pixel
                                                     // 設定 backBuffer 每一點像素言顏色
                    backBuffer[j * Length + l][i * Length + k] = BlockImage[screen[i][j]][k][l];
    // 顯示 backBuffer 到 TFT
    tft.pushImage(36, 20, 165, 220, *backBuffer);
}
/* #endregion */
明天我們會繼續講剩下的方法。