M5Stack:テトリス改造

テトリスのプログラムがシンプルでわかりやすかったので、少し改造をしてみました。内容は3つ。
・NEXTブロックの表示
・スコアの表示
・シリアルコンソール経由の操作


NEXTブロックの表示

テトリスは、次に出るブロックがあるとないとでゲーム性がかなり異なってきます。画面左側に、表示できそうなちょうどよい場所もあるので、NEXTブロックを表示するようにしてみます。

uint16_t nextBlockBuffer[60][48]; // NEXT BLOCK AREA

NEXT ブロック表示エリア用のグラフィックバッファです。XとYは入れ替えて保持するみたいですね。(表示領域は 48 x 60)

int nextBlockType = -1;
Block nextBlock;

次のブロックのタイプと、ブロック情報を保持する変数です。初期値 -1 にして、初回のみ現在のブロックと次のブロック両方の抽選をするようにします。

void PutStartPos() {
   pos.X = 4; pos.Y = 1;
   if (nextBlockType == -1){
     block = blocks[random(7)];
   }else{
     block = blocks[nextBlockType];
   } 
   nextBlockType = random(7);
    rot = random(block.numRotate);
}

ブロックの抽選ロジックです。次ブロック抽選済みの場合はそれを用いて、次ブロックを再抽選&保持します。

void DrawNextBlock() { 
   for(int x = 0; x < 48;x++) {
     for(int y = 0; y < 60;y++){
       nextBlockBuffer[y][x]=0;
     }
   }
   nextBlock = blocks[nextBlockType];
   int offset = 6 + 12;
 
   for (int i = 0; i < 4; ++i) {
     for (int k = 0; k < Length; ++k) for (int l = 0; l < Length; ++l){
       nextBlockBuffer[60 - (nextBlock.square[0][i].X * Length + l + offset)][nextBlock.square[0][i].Y * Length + k + offset] = BlockImage[nextBlockType + 1][k][l];
     }
   }
   M5.Lcd.drawBitmap(26, 100, 48, 60, (uint16_t *)nextBlockBuffer);
   M5.Lcd.fillRect(2, 76, 96, 19, BLACK);
   M5.Lcd.setCursor(10, 78);
   M5.Lcd.printf("%7d",score);
}

次ブロックを画面に表示する処理です。ここは正直、試行錯誤しつつ、うまいこと出るように調整してる感じなので、あんまりキッチリと考えて書いてないです。だいたい、いい感じに出たのでまあOK!(よくないけど)

ブロックのデータは、Block blocks[7] の定義で、こういう感じになっています。

最初にバッファを黒色(0)でクリアし、次ブロックの情報を取得。その形を、Rotate = 0(無回転状態)でバッファに描いています。データは左右逆にする必要があるみたいです。(このへん、結果を見つつの試行錯誤)
あと、offset で、横からの描画位置を調整しています。
スコアの描画もここでやってます。(スコアが増えるのは、次のブロックを抽選するタイミングだけなので。)

DrawNextBlockは、初期設定時と、次のブロック抽選時に呼び出しています。


スコアの表示

スコアがあったほうが燃えますので、スコア付けます。

long score = 0;

スコア変数の保持。int じゃ足りなそうなので long で保持します。

M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(2);

スコア表示用テキストの設定。M5.Lcd.setTextSize(1)だと、ちょっと小さすぎたので(2)に。

void DeleteLine() {
   int deleteCount = 0;
   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];
         }
       }
       deleteCount++;
     }
   }  
   switch (deleteCount){
     case 1:score = score + 40;break;
     case 2:score = score + 100;break;
     case 3:score = score + 300;break;
     case 4:score = score + 1200;break;
   }
   if(score > 9999999){
     score = 9999999;
   }
}

スコアの加算は、列が消えた時(DeleteLine)に行います。何列消えたかによってスコアが違ってます。4列消し(テトリス)を狙いましょう!
7桁(9999999)でカンストです。

