いよいよゲーム制作に入ります。まずは「テトリス」ゲームです。完成したゲームです。
コントローラはESP32マイコンのライブラリに「PS3」があるため、PS3をBLE接続してコントローラとします。
画面設計
エクセルを使い、まずは画面設計を行います。
(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 件のコメント:
コメントを投稿