2025年10月17日金曜日

ESP32マイコンでビデオゲーム 4(テトリス)

 いよいよゲーム制作に入ります。まずは「テトリス」ゲームです。完成したゲームです。
コントローラはESP32マイコンのライブラリに「PS3」があるため、PS3をBLE接続してコントローラとします。



 画面設計

 エクセルを使い、まずは画面設計を行います。 

(1)オープニング画面

 ブロックを配置できるエリアを横10×縦20ブロックにします。テトリスのブロックを1つあたり、8×8ドットにし、ブロック間を1ドット空けます。
 また、右側に得点やタイトルを表示させるエリアを取ります。
 全体の画面サイズは270×215ドットとしました。

(2)プレイ画面

 プレイ画面は右図です。左側がプレイエリア、右側にタイトルのほか、ネクストブロックを表示させます。
 初期のころはなかったのですが、改良されたテトリスでは「ホールド」機能があるということで、この機能を追加するためにエリアを追加しています。

(3)エンディング画面

 ゲーム終了のエンディング画面は、オープニング画面をベースにして、エンディング画像を表示するようにします。

 ゲームプログラミング

(1)画像の準備

 プログラミングで必要となる画像を作成します。ここで作成する画像は画面設計で表示されている画像です。この画像を「画像データ」に変換します。

(2)プログラム

 テトリスのプログラミングでは、プレイエリアを表現するのに2次元配列を使い、ブロックの有無や1列揃っているかなどの判断を行います。
 画面はグラフィックライブラリを使ってすべて描きます。

 Step.1 画面設計する
 Step.2 画面設計をもとに、プログラムする。
 Step.3 テトリスブロックを画面に置く。
 Step.4 テトリスブロックを下に動かす。(インベーダのプログラムが参考になります)
 Step.5 一番下に来たら(下に移動できなくなったら)、ブロックを固定する。
 Step.6 次のブロックを画面に置く。
 Step.7 Step.4からStep.6を繰り返す。

 ここまででも1行揃った判断はしていません。Step.5のブロック固定の時に、揃った判断をして、揃った行を消す。その後、上のブロックを1段下げると何となくテトリス風になります。

(3)ブロックを回転させる方法

 ブロックを回転させるために、ブロックのパターンを定義しています。
uint16_t Tetris_Block[][5] = {
  // 回転パターン,                , 色
  {0x0000, 0x0000, 0x0000, 0x0000, 0}, // dummy
  {0x4444, 0x0F00, 0x4444, 0x0F00, 1}, // I
  {0x4620, 0x06C0, 0x4620, 0x06C0, 2}, // S
  {0x2640, 0x0C60, 0x2640, 0x0C60, 3}, // Z
  {0x4E00, 0x4640, 0x0E40, 0x4C40, 4}, // T
  {0x88C0, 0x2E00, 0x6220, 0xE800, 5}, // L
  {0x44C0, 0xE200, 0xC880, 0x8E00, 6}, // J
  {0xCC00, 0xCC00, 0xCC00, 0xCC00, 7}  // O
};
 例えば、8行目の「L」形のブロックの定義の意味は次の図のようになります。


 




16進4桁の4つのデータを2進データに置き換えます。その"1"の部分(色塗り)に注目すると、ブロックの形状になっています。これが回転させたように4パターン定義しています。
配列要素のインデックスを変更するだけでパターンを変更できるようになります。

 この変換するプログラムを示します。
