Shooting Game Project
2006/11/12 ver 0.01
2006/11/13 ver 0.02
2006/11/14 ver 0.03
2006/11/15 ver 0.04
2006/11/16 ver 0.05
2006/11/17 ver 0.06
2006/11/18 ver 0.07
2006/11/22 ver 0.08
2006/11/23 ver 0.09
2006/11/24 ver 0.10
2006/11/25 ver 0.11
2006/11/26 ver 0.12
2006/11/27 ver 0.13
2006/11/28 ver 0.14
2006/12/01 ver 0.15
2006/12/02 ver 0.16
2006/12/03 ver 0.17
2006/12/04 ver 0.18
2006/12/05 ver 0.19
2006/12/08 ver 0.20
2006/12/09 ver 0.21
2006/12/10 ver 0.22
2006/12/11 ver 0.23
2006/12/16 ver 0.24
2006/12/29 ver 0.25





→ダウンロード

- TestStage1を作成した

- 自機がダメージを受けたとき,およびアイテムを取得したときに効果音を鳴らすようにした

- 敵の出現時刻の設定ミスを修正した

- 自機が死んだあとも当たり判定を続けていた点を修正した


<感想>

上記のとおり,はじめてステージをボスも含めて作成してみました.敵やアイテムの配置が難しいことを実感.これは自分で何度もやってみて,または誰かにやってもらってちょっとずつ修正するしかないですね.

クリアできる程度には作っているので何度かチャレンジしてみてください(クリアしても何も出ませんがw)

※卒業研究のため,この続きは3月ということになります.末永くご愛顧くださいw

→ダウンロード

- 音の再生や停止、ボリューム、パン、周波数を管理するCSoundクラス、および複数のCSoundクラスを管理するCSoundManagerクラスを作成し、効果音を出すようにした

- モデルをステージに追加する部分の内部構造を変更した


<感想>

簡易的に音を出すことは非常に簡単です。例えば、WindowsAPIのPlaySound()を使うと一行で実現できます。しかしリソースの管理が面倒ということと、それ以上にせっかくDirectXをやってるのにその機能を使わない手はないだろうと思い、ようやくDirectSoundに着手してみました。

単純に音を鳴らすことはすぐにできたものの、同じ音を同時に複数再生することに時間がかかりました。IDirectSound8::DuplicateSoundBuffer()を使ってサウンドメモリを共有するセカンダリ・バッファを作ることはわかっても、そのバッファをどのように使用するのか、持っている本にもウェブをまわってもなかったからです。

正しい方法なのかはわかりませんが、それでもどうにか同時に再生することができるようになりました。

音を出してみて分かったことは、自機の発射音のように連続して再生されると結構うるさく感じてしまうということです。どこに音をつけるのか、これも重要ですね。

→ダウンロード

- 連射回数が増えるアイテム、最大HPが増えるアイテムを追加した

- アイテムをとったときに表示される文字列のセンタリングを行った

- 敵と味方のゲージ位置を変更した


<感想>

アイテムの作成や表示方法の変更などに手を入れました。このあたりはあまり考えなくても見え方を変えたり効果を追加したりすることができるので、ゲームを作っていて楽しいと感じます。

もちろん、ここまでくるためには時間のかかる内部構造の変更や原因不明のバグと戦わなければなりませんけどw

- アイテムの動的生成が行えるようにした

- 武器アイテムをとったときに武器が変更されない点を修正した

- スコア加算のアイテムをとったときの得点が表示されない点を修正した


<感想>

アイテムに関して機能拡張を行ったというより、ver 0.20における内部の大規模変更によってうまく動かなくなった部分を修正していたという感じです。

よって目に見える部分で変わった点はアイテムが画面の上から落ちてくるようになったところだけです。

- ボスに複数のウィークポイントを設定できるようにした

- ボスに複数の本体領域を設定できるようにした

- 時間または残りHPによってボスの行動パターンが遷移するようにした

- 複数の箇所から異なる種類の弾をボスが発射できるようにした


<感想>

普通の敵とは全く構造の違うボスの基本構造を作ってみました。動作や行動を起こすタイミングなどが複雑ということに加えて、ボスごとにまったくパターンが異なっているのでどのような構造にするか考えているところです。

