C#

【WPF】初心者が作るモグラ叩きゲーム(応用編)

どうも、「○NAKA」です。

今回は第2弾として、前回の「もぐら叩きゲーム」で実装できなかった仕様に取り掛かります。
前回の内容をまだ読んでない方はこちらからアクセスできます。

【WPF】初心者が作るモグラ叩きゲーム(基本編)C#のWPFで誰もが経験がある「もぐら叩きゲーム」を作成しました。3×3のマスの穴からもぐらがランダムで出現して、そのもぐらを叩く(マウスでクリックする)とスコアがもらえるゲームです。初心者でも簡単に作れるアプリなので参考にしてください。...

おさらい

3×3のマスの穴からモグラがランダムで表示されて、そのモグラを叩く(マウスでクリックする)とスコアが増えるゲームを作成しました。ゲームを面白くするための仕様を色々と盛り込んでいたのですが、難しいそうな仕様は対象外にしていました。今回は、その対象外だった仕様3つについてさっそく取り掛かってみます。

もぐら叩きゲーム仕様
  • ランダムに通常モグラとレアモグラのどちらか1匹が出現する
  • ノーマルもぐらをクリックしたら、10点のスコアがもらえる
  • レアもぐらをクリックしたら、30点のスコアがもらえる
  • 時間制限を設ける(20秒間)
  • 指定秒数毎に出現するもぐらが変わる(指定秒数:1秒)
  • 難易度毎にもぐらの出現秒数を変更できる
  • BGMを追加
  • マイナスもぐらをクリックしたら、−10点のスコアがもらえる

前回作成したもぐら叩きゲームはこんな感じです。

プログラム実装

  • 開発環境:visual studio2019
  • 開発プラットフォーム:WPF
  • ソリューション名:Whac_A_Mole_GAME

UI画面の作成

前回のUIにSlider(スライダー)というコントロールを追加します。難易度の調整は、このSliderをスライドさせて、もぐらの出現秒数を変更させます。Sliderのサイズを調整して、Scoreの下に配置しました。

コードの実装

追加仕様で実装する処理について簡単にまとめました。

  1. Sliderの設定
  2. タイマーメソッド追加
  3. ゲーム待機中のサウンドとゲーム中のサウンドの追加
  4. マイナスもぐらの画像追加
  5. マイナスもぐらの出現追加
  6. マイナスもぐらのスコア加算

Sliderの設定

もぐらの出現秒数を1秒、0.8秒、0.6秒で調整できるようにします。XAMLでSliderの設定をします。SliderのTicksに1、0.8、0.6を入力します。初期値を1秒にしたいので、Valueを1にします。

タイマーメソッド追加

前回は、タイマーメソッド(DispatcherTimer_Tick)に時間制限の処理ともぐらの出現処理を一緒にしていました。もぐらの出現間隔を変更させるために、このままタイマーのインターバルを早めると、もぐらの出現間隔は変わります。しかし、制限時間がめちゃくちゃになってしまいます。これではクレームが殺到してしまいます。
もぐら出現秒数を変更するタイマーメソッド(MoleDispatcherTimer_Tick)を用意します。前回と同じようにLoadedハンドラー内に、タイマーの初期設定を書きます。インターバルは、Sliderで選択した値を使用します。

private DispatcherTimer timer = new DispatcherTimer();
private DispatcherTimer moletimer = new DispatcherTimer();

public MainWindow()
{
    InitializeComponent();

    // Windowをドラッグできるようにする
    MouseLeftButtonDown += (e, s) => DragMove();

    // アプリケーションの終了
    btnPower.Click += (s, e) => Application.Current.Shutdown();

    Loaded += (s, e) =>
    {
         //時間制限用
         timer.Tick += new EventHandler(DispatcherTimer_Tick);
         timer.Interval = TimeSpan.FromSeconds(1);

         //もぐら出現用
         moletimer.Tick += new EventHandler(MoleDispatcherTimer_Tick);
         moletimer.Interval = TimeSpan.FromSeconds(sldrLevel.Value);
    };
}

