3Dサウンド

 3Dサウンドとは、2個以上のスピーカーで3次元の立体的な音響を作り出す機能だそうです。 どうも単なるステレオとはちょっと違うらしいですね。
細かいことはよく分かりませんが、サンプルを実行してもらえばなんとなく分かってもらえるんじゃないかと思います。

 この機能だけでも音ゲーが作れそうな気がします。しかし、普通のアクションやアドベンチャーゲームでは3Dサウンドがないと 遊べないようなゲームにならないよう気をつけたいですね。いろんなユーザーがいますから。


3Dサウンドの設定

 3Dサウンドを使用するためには事前に設定が必要な項目があります。
E3DSet3DSoundListenerE3DSet3DSoundDistanceです。

E3DLoadSound

 今回は3Dサウンドを使用します。

E3DLoadSound filename, soundid, use3dflag, bufnum

E3DLoadSound命令使用時にuse3dflag1にして3Dサウンド用として読み込んでください。

E3DSet3DSoundListener

 ドップラー効果とロールオフ係数の設定を行ないます。
 ドップラー効果とは移動している音源(音の発生場所)が、リスナー(聞き手)に近づいているとき音は高く聞こえ、 リスナーから遠ざかっているときは音が低く聞こえる現象のことです。
一番分かりやすい例として救急車がよくあげられます。近づいているときは低く聞こえ、走り去るときは高く聞こえます。
分からなければ、あるいはもっと詳しい原理が知りたければ「ドップラー効果」でぐぐって下さい。私が解説するよりよっぽど 正確で分かりやすい解説があるはずです。

 ロールオフ係数とはリスナーと音源の距離に応じて生じる音の減衰の度合いのことです。
距離が離れるほど音は小さくなりますが、この値を調整すると、この小さくなる度合いを調整できます。
減衰が大きければちょっと離れるだけでもどんどん音が小さくなり、減衰が小さければ離れても離れても音はなかなか小さくなりません。
減衰を無視(0)すれば音源やリスナーがどこにいても同じ音量になるわけです。

 どちらも100を設定すれば実世界と同じ扱いになります。とりあえず100にしておきましょう。

E3DSet3DSoundDistance

最大距離と最小距離  最小距離と最大距離の設定を行ないます。
これを使うには最小距離と最大距離の概念を知っておく必要があります。
詳しい解説はDirectXのドキュメントに載っているのでそちらを参照してください。(DirectXのドキュメントはDirectXのサイトでダウンロードできます。)
ここではおおざっぱな説明だけしておきます。

 右の図を見てください。距離は音源からリスナーの距離です。
音は距離が倍離れるとボリュームは距離減衰で半分になるそうです。このため、ボリュームと距離のグラグを書くと右の薄赤や薄青のような曲線になります。

最小距離
 近づけば近づくほどボリュームが大きくなると、リアルですが音が大きすぎてゲーム的にちょっと問題があります。
そこである程度の距離まで近づいたら音をそれ以上大きくしないように設定します。この距離が最小距離です。
この最小距離でボリュームの勾配(右図のカーブの曲がり具合)が決まってきます。
最小距離が大きければ離れてもなかなか音は小さくなりません。最小距離が小さければ音はすぐに小さくなります。
イメージ的にはこんな感じです。
・最小距離が大きい=音源の音が大きい
・最小距離が小さい=音源の音が小さい

最大距離
距離が離れるほど音は小さくなっていくのですが、ほとんど聞こえないくらいに音が小さくなっても計算を続けていては不要な計算が増えてしまいます。
というわけで、DirectXでは最大距離まで離れたら音を減衰させる計算を止めるという処理を行ないます。(最大距離以上は慣れたらボリュームは下がらない。)
最大距離にはこの距離を指定します。(下がらないのではなくボリュームゼロにしてほしい気がするのは私だけでしょうか…。)

 以上を踏まえて右のグラフのようにE3DSet3DSoundDistanceを設定すると次のようになります。
