ミニDTL.xna “ゲームはイチニチ30分まで!” 第4回
ミニDTL.xnaその4です。
ブログにまとめるのが遅くなってしまいました。これは土曜日の配信分のまとめ記事です。また、日曜日の配信はおやすみします。
Mini DTL.xna (4)
こんなことをやりました。
自機の初期座標を設定
ゲーム開始直後、自機が左上の隅にいるのが気持ち悪いので、画面下端の中央あたりに移動させました。
Game1.Initializeに
- fighter1.Position.X = 320;
- fighter1.Position.Y = 400;
と書いてます。
ゲーム座標は640*480決めうちですが、これは配信用のスクリーンサイズに適度に収まるという根拠しかないので、もう少し考えた上で定数にしたいです。
自弾・敵機のリストからの削除を書き直し
自弾・敵機ともにDestroyedというプロパティを持ち、これが真になったら破棄されたものとしてリストから削除しているのですが、前回foreach中で削除したところ例外が出たのでforを使って以下のようにしていました。
- for (int i = shots.Count - 1; i >= 0; i--) {
- Shot shot = shots[i];
- if (shot.Destroyed) {
- shots.Remove(shot);
- }
- }
- for (int i = enemies.Count - 1; i >= 0; i--) {
- Enemy enemy = enemies[i];
- if (enemy.Destroyed) {
- enemies.Remove(enemy);
- }
- }
もっと良い方法はないかと思ってListのメソッドを見てみると、RemoveAllに条件式が渡せるようなので試してみます。
- shots.RemoveAll(delegate(Shot shot) { return shot.Destroyed; });
- enemies.RemoveAll(delegate(Enemy enemy) { return enemy.Destroyed; });
意味相応の長さになり、見通しが良くなりました。
いくつかの微調整
プログラム的に新たなことはしていないのですが、ゲームの微調整を行っています。
- 敵の出現位置を上に、消える位置を下に
- 敵機の出現頻度を低く
- 敵機より自弾を上に描画
自弾と敵機の当たり判定
とりあえず、素朴に実装しました。
2Dの判定には、Rectangle.Intersects(Rectangle) と Rectangle.Contains(Point) が使えそうです。自弾は面積を持たなくても良さそうなので、後者で進めようかなと思いました。
EnemyクラスにBoundsというRectangleを持たせ、Updateで更新させます。これをShotのPositionと見比べるわけですが
- foreach (Enemy enemy in enemies) {
- enemy.Update(gameTime, this);
- if (enemy.Destroyed == false) {
- foreach (Shot shot in shots) {
- if (shot.Destroyed == false && enemy.Bounds.Contains(shot.Position)) {
- }
- }
- }
- }
とここまで書いて、ShotのPositionがVector2であってPointでないことに気づきました。
キャストしたらPointになってくれないかなとか、そういうメソッドはないかなとか素人っぽくいろいろ試したのですが、結局new Pointするのが嫌で、ShotにもBoundsを作ることにしました。
いま思うとちょっと意味不明ですね。Vector2に対応するRectangle的なものがあると良いのですが。(BoundingBoxは3Dなので違いました)
- foreach (Enemy enemy in enemies) {
- enemy.Update(gameTime, this);
- if (enemy.Destroyed == false) {
- foreach (Shot shot in shots) {
- if (shot.Destroyed == false && enemy.Bounds.Intersects(shot.Bounds)) {
- shot.Destroyed = true;
- enemy.Destroyed = true;
- }
- }
- }
- }
それ以外はいたって素朴です。
敵機に耐久力をセット
一撃で敵を倒せて面白くないので、数発撃たないと倒せないようにしました。
上記で
- enemy.Destroyed = true;
となっている代わりに
- enemy.Damage();
としました。
関数の中身はこんな感じ。lifeが0になると倒れます。
- public void Damage() {
- life--;
- if (life <= 0) {
- Destroyed = true;
- }
- }
スコア表示を追加
「スコア入れるとゲームっぽい」とwhirlpowerさんにコメントもらったので、入れてみました。
ソリューションエクスプローラからContentを右クリックして[追加]-[新しい項目]を選び、スプライトフォントを追加します。名前はScoreFontにしました。
追加されたScoreFont.spritefontは、フォントの埋め込みを指定するためのXMLです。これを編集してFontName, Size, StyleをそれぞれArial, 24, Boldにしました。
後はいつもの手順で、Game1にSpriteFont scoreFont;というメンバーを用意して
LoadContent時に
- scoreFont = Content.Load<SpriteFont>("ScoreFont");
Draw時にspriteBatchのBeginとEndの間に
- spriteBatch.DrawString(scoreFont, "000000", new Vector2(11, 11), Color.Black);
- spriteBatch.DrawString(scoreFont, "000000", new Vector2(10, 10), Color.White);
と書きました。
Color.BlackとColor.Whiteで2回描いているのはもちろん黒い擬似シャドウを付けるためです。
実行してみるとうまくいったので、score変数を作っておいて敵破壊時に加算し、描画の際にも先頭にゼロを付加するようにしてみました。
- string scoreString = "00000000" + score;
- scoreString = scoreString.Substring(scoreString.Length - 8);
- spriteBatch.DrawString(scoreFont, scoreString, new Vector2(11, 11), Color.Black);
- spriteBatch.DrawString(scoreFont, scoreString, new Vector2(10, 10), Color.White);
30分経過してしまったのでここまで。
今日の感想
- 実況をかなりさぼってしまいました。見てて寂しいんでもうちょっとしゃべります。
- リファクタリングというか、ちゃんと構造を作りたくなってきました。でも見た目の変化が出せないので迷うところです。
さて、今後やることですが、まだまだあります。ゲーム的なひねりも加えたいのですが、それ以前に
- 敵弾を作る
- 敵の種類を増やす
- ステージマップを作って敵を配置
- 背景を出してみる
は先にやっておきたいところです。いままでのペースだと毎回1.5項目ぐらいこなせそうですね。
次回は月曜を予定しています。
タグ: DeskTopLive, desktoplivexna, MiniDTL, XNA
2009 年 8 月 24 日 12:40 AM
[...] ミニDTL.xna “ゲームはイチニチ30分まで!” 第4回 [...]
2010 年 7 月 31 日 8:20 PM
はじめまして。
自分もXNAでゲーム製作をはじめました。
耐久力を持つ敵+XNAで検索してこちらにたどりつきましたが
丁寧な解説で参考になります。
こちらの記事が最終更新でしょうか?
去年の記事なのでむずかしいかもしれませんが続きを期待しております。
2010 年 7 月 31 日 8:24 PM
申し訳ありません、もう10回以上もあったのですね。
どうもブログ形式に慣れませんで失礼を申してしまいました。
参考にしていきたいと思います。