完成品はこんな感じになります。
Contents
点数を表示させる
今回は前回までに作成したぷよぷよもどきのアプリに点数を表示させます。
点数の計算式
ぷよぷよの得点計算はどのようになっているのでしょうか?
ぷよぷよの得点計算のやり方は?全消し・落下・連結・多色ボーナスの計算方法まとめ | エージの自由帳によると点数の計算式は以下のようになっています。
ぷよの消えた数×10(連鎖ボーナス+連結ボーナス+色数ボーナス)
連鎖ボーナス、連結ボーナス、色数ボーナスは以下のようになっています。
連鎖数 連鎖ボーナスは連鎖数によって変化します。
連鎖数 | 連鎖ボーナス |
---|---|
1 | 0 |
2 | 8 |
3 | 16 |
4 | 32 |
5 | 64 |
6 | 96 |
7 | 128 |
8 | 160 |
9 | 192 |
10 | 224 |
11 | 256 |
12 | 288 |
13 | 320 |
14 | 352 |
15 | 384 |
16 | 416 |
17 | 448 |
18 | 480 |
19 | 512 |
連結ボーナス 連結ボーナスはぷよぷよの連結数によって変化します。
連結数 | 連結ボーナス |
---|---|
4 | 0 |
5 | 2 |
6 | 3 |
7 | 4 |
8 | 5 |
9 | 6 |
10 | 7 |
11以上 | 10 |
色数ボーナス 色数ボーナスは消した時の色数によって変化します。
色数 | 色数ボーナス |
---|---|
1 | 0 |
2 | 3 |
3 | 6 |
4 | 12 |
5 | 24 |
「連鎖ボーナス+連結ボーナス+色数ボーナス」の合計が0になる場合は1として計算します。
1と2は、両方とも2連鎖ですが、色数ボーナスがあるので点数が違ってきます。
1図の場合
1連鎖目は
7個消えるのでぷよの消えた数は7
連鎖ボーナスは1連鎖目なので0
7個つながっているので連結ボーナスは4
消した時の色数は1色だけなので色数ボーナスは0
2連鎖目は
8個消えるのでぷよの消えた数は8
連鎖ボーナスは2連鎖目なので8
つながっているのは4個と4個なので連結ボーナスは0+0=0
消した時の色数は1色だけなので色数ボーナスは0
上記の公式に当てはめると
1連鎖目:7×10×(0+4+0) = 7×10×4 = 280
2連鎖目:8×10×(8+(0+0)+0) = 8×10×8 = 640
合計:280+640 = 920となります。
2図の場合
2はどうでしょうか?
1連鎖目は同じです。
2連鎖目は
8個消えるのでぷよの消えた数は8
連鎖ボーナスは2連鎖目なので8
つながっているのは4個と4個なので連結ボーナスは0+0=0
消した時の色数は2色なので色数ボーナスは3
2連鎖目:8×10×(8+(0+0)+3) = 8×10×11 = 880
合計:280+880 = 1160となります。
では実際に点数計算をさせてみましょう。
DeletePuyoIfNeedメソッドを一部変更する
DeletePuyoIfNeedメソッドの戻り値をint型に変更しました。戻り値が計算された得点になります。
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 69 70 71 72 73 74 75 76 77 78 79 80 81 |
public partial class Form1 : Form { async Task<int> DeletePuyoIfNeed(int rensa) { // 設置されたプヨの下に空洞があれば上のプヨを落として空洞をうめる Task<bool> task = DownPuyoIfSpaces(); await task; List<Cell> deleteCells = new List<Cell>(); // 連結ボーナス計算用 List<int> vs = new List<int>(); // 4つつながっているぷよがあるか探す for(int i = 0; i < FIELD_HEIGHT; i++) { for(int j = 0; j < FIELD_WIDTH; j++) { Cell cell = Cells[i, j]; if(deleteCells.Any(x => x == cell)) continue; List<Cell> cells = GetConectedCell(cell); ClearCheck(); if(cells.Count >= 4) { deleteCells.AddRange(cells); vs.Add(cells.Count); // 消えるぷよに連鎖回数がわかるように番号をつける Puyo puyo = cells[0].Puyo; Image image = GetImageFromPuyoTypeRensa(puyo, rensa); Graphics g = Graphics.FromHwnd(Field.Handle); foreach(Cell cell1 in cells) { Rectangle rect = new Rectangle(cell1.LeftTop, Cell.Size); g.DrawImage(image, rect); } g.Dispose(); } } } // 得点は「ぷよの消えた数 ×(連鎖ボーナス+連結ボーナス+色数ボーナス)× 10」である // ぷよの消えた数 int puyoCount = deleteCells.Count; // 連鎖ボーナス(後述) int rensaBonus = GetRensaBonus(rensa); // 連結ボーナス(後述) int renketsuBonus = vs.Sum(x => GetRenketsuBonus(x)); // 色数ボーナス(後述) int colorsKind = deleteCells.Distinct(new PuyoEqualityComparer()).Count(); int colorsBonus = GetColorCountBonus(colorsKind); int score = 0; if(rensaBonus + renketsuBonus + colorsBonus != 0) score = puyoCount * (rensaBonus + renketsuBonus + colorsBonus) * 10; else score = puyoCount * 10; // ぷよを実際に消す foreach(Cell cell0 in deleteCells) { cell0.Puyo = Puyo.None; } if(deleteCells.Count != 0) { await Task.Delay(1000); Field.Invalidate(); } return score; } } |
連鎖ボーナスと連結ボーナスの計算に関する部分を示します。
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 { // 連鎖ボーナスの計算 int GetRensaBonus(int i) { if(i == 1) return 0; if(i == 2) return 8; if(i == 3) return 16; if(i >= 4 || i <= 19) return (i - 3) * 32; if(i > 19) return 512; return 0; } // 連結ボーナスの計算 int GetRenketsuBonus(int i) { if(i == 4) return 0; if(i >= 5 || i <= 10) return i - 3; if(i < 11) return 10; return 0; } } |
次に色数ボーナスの計算に関する部分を示します。
1 2 |
int colorsKind = deleteCells.Distinct(new PuyoEqualityComparer()).Count(); int colorsBonus = GetColorCountBonus(colorsKind); |
重複を排除するためにLinqのDistinctメソッドに引数を渡しています。ここでの「重複」の意味ですが、「ぷよの種類が同じであれば重複している」と判断します。
PuyoEqualityComparerクラスとGetColorCountBonusメソッドは以下のとおりです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class PuyoEqualityComparer : IEqualityComparer<Cell> { public bool Equals(Cell cell1, Cell cell2) { if(cell2 == null && cell1 == null) return true; else if(cell1 == null || cell2 == null) return false; else if(cell1.Puyo == cell2.Puyo) return true; else return false; } public int GetHashCode(Cell cell) { int hCode = cell.Puyo.GetHashCode(); return hCode.GetHashCode(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public partial class Form1 : Form { int GetColorCountBonus(int i) { if(i == 1) return 0; if(i == 2) return 3; if(i == 3) return 6; if(i == 4) return 12; if(i == 5) return 24; return 0; } } |
OnFixedメソッドを一部変更する
DeletePuyoIfNeedメソッドの戻り値を変更したのでOnFixedメソッドも一部変更が必要です。返された値を利用して現在の点数を表示します。
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 { async void OnFixed() { isIgnoreKeyDown = true; timer1.Stop(); int rensa = 1; int tempScore = 0; // 加算される点数 while(true) { Task<int> task = DeletePuyoIfNeed(rensa); int ret = await task; if(ret == 0) break; tempScore += ret; rensa++; } Score += tempScore; // 点数を加算 isIgnoreKeyDown = false; timer1.Start(); CreateNewPuyo(); } } |
Scoreプロパティ
得点はここに表示させます。Scoreプロパティを変更すると表示されている点数も同時に変わるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public partial class Form1 : Form { int score = 0; int Score { get { return score; } set { score = value; label1.Text = "Score " + score.ToString(); } } } |
白色太字で表示させます。それからタイマー処理を書いていなかったので書いておきます。
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 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); Field.Paint += Field_Paint; Field.KeyDown += Field_KeyDown; this.BackColor = Color.Black; Field.BorderStyle = BorderStyle.None; // 追加部分(白色太字で表示させる) label1.ForeColor = Color.White; label1.Font = new Font("MS ゴシック", 12, FontStyle.Bold); // タイマー処理(デザイナで設定したのであれば不要) this.timer1.Interval = 800; this.timer1.Tick += new System.EventHandler(this.timer1_Tick); // 最初は「0」と表示 Score = 0; GetPuyoImages(); InitCells(); } public partial class Form1 : Form { private void timer1_Tick(object sender, EventArgs e) { PuyoMoveDownIfCan(); } } } |
得点のところで以下の4つ メソッドが見当たらない
あとscoreは public intでいいですよね? 宣言がみあたらない
あとtimer1は tickに PuyoMoveDownIfCan();だけしてますが
いいですかね?
GetRensaBonus(rensa);
GetRenketsuBonus(x));
PuyoEqualityComparer())
GetColorCountBonus(colorsKind);
欠けているメソッドを追加しました。
scoreはint型のフィールド変数ですが、publicにはしないでください。
Scoreプロパティを介して値の取得と設定をおこないます。
Scoreプロパティを介することで、Scoreプロパティを変更すればフィールド変数のscoreだけでなく、表示も同時に変更することができるからです。
あとtimer1はtickに PuyoMoveDownIfCan();だけしてますがいいですかね?の部分ですが、デザイナでインターバルを800ミリ秒とし、TickイベントがおきたらPuyoMoveDownIfCan()が実行できるようにしておけばよいです。
ありがとうございました
エラーなく実行できました。
うまくできたということでしょうか?
実際にこのブログのコードを試してくれている方がいて嬉しく思っています。
ただこちらも記述ミスが多く、混乱させてしまったのでその点については申し訳なく思っています。
こちらのサイトを参考にさせていただいています。
ぽよが天井まで積みあがったらゲームオーバーにしたいのですが、可能ですか?
やり方も出来れば、教えていただきたいです。
どうぞよろしくお願いいたします。
ゲームオーバー処理はやっているものと思い込んでいたのですが、できていませんでした。
ゲームオーバーかどうかの判定は新しいぷよが作られたときにその場所にすでに固定されたぷよがあるかどうかで判断できます。
すぐに対応して頂きありがとうございます。助かります。
教えていただいた。ゲームオーバーの方法を試してみたところスタートボタンを押してもゲームが開始されない状態になりました。デバッグしたところGameStartMenuItem_Clickメソッドのところが上手くいっていないところまでは確認できました。
お手数をおかけしますが確認をよろしくお願いします。
ゲーム開始部分の処理ですが、以下のように書いていました。
これだとIsGameOverがfalseの場合、なにもおきません。
だからプログラムが開始されたときはIsGameOverはtrueにしておかなければなりませんでした。
申し訳ありませんでした。
また間違いを指摘していただきありがとうございます。
こちらのサイトを参考にさせていただいております。ゲームオーバーになった際に画面上に文字を表示させると、コンテニューを実装したいと考えております。出来ればやり方を教えていただけますでしょうか。お手数をおかけしますがよろしくお願いします。
>ゲームオーバーになった際に画面上に文字を表示させる
一番簡単な方法はLabelに適当な大きさでGameOverを書いたものをゲームオーバー時だけ表示させるという方法です。VisualStudioのデザイナが使えるならできるはずです。
>コンテニューを実装
シューティングゲームとちがって落ち物パズル系の場合、ゲームオーバーになる寸前においてはいわゆる「完全に詰んだ状態」になってしまいます。そんな状態でコンティニューしても仕方ないのでどの状態まで巻き戻すかを考えないといけないのですが、どうしたものでしょうか? 質問に質問で返してしまって申し訳ないのですが・・・。