青線は最小距離に100、最大距離に500を設定した場合のボリューム変化のグラフです。次のように設定します。
E3DSet3DSoundDistance ao, 100*100, 500*100

赤線は最小距離に200、最大距離に700を設定した場合のボリューム変化のグラフです。次のように設定します。
E3DSet3DSoundDistance aka, 200*100, 700*100


音源とリスナーの位置

 3Dサウンドを利用するには、再生中に音源とリスナーの位置情報などを設定する必要があります。
音源情報  音源の位置と移動速度はE3DSet3DSoundMovementで設定します。
移動速度はドップラー効果に反映されるだけなので、特に必要なければ入れる必要はありません。

リスナー情報 リスナーの位置と向きはE3DSet3DSoundListenerMovementで設定します。
モデルデータの番号(hsid)か視点の位置のどちらかで指定が出来ます。

どちらも1ループごとに毎回設定する必要があります。(位置と向きが変わなければ設定する必要はありませんが、ゲームならほとんどの場合ループごとに変化しているため。)
E3DSet3DSoundMovementE3DSet3DSoundListenerMovementは同じタイミングでやっておくといいと思います。


サンプル解説

 2個以上のスピーカーかヘッドホンが必要です。
音源はNPCです。音源に向かって近づけば見つけることが出来ます。
NPCはぐるぐるまわっているので、移動を停止したい場合は、メインループ内の
gosub *MoveNPChara
をコメントアウトすれば動かなくなります。
また、タイトルバーに音源との距離を表示しています。音の距離減衰の効果を確認するのに参考にしてください。

離れたり近づいたりして3Dサウンドの感覚を実感してください。


サンプル

Easy3Dのパッケージ内のMediaフォルダの中にある次のwaveファイルをコピーしておいてください。
sbounce.wav
同じくMediaフォルダの中にあるtako.sigとtako01.bmpもコピーしておいてください。
コピーの保存はdataフォルダの中に入れてください。
動作確認にはスピーカーやヘッドホンなどが必要です。用意しておいてください。
その他スクリプトと必要なファイルはこちらです。→[e3d026.zip}

操作は簡単。
キーボードのカーソルキーで移動できます。地面がないところでも移動できるので大丈夫。


;
;	3Dサウンド
;		NPC音源
;

#include "e3dhsp.as"

	;/////////////////
	;
	;	初期化
	;
	E3DInit
	dim keybuf, 256		;キー入力
	E3DCreateFont 24,12,400,,,,"MS ゴシック",fontid	;フォントの設定
	fkabe = 0		;壁データとの当たり判定(0:off / 1:on)
	fcolibound = 1	;PCとNPCが衝突したとき重ならないようにする(0:off / 1:on)


	;/////////////////	
	;
	;	カメラの初期化
	;
	E3DSetProjection 100, 50000


	;/////////////////	
	;
	;	ライトの設定
	;
	E3DCreateLight lid1		;光源を作成
	lightdirx1 = 1		;平行光の向き
	lightdiry1 = -1
	lightdirz1 = 0
	lightr1 = 255	;平行光の色
	lightg1 = 255
	lightb1 = 255
	E3DSetDirectionalLight lid1, lightdirx1, lightdiry1, lightdirz1, lightr1, lightg1, lightb1	;光源を平行光源に設定


	;/////////////////	
	;
	;	形状データのロード
	;
	sdim mediadir, 2048
	mediadir = curdir + "\\data\\tako.sig"
	E3DSigLoad mediadir, hsid1
	E3DSetPos hsid1, 5000, 0, 5000
	E3DSetBeforePos hsid1
	;NPC
	E3DSigLoad mediadir, hsid3
	E3DSetPos hsid3, 15000, 0, 25000
	E3DSetBeforePos hsid3


	E3DCreateQ axisqid


	;/////////////////	
	;
	;	地面の作成
	;
	sdim pathbuf, 2048, 4
	pathbuf.0 = curdir + "\\data\\yama.bmp"	;地面の座標情報
	pathbuf.1 = curdir + "\\data\\michi.bmp"	;地面の道の情報
	pathbuf.2 = curdir + "\\data\\kawa.bmp"	;地面の川の情報
	pathbuf.3 = curdir + "\\data\\bazou.bmp"	;地面、道、川の模様を決める、BMPファイル

	;地面作成用の値
	mapsize = 60000		;X,Z座標の最大値
	mapdiv = 120		;座標の分割数
	mapheight = 3000	;高さの最大値
	E3DLoadGroundBMP pathbuf.0, pathbuf.1, pathbuf.2, pathbuf.3, mapsize, mapsize, mapdiv, mapdiv, mapheight, hsid0

	;	壁の作成
	;道のマップをそのまま壁の生成に使います。
	E3DSetMovableArea pathbuf.1, mapsize, mapsize, mapdiv, mapdiv, mapheight+100, hsid2


	;/////////////////	
	;
	;	サウンドの用意
	;
	sdim pathbuf, 2048
	; 音ファイルの読み込み
	pathbuf = curdir + "\\data\\sbounce.wav"
	E3DLoadSound pathbuf, soundid2, 1		;音の識別番号取得
	E3DSetSoundLoop soundid2, 1		;繰り返し再生

	;3Dサウンドの設定
	E3DSet3DSoundListener 100, 100	;実世界に同じ
	E3DSet3DSoundDistance soundid2, 1000*100, 20000*10000

	;再生開始
	E3DPlaySound soundid2, 0, DMUS_SEGF_GRID