DispatcherTimer_Tickメソッド内のもぐら出現処理をMoleDispatcherTimer_Tickメソッドへ移行します。20秒経過したらもぐらが出現しないように、DispatcherTimer_Tickメソッドで監視します。時間制限用のタイマーが停止したら、もぐら出現用のタイマーも停止します。

private void MoleDispatcherTimer_Tick(object sender, EventArgs e)
{
    if (timer.IsEnabled)
    {
        MoleAppearance();
    }
    else
    {
        moletimer.Stop();
    }
}

ゲーム待機中のサウンドとゲーム中のサウンドの追加

ソリューションエクスプローラーのプロジェクトを右クリックし、「追加」>「新しいフォルダ」をクリックし新規フォルダを作成します。フォルダ名を「Sounds」に変更します。このフォルダにサウンドファイルをドラッグ&ドロップして追加します。今回使用するファイルの拡張子は、wavです。フォルダに追加したサウンドファイルを右クリックしてプロパティを開き、出力ディレクトリにコピーを「新しい場合はコピーする」または「常にコピーする」に変更します。

出力ディレクトリにコピーに「コピーしない」を選択すると、ビルドした時にSoundsフォルダにファイルがコピーされず、ファイルが見つかりませんというエラーが発生しますので、注意が必要です。


サウンドの実装は「Naudio」を使用します。

・WAV
・MP3 (ACMまたはDMO codec)
・AIFF
・ADPCM
・Speex (NSpeex codec)
・SF2 (SoundFont)
・インストールされている任意のACM codec

Nugetからダウンロードして参照を追加します。Naudioは調べてみると、色々な機能があるのですが、ここでは最低限の機能のみで実装します。ゲーム中と待機中のサウンドを簡単に制御するために、WaveOutはそれぞれのインスタンスを生成します。ゲーム中とゲーム待機中のサウンドが切り替わるようにサウンド開始と停止の処理を追加していきます。

WaveOut waitingwaveOut = new WaveOut();
WaveOut playingwaveOut = new WaveOut();

public MainWindow()
{
    InitializeComponent();

    // Windowをドラッグできるようにする
    MouseLeftButtonDown += (e, s) = > DragMove();

    // アプリケーションの終了
    btnPower.Click += (s, e) = > Application.Current.Shutdown();

    Loaded += (s, e) = >
    {
        //時間制限用
        timer.Tick += new EventHandler(DispatcherTimer_Tick);
        timer.Interval = TimeSpan.FromSeconds(1);

        //もぐら出現用
        moletimer.Tick += new EventHandler(MoleDispatcherTimer_Tick);
        moletimer.Interval = TimeSpan.FromSeconds(sldrLevel.Value);

        //サウンドの追加
        AudioFileReader reader = new AudioFileReader("Sounds\\waiting.wav");
        waitingwaveOut.Init(reader);
        waitingwaveOut.Play();

        reader = new AudioFileReader("Sounds\\playing.wav");
        playingwaveOut.Init(reader);
    };
}

private void btnStart_Click(object sender, RoutedEventArgs e)
{
    count = 0;
    btnStart.IsEnabled = false;
    sldrLevel.IsEnabled = false;
    timer.Start();

    moletimer.Interval = TimeSpan.FromSeconds(sldrLevel.Value);  //タイマーのインターバルを再設定
    moletimer.Start();

    lblScorebord.Content = "00000";
    lblTime.Content = "20";

    MoleAppearance();  //もぐらの出現処理

    waitingwaveOut.Stop();  //サウンド停止
    playingwaveOut.Play();  //サウンド開始
}

private void DispatcherTimer_Tick(object sender, EventArgs e)
{
    count++;  //1秒置きにインクリメント

    lblTime.Content = (20 - count).ToString();  //残り時間表示

    if (count == 20)
    {
        timer.Stop();  //20秒でゲーム終了
        playingwaveOut.Stop();  //サウンド開始

        MessageBox.Show("ゲーム終了");
        btnStart.IsEnabled = true;
        sldrLevel.IsEnabled = true;
        waitingwaveOut.Play();  //サウンド停止
    }
    else
    {

    }
}

マイナスもぐらの画像追加

