macsbug

TETRIS with M5STACK

with one comment

TETRIS ゲームを M5STACK に移植しました。 ORG 2018.01.20
_ Revisoin 1 : ダウンロード先
_ Revisoin 2 : M5STACK version 0.1.9-dirty
_ Revisoin 3 : M5STACK version 0.1.9

Aボタン=左。Bボタン=右。Cボタンで START と ROTATE です。

GitHub の 「MhageGH」 に 「esp32_ILI9328_Tetris」 があります。
ESP32 用で Arduino IDE で使用できます。
基の仕様は ESP32 + ILI9328 TFT Display で M5STACK ( ILI9341 TFT ) に
移植致しました。Mhage氏に感謝致します。

追記:2018.03.10
1. Parts and CircuitsM5Stack-Tetris から ダウンロード できます。
2. m5stack の M5Stack/examples/Games/Tetris/ から ダウンロード できます。


.
準備:
1. ハード:M5STACK
2. ハード:microSD ( FAT32, Class 10, 16GB ) ( 約40KByte の画像保存用です)
3. スケッチ:ブログ下のスケッチを Arduino IDE で M5STACK へ書き込みます。
_ 移植の為の修正部分が多数の為 全リストを表示しました。
4. 背景画像:microSD に「tetris.jpg」を保存します。
_ 以下の画像をDLし microSD に「tetris.jpg」で保存し M5の SD SLOT に入れます。
_ DLは PCやブラウザー経由の為か情報が変化し jpg でも表示しない場合があります。
_ その場合は 手間がかかりますが 追加手順で画像変換して使用します。

5. 追加手順:背景画像が表示されない場合。
_ M5STACK を起動し 背景画像がでない場合は「tetris.jpg」を変換します。
_ Convertimage に接続し 上記の画像を変換します。
_ 1. 1 > Choose the picture output format:「JPG」を選択します。
_ 2. 2 > Select your image:上記の 「tetris.jpg」を選択します。
_ 3. 「I agree to the terms of use」のボタンを「YES」にします。
_ 4. Convert this image のボタンを押します。
_ 5. 画像を DLし microSD に「tetris.jpg」で保存します。

メモ:
_ 開発環境:iMac 27 mid 2010, MacOS 10.12.6 Sierra。Arduino IDE 1.8.5。
_ PC上での 画像作成や変換で M5STACKで動作する画像フォーマットを
_ どう作成するか理解できていない状態です。
_ iMac上で jpg の背景画像を作りますが M5では表示しない時があります。
_ ネット上の「Convertimage」で変換すると表示します。
_ さらに変換画像をブログに表示し DL して使用すると 表示しません。
_ DLした画像を「Convertimage」で変換すると表示します。


.
移植:
移植基のスケッチ:「esp32_ILI9328_Tetris
移植スケッチの構成:スケッチと「BackgroundImage.h」「BlockImage.h」です。
_ コントローラーは WiFi接続で制御できるスケッチになっています。
_ 今回、外部コントロールは使用しません。

1. 背景画像変更:「BackgroundImage.h」は jpg画像 ( R.G.B 320×240 Pixel ) に
_ 変更し SD へ保存します。
2. BLOCK変更:「BlockImage.h」は 計算で作成します。( void setup 内 )
_ 色の値を変更すると 好きな色に変更できます。
3. SD:tetris.jpg と言う名前の画像を入れて使用します。
_ 同一名の画像を入れ替える事により、好きな画面に簡単に変更する事ができます。
_ SD の使用条件は microSD FAT32 Format で 16GB Class 10 を使用しました。
_ 32GB でも動きますが M5 の使用条件になっていないかも知れません。
_ 速度:Class 10 の速いものを使用した方が良いです。
_ SD を入れる向き:M5 FRONT を上にして SD は裏側で 水平に入れてください。
_ SD がない場合:画像がない場合は表示しません。背景画像の無いゲームになります。
_ 画像フォーマット: jpg でも多種類あり M5 の使用条件に合わないと 表示しません。
_ 画像処理アプリがない場合は ネット上に jpg 変換場所があり これを使用して
_ 正しく表示する jpg に変更します。