;////////////////////////////////////////////////////////////////

;/////////////////
;
;	メインループ
;
*main

	E3DGetKeyboardState keybuf	;キー状態取得
	if keybuf.VK_ESCAPE = 1 : goto *bye ; [ESC]で終了

	gosub *MoveChara		;キャラクター移動
	gosub *MoveNPChara

	gosub *PlaySound	;音声再生

	;バックバッファへの書き込み作業を行う
	E3DBeginScene	;-----シーンスタート
		;モデルが、視野内にあるか判定
		E3DChkInView hsid1	;キャラクター(PC)
		E3DChkInView hsid3	;キャラクター(NPC)
		E3DChkInView hsid0	;地面
		E3DChkInView hsid2	;壁

		gosub *ChkConfAxis	;キャラクター同士の当たり判定
		gosub *ChkConf	;地面、壁との当たり判定(キャラクター同士の判定の後)(押し出し防止)
		gosub *MoveCamera		;カメラ移動

		;バックバッファにレンダリングする。
		E3DRender hsid1, 0
		E3DRender hsid3, 0
		E3DRender hsid0, 0

	E3DEndScene		;-----シーン終了
	E3DPresent		;バックバッファの内容を、プライマリバッファに転送。描画する。

	E3DSetBeforePos hsid1
	E3DSetBeforePos hsid3

	E3DWaitbyFPS 60 : await 0
goto *main

;////////////////////////////////////////////////////////////////
;
;	サブルーチン
;

;/////////////////
;
;	終了処理
;
*bye
	E3DDestroyLight lid1	;ライトを破棄
	E3DBye
	end



;/////////////////
;
;	キャラクター移動
;
;	PC
*MoveChara
	forwardstep = 200	;移動速度(前進)
	backstep = -forwardstep	;移動速度(後退)
	degstep = 5
	mdegstep = -degstep

	if keybuf.VK_UP    = 1 : E3DPosForward hsid1, forwardstep	;矢印上
	if keybuf.VK_DOWN  = 1 : E3DPosForward hsid1, backstep	;矢印下
	if keybuf.VK_LEFT  = 1 : E3DRotateY hsid1, mdegstep	;矢印左
	if keybuf.VK_RIGHT = 1 : E3DRotateY hsid1, degstep	;矢印右
	return

