今回はぷよぷよもどきを作ります。
によると
フィールドは基本的に縦12マス×横6マスの格子で構成される。
上からぷよが2つ1組で落下してくる(「組ぷよ」と呼ばれる)。
ぷよは種類ごとに色が異なり、色は3-5色(通常は4色)ある。
プレイヤーはぷよに対して回転、横移動、高速落下のいずれかの操作を行う。
次に落下するぷよはフィールドの枠外に「NEXTぷよ(ネクストぷよ)」として予告される。落下してきたぷよがフィールドの床やほかのぷよに衝突すると、その位置にぷよが固定される。
ただし組ぷよを横にして置いたりなどして、ぷよに1マス分でも下方向に空白がある場合は、強制的にそのぷよだけ落下する。固定されたぷよと同色のぷよが周囲4方向にいる場合、それらは互いにくっつく。
ぷよが4個以上くっつくと消滅し得点となる。
ぷよの消滅により上にあったぷよが落下する。このとき再びぷよが4個以上くっつくと消滅し、連鎖が起きる。なお、普通に4つ色を並べて消す行為だけでも1連鎖と考え、消滅した回数(○回)に応じて○連鎖と呼ばれる。複数色を同時に消した場合や同色を別箇所で消した場合でも、1連鎖扱いとなる。
となっています。
Contents
フィールドをつくる
まず最初にフィールドをつくります。縦12マス×横6マスですが、外枠になる部分もつくりたいので
1 2 |
int FIELD_WIDTH = 6 + 2; int FIELD_HEIGHT = 12 + 1 + 1; |
とします。
ユーザーコントロールでフィールドをつくる
幅は左右の枠をつくるため、2つ分広くしています。高さは底の部分と「組ぷよ」を出現させる都合上、2つ分高くなっています。
フォームのうえにあるのはユーザーコントロールです。名前はFieldとします。
変更している部分はDoubleBufferedをtrueにしています。
まずはUserControl1クラスを示します。IsInputKeyメソッドをオーバーライドしていますが、これをしておかないとキーが押されたときにイベントを捕捉することができないからです。
UserControlでカーソルキーを押した時のKeyDownイベント – アンの開発日記によると、
NET FrameworkでUserControlクラスを継承した独自コントロールを作成した場合、そのままではカーソルキーを押してもKeyDownイベントが発生しない。
これはUserControlクラスの親クラスであるContainerControlクラスの内部でカーソルキーの押下処理が行われているため。
この問題を解決するにはIsInputKeyメソッドを下記のようにオーバーライドすればよい。
と説明がされています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public partial class UserControl1 : UserControl { public UserControl1() { InitializeComponent(); } protected override bool IsInputKey(Keys keyData) { if(keyData == Keys.Right || keyData == Keys.Left || keyData == Keys.Up || keyData == Keys.Down) { return true; } return base.IsInputKey(keyData); } } |
ぷよの位置を示すCellクラス
またCellというクラスを作成しています。これはユーザーコントロールに「ぷよ」を描画するときにどの部分に描画すればよいかをわかりやすくするためのものです。
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 |
public class Cell { public Cell(int x, int y) { LeftTop = new Point(x, y); } // 矩形の左上の座標 public Point LeftTop { get; private set; } = new Point(0, 0); // サイズはすべて同じ static public Size Size { get; } = new Size(30, 30); // そのセルは左から何番目、上から何番目にあるか指定 public void SetColumRow(int colum, int row) { Colum = colum; Row = row; } // そのセルは左から何番目にあるか public int Colum { get; private set; } = 0; // そのセルは上から何番目にあるか public int Row { get; private set; } = 0; // ぷよは固定されているか public bool IsFixed { get; set; } = false; // ぷよのタイプ public Puyo Puyo { get; set; } = Puyo.None; // ぷよを消す処理をするときに使う public bool isChecked { get; set; } = false; } |
ぷよのタイプを示す列挙体
これはぷよのタイプを示す列挙体です。
1 2 3 4 5 6 7 8 9 10 |
public enum Puyo { None = 0, // なにもない Wall = -1, // 壁 Puyo1 = 1, // ぷよ1 Puyo2 = 2, // ぷよ2 Puyo3 = 3, // ぷよ3 Puyo4 = 4, // ぷよ4 } |
Form1クラスのコンストラクタとフィールド変数
Form1クラスのコンストラクタとフィールド変数は以下のとおりです。Fieldはデザイナでフォームにはりつけたユーザーコントロールです。
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 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); Field.Paint += Field_Paint; Field.KeyDown += Field_KeyDown; InitCells(); } // セルの配列 Cell[,] Cells = null; // フィールドの幅と高さ int FIELD_WIDTH = 6 + 2; int FIELD_HEIGHT = 12 + 1 + 1; // 組ぷよの回転軸になる側の位置 int puyoPositionX = puyoStartPositionX; int puyoPositionY = puyoStartPositionY; // 組ぷよが最初にあらわれる部分の位置 const int puyoStartPositionX = 3; const int puyoStartPositionY = 1; // 組プヨはどれだけ回転したか? Angle PuyoAngle = Angle.Angle0; Puyo DropingPuyo = Puyo.Puyo1; } |
Angleは以下のようになっています。
1 2 3 4 5 6 7 |
public enum Angle { Angle0 = 0, Angle90 = 1, Angle180 = 2, Angle270 =3, } |
フィールドの初期化
InitCellsメソッドはセルを作成してこれを配列のなかに格納します。また一番上の行はぷよを回転させるときにつかわれる部分で表示はさせません。
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 InitCells() { Cells = new Cell[FIELD_HEIGHT, FIELD_WIDTH]; int selWidth = Cell.Size.Width; int selHeight = Cell.Size.Height; for(int i = 0; i < FIELD_HEIGHT; i++) { for(int j = 0; j < FIELD_WIDTH; j++) { // (i - 1)とすることで一番上の行は // コントロールの外側になり表示されない Cells[i, j] = new Cell(j * selWidth, (i - 1) * selHeight); Cells[i, j].SetColumRow(j, i); } } // 外枠を表示させる for(int i = 0; i < FIELD_HEIGHT; i++) SetPuyo(0, i, Puyo.Wall); for(int i = 0; i < FIELD_HEIGHT; i++) SetPuyo(FIELD_WIDTH - 1, i, Puyo.Wall); for(int i = 0; i < FIELD_WIDTH; i++) SetPuyo(i, FIELD_HEIGHT - 1, Puyo.Wall); } } |
ぷよを表示させる
ぷよを表示させるためにはぷよを表示するセルにデータをセットして、Invalidateメソッドを呼びます。SetPuyoメソッドはぷよを表示するセルにデータをセットするためのものです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public partial class Form1 : Form { void SetPuyo(int colum, int row, Puyo puyo) { Cell cell = Cells[row, colum]; cell.Puyo = puyo; Field.Invalidate(); } private void Field_Paint(object sender, PaintEventArgs e) { DrawPuyos(e.Graphics); } // DrawPuyos(Graphics g)は後述 } |
Invalidateメソッドを呼ぶことで、そのセルがある位置にぷよを表示させます。
ぷよを表示させるのであればイメージが必要です。使用したPNGファイルはここからダウンロードしました。
作者のサイト
しろま空間
取得したPNGファイルをリソースに追加
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 |
public partial class Form1 : Form { Image redPuyoImage = null; Image bluePuyoImage = null; Image greenPuyoImage = null; Image yellowPuyoImage = null; Image wallImage = null; void GetPuyoImages() { redPuyoImage = Properties.Resources.Red; bluePuyoImage = Properties.Resources.Blue; greenPuyoImage = Properties.Resources.Green; yellowPuyoImage = Properties.Resources.Yellow; wallImage = Properties.Resources.Wall; } public Form1() { InitializeComponent(); Field.Paint += Field_Paint; Field.KeyDown += Field_KeyDown; this.BackColor = Color.Black; Field.BorderStyle = BorderStyle.None; GetPuyoImages(); // 追加 InitCells(); } } |
これでぷよのイメージを描画することができます。
GetImageFromPuyoTypeメソッド
GetImageFromPuyoTypeメソッドはぷよのタイプを指定すると対応するイメージを返します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public partial class Form1 : Form { Image GetImageFromPuyoType(Puyo puyo) { if(puyo == Puyo.Puyo1) return redPuyoImage; else if(puyo == Puyo.Puyo2) return bluePuyoImage; else if(puyo == Puyo.Puyo3) return greenPuyoImage; else if(puyo == Puyo.Puyo4) return yellowPuyoImage; else if(puyo == Puyo.Wall) return wallImage; return null; } } |
DrawPuyosメソッドでぷよを描画する
DrawPuyosメソッドでぷよを描画します。ぷよが描画される矩形を取得し、そこにGetImageFromPuyoTypeメソッドで取得したイメージを描画します。
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 DrawPuyos(Graphics g) { Field.BackColor = Color.Black; for(int i = 0; i < FIELD_HEIGHT; i++) { for(int j = 0; j < FIELD_WIDTH; j++) { Cell cell = Cells[i, j]; Rectangle rect = new Rectangle(cell.LeftTop, Cell.Size); Image image = GetImageFromPuyoType(cell.Puyo); if(image != null) g.DrawImage(image, rect); } } } } |
FieldとAngleにエラーがでます どうしたらいいですか?
Fieldはデザイナでフォームにはりつけたユーザーコントロールです。
Angleはこちらの記載漏れです。修正しておきました。
すみません。フォームにユーザーコントロールを張り付ける
はりつけかたがわかりません。
どうもツールボックスにユーザーコントロールがないので
できない感じです。
そのへんの やり方教えてもらえませんか?
まずソリューションエクスプローラーからプロジェクト(ソリューションなんとかという名前の下にある)を右クリック。
するとこんなメニューが表示されます。
ここから「ユーザーコントロール」を選択。名前は適当につけてください。記事では「UserControl1」という名前にしています。
そして一度ビルドしてください(一度ビルドをするのがポイント)。するとツールボックスにユーザーコントロールが表示されるはずです。
あとはこれをフォームにドラッグアンドドロップして名前をFieldに変更します。
はりつけできました!
丁寧で わかりやすかったです!
ありがとうございました。
はじめまして、参考にしてゲームを作ろうと思ったのですが、
public Form1()
{
Field.KeyDown += Field_KeyDown;
にて、現在のコンテキストに名前が存在しないエラーが発生します。
また、ぷよぷよ以外にも縦シューティングも参考にして作りたいのですがコードが途切れ途切れに記載してあるので分かりにくいです。
ソースコード全体の記載があると助かります。お手数をおかけしますがよろしくお願いします。
Fieldが見つからないということでしょうか?
これは https://lets-csharp.com/puyo-modoki/#comment-893 を参考にしてください。
>コードが途切れ途切れに記載してあるので分かりにくいです。
>ソースコード全体の記載があると助かります。
たしかにご指摘のとおりですね。時間がかかるかもしれませんが対応します。
ぷよぷよのソースファイルはここからダウンロードできます。
https://lets-csharp.com/zip/puyopuyo.zip
縦シューティング
https://lets-csharp.com/zip/lengthwise-shooting-game.zip
丁寧に対応して頂きありがとうございます。助かります。