効果音実装テスト

ゲームにおいて、音は重要なファクターだ。音がなくてもゲームはできるが、その面白さは9割がた落ちると思う。自分的に。それに、すごく音のいいゲームに出会うとすごく感動するよね。地味にだけど。
てなわけでショットを撃つ音やBGMを入れてみることにした。
ちょっと話がそれるが、最近プログラムしながら思うのは、C++インスタンスの概念は個人的な使用から見ると大変やりづらい。いろんなクラスからアクセスしたいクラスがあるとき、いちいちアクセスする側にポインタを渡したりしないといけないので、すぐに依存関係が複雑になる。場合によっては両者を結びつける方法が見つからなくて困ることもある。だからそういうデータはグローバルスコープに置いてしまうのがラクなんだけど、それじゃあなんかC++使ってる意味って…という感じだ。ゲームでC++よりもC使う人が多分多いのはこういうところなんだろうか。別にゲームのソースなんて他人が再利用することはほぼないだろうし、本格的に切り出してライブラリにしたり、多人数大規模でやるプログラムでなければC++を使う意義は薄いのかも。でもすでにオブジェクト指向に浸ってしまったからなあ。いまのところ離れる気はない。
で、なぜこういう話になったかというと、例えば自機がショットを撃つときにいちいち音のオブジェクトを生成していたのでは負荷が大きい。それに同じ音を別のところで使うかもしれないから、いろんなところからアクセスできるようにグローバルデータとして保持しておきたい。しかしC風の生のグローバルオブジェクトを使うのもちょっとしり込みするので、そういうシステムデータをまとめたクラスを用意する。で、このままだとそのインスタンスやポインタを持つクラスからしかアクセスできないので、yaneSDKのsingletonテンプレートを使って、アプリケーション内唯一のスタティックオブジェクトにする。こうすれば、

CSystemData::GetObj()

とやると、どこからでも取得できる。もちろんアクセスできるのはpublicメンバだけなので、グローバルオブジェクトを使うよりは安全だ。これはもちろん音だけじゃなくて、いろんなシステムデータを扱うコントロールセンターみたいな感じで使える。
さて本題。
yaneSDKには音を統合的に扱うCSoundというクラスがある(実体はsmart_ptr)。これを使うには、まずCSoundFactoryというクラスをどこかで実体化しておかなくてはいけない。それと、音を読み込むときにいちいちパスとファイル名を指定してコーディングしていたのではデバッグに支障をきたすので、yaneSDKのCSoundLoaderクラスをCSystemDataに持たせて、テキストで指定した番号のファイルを読み込めるようにする。
以上の設計にした。しかしここで問題発生。
まず、CSoundLoaderは一度読み込んだ音を保持しているので、当然CSound(正確にはsmart_ptr)を持っている。CSoundLoaderはグローバルであるCSystemDataとライフタイムが同じなので、解体されるタイミングはCApp(アプリケーションクラス)よりあとである(不定かもしれないけど)。そして、すべてのCSoundオブジェクトが解体される前にCSoundFactoryが解体されてはいけない(例外が出る)。
つまり、CSoundFactoryを保持しているCAppが解体されてから、グローバルのCSystemDataが内包するCSoundLoaderが解体されるという順番になり、例外が投げられる。
よってこれを回避するには、CAppが解体される前にsingletonの実オブジェクトを明示的に解放しなければならない(中のCSoundLoaderだけReleaseAllしてもいいけど)。
CSoundFactoryをCAppじゃなくてCSystemDataに持たせればいい気もするが(CSoundLoaderより先に宣言する)、それをやるとなんか変なエラーが出て落ちる。
それともう一つ。CSoundLoaderがファイル読み込みに使ってるCLoadCacheだけど…設定テキストをSetで指定する前にデータのあるディレクトリの相対パスをSetReadDirで指定しなければならない。やね先生そういうことはソースの下のほ〜〜〜に書かないでよ…サンプルにもまったく触れられてないしorz
ということでいろいろありましたが音は鳴りましたヽ(´∀`)ノ