クラスが増えてきたのでまとめてみます。
クラス名 主な役割 備考
CGame メッセージハンドラ、ウィンドウ作成、メッセージループ (既存ライブラリ)
CInput キーボード、ジョイスティックの入力 (既存ライブラリ)
CGraphic デバイスの初期化、ウィンドウモードの切り替え (既存ライブラリ)
CImage テクスチャの管理、ビューポートの設定 (既存ライブラリ)
CShooting ステージの設定、移動・描画・当たり判定の指示 CGameを継承
CMover すべてのMover(自機、敵、ボスなど)の基本クラス -
CMoverManager すべてのMoverを保持し、動的な生成や削除 -
CObject3D Moverを構成するマテリアルやテクスチャの管理 -
CStage ステージ全体の管理 -
CShooter 弾の発射 CMoverを継承
CMyShip 自機の移動・描画・当たり判定など CShooterを継承
CEnemy 敵の移動・描画・当たり判定など CShooterを継承
CBullet 弾の移動・描画など CMoverを継承
CItem アイテムの移動・描画・当たり判定など CMoverを継承
CBoss ボスの移動・描画・当たり判定など CEnemyを継承
CLine ラインの設定や描画 -
CMyFont フォントの設定や描画 -

→ダウンロード

- 敵や弾は表示する時刻になったとき動的に作成するようにした

- 敵と自機の弾の発射に関する共通部分をCShooterクラスに移した(継承関係 CMover←CShooter←CMyShip,CEnemy)

- ダメージをくらったMoverのHPが0より小さくなってしまう点を修正した

- キーを押しているときに発射される弾の時間間隔をアイテムによって変更できるようにした


<感想>

かつて解決できなかったところに手を入れ、根本的な部分の書き直しを行いました。

一つめは、ver 0.08において断念していたオブジェクトの動的生成です。前回の時点では敵や弾の数が比較的少なかったため後回しにしていたものの、最近は表示数が多くなり配列の数を次第に大きくしている状態でした。これを解決できたため、例えば敵が無限に弾を撃ち続けることも可能になりました。

二つめは、敵と自機の弾の発射に関する部分です。実はNway弾や二重弾といった弾の種類はこれまで敵のみが装備できて、自機はノーマルな弾しか撃てなかったのです。共通化したことでこれも解決できました。

今回のプロジェクトの山場は乗り越えたと思うので、あとはゲームとして完成させていきたいと思います。

- アイテムとして回復とポイント加算を追加した

- 倒した敵の下にコンボ数や得られた得点を表示できるようにした

- フレームごとのスコアの管理をCMoverクラスからCMoverManagerクラスに移行した


<感想>

当たり判定の部分でHPが0になったことを確認して、そこで得点を表示する関数を呼び出す、ということは誰でも考えつくことです。でも、当たり判定と描画の処理は別々に行っているためそれはできません。その部分をスマートに解決できたことが今日の収穫だと思います。

→ダウンロード

- 連続して倒した敵の数に応じて、ボーナス得点を与えるようにした

- 当たり判定の設定ミスを修正した

- アイテムが目立つように回転させた


<感想>

ボーナス得点について。フレームのたびに連続して倒すことは不可能なので、30フレーム(約0.5秒)以内に連続して倒した敵の数をもとに算出しました。具体的な計算式は、コンボ数の二乗×100。ただしこの式は、ゲームバランスの調整の際に変更する可能性があります。

このあたりの表示に関しては面白い演出ができそうです。

- 弾の種類と発射後の動きを別々に設定できるようにした

- 弾の種類に二重弾と三重弾を追加した

- 自機が登録されていないときは警告を出し、終了するようにした


<感想>

バージョンアップが進まなかったのは1つめの項目の修正に時間がかかったからです。これまで弾の種類と発射後の動きは別々に作っており、初速度の設定などもそれぞれがやっていました。そのためこれらの二つを設定したときの整合性を守る必要があったのです。

あとシューティングゲームとは直接関係のないウィンドウまわりをいじろうと思ったのですがうまくいかず。まあこれは後回しにします。

→ダウンロード

- 敵と自機の当たり判定をつけた

- 各クラスで行っていたMover(弾、自機、敵など)の生成をMoverManagerに一元化した

- 弾の種類によって攻撃力を設定できるようにした

- 自機のHPをバーの長さで表すようにした

- 同様にボス戦のときはボスのHPもバーの長さで表すようにした

- CMoverクラスを継承するCItemクラスを作成し、アイテムをとったとき武器がチェンジされるようにした


<感想>

一発当たったら終わりじゃなくてゲージ制にしたのはロックマンのようなものにしたいという考えが頭の中にあったからです。敵と自機のバーを右のほうに並べているのもその影響を受けてます。

ゲージ制である代わりにステージを作成する際には難易度を上げて参りますw

(ダウンロード版では下の星をとると攻撃力5倍のレーザーに武器が変わり、上の星をとると元に戻ります)

→ダウンロード

