C#でゲームをつくろう!
ということでゲームをつくってみることにします。検索してみるとインベーダーゲームの作成方法を解説しているサイトはたくさんありますが、ギャラクシアンをつくっているページはそんなにないので、これをつくることにします。完成品はこんな感じになります。
どうやって作るか?
どうやって作るか? ピクチャーボックスに敵キャラや自機を描画してこれを移動させるという極めて安易な方法でやってみることにします。
敵キャラや自機、弾丸はピクチャーボックスを使う
タイマーをつかってこれらを移動させる
あたり判定をして得点加算やミス時の処理をおこなう
デザイナで以下のようなものをつくります。
敵は複数なのでイメージをコピーしてつくります。
自機を移動させる
操作方法は←→キーとスペースキー、Sキーをつかいます。矢印キーで左右の操作、スペースキーで弾丸を発射します。ゲームスタートは「S」キーを使います。これはformのKeyDownイベントを使えば簡単にできそうです。
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 |
public Form1() { InitializeComponent(); KeyDown += Form1_KeyDown; } private void Form1_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Left) { MoveLeft(); } else if (e.KeyCode == Keys.Right) { MoveRight(); } else if (e.KeyCode == Keys.Space) { } else if (e.KeyCode == Keys.S) { } } void MoveLeft() { Point pt = pictureBoxSpaceship.Location; pt.X -= 10; pictureBoxSpaceship.Location = pt; } void MoveRight() { Point pt = pictureBoxSpaceship.Location; pt.X += 10; pictureBoxSpaceship.Location = pt; } |
一応、これでも動くのですが、これだとキーを押し続けたときに最初に少しだけ動き、そのあとしばらくして連続で動くという変な動き方になります。
改善策として「左のキーが押されている」「右のキーが押されている」「どちらも押されていない」という状態を保存するフィールド変数を用意します。タイマーをつかって現在どの状態なのか調べて動かすようにします。
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 |
public enum Direct { None = 0, Left = 1, Right = 2, } Direct direct = Direct.None; public Form1() { InitializeComponent(); KeyDown += Form1_KeyDown; KeyUp += Form1_KeyUp; timer1.Tick += Timer1_Tick; timer1.Interval = 50; } private void Form1_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Left) { direct = Direct.Left; } else if (e.KeyCode == Keys.Right) { direct = Direct.Right; } else if (e.KeyCode == Keys.Space) { Bulletlaunch(); // あとで考える } else if (e.KeyCode == Keys.S) { GameStart(); } } private void Form1_KeyUp(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Left) { direct = Direct.None; } else if (e.KeyCode == Keys.Right) { direct = Direct.None; } } void GameStart() { timer1.Start(); } |
ゲームがスタートするときにタイマーをStartさせます。Tickイベントが起きたら
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
private void Timer1_Tick(object sender, EventArgs e) { SpaceshipMove(); } void SpaceshipMove() { if (direct == Direct.Left) { MoveLeft(); } else if (direct == Direct.Right) { MoveRight(); } } |
これでうまく自機が動くようになりました。
自機から弾丸を発射させる
スペースキーを押すと自機から弾丸が発射されるようにします。発射された弾丸は上昇していきます。
まず自機から弾丸を発射させるメソッドを完成させます。
1 2 3 4 |
void Bulletlaunch() { // さてどうしたものか? } |
弾丸は自機の上部中心から発射されます。そこで上部中心の座標をもとめます。
1 2 3 |
Point pt = pictureBoxSpaceship.Location; int width = pictureBoxSpaceship.Size.Width; int centerX = pt.X + width / 2; |
centerXが弾丸の中心になるようにする必要があります。弾丸のサイズを決めてしまいましょう。
1 2 |
int BULLET_WIDTH = 2; int BULLET_HEIGHT = 10; // フィールド変数 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void Bulletlaunch() { Point pt = pictureBoxSpaceship.Location; int width = pictureBoxSpaceship.Size.Width; int centerX = pt.X + width / 2; //centerX- BULLET_WIDTH/2 Point point = new Point(centerX - BULLET_WIDTH / 2, pt.Y); PictureBox bullet = new PictureBox(); bullet.Location = point; bullet.Size = new Size(BULLET_WIDTH, BULLET_HEIGHT); bullet.BackColor = Color.White; bullet.Parent = panel1; } |
これで弾丸は自機の向こう側に表示されます(自機を移動させてみるとわかる)。これでは発射されたというよりパネルのうえに置かれただけなので、上昇させる方法を考えます。
弾丸を上昇させる
弾丸は単発ではなく複数発射されます。そこでリストに格納してまとめて処理してしまいましょう。
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 |
List<PictureBox> _bullets = new List<PictureBox>(); List<PictureBox> Bullets { get { _bullets = _bullets.Where(x => !x.IsDisposed).ToList(); return _bullets; } } void Bulletlaunch() { Point pt = pictureBoxSpaceship.Location; int width = pictureBoxSpaceship.Size.Width; int centerX = pt.X + width / 2; Point point = new Point(centerX - BULLET_WIDTH / 2, pt.Y); PictureBox bullet = new PictureBox(); bullet.Location = point; bullet.Size = new Size(BULLET_WIDTH, BULLET_HEIGHT); bullet.BackColor = Color.White; bullet.Parent = panel1; Bullets.Add(bullet); // これを追加した。 } void BulletsMove() { foreach (var bullet in Bullets) { Point pt = bullet.Location; pt.Y -= 10; bullet.Location = pt; if (bullet.Location.Y < -BULLET_HEIGHT) bullet.Dispose(); } } |
画面上まで上昇した弾丸は必要ないのでDisposeしてしまいます。またプロパティの処理でDisposeされた弾丸はリストのなかから排除しています。
最後にTimer1_TickにBulletsMoveを追加します。
1 2 3 4 5 |
private void Timer1_Tick(object sender, EventArgs e) { SpaceshipMove(); BulletsMove(); } |
これで自機から発射された弾丸は上昇できるようになりました。めでたしめでたし。続きは次回。