void Tetris_setBlock( int blk, int rot) {
  uint16_t dat = Tetris_Block[blk][rot % 4];
  uint16_t col = Tetris_Block[blk][4];
  for (int x = 0; x <= 3; x++) {
    for (int y = 0; y <= 3; y++) {
      BLOCK[x][y] = 0;
    }
  }
  if ( ( dat & 0x8000 ) != 0 ) BLOCK[0][0] = col;
  if ( ( dat & 0x4000 ) != 0 ) BLOCK[1][0] = col;
  if ( ( dat & 0x2000 ) != 0 ) BLOCK[2][0] = col;
  if ( ( dat & 0x1000 ) != 0 ) BLOCK[3][0] = col;
  if ( ( dat & 0x0800 ) != 0 ) BLOCK[0][1] = col;
  if ( ( dat & 0x0400 ) != 0 ) BLOCK[1][1] = col;
  if ( ( dat & 0x0200 ) != 0 ) BLOCK[2][1] = col;
  if ( ( dat & 0x0100 ) != 0 ) BLOCK[3][1] = col;
  if ( ( dat & 0x0080 ) != 0 ) BLOCK[0][2] = col;
  if ( ( dat & 0x0040 ) != 0 ) BLOCK[1][2] = col;
  if ( ( dat & 0x0020 ) != 0 ) BLOCK[2][2] = col;
  if ( ( dat & 0x0010 ) != 0 ) BLOCK[3][2] = col;
  if ( ( dat & 0x0008 ) != 0 ) BLOCK[0][3] = col;
  if ( ( dat & 0x0004 ) != 0 ) BLOCK[1][3] = col;
  if ( ( dat & 0x0002 ) != 0 ) BLOCK[2][3] = col;
  if ( ( dat & 0x0001 ) != 0 ) BLOCK[3][3] = col;
}
 ブロックのパターン番号、回転を与えてTetris_setBlock関数を呼び出すと、BLOCK[4][4]配列にパターンを展開します。
 このBLOCK配列を使いブロックを描くと、テトリスブロックを画面に描画できます。これをTetris_drawBlock関数しました。表示させる座標(X,Y)、ブロック番号、回転を与えて呼び出すとブロックを描いてくれます。
void Tetris_drawBlock( int x, int y, int blk, int rot){
  Tetris_setBlock( blk, rot); // BLOCK配列にブロック状態を展開
  for(int j=0;j<4; j++){
    for(int i=0;i<4; i++){
      if( BLOCK[i][j]>0 ){
        // ブロックがあるときはブロックを描く
        Vout.drawRect( (x+i)*9  , (y+j)*9  , 8, 8, Tetris_Block_Color[BLOCK[i][j]] );
        Vout.fillRect( (x+i)*9+2, (y+j)*9+2, 4, 4, Tetris_Block_Color[BLOCK[i][j]] );
      }else{
        // ブロックが無い時は黒で塗潰す
        Vout.fillRect( (x+i)*9  , (y+j)*9  , 8, 8,  BLACK  );        
      }
    }
  }
}
 例えば、ブロック番号を変えて、Tetris_drawBlock関数を呼び出すサンプルです。
int blockX=5, blockY=8, blockNo=1, blockRot=0;
void loop(void) {
  // ブロックを表示    X座標 ,  Y座標 , ブロックNo, 回転
  Tetris_drawBlock( blockX, blockY, blockNo, blockRot);
  Vout.display();
  blockNo=blockNo+1;
  if( blockNo > 7 ) blockNo =1;
  delay(300);
}
 これで画面にテトリスブロックが順番に表示されます。

 変数「blockRot」を0から3まで順番に変化させるとテトリスのブロックが回転します。



完成させてみましょう

 機能を追加するときに、一つの機能ごとに関数化して作成してください。この関数がしっかりと機能していれば、使用するプログラムも安定します。また関数化によりプログラムが見やすくなります。

 あとは「ブロックをコントローラで動かす」、「ブロックが移動できるか」、「一列揃っているかの確認」「得点を付ける」、「ゲームオーバーの判断をする」などの機能がまだまだ必要です。

※プログラムを掲載してもよいのですが、プログラミングの実力を付けるには、一歩ずつです。参考にしてプログラムしてみてください。

0 件のコメント:

コメントを投稿

LEDマトリックス表示装置の設計・製作 9(画像表示)

  LEDマトリックスに画像を表示させるには、いろいろな画像形式を知る必要があります。現在、よく使われるJPG形式やPNG形式は圧縮処理されて保存されています。これに対して、BMP形式やTIFF形式は非圧縮形式で、画像データがそのまま保存されています。このため、ファイルは大きくな...