.
表示:M5STACK の ディスプレー画素数は 320×240 pixel です。
_  BLOCK が 動く画面は 中央の 120×240 pixel のエリアです。
_  背景画像は M5STACK の画素 320×240 pixel です。
_  BLOCK は 8つの種類があります。BLOCK は 4個の正方形で作られ
_  1つは 12×12 pixel です。
速度:BLOCK の落下速度は int game_speed = 25; で設定しています。
_  速度を速くするには 数値を減らしてください。
操作:Aボタン、Bボタン、Cボタン を使用します。
_  ボタン数の制約により BLOCKのDOWN 機能はありません。
注意:ボタンを酷使すると壊れるかも知れません。
_  M5STACK のスイッチは耐久性が少ないかも知れません。
_  操作方法は WiFi や BLE コントローラー等のアイデアがあるかと思います。
_  microSD の挿入は丁寧に行なってください。


.
追記:2018.05.19 : version 0.1.9 により以下の内容は解決されました。
_  M5.Lcd.drawBitmap は使用できるようになりました。
追記:2018.05.16 : version 0.1.9-dirty

2018.05.11 に M5STACK Version 0.1.9-dirty になりました。
_ 5月に幾度か version up があり 従来の命令が使用できなくなったものがあります。
_ 例として M5.Lcd.drawBitmap は M5.Lcd.pushImage に変更されました。
_ M5STACK v0.1.9 を使用する時は TETRIS の抽画の drawBitmap を pushImage
_ 変更する必要があります。
_ TFT_eSPI Library に使用されている ILI9341_Defines.h, ILI9341_Rotation.h,
_ In_eSPI_Setup.h, In_eSPI.cpp, In_eSPI.h が組み込まれています。
_ TFT_eSPI Library の Sprite を含んだ全てでは無い事や TFT PARALELS の記述が
_ 混在しています。


.
参考:
Mhage氏:MhageGH/esp32_ILI9328_Tetris:今回の基です。
Mhage氏:ESP32でテトリスを作ってみた
Mhage氏:【ゆっくり解説】テトリスの作り方【ゲームプログラミング】
Mhage氏:【ゆっくり解説】 WiFiの使い方2 【電子工作】。:これは最高です!

Qiita:@inachi 氏の記事。1 – 5。
1. 2018.01.19:M5stackでMicroPythonを使えるようにする
2. 2018.01.07:M5Stack MicroPython API調査記録 ボタン編
3. 2018.01.08:M5Stack MicroPython API調査記録 RTC編
4. 2018.01.08:M5Stack MicroPython API調査記録 スレッド編
5. 2018.01.11:M5Stack MicroPython API調査記録 LCD編
この中で LCD の仕様で参考になる記事がありました。:配線やサイズ等です。


.
感想:
M5STACK の API や 画像構成、特に 画面配置と色の理解ができていません。
M5STACK のディスプレー ILI9341 は 他の ILI9341 TFT と何かが異なるようです。
_ 理由:
_ M5STACK + 外部ディスプレー + 外部ライブラリーの表示は 正常に動きます。
_ M5STACK + 外部ディスプレー + M5STACKライブラリーの表示は 正常に動きません。
_ M5STACK + TFT外部ライブラリーの表示は 正常に動きません。
_  240以上 の所で 表示がされません。
_ M5STACK の ディスプレーの構造や配線が解れば 理解できるのですが現在不明です。
_ M5STACK の ディスプレーが 一般的に販売されている TFT と仕様が異なると
_ 他の製品にした方が良い場面が出てきます。