;	NPC
*MoveNPChara
	forwardstep = 50	;移動速度(前進)
;	backstep = -forwardstep	;移動速度(後退)
	degstep = 2;5
	mdegstep = -degstep

	E3DRotateY hsid3, mdegstep, 3
	E3DPosForward hsid3, forwardstep
	return


;/////////////////
;
;	カメラ移動(キャラクター追跡型)
;
*MoveCamera
	;キャラクターを後ろに2500移動して座標を取得。backpos
	E3DGetPos hsid1, saveposx1, saveposy1, saveposz1
	E3DPosForward hsid1, -2500
	E3DGetPos hsid1, backposx, backposy, backposz
	E3DSetPos hsid1, saveposx1, saveposy1, saveposz1
	;カメラをキャラクタ後方に設置
	E3DSetCameraPos backposx, backposy + 1500, backposz
	E3DSetCameraTarget saveposx1, saveposy1 + 800, saveposz1, 0, 1, 0

	return


;/////////////////
;
;	キャラクタの地面との当たり判定
;
*ChkConf
	;	壁データとキャラクター
	if fkabe ! 0 {
		; 壁(hsid2)とキャラクタ(hsid1)のあたり判定
		resultwall = 0
		E3DChkConfWall hsid1, hsid2, 10, resultwall, adjustx1, adjusty1, adjustz1, nx1, ny1, nz1
		if resultwall!0 {
			E3DDrawTextByFontID fontid, 10,10,"壁に激突しました!", 255,255,255,255	;壁に激突したときのメッセージ。
			E3DSetPos hsid1, adjustx1, adjusty1, adjustz1
		}
	}

	;	地面データとキャラクター
	mapminy = -100	;地面データのY座標の最小値。実際の最小値より、少し小さな値を入れる。
	repeat 2	;キャラ2つ
		if cnt = 0 : hsid_c = hsid1
		if cnt = 1 : hsid_c = hsid3
		E3DChkConfGround hsid_c, hsid0, 1, mapheight, mapminy, result, adjustx, adjusty, adjustz, nx, ny, nz
		if ( result != 0 ) : E3DSetPos hsid_c, adjustx, adjusty, adjustz	;地面の上に移動
	loop

	return


;/////////////////
;
;	キャラクタどうしの当たり判定
;
*ChkConfAxis
	E3DChkConflict hsid1, hsid3, confflag
	if confflag!0 {
		if fcolibound ! 0 {
			;衝突したときにキャラクターが重ならないように移動する
			;PCを衝突したNPCと反対方向に50移動させる
			E3DGetPos hsid1, posx1, posy1, posz1
			E3DGetPos hsid3, posx3, posy3, posz3
			posx = posx1 - posx3	;NCPからPCに向かうベクトル
			posy = posy1 - posy3
			posz = posz1 - posz3
			;ベクトルを正規化(長さ1に)して、長さを50倍する。
			E3DVec3Normalize posx, posy, posz, 50, posx, posy, posz
			posx = posx + posx1	;移動ベクトルに現在位置を加算
			posy = posy + posy1
			posz = posz + posz1
			E3DSetPos hsid1, posx, posy, posz
		}
		E3DDrawTextByFontID fontid, 10,40,"NPCと激突しました!", 255,255,255,255	;激突したときのメッセージ。
	}
	return


*PlaySound
	;音源とリスナー間の距離を表示
	E3DGetPos hsid1, posx1, posy1, posz1
	E3DGetPos hsid3, posx3, posy3, posz3
	posx = posx1 - posx3	;NCPからPCに向かうベクトル
	posy = posy1 - posy3
	posz = posz1 - posz3
	E3DVec3Length posx, posy, posz, length
	title "音源との距離:"+length

	;音源とリスナーの位置・向き情報を設定
	E3DSet3DSoundMovement soundid2, posx3, posy3, posz3
	E3DSet3DSoundListenerMovement hsid1
	return





- HOME -

GHP(仮)