C#とWPFでインベーダー風ゲームを作る

VisualStudio2017が出たということで記念するわけではないですが、C#とWPFのみでインベーダー風ゲームを作ってみたいと思います。

すいません、嘘つきました。インベーダーを意識した気がするオリジナルなシューティングゲームです。リスペクトしたということでインベーダー風と言っています。

制作時間は3日に分けて3時間~4時間くらいで作成しました。

ゲームのルールと操作方法

  • スペースキーを押すと自機が弾を発射
    • 自機が撃てる弾は1画面に3発まで
  • 左右キーで自機の移動
  • 画面上部に侵略者(イカ)を生むUFOがいる
    • UFOは一定間隔でイカを生成する
  • イカは弾を出しながら手前に移動してくる

画面構成

今回、特にゲームエンジンを使わないのでWPFのWindowクラスの上にCanvasを張り付けてImageを動かしながらゲームを表現します。

f:id:Takachan:20170317012039p:plain:h350

作成環境

今回制作に使うツールは以下の通りです。

  • VisualStudio2017Community
    • .NET Framework4.6.2をターゲットにしています
  • Paint.net
    • 敵画像の作成に使用します

出来上がったゲームの画面

f:id:Takachan:20170317012256p:plain:h330

f:id:Takachan:20170317012301p:plain:h330

f:id:Takachan:20170317012305p:plain:h330

気が付いたこと

WPFはゲーム向けのライブラリとか一切ないので、60fpsをタイマークラスのElapsedイベントハンドラを1秒間に60回呼び出すことで対応しました。

またキー入力は、キーを押しっぱなしにすると、不連続に「テ、、、テテテテテ」と入力が走るので、キー入力処理の識別を自前で書く必要があります。

弾のあたり判定も無いので以下の通り自作しています。

public class GameUtil
{
    /// <summary>
    /// 指定した2つのオブジェクトが衝突しているか確認します。
    /// </summary>
    public static bool IsCollision(FrameworkElement a, FrameworkElement b)
    {
        double x_abs = 
          Math.Abs((Canvas.GetTop(a) + a.Width / 2)
                - (Canvas.GetTop(b) + a.Width / 2));
        double y_abs = 
            Math.Abs((Canvas.GetLeft(a) + a.Height / 2)
                - (Canvas.GetLeft(b) + a.Height / 2));

        double aw = a.Width / 2 + b.Width / 2;
        double ah = a.Height / 2 + b.Height / 2;

        return x_abs < aw && y_abs < ah;
    }
}

制作過程

以下のような順番で作成しました。

  • 1日目
    • 背景が黒い画面の作成
    • 自機の表示、左右キーでの移動
    • 自機の弾の発射、取り除き
  • 2日目
    • 防御ブロックの配置と自機の弾とのあたり判定
    • 敵UFOの表示と左右移動
  • 3日目
    • UFOのイカ生成処理
    • イカの移動、弾の発射
    • イカの弾と自機のあたり判定
    • ゲームオーバー/ゲームクリア画面表示の作成

それぞれ1日1時間ちょいずつ作業しています。

成果物

作ったものをGitHubへアップしました。

github.com

作った感想

気がついたことや残件、作った感想です

  • 絵がしょぼい
  • 効果音が無い
  • あたり判定の範囲が左にずれている
  • インベーダークローンを作ってたつもりなのにいつの間にか全然違うものができていた
    • 完全なクローンも時間をかければ出来そうな感はありましたが、丁寧作業すると結構時間がかかりそうな事に気が付きました

プログラムの内容はImageクラスをCanvasクラス上で1フレームごとに移動したりするだけなので特に難しいことはありませんでした。

  • 自機や敵の移動は、移動をする前に除去判定をしないといけない
  • 弾が当たったらループを中断して処理を打ち切らないとヤバイことになる
  • ImageはCanvasに追加してからCanvas.SetXXXした方がいい
  • ループ変数のインデックスのiとかjとかdfiを間違えて使わない
  • ループはforeach使わない、forで逆順で回す
    • リストから削除する事が多かったので逆順です

が大変だったかもしれません。

2日目くらいに、「なんでこんなもの作ってるんだろう」という気がしてやめようかと思いましが、自分に鞭をうって強引に進めました。多分4日目に突入したら完成しなかったと思います。

細かいことは気にせずにガーっと勢いでやるのが大切だと思いました。

後半戦

すいません、ちょっとこのままインベーダーを名乗るのはリスペクトが足りないと思ったので後編を書きました。

takachan.hatenablog.com