次の方法で共有


Coding4Fun: DirectSound

ゲーム開発入門

Derek Pierson
3Leaf Development

Part VIII - DirectSound

May 19, 2006
日本語版最終更新日 2006 年 8 月 9 日

はじめに

「ゲーム開発入門」の第 8 部へようこそ。これまで、DirectX のグラフィック機能に多くの時間を費やしてきました。また、DirectX API を使用して入力デバイスを制御できるようにする方法についても説明しました。今回は DirectX の別の一面としてサウンド デバイスを制御する機能を見ていきます。この機能は DirectSound 名前空間と AudioVideoPlayback 名前空間にあります。この記事には英語のページへのリンクも含まれています。サンプル プログラム ファイル内では実際のコメント行は英語で書かれていますが、この記事内では説明目的で日本語で書かれています。

ゲームのサウンド

サウンドは、ゲームに夢中になれるようなゲームの雰囲気を作り出します。サウンド効果がなかったらゲームがどれだけ退屈かを想像してみてください。大砲を発射したときや爆発が起こったときに何の音もないのです。また、サウンドを使用すると、アクションが増えるにつれてサウンドのテンポを上げることにより、シーンのドラマ性を高めることができます。

サウンド効果は実生活で聞こえてくるような音による手掛かりも提供します。たとえば、足音のボリューム、方向、および周波数に基づいて、近づいてくる人の方向や速度を表したりします。このようなサウンド効果によって、まるで目標物に固有の物理的挙動 (今後の記事で物理的な面から説明するときに詳細を扱います) が生じたかのように、ゲームにリアル感が加わります。また、ゲームで BGM を流すことで、ゲームがもっと楽しくなるようにします。最近のゲームでは、人気のあるゲームのサウンド トラックに収録されている音楽を提供しているアーティストの売り上げが急増しています。有名なアーティストがゲームのサウンド トラックを提供するのは珍しいことではありません。実際、サウンド トラックの重要性は、映画と同じレベルに達しています。

BattleTank2005 では、次のようにサウンドを取り入れようと考えています。まず、射撃、爆発、エンジン音について標準のサウンド効果が必要です。そのサウンドは方向面も正確である必要があります。つまり、左側にいる敵を撃つときには、左側からサウンドが聞こえるようにし、サウンドのボリュームによって距離感がわかるようにします。

次に、ゲーム中に BGM を再生でき、また、どの音楽を再生するのかを制御できるようにします。つまり、スプラッシュ スクリーンとゲームの設定中に 1 曲、ゲーム中には別の音楽を、ハイスコアの取得中にはさらに別の音楽を再生したいと考えています (今後の記事でこのようなスクリーンと状態を追加していきます)。

この記事では最初の要件を説明し、次回の記事で AudioVideo 名前空間を使用してサウンド効果、および MP3 ファイルと WMA ファイルの再生について説明します。これらの機能を追加する前に、DirectSound の機能を確認しましょう。

DirectSound

DirectSound 名前空間は、固定サンプリング レート (PCM) での 2 チャンネルのウェーブフォーム オーディオ データの再生のみをサポートします。それがどういうことかはまったくわかりませんが、DirectSound 名前空間は短い WAV ファイルの再生に、AudioVideoPlayback 名前空間はより長い MP3 ファイルまたは WMA ファイルの再生に使用すると説明すれば間違いないでしょう。DirectSound 名前空間のサウンドのキャプチャ/録音機能の使用方法について説明する予定はありません。ただし、ゲームでサウンドの再生だけでなくサウンドの録音が必要になったときにどうすればいいか見当が付くように、これらの機能が存在することは覚えておいてください。

DirectSound 名前空間を使用すると、3 次元のポジショニング効果を使用してサウンドの再生とキャプチャを行うことができます。また、再生および録音したオーディオにサウンド効果を追加する機能も提供します。DirectX3D 名前空間および DirectInput 名前空間と同様に、使用する実際のハードウェア デバイスはデバイス クラスに抽出されます。これら 2 つの名前空間のデバイス クラスと同様に、DirectSound デバイスはバッファを使用し、協調レベルを持ち、デバイス機能を備えています。この記事ではサウンド効果については説明しませんが、忘れているわけではありません。次回の記事で詳しく説明します。

デバイス

