[完]Visual StudioとC#を初めて使ってブロック崩しゲームを作ってみた④

![ダウンロード.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/0dc83970-89e3-abc1-ceb7-bb651987e258.gif)![ダウンロード (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/10b2e4af-7e97-90be-35a8-53bfd964aaf6.gif)


前回：[Visual StudioとC#を初めて使ってブロック崩しゲーム
を作ってみた③](https://qiita.com/oh_yeah_sayryo/items/68fab28be412a49de315)

前回で一応完成していますが
今回はさらにゲームっぽくする為、機能追加をします。
##　
####アウトライン
- 設定ボタン追加
- カウントダウン開始3秒
- 経過中タイム表示
- CLEAR表示(動きを付けたい)
- ランキング表示(アプリ起動中)

####ディティール
- 設定ボタンの画面
    - ボール加速　on/off(ラジオボタン)
    - パドル移動距離 最大で1個分(スライドボタン)、下画面にパドル追加

##CLEAR表示
気分でCLEAR表示作成から作っていきます！
JavaScriptで作れるようなポヨンポヨン動く文字を作りたかったのにWindowsフォームじゃできない？みたいです。
なのでプロパティを活用して見た目をよくします。
CLEAR用のラベルだけ追加してサイズ、色、配置等はお任せします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/80409d73-a715-764d-34f1-cdccecbf94d1.png)
頑張っても古臭いデザインにしかなりませんでしたｗ

初期値でCLEARを入れているので、
ブロック数が1以上のときに文字を変更します。
以下をform3_loadメソッドに追加。

```Form3.cs
            if (Form1.blockNum > 0)
            {
                label7.Text = "NotGood..";
                label7.ForeColor = Color.Black;
            }
```
イメージ↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/8bb9799c-2c11-7b74-469d-d12cc2272e02.png)

####CLEAR時の遷移処理を書き忘れていました。
ブロック数0のときを失敗時の条件を追加します。

```Form1.cs
            //失敗時・成功時
            if (ballPos.Y > this.Height || blockNum == 0)
            {
                //画面閉じてリザルト表示
                keikaTime.Stop();
                timer.Stop();
                this.Close();
                this.Hide();
                Form3 form3 = new Form3();
                form3.ShowDialog();
            }
```
##カウントダウン開始
これも数字が縮小していって次の数字を出したかったのに難しいようで、、、
いや縮小させるだけならタイマーでサイズダウンさせます！…。

Form1で以下ラベル作成
プロパティのFontでサイズを大きくします。これは48pt
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/6bef796f-c758-08e8-0922-14196dd06202.png)

追加コードは以下になります。

```Form1.cs
    public partial class Form1 : Form
    {
        Timer timerCount = new Timer(); //カウントダウンタイマー
        int countSize = 200; //カウント文字のサイズ
        int countNo = 3; //カウント数

        public Form1()
        {
            label1.Font = new Font(label1.Font.OriginalFontName, countSize + 1); //カウントダウン初期表示サイズ指定

            //カウントダウン開始
            timer.Interval = 100;
            timerCount.Tick += new EventHandler(countDown); //timer.Trik：Timer有効時に呼ばれる
            timerCount.Start();
        }

        private void countDown(object sender, EventArgs e)
        {
            if (countSize == 0)
            {
                countNo--;
                label1.Text = countNo.ToString();
                countSize = 200;
            }

            if (countSize == 20 && countNo == 1)
            {
                timerCount.Stop();

                //ゲームスタート
                countDownAfter();
            }
            countSize -= 20;
            label1.Font = new Font(label1.Font.OriginalFontName, countSize +1);
        }

        private void countDownAfter()
        {
            //ゲームタイマー
            timer.Interval = 33;
            timer.Tick += new EventHandler(Update); //timer.Trik：Timer有効時に呼ばれる
            timer.Start();

            //経過時間スタート
            keikaTime.Restart();
        }
```
①まずカウントダウン用のタイマーを作成
②タイマー有効時に呼ばれるcountDownメソッドを作成
③countDownAfterメソッド作成。カウントダウン後にゲームを開始するので
ゲーム中のタイマーと経過時間処理をそこへ移動。

フォントの変更の仕方結構調べました。
この記事に感謝します。https://qiita.com/r-ngtm/items/276a6ee832bd32afed36

実行してみます。
![カウントダウン.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/0594046e-0226-f679-9146-b8e0b2a7f24e.gif)


文字ボックスのサイズを小さくしているので値が小さくなるにつれて
左上に寄ってしまいます。
画面のXY軸を半分にすると画面の中心になります。
そこからカウントダウンのラベルサイズの半径を引くと
画面の中心に表示されるようになります。