M5.Lcd.fillRect(2, 76, 96, 19, BLACK);
M5.Lcd.setCursor(10, 78);
M5.Lcd.printf("%7d",score);

スコア表示は、DrawNextBlock の中。スコアのエリアを黒で塗りつぶして、テキストでスコア表示しています。
7桁カンストは、画面上の表示エリア的な制限で決めました。


シリアルコンソール経由の操作

これは、移植元となったソースに入っていたロジックを復活させました。

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;}
 
   if (Serial.available()) {
     char r = Serial.read();
     while(Serial.read() != -1);
     if (r == 'z') { ClearKeys(); but_A=true; } //else but_A=false;
     if (r == '2') { ClearKeys(); but_DOWN=true; } //else but_DOWN=false;
     if (r == '4') { ClearKeys(); but_LEFT=true; } // else but_LEFT=false;
     if (r == '6') { ClearKeys(); but_RIGHT=true; } //else but_RIGHT=false;
     return true;
   }
 
 return false;
}

USBでPCと接続していれば、M5Stackでテトリス起動後に、コンソールから 4(左)/ 6(右)/ 2(下)/ Z (回転)でコントロール可能です。
Arduino IDEのシリアルモニタだとEnterで入力が必要になってしまうので、直接入力可能なPuTTYやTeraTerm等で接続するといいと思います。キーが不足して省略していたDOWN処理も追加してます。

同じく、元ソースに入っている Wi-Fi TCP/UDP Controller での操作も入れたかったのですが、WiFi.softAP(ssid, password); で再起動かかってしまって、うまく動作できませんでした。


ということで、完成!


プログラム全体はこうなります。もともとの Tetris.ino に上書きしてください。


“M5Stack:テトリス改造” への4件の返信

  1. shikarunochi 様、M5stack TETRIS の改造をありがとうございます。
    丁寧な説明と解説は とても解りやすかったです。特にブロックデーターの構造は勉強になりました。
    移植時に削除した所と追加したい内容が改造され とても楽しくなりました。感謝致します。
    M5STACK の Version は 5月16日現在、M5STACK Version 0.1.9-dirtyです。M5STACK の Library は 2018年5月に幾度かの version up がありました。これにより従来の命令が一部使用できなくなっています。例として M5.Lcd.drawBitmap は In_eSPI.cpp にて M5.Lcd.pushImage に変更されました。TETRIS の抽画では「drawBitmap」を使用していますので「pushImage」に変更する必要があります。
    TFT_eSPI Library に使用されている ILI9341_Defines.h, ILI9341_Rotation.h, In_eSPI_Setup.h, In_eSPI.cpp, In_eSPI.h が M5Stack に組み込まれています。現在 TFT_eSPI Library の Sprite を含んだ全てでは無い事や TFT PARALELS の記述が混在しています。
    尚、勝手ながら facebook の ESP8266/ESP32環境向上委員会 に shikarunochi氏(しかるのち)の M5STACK TETRIS 改造版 を紹介させて頂きました。
    https://www.facebook.com/groups/927623023964478/

  2. macsbug さん、情報ありがとうございます。勝手に改造してしまってすいません。Arduino初めてだったので、いろいろと参考にさせていただきました!
    ライブラリ改修時の互換性は悩みどころですね。新しい方式を取り入れるなら、過去との互換性を切るのも、ある程度仕方ないところかなと思います。

    • shikarunochi 様
      改造をありがとうございます。
      私には 此処までの力量がなく 大変助かっています。
      どなたかが改造して頂けないかと希望していた所で大変感謝しています。
      Version 0.1.9-dirty の途中経過の情報で、慌てた内容になり、ご迷惑をお掛け致しました。
      2018年5月18日に M5STACK v0.1.9 となりました。
      これにより M5.Lcd.drawBitmap は使用できるようになりました。
      尚、v0.1.9で Sprite も使用できる様になりサンプルも動作しています。

  3. ピンバック: Easy M5STACK JOYSTICK | macsbug