デバイスは、コンピュータのオーディオ ハードウェアに対するインターフェイスです。既定の GUID (DSoundHelper.DefaultPlaybackDevice) を使用して Device クラスを作成するか、システム上のすべてのデバイスを列挙することができます。他のデバイス クラスと同様に、列挙された各デバイスにはデバイスの Caps プロパティの Caps 構造体に格納されている機能の一覧が用意されています。デバイスを選択したら、特定の GUID を使用してデバイス クラスをインスタンス化します。

オーディオ デバイスにも、入力デバイスと同様に協調レベルがあります。考えられる 3 つの値は、Normal (標準)、Priority (優先)、および Write Primary (書き込み優先) です。これらの値は、CooperativeLevel 列挙を使用して設定されます。

協調レベル 意味
Normal (標準) アプリケーションは、プライマリ バッファのフォーマットの設定やプライマリ バッファへの書き込みを行えません。この協調レベルを使うすべてのアプリケーションでは、プライマリ バッファの設定が 22 kHz、ステレオ サウンド、8 ビット サンプリングでロックされます。
Priority (優先) ミキシングなどのハードウェア リソースにアクセスするための優先権を与えて、プライマリ サウンド バッファのフォーマットを変更できます。ゲームにはこの設定をお勧めします。
Write Primary (書き込み優先) プライマリ サウンド バッファに直接アクセスできますが、アプリケーションはプライマリ バッファに直接書き込みを行わなければなりません。

バッファ

DirectX のすべてのサウンドはバッファを使って制御されます。これらのバッファはコンピュータのメモリまたはサウンド カード自体に存在します。DirectSound で使用される 2 つのバッファはプライマリ バッファとセカンダリ バッファと呼ばれます。

"プライマリ バッファ" には、デバイスに送られる実際のオーディオ データが含まれていて、このバッファは DirectX API によって自動的に作成され管理されます。この API により、任意の数のセカンダリ バッファを含むプライマリ バッファでサウンドがミキシングされます。プライマリ バッファと直接対話する必要がある場合は、デバイスの協調レベルを Write Primary (書き込み優先) に変更してください。

"セカンダリ バッファ" には 1 つのオーディオ ストリームが保持され、このバッファはアプリケーションによって明示的に作成される必要があります。各アプリケーションが少なくとも 1 つのセカンダリ バッファを作成して、サウンドを格納し再生しなければなりません。また、各セカンダリ バッファには特定のウェーブ フォーマット (WaveFormat 構造体に記述されています) があり、そのフォーマットに一致するサウンド データだけを対象のセカンダリ バッファに読み込むことができます。アプリケーションは、各フォーマット用に個別のセカンダリ バッファを作成し、API を使ってプライマリ バッファ内の共通フォーマットにミキシングすることによって異なるフォーマットのサウンドを再生できます。2 つの異なるセカンダリ バッファ内のサウンドをミキシングするには、それらのサウンドを同時に再生し、プライマリ バッファで API を使ってそれらのサウンドをミキシングするだけです。異なるセカンダリ バッファの中でミキシングできるセカンダリ バッファの数に対する制限は、システムの処理能力だけです。ただし、必要な処理を追加すると、ゲームの速度が遅くなることも忘れないでください。ここでは AI や物理的な計算を追加しませんでしたが、使用可能な処理能力には注意する必要があります。

セカンダリ バッファはアプリケーションが有効な間は常に使用することも、必要に応じて作成したり削除したりすることもできます。1 つのセカンダリ バッファにはゲーム全体を通して同じデータを格納しておくことも、そのバッファにさまざまなサウンドを含めておくこともできます (フォーマットは一致している必要があります)。セカンダリ バッファ内のサウンドは、一度だけ再生することも、ループするように設定することもできます。再生するサウンドが短い場合、サウンド全体をバッファに読み込むことができますが (静的バッファといいます)、長いサウンドについてはストリーミングする必要があります。アプリケーションは、バッファへのサウンドのストリーミングを管理します。

バッファを作成するときに、BufferDescription クラスを使用してそのバッファのコントロール オプションを指定する必要があります。最初にこのコントロール プロパティにコントロール オプションを設定せずにバッファのプロパティを使用すると、例外がスローされます。コントロール オプションは、各プロパティを True に設定して組み合わせるか、Flag プロパティで組み合わせることができます。

  BufferDescription bufferDescription = new BufferDescription ( );