Form()とcountDown()でラベルをnewで作り出した後に位置を指定します。
「label1.Left」と「label1.Top 」の2行をそれぞれに追加します。

```Form1.cs
public Form1()
        {
          label1.Font = new Font(label1.Font.OriginalFontName, countSize + 1); //カウントダウン初期表示サイズ指定
          label1.Left = (this.Width / 2) - (countSize / 2);
          label1.Top = (this.Height /2) - (countSize / 2);
・・・
private void countDown(object sender, EventArgs e)
        {
         label1.Font = new Font(label1.Font.OriginalFontName, countSize +1);
          label1.Left = (this.Width / 2) - (countSize / 2);
          label1.Top = (this.Height / 2) - (countSize / 2);
```
「コントロール名.Left」でX軸、TopでY軸を位置指定できます。

ラベルの四角い背景が後ろと被らないように透明にします。
Form1()のlabel1.Top処理の下に書きます。

```Form1.cs
label1.BackColor = Color.Transparent; //背景透明化
```
![カウントダウン完成.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/368ff339-a703-3e33-3977-853de00650bb.gif)

##経過中タイム表示
Form1にラベルを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/78d92262-1651-9c2a-c150-97fd7a985aa7.png)

新しくタイマーを作るのも面倒だし、
定期処理のUpdate()に追加します。

```Form1.cs
Form(){
label2.BackColor = Color.Transparent; //背景透明化
・・・
private void realTime() //リアルタイム表示
        {
            label2.Text = keikaTime.Elapsed.ToString().Substring(6, 6);
        }
・・・
private void Update(object sender, EventArgs e)
        {
            //リアルタイム
            realTime();
```
先ほどのlabel1の透明化処理の下にlabel2の透明化処理も書きます。
realTime()で表示するテキストを指定（リザルト画面の処理を丸パクリする）。

##設定ボタン追加
ここでイメージしている画面は、スマホ版バトロワのエイム感度の設定画面
![COD_SEN005.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/334477c5-128e-bd28-2d64-396e244aca55.jpeg)
上の画面はタブ切り替えでページが切り替わります。
ユーザコントロールを利用してこれを作成、と思いましたがTabControlを使えば簡単そうです。

設定ボタンは後にして、先に設定画面を作ります。
Form4画面を追加します。
ツールボックス→すべてのWindowsフォーム→「TabControl」を画面に設置する。
テキストは対象タブを選択して、タブの画面エリア部をクリックし
そこからのプロパティでタブのテキストを変更できる(パドル距離・ボール加速)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/621a9bea-857d-834e-1508-ffcc6f78e469.png)
###パドル設定
ツールボックス→すべてのWindowsフォーム→「TrackBar」を画面に設置する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/f45235ab-48a2-1681-1274-a00c6131ab0f.png)
プロパティを変更します。Form4に記述するか、プロパティに直接打ちます。
 trackBar1.Minimum = 1; //最小値
 trackBar1.Maximum = 100; //最大値
 trackBar1.Value = 50; //初期値
 trackBar1.TickFrequency = 0; //メモリ刻む値
 trackBar1.SmallChange = 1; //マウスで移動できる値
 trackBar1.BackColor= Window; //とりあえず白く
 trackBar1.TickStyle = Both; //つまみの形

参考→[TrackBarの基本](http://codepanic.itigo.jp/cs/trackbar.html)
メモリを刻むのは今どきダサいのでしませんw
以下のようになったはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/b07d1b55-3c34-d64c-1017-417e491f5446.png)
左右にボタン、下にラベルを設置します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/c31c8134-b4e5-bffa-c079-87f7109d598f.png)
TrackBarのイベントを作成します。
プロパティ→イベント→MouseMoveにbar_Move入力。

```Form4.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Breakout
{
    public partial class Form4 : Form
    {
        public static int paddleMove { get; set; } //パドル距離

        public Form4()
        {
            InitializeComponent();
            label1.Text = trackBar1.Value.ToString(); //距離初期値
        }

        //バー移動時
        private void bar_Move(object sender, EventArgs e)
        {
            label1.Text = trackBar1.Value.ToString();
            paddleMove = trackBar1.Value;
        }

        private void down_Click(object sender, EventArgs e)
        {
            trackBar1.Value --;
            bar_Move(sender, e);
        }

        private void up_Click(object sender, EventArgs e)
        {
            trackBar1.Value ++;
            bar_Move(sender, e);
        }
    }
}
```

Form2にて設定ボタン追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/f9d6ec35-af6a-023a-0ca4-97c932129a33.png)
プロパティのイベントのClickに「setting_Click」を入力しエンター押下。
以下の内容を追記