- CEnemyクラスを継承したCBossクラスを作成し、ボスを倒すことでそのステージをクリアし次のステージに進めるようにした

- CStageクラスが管理していたスコアをCShootingクラスに移行した

- 自機に弾をセットするときの不具合を修正した

- 自機が死んだ後に敵を殺してもスコアに加算されないように修正した

- 自機が死んだ後にボスを殺したとき、ゲームオーバーと次のステージへの案内が同時に出てしまう不具合を修正した

- ステージ名を表示するようにした


<感想>

ボスを登場させたことでようやく終了条件をつけることができました。ボスの動きは普通の敵とは違って複雑なので、そのあたりをこれから考えようと思います。

- CStageクラスを作成し、このクラスがCMoverManagerクラスやCObject3Dクラスをもつようにした

- ステージ設計者はメインであるCShootingクラス以外はさわらないでいいように内部構造を変更した

- スコアをCShootingクラスがもつように変更した


<感想>

動きが速くなるとかメモリの使用量が少なくなったわけではありませんが、どうしても設計が気に入らなかったので内部構造を大幅に書き直しました。結果、割とすっきりしてきたものの、自分が本当にやりたいことを実現しようとすると根本に手を入れないといけないので、そこは次のプロジェクトの課題とします。

なお、現在のクラスの関係は以下のとおりです(凡例 A←B:クラスAはクラスBをもっている(集約))。

CShooting←CStage←CMoverManager←CMoverを継承するCMyShip,CEnemy,CBullet

→ダウンロード

- 敵機を倒すことでスコアが加算されるようにした

- スコアの下二桁でゲームオーバーの回数を表すようにした

- ゲームオーバー後、「コンティニュー」と「スタート画面に戻る」を選択できるようにした

- フォントクラスの設定ミスを修正した


<感想>

スコアが出てまた一歩ゲームらしくなりました。多倍長を使おうと思いましたが今回はそこまで点数が高くなることはないので、普通にUINT(unsigned int)を使っています。

- ゲームオーバー時における敵の弾の発射制限に関するデバッグを行った

- 敵の動作として実装した部分をどのオブジェクトでも使えるようにCEnemyクラスからCMoverクラスに移した

- 弾の設定に関する内部構造を変更した

- Moverの動きとして双曲線、リサージュ曲線、インボリュート曲線(渦巻形状)の動きを追加した


<感想>

昨日の時点で1つ目のバグには気づいていたものの、時間がなかったため後回しにしていました。そのためそのバグがでないように、ver0.11のダウンロード版ではゲームオーバーにならないように敵の当たり判定をなくしていました。寝る前、床についてからあの部分だ!とひらめいて解決したバグでしたw

今日は主にMoverの動きを作っていました。双曲線は作るのに時間がかかった割には楕円曲線と見た目の違いが出なかったですね。楕円よりも計算に少し時間がかかるという点と、パラメータの指定がしにくい点があるのであまり使うことはないかな。(図はリサージュ曲線上を敵100匹がぐるぐる廻っているところ)

動きを作るのは楽しいんですが、そろそろシステムのほうに取りかからなければ。

→ダウンロード

- 自機にも当たり判定を設定した

- 自機のHPが0になるとゲームオーバーになるようにした

- スタート画面を作成し、ボタンを押すことでゲーム画面に入るようにした

- ゲームオーバー後、新たな敵を発生させず、自機の移動および弾の生成を制限した

- 敵の動きとして加速と楕円曲線上の移動を実装した


<感想>

これまで弾の動きとしていくつかのパターンを実装してきたものの、敵は直線上を等速で動いてくるだけでした。そこで今日は初めて敵の動きを実装。楕円曲線を表すために高校の数学の教科書を読み返しましたよw 媒介変数表示を行って、角度だけぐるぐるまわしてます。結果、思っていたような動きを出せてよかったです。

あとは、ゲームを開始したときにオプションやステージを選択することを意識して、それらしい最初の画面を作ってみました("Press Z Key"としか書いていない画面ですが)。また、ゲームオーバーになったときその画面に戻るような部分も追加してみました。

次回はクリアの条件も考えてみたいと思います。

- 自機の行動範囲を画面内に制限した

- nway弾を実装した

- 画面外の敵はダメージを食らわないようにした


<感想>

典型的な攻撃パターンであるnway弾(扇状に広がる弾)を実装しました。これを複数の敵に連続で撃たれたらきついかもw このあたりの設定が腕の見せ所か?

→ダウンロード

- 敵が死んでも弾が発射され続けるバグを修正した

- 画面左に新たにビューポートを作成し、そこにデバッグのための情報を表示するようにした(将来的には得点などを表示する場所に)