// 個別のプロパティを使用します bufferDescription.ControlVolume = true; bufferDescription.ControlPan = true;

// または、個別のプロパティを Flags プロパティで組み合わせます bufferDescription.Flags = BufferDescriptionFlags.ControlVolume | BufferDescriptionFlags.ControlPan;

ボリューム、パン、および周波数の制御

バッファのこのような設定を制御するには、まず、バッファの ControlPan、ControlVolume および ControlFrequency プロパティを True に設定する必要があります。その後、バッファの Pan、Volume、および Frequency プロパティを使用して、パン、ボリューム、周波数の値を設定できます。

Volume は 1/100 dB 単位で表され、0 (フル ボリューム) ~ -10,000 (完全に無音) の範囲になります。dB スケールは線形ではないので、ボリュームの設定が真の無音である - 10,000 に到達するよりもかなり前に、事実上の無音が発生することがあります。また、サウンドのボリュームを録音した時点よりも大きくする方法はないので、少なくともゲームで求められる最大ボリュームを満たすほど十分に大きいボリュームでサウンドを録音するようにしてください。

Pan は整数として表記され、-10,000 (最も左) ~ +10,000 (最も右) の範囲になります。0 は中央を意味します。

Frequency 値は、1 秒あたりのサンプリング回数で表され、バッファの再生速度を示します。数値が大きいほど、サウンドの再生速度が速くなり、ピッチが上がります。逆に、数値が小さいほど速度が遅くなり、ピッチが下がります。サウンドを元の周波数にリセットするには、Frequency 値を 0 に設定するだけです。周波数の最小値は 100 で、最大値は 200,000 です。

この時点では、ボリューム、パン、および周波数の設定を使用して、サウンドを操作し、原点からの方向と距離を反映できるようにすることができるとお考えかもしれませんね。最終的には、これがもともとの要件の 1 つでした。ただし、自分で計算を行ってそれぞれの目標物の位置と距離を判断するのではなく、DirectX によってこのための API が提供されます。

3D サウンド

DirectX の 3D 機能を使用すると、空間にサウンドを配置し、移動するサウンドにドップラー偏移を適用できます。

   Wikipedia でこのすばらしいドップラー効果の説明 (英語) を確認するか、このアプレット (英語) を使用してドップラー効果をビジュアル化してみてください。