```Form2.cs
private void option_Click(object sender, EventArgs e) //設定押下時
        {
            // Form4のインスタンスを生成
            Form4 form4 = new Form4();
            // form4を表示
            form4.ShowDialog();
        }
```

Form1のパドル移動処理(KeyPressed)を変更します。

```Form1.cs
        private void KeyPressed(object sender, KeyPressEventArgs e) //押下毎
        {
            if (Form4.paddleMove == 0) //設定行っていない場合
            {
                Form4.paddleMove = 20;
            }
            if (e.KeyChar == 'a' && paddlePos.Left > 0) //A押下時
            {
                this.paddlePos.X -= Form4.paddleMove;
            }
            else if (e.KeyChar == 's' && paddlePos.Right < this.Width) //S押下時
            {
                this.paddlePos.X += Form4.paddleMove;
            }
        }
```

こんな感じで設定できるようになりましたね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/ae23bcef-5948-0b89-aa89-b4dc1b31fa2f.png)

次は下画面に設定した値に合わせて自動で動くイメージ図を追加します。
Form4のデザインにツールボックスから「GroupBox」を設置します。(ツールボックス→コンテナー)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/34be2f08-d473-be73-25a8-352263045964.png)


![S6drGV2jbS0B1rudzuQp1587379619-1587379637.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/564990dc-ab97-7667-5efc-16c4a273601b.gif)
↑のようにパドルを追加していきます。
パドルを前後させる処理をタイマーを使って作成します。
前後の判断はフラグ用変数を使います。
GroupBoxのプロパティ→イベント→表示に「DrawPaddle」を入力してメソッドを作成します。
(GroupBoxのプロパテじゃないと他と被って見えなくなるので注意)
以下ソースです。

```Form4.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Breakout
{
    public partial class Form4 : Form
    {
        Rectangle paddlePos; //パドル位置(Rectangle:四角形を作成)

        Timer paddleTimer = new Timer();

        Boolean paddleFrag;

        public static int paddleMove { get; set; } //パドル距離

        public Form4()
        {
            InitializeComponent();

            label1.Text = trackBar1.Value.ToString(); //距離初期値

            this.paddlePos = new Rectangle(10, groupBox1.Height / 2, 100, 5); //パドル(位置横縦,サイズ横縦)

            paddleTimer.Interval = 1000;
            paddleTimer.Tick += new EventHandler(paddleAutoMove); //timer.Trik：Timer有効時に呼ばれる
            paddleTimer.Start();

            paddleMove = 50;
        }

        //バー移動時
        private void bar_Move(object sender, EventArgs e)
        {
            label1.Text = trackBar1.Value.ToString();
            paddleMove = trackBar1.Value;
        }

        private void down_Click(object sender, EventArgs e)
        {
            trackBar1.Value --;
            bar_Move(sender, e);
        }

        private void up_Click(object sender, EventArgs e)
        {
            trackBar1.Value ++;
            bar_Move(sender, e);
        }

        //パドル表示
        private void DrawPaddle(object sender, PaintEventArgs e)
        {
            SolidBrush grayBrush = new SolidBrush(Color.DimGray);

            //e.描画.長方形(色, 長方形)
            e.Graphics.FillRectangle(grayBrush, paddlePos);
        }

        //パドル動作
        private void paddleAutoMove(object sender, EventArgs e)
        {
            if (paddleFrag == false)
            {
                this.paddlePos.X += paddleMove;
                paddleFrag = true;
            }
            else
            {
                this.paddlePos.X -= paddleMove;
                paddleFrag = false;
            }
            //再描画
            groupBox1.Invalidate();
        }
    }
}
```
起動させてパドルを動かしてみてください。
パドルの値に応じてパドルの移動距離が変わります。

パドルの移動距離を上げてからヌルゲーになってしまったので難易度を調整します。

```Form2.cs
 switch (selectedMode)
            {
                case 0:
                    //Easy
                    x = -5;
                    y = -10;
                    break;
                case 1:
                    //Normal
                    x = -8;
                    y = -15;
                    break;
                case 2:
                    //Hard
                    x = -10;
                    y = -20;
                    break;
                case 3:
                    //Expert
                    x = -15;
                    y = -30;
                    break;
                default:
                    //未選択時はNormal
                    x = -8;
                    y = -15;
                    break;
            }
```

Expertモードでやってみました。
![ダウンロード.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/c8fe52a0-933c-2a02-a8d9-5d5d8809ad6c.gif)
難しいｗ

####加速設定

ボールを加速させる機能を追加します。
以下のようにラジオボタンを追加してください。
OFFの方にチェックを入れる方法はプロパティから「Checked」の値をTrueへ変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/603579be-7c69-af89-c2ef-ad9924ec6f81.png)