マイナスもぐらは、ノーマルもぐら・レアもぐらと同じもぐらのイラストで、色だけを変更しました。この画像をImagesフォルダにドラッグ&ドロップして追加します。

マイナスもぐらの出現追加

乱数の結果の分類にマイナスもぐらも追加します。1~9までの乱数で、1であればレアもぐら、2であれば、マイナスもぐら、その他の数値であればノーマルもぐらとします。スコアをつけるときに、もぐらの種類がわかるようにボタンのTabにもぐらの種類を記録をしておきます。あとマイナスもぐらの画像が表示されるように処理を追加します。

private void MoleAppearance()
{
    MoleReset();

    //シート値を加えてインスタンスを生成する
    //モグラの出現場所の乱数
    var seed = Convert.ToInt32(Guid.NewGuid().ToString("N").Substring(0, 8), 16);
    var appearancePlace = new Random(seed).Next(1, 10);

    //モグラのレア度の乱数
    seed = Convert.ToInt32(Guid.NewGuid().ToString("N").Substring(0, 8), 16);
    var appearanceRarity = new Random(seed).Next(1, 10);

    Mole mole = Mole.None;
    switch (appearanceRarity)
    {
        case 1:
            mole = Mole.Rare;  //レアのモグラ
            break;
        case 2:
            mole = Mole.Minus;  //マイナスのもぐら
            break;
        default:
            mole = Mole.Nomal;  //通常のモグラ
            break;
    }

    //モグラを表示
    var btncrl = FindName(string.Format("btnMole_{0}", appearancePlace));
    if (btncrl != null)
    {
        var btn = (Button)btncrl;

        //もぐらの種類をTagに記録
        btn.Tag = mole;
        btn.IsEnabled = true;

        //モグラの画像を表示
        var Imgcrl = FindName(string.Format("ImgMole_{0}", appearancePlace));
        if (Imgcrl != null)
        {
            var Img = (Image)Imgcrl;
            if (Mole.Rare == mole)
            {
                Img.Source = new BitmapImage(new Uri(@".\Images\レアもぐら.png", UriKind.Relative));
            }
            else if (Mole.Minus == mole)
            {
                Img.Source = new BitmapImage(new Uri(@".\Images\マイナスもぐら.png", UriKind.Relative));
            }
            else
            {
                Img.Source = new BitmapImage(new Uri(@".\Images\ノーマルもぐら.png", UriKind.Relative));
            }
        }
    }
}

マイナスもぐらのスコア加算

最後にマイナスもぐらをクリックしたときにスコアが加算されるようにします。

private void btnMole_Click(object sender, RoutedEventArgs e)
{
    Button btn = (Button)sender;
    btn.IsEnabled = false;

    int score = int.Parse((string)lblScorebord.Content);

    switch (btn.Tag)
    {
        case Mole.Rare:
            score += 30;
            break;
        case Mole.Minus:
            score += -10;
            break;
        case Mole.Nomal:
            score += 10;
            break;
    }

    var str = "00000" + score.ToString();
    lblScorebord.Content = str.Substring(str.Length - 5, 5);  //スコア反映
}

アプリケーションの起動

難易度を設定してスタートボタンをクリックすると、難易度に合わせてもぐらが一定間隔で出現します。もぐらをクリックすると、もぐらの種類によってスコアが加算されたり、減算されたりします。サウンドが追加されたことでゲームぽくなりました。

以上、最後までご覧いただきありがとうございました。

プログラミングを学習したいなら…

プログラミングスキルを身に付けるなら、プログラミングを効率良く学べる
プログラミングスクール」がオススメです。

特にこんな方にオススメ!!
これからエンジニアを目指したい
プログラミングの専門性を高めたい
プログラミングを学んで副業をしたい
エンジニアに転職して年収をアップさせたい

プログラミングを触ったことがない未経験からでも、プログラミングスクールで学習すれば、エンジニアへ就職・転職することも可能です。

あなたの「行動力」と「やる気」で、あなたの人生を大きく変えるチャンスになることでしょう。

プログラミングスクールに興味がある方は是非チェックしてみてください。

> プログラミングを学ぶ <

COMMENT

メールアドレスが公開されることはありません。

CAPTCHA