DirectX が 3D のサウンドを処理する方法を説明する前に、人間がどのようにしてサウンドを感知するのかをお話した方が役に立つでしょう。DirectX はこれと同じ原理を使用して、サウンドをできる限りリアルにします。

  1. リスナーとサウンドの距離が長くなるにつれて、サウンドのボリュームは固定比率で下がります。この効果はロールオフと呼ばれます。サウンドのボリュームとリスナーからサウンドまでの距離とは反比例の関係にあります。つまり、距離が半減するとボリュームが 2 倍になります (http://ja.wikipedia.org/wiki/%E9%80%862%E4%B9%97%E3%81%AE%E6%B3%95%E5%89%87 を参照)。

  2. リスナーの左側で発生した音は、右耳よりも左耳の方で大きく聞こえ (両耳間強度差)、左方向で発生した音は、右耳よりも早く左耳に到達 (両耳間時間差) します (http://en.wikipedia.org/wiki/Interaural_Intensity_Difference (英語) を参照)。

  3. 耳の形は "消音" と呼ばれる効果を作り出します。前で発生した音は後ろで発生した音よりも大きく聞こえます。これはもちろん人間の耳が前向きについているためです。

  4. 耳たぶのカーブによって、異なる方向から到達するサウンドが微妙に変わります。これは音源の位置についての手掛かりを脳に伝えます。この効果は数学的な理論に基づいていて、頭部伝達関数 (HRTF) と呼ばれています (http://en.wikipedia.org/wiki/Head_Related_Transfer_Function (英語) を参照)。

DirectX が左手系のデカルト座標系とベクトルを使用して、位置と方向の情報を表すことは既にわかっています。これと同じシステムが DirectSound によっても計算で使用されています。3D サウンドを扱うときに最も重要な情報の一部は、距離の既定の測定単位がメートルであり、速度の既定の測定単位が 1 秒あたりのメートル数 (m/秒) であることです。ゲームでは共通の測定システムを使用する必要があります。これを変更するには、Listener3D オブジェクトの DistanceFactor プロパティを、アプリケーション指定の距離単位あたりのメートル数を表す値に設定します。この時点まですべての計算でフィートを使用していた場合は、この値を 0.3048 (1 フィートは 0.3048 メートルなので) に設定するだけです。

また、(ボリューム、パン、および周波数はユーザーが変更しますが) サウンドの操作は DirectX に任せているため、使用している音源がモノラルでありステレオ ソースではないことを確認する必要があります。最終的には、バッファの Control3D プロパティを True に設定して 3D サウンドを有効にするようにしてください。

DirectSound は 2 つのオブジェクトを使用してアプリケーションで 3D サウンドを管理します。使用するオブジェクトは Buffer3D と Listener3D です。

3D バッファ

SecondaryBuffer とは異なり、Buffer3D オブジェクトは Buffer クラスから継承されません。代わりに、SecondaryBuffer を Buffer3D コンストラクタに渡すことによって 3D バッファ オブジェクトを作成します。3D バッファはサウンドの処理方法を決定する数多くのプロパティを公開しています。

MinDistance プロパティは、サウンドのボリュームがこれ以上増加しなくなる距離を決定します。この設定を使用すると、いくつかのサウンドが同じボリュームで録音された場合でも、特定のサウンドのボリュームを大きくすることができます (詳細については、DirectX のドキュメントを参照してください)。このプロパティの既定値は 1 メートルです。つまり、リスナーと音源の距離が 1 メートルの場合、サウンドはフル ボリュームです。

MaxDistance プロパティはその逆で、サウンドのボリュームがこれ以上減衰しなくなる距離を決定します。このプロパティの既定値は、10 億メートルで、聞こえる範囲よりはるかに大きい値です。不要な処理を避けるために、この値を妥当な値に設定し、BufferDescription の Mute3DAtMaximumDistance プロパティを True に設定する必要があります。

最後に、方向のあるサウンドの場合にサウンド コーンの値を指定することもできます。サウンド コーンはスポットライトによって生成されるコーンとほぼ同じです (記事 6 を参照)。サウンド コーンは、角度 (内部コーンの角度と外部コーンの角度)、向き、外部ボリューム プロパティで構成されます。サウンド コーンの詳細については、DirectX のドキュメントを参照してください。

3D リスナー

3D バッファは音源を説明するものですが、3D リスナーはリスナーを説明するものです。サウンドは、バッファの位置、向き、および速度だけでなく、リスナーの位置、向き、および速度の影響を受けます。DirectX の既定のリスナーは、0 の位置にあり、z 軸の正の方向を向き、頭を y 軸の正の方向にしています。各アプリケーションには、Listener3D オブジェクトが 1 つだけ存在します。

ゲームでプレーヤーがサウンドを聞き取る方法を変更するには、リスナーの位置、向き、および速度を操作します。ドップラー偏移またはロールオフ係数と同様に、音響環境のグローバル設定を制御することもできます。

   DopplerFactor プロパティと RolloffFactor プロパティは 0 ~ 10 の数値になります。0 は値が無効であることを意味します。1 はこのような音響環境の実世界での値を表します。その他すべての値は乗数なので、2 は実世界の効果の 2 倍、3 は 3 倍といったようになります。

BattleTank2005 では、3 つの新しいクラスを追加する予定です。追加するクラスは、実際のオーディオ デバイスを表す SoundDevice クラス、Listener3D オブジェクトをカプセル化する SoundListner クラス、および個別のサウンド効果を表す SoundEffects クラスです。BattleTank2005 のそれぞれの戦車には、エンジンの出す騒音や発砲時の音など、複数のサウンドが関連付けられています。サウンドをまとめるために、Tank クラスを更新して適切な時点と適切な位置情報でこれらのサウンドを再生します。静止している物体 (障害物) にはサウンドを関連付けないので、この機能は UnitBase クラスには追加しませんでした。

これらのクラスを追加する前に、Microsoft.DirectX.DirectSound.DLL アセンブリへの参照を追加する必要があります。バージョン 2.0 ではなく 1.0 を選択するようにしてください。BattleTank2005 はまだ 2.0 DirectX API のベータ版を使用するように更新されていません。SoundDevice という新しいクラスを追加し、Microsoft.DirectX.DirectSound の using ステートメントを追加します。3 つすべてのクラスが IDisposable インターフェイスを実装して、Keyboard クラスと Mouse クラスで使用していたのと同じ Dispose パターンを使用します (より簡潔で理解しやすくするために、以下のコード サンプルではこの部分を省略しています)。

ここでは馴染みのあるパターンに従って、_device というプライベート変数を SoundDevice クラスに追加して、コンストラクタでインスタンス化します。また、デバイスを使用する前に、協調レベル (CooperativeLevel) を設定する必要もあります。前述のとおり、CooperativeLevel.Priority 設定を使用します。このメソッドにゲーム形式の参照を渡して、デバイスが Windows メッセージを受け取れるようにします。最後に、デバイスを操作するときは常に失敗する可能性があるので、デバイス生成コードを Try/Catch で囲みます。

  
    using System;
using Microsoft.DirectX.DirectSound;

namespace BattleTank2005 { class SoundDevice :IDisposable { public SoundDevice( System.Windows.Forms.Form parentForm ) { try { _device = new Microsoft.DirectX.DirectSound.Device(); _device.SetCooperativeLevel(parentForm, CooperativeLevel.Priority); } catch { // サウンドを使用できません } }

public Microsoft.DirectX.DirectSound.Device AudioDevice { get { return _device; } }

private Microsoft.DirectX.DirectSound.Device _device; } }

次に必要なクラスは SoundListener クラスです。DirectSound と DirectX の using ステートメントを追加した後、上記で作成した SoundDevice クラスへの参照を渡すことによってこのクラスを構築します。Listener は最後にミキシングされたサウンドを "聞き取る" ことができるように、オーディオ カードのプライマリ バッファに関連付けられている必要があります。その場合でも、SoundListener クラスはほとんど受動的なので、その位置、速度、向きを更新して、バッファで 3D サウンドを有効にする必要があります。バッファが作成されたら、Listener3D クラスのコンストラクタに渡して、Listener を作成します。最後に、リスナーの Listener3DSettings をローカル変数に格納して、Update メソッドでアクセスできるようにします。

Update メソッドは、Listener オブジェクトに位置情報を渡す方法を提供します。

最後のステップは、Listener に更新した値を適用することです。これを行うには、Listener3DSettings 値の更新後に Listener3D クラスの CommitDeferredSettings メソッドを呼び出します。このメソッドは、前回の呼び出し時以降に Listener3DSettings に加えられた変更をすべてコミットします。すべてが完了すると、SoundListener クラスはゲーム内の正しい位置に配置されます。

  
    using System;
using Microsoft.DirectX;
using Microsoft.DirectX.DirectSound;

namespace BattleTank2005 { class SoundListener :IDisposable { public SoundListener(SoundDevice soundDevice) { BufferDescription bufferDescription = new BufferDescription(); bufferDescription.PrimaryBuffer = true; bufferDescription.Control3D = true;

// プライマリ バッファを取得します Microsoft.DirectX.DirectSound.Buffer buffer = new Microsoft.DirectX.DirectSound.Buffer(bufferDescription, soundDevice.AudioDevice);

// リスナーをプライマリ バッファにアタッチします _listener3d = new Listener3D(buffer);

// 初期パラメータを格納します _listenerSettings = new Listener3DSettings(); _listenerSettings = _listener3d.AllParameters; }

public void Update(Vector3 position ) { _listener3d.Position = position;

_listener3d.CommitDeferredSettings(); }

private Microsoft.DirectX.DirectSound.Listener3D _listener3d; private Listener3DSettings _listenerSettings; } }

既にオーディオ デバイスを表す複数のクラスと、サウンドを "聞き取る" 1 つのクラスがあるので、実際にサウンドを作成する必要があります。サウンドを作成するために、SoundEffect クラスを追加します。再び、DirectSound 名前空間の using ステートメントを追加する必要があります。また、Vector3 クラスを使用するので、DirectX 名前空間の using ステートメントを追加する必要もあります。

SoundEffect は SoundDevice への参照と、サウンドを含んでいる WAV ファイルへのパスを渡すことによって作成されます。コンストラクタでは、必要な機能をすべて "有効にする" BufferDescription オブジェクトを作成します。ここで 3D サウンドの制御、ボリュームの調整、周波数の変更を行う能力が必要です。次に、サウンド ファイルへのパスと、先ほど作成した BufferDescription を渡すセカンダリ バッファを作成します。3D サウンドを再生する必要がある Buffer3D を作成するには、インスタンス化するときに SecondaryBuffer を Buffer3D クラスに渡します。そうすると、3DBuffer ができあがります。

Buffer3D クラスを設定したら、プロパティにアクセスして設定を変更できます。MaxDistance をもっと管理しやすい数値に設定します。この設定を SecondaryBuffer の Mute3DAtMaximumDistance 設定と組み合わせると、遠すぎて聞こえないサウンドはまったく再生されず、処理サイクルは使用されません。

ゲームで実際に SoundEffect を使用するために、3 つのパブリック メソッドを提供します。各メソッドは、サウンドの再生用、サウンドの停止用 (ループしている場合)、およびクラスの更新用です。Play メソッドは、_isLooping 変数の設定によって、サウンドを一度再生するかサウンドをループします。サウンドがループしている場合は停止できる必要があります。つまり、Stop メソッドがその役割を果たします。Update メソッドは、サウンドが関連付けられている戦車の更新された位置をサウンド バッファに渡す方法で、正確な計算を行うことができます。

  
    using System;
using Microsoft.DirectX.DirectSound;
using Microsoft.DirectX;

namespace BattleTank2005 { public class SoundEffects :IDisposable { public SoundEffects( SoundDevice soundDevice, string soundFile) { BufferDescription bufferDescription = new BufferDescription(); bufferDescription.Control3D = true; bufferDescription.ControlVolume = true; bufferDescription.ControlFrequency = true; bufferDescription.Mute3DAtMaximumDistance = true;

try { _secondaryBuffer = new SecondaryBuffer(soundFile, bufferDescription, soundDevice.AudioDevice);

_3dBuffer = new Buffer3D(_secondaryBuffer); _3dBuffer.MaxDistance = 10000; } catch { // サウンドを使用できません } }

public void Update(Vector3 position) { _3dBuffer.Position = position; }

public void Play() { if (_isLooping == true) _secondaryBuffer.Play(0, BufferPlayFlags.Looping); else _secondaryBuffer.Play(0, BufferPlayFlags.Default); }

public void Stop() { _secondaryBuffer.Stop(); _secondaryBuffer.SetCurrentPosition(0); }

public bool IsLooping { set { _isLooping = value; } }

public int Volume { set { _secondaryBuffer.Volume = value; } }

public int Frequency { set { _secondaryBuffer.Frequency = value; } }

private SecondaryBuffer _secondaryBuffer; private Buffer3D _3dBuffer; private bool _isLooping; } }

ここで、これらの新しいクラスをゲーム全体に統合する必要があります。これらの各クラスのうち 1 つだけを所有できるので、まずは、プライベート変数を GameEngine クラスに追加して、SoundDevice クラスと SoundListener クラスへの参照を保持します。GameEngine クラスの最下部に、次のコードを追加します。

  
    private SoundDevice _soundDevice;
private SoundListener _soundListener;

次に、各クラスをインスタンス化する必要があります。これには GameEngine の Initialize クラスが最適ですが、サウンド関連アイテムを共に保持するために、ConfigureSounds というメソッドを作成します。GameEngine クラスに次のメソッドを追加します。

  
    private void ConfigureSounds()
{
_soundDevice = new SoundDevice(this);
_soundListener = new SoundListener(_soundDevice);
}

ここで、ConfigureDevice メソッドへの呼び出し直後に、ConfigureSounds メソッドへの呼び出しを GameEngine の Initialize メソッドに追加します。

  ConfigureDevice();

<B>ConfigureSounds();</B>

SoundDevice クラスに関しては、やるべきことはこれで全部です。SoundDevice クラスの作成後、このクラスに必要なのは Device オブジェクトへの参照だけです。ただし、SoundListener クラスは各フレームの正しい位置情報で更新する必要があります。これには、GameEngine クラスの Render メソッドが最適です。camera クラスはゲーム内でのプレーヤー (リスナー) の位置を表すので、camera クラスの位置を渡すだけです。GameEngine クラスの Render メソッドで、camera の Update メソッドを呼び出した直後に次のコードを追加します。

  _soundListener.Update(_camera.Position );

各戦車では、作成できるそれぞれのサウンド用に SoundEffect クラスの一意のインスタンスを取得します。まず、いくつかのプライベート変数を追加して、戦車で作成する 2 つのサウンド効果を保持します。Tank クラスの最下部に、次のコードを追加します。

  
    private SoundEffects _engineSound;
private SoundEffects _fireSound;

各戦車に必要なサウンドを備えておくために、それらのサウンドをコンストラクタに渡します。次のように Tank クラスのコンストラクタを更新します (新しいコードは太字で示してあります)。

  
    public Tank(Device device, string meshFile, Vector3 position, float scale, float speed, <B>SoundEffects fireSound, SoundEffects engineSound</B>)
: base(device, meshFile, position, scale)
        {
_speed = speed;

<B> _engineSound = engineSound;</B> <B> _engineSound.IsLooping = true;</B> <B> _engineSound.Play();</B>

<B> _fireSound = fireSound;</B> }

サウンド効果が戦車インスタンスに関連付けられたら、エンジン音をループに設定してからエンジン音に取りかかります。

次のステップは、Tank クラスの Update メソッドを変更して、戦車の位置で 3D バッファを更新することです。Update メソッドの末尾に次のコードを追加します。

  
    if (_engineSound != null)
{
_engineSound.Update(base.Position);
}

戦車の発砲音は断続的なので、戦車が発砲したかどうかは、後で追加する AI によって判断されます。現時点ですべきことは、必要なフレームワークを作成して発砲音を再生することだけです。Tank クラスに次のメソッドを追加します。

  
    public void Shoot()
{
if (_fireSound != null)
{
_fireSound.Update(base.Position);
_fireSound.Play();
}
}

tank クラスを更新してサウンドの準備を完了するための最後のステップは、GameEngine の CreateTanks メソッドを更新することです。エンジンの出す騒音用と発砲音用の SoundEffect を作成し、作成時に tank クラスに渡す必要があります。CreateTanks メソッドを次のように変更します。

  
    private void CreateTanks()
{
_tanks = new List<UnitBase>();

SoundEffects engineSound1 = new SoundEffects(_soundDevice, @"EngineSound1.wav"); engineSound1.Volume = -1000; engineSound1.Frequency = 100000; // 100 ~ 200000 の値に設定します SoundEffects engineSound2 = new SoundEffects(_soundDevice, @"EngineSound2.wav"); engineSound2.Volume = -1000; engineSound2.Frequency = 10000; // 100 ~ 200000 の値に設定します SoundEffects fireSound = new SoundEffects(_soundDevice, @"Fire.wav");

Tank newTank1 = new Tank(_device, @"bigship1.x", new Vector3(0.0f, 20.0f, 100.0f), 1f, 10.0f, fireSound, engineSound1); Tank newTank2 = new Tank(_device, @"bigship1.x", new Vector3(100.0f, 20.0f, 100.0f), 1f, 10.0f, fireSound, engineSound2);

_tanks.Add(newTank1); _tanks.Add(newTank2); }

これで、サウンド効果のボリュームまたは周波数を変更してみることができます。飛び回ってそれぞれの宇宙船に近づいてみると、自分の位置によって方向とボリュームの点でサウンドが変化します。周波数を変更するとピッチが変わるので、ボリュームの設定がどうなっているかを推測できます。

まとめ

この時点で BattleTank2005 ゲームをすると、リスナー (プレーヤー) との位置関係に従って正しく調整された、さまざまな敵の戦車のエンジンの出す騒音が聞こえるはずです。この時点では、BattleTank2005 に追加するオーディオ機能の最初のセットを統合しました。これらのサウンド効果を聞くためにさまざまな設定を試してみてください。コード内にオプションの設定をいくつかコメントしてあるので、それらの設定を簡単に変更できます。次回の記事では、DirectSound の組み込みのサウンド効果を使用してサウンドを変更する方法と、AudioVideoPlayback 名前空間を使用してサウンド トラック用の通常の MP3 ファイルを再生する方法について説明する予定です。

それまでは、コーディングを楽しんでください。

Derek Pierson は、テクノロジ エバンジェリズム ツールの開発と周辺技術のコンサルティングを専門とする 3Leaf Development で、テクニカル エバンジェリストを務めています。Derek は 10 年以上にわたって各種プログラミング言語によるエンタープライズ アプリケーションの設計および開発に携わっています。また、プログラミング教育に熱意を抱き、洗練され、簡素であることが優れたソフトウェアの条件であるという信念を持っています。

top of page Top of Page