- 方向弾、狙い撃ち弾、ホーミング弾を実装した


<感想>

敵が狙い撃ちしてくるようになりましたw 自機の当たり判定がついたら大変かもしれませんw

もはやスクリーンショットだけでは何ができるかわからないので、実行ファイルをときどきダウンロードできるようにしておきます。

- 武器と自機・敵機のオブジェクトを別々に作り、自機・敵機に対して武器をセットする構成に変更した

- 敵の小隊が表示されるときに弾のオブジェクトを作成していた構成を、最初に作成する構成に変更した

- 敵の小隊が連続して弾を撃つことができるようにした

- 敵に回転角を導入し、向いている角度に対して移動と弾の発射ができるようにした


<感想>

だいぶ大幅な変更だったため新しい版を出すのに時間がかかりました。特に2番目の項目はやりたいと思っていたことが2日かけてもできずだいぶ悩みました。結局その方法はできず、違う構成にせざるを得なかったのですが、次へつながる基礎的な部分は勉強できたと思います。

回転の部分は、ラジアンにすべきところを度で代入していたためうまくいかないということが一時ありましたが、なんとかクリア。敵が自分に向けて弾を撃ってくるようになったら、基礎的な部分はできたといっていいんじゃないでしょうか。

→ダウンロード

- 敵に当たり判定を設定した

- 時間差で登場し同じ動きをするグループ(小隊)を表示できるようにした

- 敵と弾にHPを設定し、ダメージを受けてHPが0になると画面から消去するようにした

- ボタンを押している間はフレームのたびに弾を発射させていたのを数フレームに1回の割合で発射するように変更した


<感想>

シューティングゲームにおいて重要な部分である「当たり判定」を実装しました。このあたりは考え方は簡単などですんなりいった…と思いきや、昨日に引き続いてcontinue;とreturn;を書き間違えなどの単純ミスがあり少し時間がかかっています。

実際には目には見えないオブジェクトの管理の部分に最も時間を割いています。これが一番難しいと感じているところです。

- 敵を管理するCEnemyクラス(CMoverクラスを継承)を作成した

- 指定した時刻に敵が出現するようにした

- フレームレートを制御するようにした


<感想>

やっと敵が出現するところまでいきました。タイマーを設定して出現させるところはすぐ実装することができました。気になるのは、敵のポインタをいつ作成して削除するのかの設定です。今のところプログラム起動時に生成してウィンドウを閉じるときに削除していますが、これではちょっと無駄な気も…。このあたりは、一通り動くようになってから考えようと思っています。

フレームレートの制御は簡単…と思っていたら思わぬ罠が。

   // 早すぎたらスリープ
   if (sleepTime > 0) {
      ::Sleep(sleepTime);
      nowTime = ::GetTickCount();
   }

これを実行したところ画面がいつまでたっても動かない状態になったのです。sleepTimeの計算方法が悪いのではないかと思って調べてみても、そうでもなさそうだし…。一応数値が正しく出ているか確認してみたところ、なんと421234567のようなとんでもない数になっていました。原因はsleepTimeをDWORD型で定義していたこと。この型はunsinged longの別名であり負の数はとることはないので、このような結果になってしまったのです。ちょっとしたところでも気づかないとはまってしまうプログラムの怖さを改めて感じたのです。

- すべてのMoverを管理するCMoverManagerクラスを作成した

- 上記に伴い、オブジェクト(=モデル)とMover(=実際の弾など)をこのクラスで作成し、CShootingクラスを整理した

- キーを押すことで弾が連続で出るようにした


<感想>

最初、弾の配列にはvectorを使っていたのですが、無駄なポインタが蓄積されていくので配列を循環で使うことにしました。

   // NULLであるポインタを探す
   while (ArrayMoverBullet[ArrayCnt] != NULL) {
      ArrayCnt++;
      
      if (ArrayCnt >= NUM_BULLET)
         ArrayCnt = 0;
   }
   
   // ポインタを代入
   ArrayMoverBullet[ArrayCnt] = moverBullet;

ついにキー操作で弾が出たのです! ここまでで4日…。結構道のりは長そう。

- CBulletクラスの機能を新しく作成したCMoverに移し、このクラスを継承させるようにした

- 同様にCMoverクラスを継承するCMyShipクラスを作成した

- 視野内判定を用いて、視界外のオブジェクトを削除する機能を追加


<感想>

右の図で、外側の白い線はビューポートの範囲を示していて、その外側に達したオブジェクトは削除されるため描画されません。そのあたりの基本ソースがこちら。

