今回はテトリスをつくります。実際にYouTubeでもプログラミングしている方も多くいるのでやってみることにしました。
Contents
フィールドを作成する
フィールドのサイズは、公式には縦20行 × 横10列とされているので、今回はこれに従います。フィールドに20×10のPictureBoxを敷き詰めて色を変えていくことでテトリミノが落下しているように見せかけることができます。でもC#でテトリスを作成している動画はかなりありますが、PictureBoxを使う方法を採用している方を見たことがありません。このやり方はなにか問題があるのでしょうか?
まずフィールドをつくります。またブロックを表すPictureBoxはそのまま使わずに継承して使います。PictureBoxクラスを継承してBlockクラスをつくります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
class Block : PictureBox { const int BLOCK_SIZE = 20; Point LeftTopBlock = new Point(50, 30); public Block(int colum, int row, Form parent) { this.Width = BLOCK_SIZE; this.Height = BLOCK_SIZE; this.Parent = parent; this.BackColor = Color.Gray; this.BorderStyle = BorderStyle.FixedSingle; this.Location = new Point(BLOCK_SIZE * colum + LeftTopBlock.X, BLOCK_SIZE * row + LeftTopBlock.Y); Colum = colum; Row = row; } public int Colum { get; private set; } = 0; public int Row { get; private set; } = 0; } |
ブロックの大きさは20ピクセルにしました。またこのブロックはフィールドのどの部分に存在するのかわかるように、Colum、Rowというプロパティも作成しました。またコンストラクタの引数を指定することでフォーム上に表示させることまでできるようにしました。
それから左上に表示されるブロックの座標はフォーム上の座標(0,0)ではなく、もうちょっと内側にしたいので、Point型フィールド変数 LeftTopBlockをつかって調整しています。
ではフィールドを作成することにしましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public partial class Form1 : Form { const int FIELD_INNER_WIDTH = 10; const int FIELD_INNER_HEIGHT = 20; const int TETORO_SIZE = 4; private void Form1_Load(object sender, EventArgs e) { InitField(); } Block[,] Field = new Block[FIELD_INNER_WIDTH, FIELD_INNER_HEIGHT]; void InitField() { for(int colum = 0; colum < FIELD_INNER_WIDTH; colum++) { for(int row = 0; row < FIELD_INNER_HEIGHT; row++) { Block block = new Block(colum, row, this); Field[colum, row] = block; } } ClearField(); } } |
フィールドは縦20行 × 横10列です。またテトリミノは縦横4つずつブロックが並んで作られているので
1 2 3 |
const int FIELD_INNER_WIDTH = 10; const int FIELD_INNER_HEIGHT = 20; const int TETORO_SIZE = 4; |
としています。
フォームがロードされたら自作メソッドInitFieldを使用して10×20個のブロックを作成して、必要なときにアクセスできるようにフィールド変数のFieldに格納しています。フィールド変数のFieldはBlockの2元配列です。
フィールドの作成が終わったらゲーム開始前はなにも存在しないはずなので、自作メソッド ClearFieldでBackColorを白にしています。
またMovingTetorosはList
ゲーム開始時はMovingTetorosもFixedTetoroも空なのでClearメソッドで要素を空にしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public partial class Form1 : Form { List<Block> FixedTetoro = new List<Block>(); List<Block> MovingTetoros = new List<Block>(); Point MovingTetoroPos = new Point(); void ClearField() { MovingTetoros.Clear(); FixedTetoro.Clear(); for(int colum = 0; colum < FIELD_INNER_WIDTH; colum++) { for(int row = 0; row < FIELD_INNER_HEIGHT; row++) { Block block = Field[colum, row]; block.BackColor = Color.White; } } } } |
テトリミノを出現させ移動させる
メニューの[スタート]をクリックしたらゲーム開始です。まず最初のテトリミノを出現させます。
テトリミノには種類があって
I-テトリミノ(水色)
O-テトリミノ(黄色)
S-テトリミノ(緑)
Z-テトリミノ(赤)
J-テトリミノ(青)
L-テトリミノ(オレンジ)
T-テトリミノ(紫)
があります。
そこでこのような形のテトリミノをつくるために2元配列の配列をつくります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
public partial class Form1 : Form { bool[][,] Tetoros = { // I new [,] { {false, false, false, false}, {true, true, true, true}, {false, false, false, false}, {false, false, false, false}, }, // O new [,] { {false, false, false, false}, {false, true, true, false}, {false, true, true, false}, {false, false, false, false}, }, // S new [,] { {false, false, false, false}, {false, true, true, false}, {true, true, false, false}, {false, false, false, false}, }, // Z new [,] { {false, false, false, false}, {true, true, false, false}, {false, true, true, false}, {false, false, false, false}, }, // J new [,] { {false, false, false, false}, {true, false, false, false}, {true, true, true, false}, {false, false, false, false}, }, // L new [,] { {false, false, false, false}, {false, false, true, false}, {true, true, true, false}, {false, false, false, false}, }, // T new [,] { {false, false, false, false}, {false, true, false, false}, {true, true, true, false}, {false, false, false, false}, }, }; } |
そしてメニューの[スタート]が選択されたら最初のテトリミノを表示させます。そのために作成したのが自作メソッドShowNewTetoroです。どのタイプのテトリミノを出現させるかは乱数をつかって決めます。
タイプが決まればこれでフィールドのなかに描画することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Tetoro_Type = random.Next() % 7; // タイプ決定 bool[,] tetoro = Tetoros[Tetoro_Type]; List<Block> blocks = new List<Block>(); for(int colum = 0; colum < TETORO_SIZE; colum++) { for(int row = 0; row < TETORO_SIZE; row++) { if(tetoro[row, colum]) { Field[colum, row].BackColor = tetoroColor; } } } |
現在落下している最中で左右、回転のコントロールができるテトリミノと完全に落下して動かすことができないテトリミノはわけて考えます。現在落下しているテトリミノの位置は着地してその場所が確定されるまでMovingTetorosで管理します。
テトリミノは4×4マスのなかに存在する4つのブロックで構成されています。そこでテトリミノのタイプが決まったらTetorosをつかって描画し、古いMovingTetorosのデータはクリアし、そこに新しく作成されたテトリミノの位置情報を格納します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
public partial class Form1 : Form { Random random = new Random(); void ShowNewTetoro() { MovingTetoros.Clear(); // 古いデータは破棄 int type = random.Next() % 7; Tetoro_Type = type; MovingTetoroPos = new Point(0,0); ClearOldTetoro(); bool[,] tetoro = Tetoros[Tetoro_Type]; Color tetoroColor = Color.Empty; if(Tetoro_Type == 0) tetoroColor = Color.Aqua; if(Tetoro_Type == 1) tetoroColor = Color.Yellow; if(Tetoro_Type == 2) tetoroColor = Color.Green; if(Tetoro_Type == 3) tetoroColor = Color.Red; if(Tetoro_Type == 4) tetoroColor = Color.Blue; if(Tetoro_Type == 5) tetoroColor = Color.Orange; if(Tetoro_Type == 6) tetoroColor = Color.Violet; List<Block> blocks = new List<Block>(); for(int colum = 0; colum < TETORO_SIZE; colum++) { for(int row = 0; row < TETORO_SIZE; row++) { if(tetoro[row, colum]) { Field[colum, row].BackColor = tetoroColor; blocks.Add(Field[colum, row]); } } } MovingTetoros = blocks; } } |