テトリスでゲームオーバーになるのはガイドラインによると以下の3パターンです。
(1)ネクストミノが出現する場所にブロックがある。
(2)ミノを完全にフィールド外に置く。
(3)敵の攻撃等によりフィールドの更に20ブロック上までブロックがせり上がる。
これは対戦型ではないので(3)に関しては完全無視で対応。現実的には(1)だけでよいのではないかと思います。
ネクストミノが出現する場所ですが、これもガイドラインで決められていて、
向きはネクストに表示されている状態(北向き)。
場所は横はOミノ以外は左から4列目にミノの左端がくるように出現、Oミノだけは左から5列目にミノの左端が来るように出現する。縦はミノの下端が21行目になるように出現。
これまではテトリミノを全部で20行あるフィールドのなかに完全にみえる形で出現させていましたが、これが間違い。正しくは縦はミノの下端が21行目になるようにして、そこから落下することで下側から見えてくるようにしなければなりません。
そこでフィールドは20行ではなく22行にして、実際にみえるのは下の20行というふうに作りかえます。
Blockクラスのコンストラクタだけ示します。最初の2行は非表示にします。フィールド変数LeftTopBlockはフィールドのみえる部分の左上の座標です。これだと左上にみえるブロックの座標が(150, 50)になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Block : PictureBox { const int BLOCK_SIZE = 20; Point LeftTopBlock = new Point(150, 50 - BLOCK_SIZE * 2); 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); if(row < 2) this.Visible = false; Colum = colum; Row = row; } } |
1 2 3 4 5 6 7 |
public partial class Form1 : Form { const int FIELD_INNER_WIDTH = 10; const int FIELD_INNER_HEIGHT = 20+2; Block[,] Field = new Block[FIELD_INNER_WIDTH, FIELD_INNER_HEIGHT]; } |
これで「縦はミノの下端が21行目になるように出現」させることができるようになりましたが、もうひとつ条件があります。
「横はOミノ以外は左から4列目に、Oミノだけは左から5列目にミノの左端が来るようにする」。
そこでミノを出現させるメソッドを変更します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public partial class Form1 : Form { void CreateTetoroI() { MovingTetoros.Clear(); List<Block> blocks = new List<Block>(); Color tetoroColor = GetTetoroColor(TetoroTypes.I); int startX = 3; // これを追加しただけ blocks.Add(Field[0 + startX, 1]); blocks.Add(Field[1 + startX, 1]); blocks.Add(Field[2 + startX, 1]); blocks.Add(Field[3 + startX, 1]); foreach(var block in blocks) block.BackColor = tetoroColor; MovingTetoros = blocks; tetoroTypes = TetoroTypes.I; tetoroAngle = TetoroAngle.Angle0; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public partial class Form1 : Form { void CreateTetoroO() { MovingTetoros.Clear(); List<Block> blocks = new List<Block>(); Color tetoroColor = GetTetoroColor(TetoroTypes.O); int startX = 4; // これを追加しただけ blocks.Add(Field[0 + startX, 0]); blocks.Add(Field[0 + startX, 1]); blocks.Add(Field[1 + startX, 0]); blocks.Add(Field[1 + startX, 1]); foreach(var block in blocks) block.BackColor = tetoroColor; MovingTetoros = blocks; tetoroTypes = TetoroTypes.O; tetoroAngle = TetoroAngle.Angle0; } } |
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 |
public partial class Form1 : Form { void CreateTetoroS() { MovingTetoros.Clear(); List<Block> blocks = new List<Block>(); Color tetoroColor = GetTetoroColor(TetoroTypes.S); int startX = 3; // これを追加しただけ blocks.Add(Field[1 + startX, 1]); blocks.Add(Field[0 + startX, 1]); blocks.Add(Field[1 + startX, 0]); blocks.Add(Field[2 + startX, 0]); foreach(var block in blocks) block.BackColor = tetoroColor; MovingTetoros = blocks; tetoroTypes = TetoroTypes.S; tetoroAngle = TetoroAngle.Angle0; // void CreateTetoroZ() // void CreateTetoroJ() // void CreateTetoroL() // void CreateTetoroT() // も同様にする } } |
ゲームオーバー判定ですが、
(1)ネクストミノが出現する場所にブロックがある。
(2)ミノを完全にフィールド外に置く。
(1)はこれまでどおりです。ネクストミノが出現させたときにすでに固定されたブロックがあればゲームオーバーであると判定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public partial class Form1 : Form { void ShowNewTetoro() { // ここより上は変更なし // ネクストミノが出現する場所にブロックがある if(FixedTetoro.Intersect(MovingTetoros).Count() != 0) { OnGameOver(); } ShowGohst(); } } |
(2)の判定はミノを固定する直前にミノのY座標が0または1であることでおこなうことができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public partial class Form1 : Form { void TetoroDowned() { // ミノが完全にフィールド外に置かれた int rowMax = MovingTetoros.Max(x => x.Row); if(rowMax < 2) { OnGameOver(); return; } // 以下は変更なし } } |
それからゲームオーバーになったにも関わらずミノを左右に操作できてしまうのはおかしいのです。ゲームオーバーになったらキー操作を受け付けないようにしてしまいましょう。
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 |
public partial class Form1 : Form { bool isGameOvered = true; private void Form1_KeyDown(object sender, KeyEventArgs e) { // ゲームオーバーなら何もしない if(isGameOvered) return; // 以下は変更なし } private void Timer_Tick(object sender, EventArgs e) { if(isGameOvered) { // ゲームオーバーならタイマーは停止して何もしない Timer.Stop(); return; } // 以下は変更なし } void OnGameOver() { isGameOvered = true; // フラグをセット Timer.Stop(); labelGameOver.ForeColor = Color.White; labelGameOver.Visible = true; } void GameStart() { isGameOvered = false; // 新たにゲームを開始するときはフラグをクリア // 以下は変更なし } } |