// オブジェクト移動
void CShooting::Move()
{
   ...

   if (Bullet1 != NULL) {
       if (Bullet1->IsInsight())
           Bullet1->Move();
       else {
           delete Bullet1;
           Bullet1 = NULL;
       }
   }
	
   ...
}

現段階でできるのは自機の移動だけですが徐々にベースが整いつつあります。ここからシューティングゲームらしい機能を作る段階に入ると思うと楽しみなのです。

複数のオブジェクトを表示

- 一つのXファイルが一つのオブジェクトに対応していた方式を、複数に対応する方式に変更(これにより、一つのXファイルは一回のみの読み込みでよくなった)

- 弾を管理するCBulletクラスを作成し、位置情報などのこれまでCObject3Dクラスが保持していた情報をこちらで管理

- CBulletクラスでオブジェクトがビューポートの内外のどちらにあるかを調べる機能(視野内判定)を実装

- 視野マトリクスと投影マトリクスを調整し、フィールドの大きさを横幅100、縦幅100に変更(アスペクト比 1:1)


<感想>

右の図にもあるように、一回のXファイルの読み込みで同じ形状の2つのオブジェクトを表示することができるようになりました。こうしないとオブジェクトを生成するたびにモデルを読み込んでいたら、まともに動きませんからね。でもこういった当たり前のことも、実装している段階では考えていなかったのです。やってみて初めて気づくのもよい経験です。

ラインとXファイル

- 後方から見ていたオブジェクト(Xファイル形式)を回転させることで真上からの視点に変更

- ラインを描画するCLineクラスを作成


<感想>

DirectXに慣れていないこともあり、どのタイミングでモデルを回転させるべきか迷いました(それ以上にどこに記述すればよいのかということに)。しっかりと基礎を勉強したことがないので少しずつ学んでいかなければ。

CLineクラスは敵のHPを表示するときなどには必須だと考えたので実装してみました。デバッグにも使えると思います。

あと開発ではないですが、初めてXファイルを自分で作ってみました。写真中央の四角形がそれです。本当は正方形ですが今はビューポートがゆがんでいます。それはまた次の機会にどうにかしましょう。

文字列とXファイル

- プロジェクトを作成した

- 既存の4つのライブラリをプロジェクトに追加した

- プログラムのメインとなるCShootingクラスを作成し、ウィンドウを開いて閉じるまでの基本的な部分を実装した

- オブジェクトを管理するCObjectクラスを作成し、Xファイルの読み込み、ライティング、描画、テクスチャの削除を行えるようにした

- フォントを管理するCMyFontクラスを作成し、文字列を描画できるようにした


なお、各クラスの役割は次の通り。
クラス名 主な役割 備考
CGame メッセージハンドラ、ウィンドウ作成、メッセージループ (既存ライブラリ)
CInput キーボード、ジョイスティックの入力 (既存ライブラリ)
CGraphic デバイスの初期化、ウィンドウモードの切り替え (既存ライブラリ)
CImage テクスチャの管理、ビューポートの設定 (既存ライブラリ)
CShooting 各種初期化、描画、オブジェクトの移動などの指示 CGameを継承
CObject3D 自機、敵機、弾などすべてのオブジェクトを管理 -
CMyFont フォントの管理 -

<感想>

自機を動かすところまではすんなりいったのですが、時間がかかったのが文字の表示です。D3DXCreateFont関数を使って実装してみたところ、「引数が合わない」というエラーが出たので調べてみました。すると、引数3つの

HRESULT D3DXCreateFont(
   LPDIRECT3DDEVICE9 pDevice,
   HFONT hFont,
   LPD3DXFONT *ppFont
);

という関数が、

HRESULT D3DXCreateFont(
   LPDIRECT3DDEVICE9 pDevice,
   INT Height,
   UINT Width,
   UINT Weight,
   UINT MipLevels,
   BOOL Italic,
   DWORD CharSet,
   DWORD OutputPrecision,
   DWORD Quality,
   DWORD PitchAndFamily,
   LPCTSTR pFacename,
   LPD3DXFONT *ppFont
);

と変わったらしいことが分かったのです。どうやらMicrosoftはフォントを2Dベースから3Dベースにしたかったのですが、こんな大幅な変更を同じDirectX 9.0でやるってのはどうなんでしょう。そんなことを言っても始まらないのでこれを使って実装してみたのですが、いっこうに文字が表示されず…。

ウェブ上をまわってみたところ、これとは別にD3DXCreateFontIndirect()という関数で実装している人がいたので、参考にしつつこの関数に書き換えてみたらしっかりと表示することができました。数値を表示できるだけでずいぶんゲームっぽくなるんじゃないかと期待しています。