前回はテトリミノを下に移動させるために以下のようなメソッドを作成しました。
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 { void MoveDownTetoro() { if(MovingTetoros.Max(x => x.Row) >= FIELD_INNER_HEIGHT - 1) { TetoroDowned(); return; } var newPos = MovingTetoros.Select(x => Field[x.Colum, x.Row + 1]).ToList(); if(newPos.Intersect(FixedTetoro).Count() != 0) { TetoroDowned(); return; } // そのまま下に移動させる処理の部分は省略 } } |
テトリミノがこれ以上、下に移動することができない場合、着地したとみなし、そのときにどのような処理をするべきか考えます。この処理は自作メソッド TetoroDownedでおこないます。
1 2 3 4 5 6 7 8 9 |
public partial class Form1 : Form { void TetoroDowned() { FixedTetoro.AddRange(MovingTetoros); DeleteLineIfNeed(); ShowNewTetoro(); } } |
ここでやっていることはMovingTetorosの要素をFixedTetoroに追加し、横のラインがそろっていないか、そろっている場合はラインを消す処理をおこなっています(MovingTetorosに格納されているデータはShowNewTetoro()内でクリアされます)。
自作メソッドGetDeleteLineは横のラインがそろっているかチェックして、そのラインのナンバーをリストにして返します。各ラインのブロックを取得してFIELD_INNER_WIDTH個あれば全部そろっていると判定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public partial class Form1 : Form { List<int> GetDeleteLine() { List<int> deleteLines = new List<int>(); for(int i=0; i< FIELD_INNER_HEIGHT; i++) { if(FixedTetoro.Where(x => x.Row == i).Count() == FIELD_INNER_WIDTH) deleteLines.Add(i); } return deleteLines; } } |
次に自作メソッドDeleteLineIfNeedについて。これはそろっているラインがあればラインを削除します。
まずFixedTetoroに格納されるデータを入れ替える必要があります。横一列にそろったものは取り除きます。そして消された列より上にあるものを下に下げます。
それから視覚的に上にあったブロックが下がってくるように見せかけるためにブロックの色を変えています。
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 |
public partial class Form1 : Form { void DeleteLineIfNeed() { List<int> lines = GetDeleteLine().OrderBy(x=>x).ToList(); foreach(int i in lines) { Text = i.ToString(); var deleteBlocks = FixedTetoro.Where(x => x.Row == i).ToList(); // FixedTetoroから横一列にそろったものを取り除く FixedTetoro = FixedTetoro.Except(deleteBlocks).ToList(); // 横一列にそろった部分より上にあるものを下に下げる FixedTetoro = FixedTetoro.Select(x => { if(x.Row < i) return Field[x.Colum, x.Row + 1]; else return Field[x.Colum, x.Row]; }).ToList(); // 上のブロックが下がってくるので色を変える for(int row = i; row> 0; row--) { for(int colum = 0; colum < FIELD_INNER_WIDTH; colum++) { Field[colum, row].BackColor = Field[colum, row - 1].BackColor; } } // 一番上はなにもないので「白」 for(int colum = 0; colum < FIELD_INNER_WIDTH; colum++) Field[colum, 0].BackColor = Color.White; } } } |
それからゲームオーバーになったときの処理も考えなければなりません。
これはShowNewTetoroメソッド内で新しいデータがMovingTetorosに格納されたときに、FixedTetoroとの積集合を調べて要素数が1以上の場合、ゲームオーバーになっていると判断するという考え方でよいと思われます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public partial class Form1 : Form { void ShowNewTetoro() { // 新しいテトリミノを作成し、MovingTetorosにデータを格納したときに MovingTetoros = blocks; // すでにFixedTetoroとぶつかっている if(FixedTetoro.Intersect(MovingTetoros).Count() != 0) { OnGameOver(); } } void OnGameOver() { // ゲームオーバー時の処理 } } |
タイマーイベントをつかってテトリスらしくする
テトリスは↓キーをおさなくても少しずつテトリミノは下に移動しています。タイマーイベントをつかってMoveDownTetoroメソッドを呼び出せばできそうです。
タイマーはゲーム開始とともにスタートさせ、ゲームオーバー時に止めます。
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 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); Timer.Interval = 1000; Timer.Tick += Timer_Tick; } private void startToolStripMenuItem_Click(object sender, EventArgs e) { ShowNewTetoro(); Timer.Start(); } void OnGameOver() { Timer.Stop(); } private void Timer_Tick(object sender, EventArgs e) { if(MovingTetoros.Count == 0) return; MoveDownTetoro(); } } |
これで1秒おきにひとつずつテトリミノは下がっていきます。
←・→キーを押したときにMoveLeftTetoro、MoveRightTetoroメソッドを呼べばテトリミノを左右に動かすことができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public partial class Form1 : Form { private void Form1_KeyDown(object sender, KeyEventArgs e) { if(e.KeyCode == Keys.Left) MoveLeftTetoro(); if(e.KeyCode == Keys.Right) MoveRightTetoro(); if(e.KeyCode == Keys.Space) RotateTetoro(); if(e.KeyCode == Keys.Down) Timer.Interval = 40; // タイマーイベントの間隔を変更 } void TetoroDowned() { Timer.Interval = 1000; // タイマーイベントの間隔をもとに戻す FixedTetoro.AddRange(MovingTetoros); DeleteLineIfNeed(); ShowNewTetoro(); } } |
↓キーがおされたときにはテトリミノが急速で落下します。タイマーイベントの間隔を変更すれば急速落下をさせることができます。そしてテトリミノが着地したタイミングでタイマーイベントの間隔をもとに戻せばよいのです。
とりあえず完成です。ビジュアル的に問題がある、得点が表示されないなどの問題がありますが、今後改良していくことにします。