0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-04-17

ダウンロード.gifダウンロード (1).gif

前回:Visual StudioとC#を初めて使ってブロック崩しゲーム
を作ってみた③

前回で一応完成していますが
今回はさらにゲームっぽくする為、機能追加をします。

 

アウトライン

  • 設定ボタン追加
  • カウントダウン開始3秒
  • 経過中タイム表示
  • CLEAR表示(動きを付けたい)
  • ランキング表示(アプリ起動中)

ディティール

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

CLEAR表示

気分でCLEAR表示作成から作っていきます!
JavaScriptで作れるようなポヨンポヨン動く文字を作りたかったのにWindowsフォームじゃできない?みたいです。
なのでプロパティを活用して見た目をよくします。
CLEAR用のラベルだけ追加してサイズ、色、配置等はお任せします。
image.png
頑張っても古臭いデザインにしかなりませんでしたw

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

Form3.cs
            if (Form1.blockNum > 0)
            {
                label7.Text = "NotGood..";
                label7.ForeColor = Color.Black;
            }

イメージ↓
image.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

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

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

文字ボックスのサイズを小さくしているので値が小さくなるにつれて
左上に寄ってしまいます。
画面の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

経過中タイム表示

Form1にラベルを追加します。
image.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
上の画面はタブ切り替えでページが切り替わります。
ユーザコントロールを利用してこれを作成、と思いましたがTabControlを使えば簡単そうです。

設定ボタンは後にして、先に設定画面を作ります。
Form4画面を追加します。
ツールボックス→すべてのWindowsフォーム→「TabControl」を画面に設置する。
テキストは対象タブを選択して、タブの画面エリア部をクリックし
そこからのプロパティでタブのテキストを変更できる(パドル距離・ボール加速)。
image.png

パドル設定

ツールボックス→すべてのWindowsフォーム→「TrackBar」を画面に設置する。
image.png
プロパティを変更します。Form4に記述するか、プロパティに直接打ちます。
trackBar1.Minimum = 1; //最小値
trackBar1.Maximum = 100; //最大値
trackBar1.Value = 50; //初期値
trackBar1.TickFrequency = 0; //メモリ刻む値
trackBar1.SmallChange = 1; //マウスで移動できる値
trackBar1.BackColor= Window; //とりあえず白く
trackBar1.TickStyle = Both; //つまみの形

参考→TrackBarの基本
メモリを刻むのは今どきダサいのでしませんw
以下のようになったはずです。
image.png
左右にボタン、下にラベルを設置します。
image.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
プロパティのイベントの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

次は下画面に設定した値に合わせて自動で動くイメージ図を追加します。
Form4のデザインにツールボックスから「GroupBox」を設置します。(ツールボックス→コンテナー)
image.png

S6drGV2jbS0B1rudzuQp1587379619-1587379637.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
難しいw

加速設定

ボールを加速させる機能を追加します。
以下のようにラジオボタンを追加してください。
OFFの方にチェックを入れる方法はプロパティから「Checked」の値をTrueへ変更します。
image.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

不具合対応

・カウントダウン時に画面を閉じるとリザルト画面が表示される
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へ

感想

ここまでお付き合いいただきありがとうございました!

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

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

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

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

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?