設定画面からONかOFFなのかを変数で渡します。
ボール移動処理を行っているForm1(プレイ)と、
得点も増やしたいのでForm3(リザルト)にも渡します。

ONボタンのイベントから「CheckedChanged」でメソッドを作成します。
加速判定をする変数を追加し、ONを選択されていたら加速をONにします。

```Form4.cs
public static Boolean accelFrag; //加速フラグ
・・・
 //加速ONボタン
        private void radioButton1_CheckedChanged(object sender, EventArgs e)
        {
            if (radioButton1.Checked == true)
            {
                accelFrag = true;
            }
        }
```
Form1にて加速処理を追記します。
加速速度変数を用意し、Updateメソッドの「//ボールの移動」を以下に書き換えます。

```Form1.cs
double accelSpeed = 1; //加速速度
・・・
//ボールの移動
            //加速判定
            int x = int.Parse(keikaTime.Elapsed.ToString().Substring(9, 3)); //リアルタイムのコンマ3桁を取得
            if (Form4.accelFrag == true && x/100 == 1) //0.1秒毎にスピードUP
            {
                ballSpeed.X *= accelSpeed;
                ballSpeed.Y *= accelSpeed;
                ballPos += ballSpeed;
                accelSpeed += 0.001;
            }
            else
            {
                ballPos += ballSpeed;
            }
```
加速ON状態でのスコアを増やします。
Form3のscoreメソッドの「//倍率設定」の下に追記します。

```Form3.cs
            //倍率設定
            double bairitsu = ((Form2.mode + 1) /10 * 2) + 1;
            //加速ONで倍率2倍
            bairitsu *= 2;
```



完成です。

あと、VisualStudioを開かなくても、プロジェクト→プロジェクト→bin→Debugからいつでも遊べます！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384409/ec0ad1d3-20ef-b980-0411-4cc844b834e3.png)



##不具合対応
・カウントダウン時に画面を閉じるとリザルト画面が表示される
Form1.csのform1_Closingメソッドに以下を追加する。
` timerCount.Stop();`
カウントダウン終了時にUpdate処理のタイマーを起動しているので元となるカウントダウンタイマーを止める必要がある。

・カウントダウン時にパドル移動するとおかしな挙動
カウントダウン時のパドル操作を不可にする。
Form1.csで以下のif文でKeyPressedの処理全てを囲む。
`if (!timerCount.Enabled){}`

・リプレイ時にブロックがなくなっても終わらない
Form1.csのForm1メソッド(コンストラクタ)の始めに初期値を追加。
`blockNum = 0;`
・パドル端にボールが触れたときに離れない問題
Expertモード時ボールが早すぎてパドルを貫通しているのが原因？。
ゲームタイマーのインターバルを33から10にする。
ボールがかなりスムーズになる。ボールのスピードも3倍になってしまっているので、
Form1.csのballSpeedを1/3にするか　`this.ballSpeed = new Vector(Form2.x/3, Form2.y/3);`
Form2.csのmode_Selectメソッドのそれぞれの値を小さくする。ん、直ってなかった…。
単純にForm1のupdateメソッドの「//パドルの当たり判定」のballSpeed.Y *= -1;を
if (ballPos.Y  > paddlePos.Top)｛｝で囲めば直った。

ここまでのソースを公開　[githubへ](https://github.com/seyryo/sayryo/tree/sub/Breakout)
##感想
ここまでお付き合いいただきありがとうございました！

Visual StudioとC#を使ってみて。
Eclipseより楽しいし簡単に作れる！
入門はJavaよりC#選ぶべきだったかも・・
ただWindowsフォームだとCSSとかJavaScriptがないのは不便。
動きを付けるのには向いてなく、
本当に業務システム向けだなって感じでした。

学びやすいからC#でスタートダッシュをして、
それから他の言語をやるのもありかと。
追加機能でスコア毎にS～Dなどのランクをつけて表示するなど色々いじって身につけるください！
1ファイルが長くなったり、ファイル数が増えてきたら、フォルダ―を追加したり共有ファイルを作ったりしましょう。
実際の現場では何十何百とファイルがありますが
小分けにされているだけなので怖がらないように！

興味もない業務システム作るのはあんまり楽しくないですよね。。
今回ゲーム作ってみてプログラミングが楽しいというよりは自作ゲームが楽しいだけでした。
プログラミングは手段でしかないと実感しましたね。
次の案件がVisual Studio(C#)で作られた業務システム。

この為に時間を与えられ勉強したのに、急遽別案件へ…。
言語はvbaらしい。
(知らねぇ…)
部長は「C#と大体同じやから大丈夫！」
と言ってたが、どうなるやら。