Tetrisには版権を持っている会社が示しているガイドラインがあります。このページの解説がわかりやすいです。
テトリス(tetris)のガイドラインを理解する – Qiita
前回はスーパーローテーションシステムに対応させるためのプログラムを作成してきましたが、ガイドラインには他のことも定められています。今回はソフトドロップとハードドロップを考えます。
ガイドラインによるとテトリミノの落下について以下のように定めています。
ハードドロップは0.0001秒で落下
ソフトドロップは押してるあいだ落下速度が現在の自然落下速度の20倍とする
これまで作成したプログラムでは↓キーを押すと急速で落下するのですが、その速度はテキトーに考えていました。今回はガイドラインに沿う形で書き直すことにします。
これまでは押したら最後、ミノが固定されるまで急速落下は取り消すことができませんでした。
押してるあいだ落下速度が現在の自然落下速度の20倍とする
ということは離すと元の速度に戻るということになります。
また
落下ハードドロップは0.0001秒で落下
ということは即設置でよいということになります。
ではそのように書き換えましょう。
これまではこのような処理にしていました。
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 { int dropPoint = 0; bool isDroping = false; private void Form1_KeyDown(object sender, KeyEventArgs e) { if(e.KeyCode == Keys.Left) MoveLeftTetoro(); else if(e.KeyCode == Keys.Right) MoveRightTetoro(); else if(e.KeyCode == Keys.Space) RotateTetoro(); else if(e.KeyCode == Keys.Z) RotateTetoro2(); else if(e.KeyCode == Keys.X) RotateTetoro(); else if(e.KeyCode == Keys.Down) Drop(); } void Drop() { Timer.Interval = 40;// 落下速度はテキトー。一度急速落下になると取り消せない dropPoint = 0; isDroping = true; } } |
↓キーを押したらソフトドロップ、↑キーを押したらハードドロップにします。
ハードドロップの場合はそのまま落ちたらどこになるのかを調べて移動させます。
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 { private void Form1_KeyDown(object sender, KeyEventArgs e) { if(e.KeyCode == Keys.Left) MoveLeftTetoro(); else if(e.KeyCode == Keys.Right) MoveRightTetoro(); else if(e.KeyCode == Keys.Space) RotateTetoro(); else if(e.KeyCode == Keys.Z) RotateTetoro2(); else if(e.KeyCode == Keys.X) RotateTetoro(); else if(e.KeyCode == Keys.Down) SoftDrop(); // Drop() メソッド名変更 else if(e.KeyCode == Keys.Up) HardDrop(); // 追加 } } |
これはハードドロップしたときの着地点を取得するメソッドです。回転処理のときに作成したCanRotateメソッドを流用しています。
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> GetBlocksIfHardDrop(ref int addpoint) { List<BlockPosition> tempPos = MovingTetoros.Select(x => new BlockPosition(x.Colum, x.Row)).ToList(); addpoint = 0; while(true) { List<BlockPosition> newPos = tempPos.Select(x => new BlockPosition(x.Colum, x.Row+1)).ToList(); if(CanRotate(newPos)) { tempPos = newPos; addpoint++; } else break; } return GetBlocksFromBlockPositions(tempPos); } } |
ハードドロップしたときの着地点がどこかわかったら実際に移動させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public partial class Form1 : Form { void HardDrop() { isDroping = true; List<Block> newTetoro = GetBlocksIfHardDrop(ref dropPoint); ClearOldTetoro(); foreach(Block block in newTetoro) { block.BackColor = GetTetoroColor(tetoroTypes); } MovingTetoros = newTetoro; TetoroDowned(); } } |
次にソフトドロップですが、↓ボタンが離されたら元の速度に戻す必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public partial class Form1 : Form { private void Form1_KeyUp(object sender, KeyEventArgs e) { if(e.KeyCode == Keys.Down) { Timer.Interval = TimerInterval; isDroping = false; } } void SoftDrop() { Timer.Interval = TimerInterval/20; dropPoint = 0; isDroping = true; } } |
ゴーストを表示させる
それからゴーストも表示させましょう。これは操作してるテトリミノをハードドロップしたときに設置される場所です。半透明やアウトラインのみで表示することになっていますが、
ゴーストを表示する部分はGetBlocksIfHardDropメソッドで取得できます。ゴーストを表示したら古いゴーストは消さないといけません。そのためフィールド変数 GohstBlocksに保存することにしました。
新しいゴーストを表示する前に古いゴーストは元の色に戻します。
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 { List<Block> GohstBlocks = new List<Block>(); Color GohstColor = Color.FromArgb(55, 55, 55); // 暗めの色にする void ShowGohst() { int addpoint = 0; List<Block> blocks = GetBlocksIfHardDrop(ref addpoint); // 元の色に戻す foreach(Block block in GohstBlocks) { if(!MovingTetoros.Any(x => x == block)) block.BackColor = ClearColor; } foreach(Block block in blocks) { if(!MovingTetoros.Any(x => x == block)) block.BackColor = GohstColor; } // 新しいゴーストの位置を保存 GohstBlocks = blocks; } } |
GohstBlocksに保存した要素をそのままにしておくと、新しいミノを作成したときにGohstBlocksに保存されている部分がClearColorに戻ってしまいます。そこでミノが固定されたときにGohstBlocksをクリアしています。
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 TetoroDowned() { GohstBlocks.Clear(); // ゴーストの位置をクリア Timer.Interval = TimerInterval; if(isDroping) { isDroping = false; AddPoints(dropPoint); } dropPoint = 0; FixedTetoro.AddRange(MovingTetoros); DeleteLineIfNeed(); ShowNewTetoro(); } } |
ゴーストを表示しなければならないときとは新しいミノが表示されたとき、左右に移動したとき、回転したときです。そこで対応するメソッドの最後にShowGohstメソッドを呼んでおきます。
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 { void MoveLeftTetoro() { if(MovingTetoros.Min(x => x.Colum) <= 0) return; var newPos = MovingTetoros.Select(x => Field[x.Colum-1, x.Row]).ToList(); if(newPos.Intersect(FixedTetoro).Count() != 0) return; Color tetoroColor = MovingTetoros[0].BackColor; ClearOldTetoro(); MovingTetoros = newPos; foreach(Block block in newPos) block.BackColor = tetoroColor; ShowGohst(); // 最後に // void MoveRightTetoro() // void RotateTetoro() // void RotateTetoro2() // void ShowNewTetoro() // も同様にする } } |