ミニDTL.xna “ゲームはイチニチ30分まで!” 第3回

by key

ミニDTL.xnaその3です。

今回は配信にlivestreamを使ってみました。640*480で配信しても24fpsで受信でき、弾が飛んでる様子も僕がビルドエラー起こしている様子も丸見えとなりました。

内容は以下の通り。

Mini DTL.xna (3)

自機をクラス化

前回作ったShotクラスと同じ要領でFighterクラスを作り、fighter1変数として持つようにしました。

自機のコントローラ判定はFighter.Updateに任せ、Game1クラスからは自機に関わる変数shobo, shoboPos, gamePadStateを追い出しました。

将来的に複数のコントローラやプレイヤーに対応できるように、コンストラクタでPlayerIndexを渡すようにしてあります。

自弾を適度な間隔で撃つように

ショットボタンを押しっぱなしにしている間、毎フレーム自弾を撃ってしまっているので、4フレームに1発だけ撃つようにしました。

FighterクラスにShotButtonStateというint変数を作っておいて、符号で押下状態を、絶対値で継続フレーム数を保持するようにしました。

  1. if (gamePadState.Buttons.A == ButtonState.Pressed) {
  2.     if (ShotButtonState < 0) {
  3.         ShotButtonState = 0;
  4.     }
  5.     ShotButtonState++;
  6. } else {
  7.     if (ShotButtonState > 0) {
  8.         ShotButtonState = 0;
  9.     }
  10.     ShotButtonState--;
  11. }

自弾の発射判定はいまだにGame1クラスのほうでやっていて、

  1. if (fighter1.ShotButtonState > 0) {
  2.     if (fighter1.ShotButtonState % 4 == 1) {
  3.         Shot shot = new Shot();
  4.         shot.Position.X = fighter1.Position.X;
  5.         shot.Position.Y = fighter1.Position.Y;
  6.         shots.Add(shot);
  7.     }
  8. }

なんてことをやっちゃってます。本当はイベントなり使いたかったのですが、調べる時間を惜しんでしまいました。こまけぇこたぁいいんです。

ちなみに押下した次のUpdateではShotButtonStateは1になるので、==1で比較すると押下した瞬間に常に自弾が出るのでゲーム的に正しいです。

自機のサイズと中心点を移動

自機がでかいのと、画像の左上に自機の原点があるのが気になったので、改善してみました。

SpriteBatch.Drawがオーバーロードされていて、第二引数にRectangleを渡すとリサイズするようなので、以下のようにしました。

  1. private Rectangle drawDest;

Initialize中に

  1. drawDest = new Rectangle(0, 0, 48, 48);

Update中に

  1. drawDest.X = (int)(Position.X - drawDest.Width * 0.5);
  2. drawDest.Y = (int)(Position.Y - drawDest.Height * 0.5);

Draw中に

  1. spriteBatch.Draw(texture, drawDest, Color.White);

これでfighter1.Positionの位置に自機の画像の中心が来るようになりました。

同じ要領でShotにもdrawDestを用意しましたが、リサイズすると重くなるかと考え、こちらはVector2で行いました。

溜まった自弾を掃除する

自弾を撃つときnewしてて、画面外に出ても保持しっぱなしだったので、とりあえずshotsからRemoveするようにしてみました。

Shotクラスにpublic Boolean Destroyedを用意し、Update中に画面の外に出たらtrueを入れます。

Game1クラス中で、ものは試しに

  1. foreach (Shot shot in shots) {
  2.     shot.Update(gameTime);
  3.     if (shot.Destroyed) {
  4.         shots.Remove(shot);
  5.     }
  6. }

とやってみたところ例外が出たので、まあそんなに世の中甘くないよねと思いながら以下のように修正。

  1. foreach (Shot shot in shots) {
  2.     shot.Update(gameTime);
  3. }
  4. for (int i = shots.Count - 1; i >= 0; i--) {
  5.     Shot shot = shots[i];
  6.     if (shot.Destroyed) {
  7.         shots.Remove(shot);
  8.     }
  9. }

Shot中の閾値を変えて試してみたところ、ちゃんとRemoveされているようです。

でも本当はいちいち新しいインスタンスnewするなんて言語道断です。ちゃんとプールすべきなのですが、そこは例によって先送りします。

敵を作る

この段階で残り5分でしたが、無謀にも敵の追加に挑戦しました。

例によってペイントを起動します。プロなのでWin, mspaint, Enterで起動します。(Vistaじゃない人はWin+R, mspaint, Enterを使いましょう)

enemy.png

enemy.png

絵を描きました。やる気はあります。時間がないのです。

画像をアセットに追加し、Enemy.csを追加し、Shotクラスをほぼコピペしてでっちあげ、List<shot> shotsをほぼコピペしてenemiesをでっちあげました。

敵が出現するようにする

毎フレーム出現するとうるさいので、1/10の確率で出現するようにしました。

  1. if (Math.Random() < 0.1) {
  2. }

と打ってMath.Randomが無いことに気づき、Randomクラスを思い出してインスタンスを作りました。

次に

  1. if (rand.NextDouble < 0.1) {
  2. }

と書いたところ、XELFさんにNextDoubleはメソッドだよとコメントをもらいました。ありがとうございます!

その後ひととおりビルドエラーを取ったのですが、弾が画面に出現しない。もう30分経ったし、断念するかーと思ってたところ、Drawしてないことに気づきました。恥ずかしい。

無事敵が画面に出るようになりました。まだ自機とも自弾とも当たり判定をしていませんが、30分経過してしまったのでここまで。

今回の感想

  • livestreamがかなり強力。ソースコードが読めるぐらいの解像度と、弾の動きが分かるぐらいのフレームレート。次回からもこれを使いたいです。
  • コメントで日本語を使えないのと、コメントが保存されない 嘘。保存されてました のが欠点なので、Twitterを使おうかな。
  • コメントでアドバイスもらえたのが楽しかったです!やってて良かった。

明日は順当に当たり判定を入れる予定です。ではまた!

タグ: , , ,

コメント / トラックバック 2 件

  1. key より:

    shotsの掃除にforを使ってますが、shots.RemoveAllにdelegateを渡す夢を見ました。夢の中でそれはPredicateと呼ばれていました。また明日。

  2. XNA Today » ミニDTL.xna 第3回 より:

    [...] ミニDTL.xna “ゲームはイチニチ30分まで!” 第3回 [...]

コメントをどうぞ