.
スケッチ
追記:2018.05.19 : M5STACK v0.1.9 により 以下の改訂は不要となりました。
追記:2018.05.16 : M5STACK v0.1.9-dirty による改訂。
_ 
_ 改訂内容:M5.Lcd.drawBitmap -> M5.Lcd.pushImage

//========================================================================
// TETRIS with M5STACK : 2018.01.20 Transplant by macsbug
// Controller : Buttons A = LEFT, B = RIGHT, C = START, ROTATE
// Display    : Left = 100x240, Center = 120x240, Right = 100x240
// Block      : 8ea, 12x12 pixel
// SD         : tetris.jpg : BackGround Image : R.G.B 320x240 pixel
//========================================================================
#include <M5Stack.h>                                       // M5STACK
uint16_t BlockImage[8][12][12];                            // Block
uint16_t backBuffer[240][120];                             // GAME AREA
const int Length = 12;     // the number of pixels for a side of a block
const int Width  = 10;     // the number of horizontal blocks
const int Height = 20;     // the number of vertical blocks
int screen[Width][Height] = {0}; //it shows color-numbers of all positions
struct Point {int X, Y;};
struct Block {Point square[4][4]; int numRotate, color;};
Point pos; Block block;
int rot, fall_cnt = 0;
bool started = false, gameover = false;
boolean but_A = false, but_LEFT = false, but_RIGHT = false;
int game_speed = 25; // 25msec
Block 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}
};
//========================================================================
void setup(void) {
  Serial.begin(115200);         // SERIAL
  M5.begin();                   // M5STACK INITIALIZE
  M5.Lcd.setBrightness(200);    // BRIGHTNESS = MAX 255
  M5.Lcd.fillScreen(BLACK);     // CLEAR SCREEN
  M5.Lcd.setRotation(0);        // M5STACK BASIC
  //M5.Lcd.setRotation(1);        // M5STACK GRAY
  //----------------------------// Make Block ----------------------------
  make_block( 0, BLACK);        // Type No, Color
  make_block( 1, 0x00F0);       // DDDD     RED
  make_block( 2, 0xFBE4);       // DD,DD    PUPLE 
  make_block( 3, 0xFF00);       // D__,DDD  BLUE
  make_block( 4, 0xFF87);       // DD_,_DD  GREEN 
  make_block( 5, 0x87FF);       // __D,DDD  YELLO
  make_block( 6, 0xF00F);       // _DD,DD_  LIGHT GREEN
  make_block( 7, 0xF8FC);       // _D_,DDD  PINK
  //----------------------------------------------------------------------
  M5.Lcd.drawJpgFile(SD, "/tetris.jpg");     // Load background from SD
  PutStartPos();                             // Start Position
  for (int i = 0; i < 4; ++i) screen[pos.X + 
   block.square[rot][i].X][pos.Y + block.square[rot][i].Y] = block.color;
  Draw();                                    // Draw block
}
//========================================================================
void loop() {
  if (gameover) return;
  Point next_pos;
  int next_rot = rot;
  GetNextPosRot(&next_pos, &next_rot);
  ReviseScreen(next_pos, next_rot);
  M5.update();
  delay(game_speed);                                      // SPEED ADJUST
}
//========================================================================
void Draw() {                               // Draw 120x240 in the center
  for (int i = 0; i < Width; ++i) for (int j = 0; j < Height; ++j)
   for (int k = 0; k < Length; ++k) for (int l = 0; l < Length; ++l)
    backBuffer[j * Length + l][i * Length + k] = BlockImage[screen[i][j]][k][l];
    M5.Lcd.drawBitmap(100,0,120,240, (uint16_t *)backBuffer);
}
//========================================================================
void PutStartPos() {
  pos.X = 4; pos.Y = 1;
  block = blocks[random(7)];
  rot = random(block.numRotate);
}
//========================================================================
bool GetSquares(Block block, Point pos, int rot, Point* squares) {
  bool overlap = false;
  for (int i = 0; i < 4; ++i) {
    Point p;
    p.X = pos.X + block.square[rot][i].X;
    p.Y = pos.Y + block.square[rot][i].Y;
    overlap |= p.X < 0 || p.X >= Width || p.Y < 0 || p.Y >= 
      Height || screen[p.X][p.Y] != 0;
    squares[i] = p;
  }
  return !overlap;
}
//========================================================================
void GameOver() {
  for (int i = 0; i < Width; ++i) for (int j = 0; j < Height; ++j)
  if (screen[i][j] != 0) screen[i][j] = 4;
  gameover = true;
}
//========================================================================
void ClearKeys() { but_A=false; but_LEFT=false; but_RIGHT=false;}
//========================================================================
bool KeyPadLoop(){
  if(M5.BtnA.wasPressed()){ClearKeys();but_LEFT =true;return true;}
  if(M5.BtnB.wasPressed()){ClearKeys();but_RIGHT=true;return true;}
  if(M5.BtnC.wasPressed()){ClearKeys();but_A    =true;return true;}
  return false;
}
//========================================================================
void GetNextPosRot(Point* pnext_pos, int* pnext_rot) {
  bool received = KeyPadLoop();
  if (but_A) started = true;
  if (!started) return;
  pnext_pos->X = pos.X;
  pnext_pos->Y = pos.Y;
  if ((fall_cnt = (fall_cnt + 1) % 10) == 0) pnext_pos->Y += 1;
  else if (received) {
    if (but_LEFT) { but_LEFT = false; pnext_pos->X -= 1;}
    else if (but_RIGHT) { but_RIGHT = false; pnext_pos->X += 1;}
    else if (but_A) { but_A = false;
      *pnext_rot = (*pnext_rot + block.numRotate - 1)%block.numRotate; 
    }
  }
}
//========================================================================
void DeleteLine() {
  for (int j = 0; j < Height; ++j) {
    bool Delete = true;
    for (int i = 0; i < Width; ++i) if (screen[i][j] == 0) Delete = false;
    if (Delete) for (int k = j; k >= 1; --k) 
    for (int i = 0; i < Width; ++i) screen[i][k] = screen[i][k - 1];
  }
}
//========================================================================
void ReviseScreen(Point next_pos, int next_rot) {
  if (!started) return;
  Point next_squares[4];
  for (int i = 0; i < 4; ++i) screen[pos.X + 
    block.square[rot][i].X][pos.Y + block.square[rot][i].Y] = 0;
  if (GetSquares(block, next_pos, next_rot, next_squares)) {
   for (int i = 0; i < 4; ++i){
     screen[next_squares[i].X][next_squares[i].Y] = block.color;
   }
   pos = next_pos; rot = next_rot;
  }
  else {
   for (int i = 0; i < 4; ++i) screen[pos.X + 
    block.square[rot][i].X][pos.Y + block.square[rot][i].Y] = block.color;
   if (next_pos.Y == pos.Y + 1) {
    DeleteLine(); PutStartPos();
    if (!GetSquares(block, pos, rot, next_squares)) {
     for (int i = 0; i < 4; ++i) screen[pos.X + 
      block.square[rot][i].X][pos.Y + block.square[rot][i].Y] = block.color;
      GameOver();
    }
   }
  }
  Draw();
}
//========================================================================
void make_block( int n , uint16_t color ){            // Make Block color       
  for ( int i =0 ; i < 12; i++ ) for ( int j =0 ; j < 12; j++ ){
    BlockImage[n][i][j] = color;                           // Block color
    if ( i == 0 || j == 0 ) BlockImage[n][i][j] = 0;       // BLACK Line
  } 
}
//========================================================================


.

Written by macsbug

1月 20, 2018 @ 6:52 pm

カテゴリー: ESP32, M5STACK

1件のフィードバック

Subscribe to comments with RSS.


コメントを残す