はじめに
この記事ではこれから3回に分けて実際にFPSのサンプルを改良しつつ、Unityのデフォルト機能を使って制作されたサウンドシステムをCri ADX2で実装したものに差し替える方法を紹介しようと思います。今回は前回載せたサンプルプロジェクトの中身をのぞきつつ、実装をADX2のものに書き換えてみます。
- 1.コンセプト紹介・CRI ADX2チュートリアル編
- 2.サンプルプロジェクトのシステムをADX2で置き換えてみよう!編 1←ココ!
- 3.サンプルプロジェクトのシステムをADX2で置き換えてみよう!編 2
サンプルプロジェクトの紹介
今回制作したのはサンプル用の簡単なFPSです。 WASDキーでキャラクターを操作してターゲットを撃ちましょう。制作に用いたモデル、シェーダーなどは全て今回のために制作したものなので、もし必要ならばそれらに関しては再配布しても問題ありません。
音響周りの実装の紹介
一旦キャラクターコントロールや射撃システムの実装はおいておいて、肝心の音響周りの実装とその問題点についてみてみましょう。
弾丸の発射音
今回の実装では、「弾丸」が生成されたタイミングで弾丸にアタッチされたAudioSourceから音が鳴るように設定されています。
問題点
全ての射撃が同一の音源に再生しています。リアリティを出すために音をランダムに出そうとすると、新しくMonoBehaviour継承クラスを生成してUnityC#側で疑似乱数を生成して鳴らす音を決定する必要がありそうです。それぞれの音について音量や生成バランスなどのパラメータをUnity側で設定してしまうと、サウンドクリエイター側がそのバランスを調整するのが難しくなってしまいます。
//Unity Audioでの実装のサンプル例
//種類を増やしたい場合は?抽選確率に偏りを与えたいときは?
void Start(){
var rand = Random.Range(0,3);
switch(rand){
case 0:
audioSource.PlayOneShot(audioClipA);
break;
case 1:
audioSource.PlayOneShot(audioClipB);
break;
case 2:
audioSource.PlayOneShot(audioClipC);
break;
}
}
爆弾樽の爆発音
銃で樽を打つと爆発します。爆発される際のエフェクトに別の樽がヒットすると連鎖的に爆発します。爆発時のエフェクトPrefabにAudioSourceがついており、生成時に音を鳴らします。
問題点
複数の爆弾樽が同時に爆発した際、爆発音も全て同時になるため同時爆発した爆弾樽の数が大ければ多いほど過剰に巨大な音が鳴ります。同種の音が複数なるような場合、その音の最大値または最大音量を制限するとプレイヤーの耳を破壊せずに済みそうです。
足音
キャラクターが移動する際に足音がなります。CharacterController経由で一定時間ごとに音を鳴らす実装になっています。
問題点
足音は一種類だけで音が繰り返しになっていることが分かってしまいます。また、音を鳴らす間隔などは実際にUnityエディタ上かコード上で編集する必要があります。足元の材質に応じて音が変わったり、足音にいくつかのバリエーションがあるとリアリティが増すはずです。
環境音
ユーザがインタラクションを起こす以外の要因で音を再生するような仕組みは現在実装されていません。そのため、プレイヤー以外の環境要因から再生される音声は存在しません。
問題点
現実世界では通常、自分以外の要因によって発生する音声が無数に存在します。例えば自然現象によって発生する音声(風切音、雨音、植物がこすれる音など)や、他者によって再生される音声(雑踏、話し声など)などが存在します。このように環境(自分以外の要因)による音声を考慮すると、ゲームによりリアリティを与えることができるはずです。
反響
今回のFPSでは、最初のステージは室内→屋外で、次のステージは下水道になっています。この環境を反映するような実装は特にしていません。
問題点
最初のステージは割と開けた場所で、次のステージは閉所です。音響の反射を考えると、下水道の方はエコーがかかっていてもおかしくなさそうです。
改善の実践
それでは、実際にコードをのぞきながらADX2を用いた改善していきましょう。
音声にランダム性を持たせる
Unity Audioではランダムな音声を持たせるのに、C#スクリプトを書く必要がありました。これでは普段コードを書かない人にとっては手間になってしまいます。また、発生する音声の確率を調整するのにUnityを開いてコードかインスペクタ上のパラメータを調整する必要があります。一方、ADX2では、Unity側で一行もコードを書くことなく、AtomCraftの側で音源のランダム再生を実装することができます。
キュー(ランダム)を利用して再生する音源をランダムに設定する
それでは早速Atom Craftを開いてみましょう。前回作成したプロジェクトを開いて、ワークユニットツリー上を右クリックして新しいCueSheetを作成します。作成したCueSheetを右クリックして、「新しいオブジェクト→キュー「ランダム」」を選択して新しいCueを作成します。この時生成されるキューはトラック上に並列に並べられた音源のうち一つをランダムに再生する機能を持っています。試しに再生ボタンを押して音声を再生してみましょう。トラック上に登録した音声のうち、ランダムなものが再生されたのが確認できたと思います。
また、音源の再生確率を調整して「低確率でレアボイスを流す」といった実装もランダムキューでできます。それぞれの音声の再生確率はAtom Craftのインスペクタから指定することが出来ます。試しにキューをクリックしてインスペクタを開き、ランダムと書かれたボタンをクリックしてみましょう。
もしインスペクターが表示されてない場合は、レイアウトから適当なレイアウトを選択するか、下のバーのインスペクターボタンをクリックして青い状態にしましょう。レイアウトボタンからはそれぞれの編集に便利なウィンドウレイアウトが入っているので、必要なら適宜使い分けると良いと思います。
インスペクタに表示される「乱数重み」が対応する音声の再生される確率の大きさに相当しています。この「乱数重み」の指定は非常に便利で、合計確率が100以上なら、合計が100になるように比率を調整して音声を再生してくれて、合計確率が100未満なら、100に足りない分の値は「音がならない」という事象が起こる確率となります。
上記スクショの場合は、shot_gun_1とshot_gun_2の乱数重みが両方100で、どちらも発生する確率は50%ずつとなっています。Atom Craft上で再生してみると、二つの音が大体同じぐらいの確率で鳴るのが確認できると思います。
この「キュー(ランダム)」を用いることで、例えば打つたびに異なる銃声を鳴らしたり、攻撃すると一定の確率でセリフをしゃべったり(逆に喋らなかったり)、異なる足音を鳴らしたり、ということができます。
Unityで再生する
実際にUnity上でこれらの音源を再生するのに必要なことは、通常のキューと同じようにビルドすることだけです。試しにエクスポートして再生してみます(エクスポート手順については「Part1」を参照ください)。前回同様AtomBrowserを開き、ビルドしたバイナリを読みこみ、再生してみます。
特にUnity側でランダムに関する設定をしなくとも、ランダムな音源が再生されていることがわかると思います。これによって音源のランダム性をUnityに依存することなく制御することができるようになりました。ちなみに実際のゲーム内でCri Atom Source(Create GameObjectボタンを押すと生成されるGameObjectについているComponentのこと)から音声を再生するには、次のようなスクリプトを実行します
public class Test : MonoBehaviour
{
[SerializeField] private CriAtomSource source;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
//キューIDかキュー名、どっちでも再生可能
source.Play(0);
}
}
}
このComponentを付けたGameObjectをUnity上に生成してPlayModeに入り、右クリックを連打すると実際にランダムな音声が再生されることがわかります。このように、AudioSourece同様PlayメソッドをたたくだけでAtomで設定されたキューを再生することができます。
音源のパラメータをランダムにする
音声に「ランダム性」を持たせる方法は、「異なる音声を再生する」以外にもあります。例えば再生する音のピッチ、再生速度、再生開始位置などをランダムにすることでも、同じ音源に違った印象を与えることができます。
簡単な方法
Atom Craftは再生する音声のパラメータにランダム性を持たせることができます。ランダム性を持たせたいトラックをクリックしてインスペクタを開くと、設定可能な項目の中に「ピッチ」というカテゴリがあるのがわかります。このピッチカテゴリの中には「ピッチランダム幅」というパラメータがあるので、これを任意の値にすることで再生されるたびに異なるピッチで音源を再生する、といったことができます。試しに1000ぐらいのパラメータにすると明らかに音声の高さが変化しているな、というのがわかると思います。
また、ボリュームやピッチの値に対してより直観的にどの程度の大きさのランダム幅を持たせるかを決定させることもできます。インスペクタのスライダ部分を上下にドラッグすることで、緑の「ランダム幅」の大きさを決定することができます。この方法は他のパラメータにも用いることが出来て、例えば音声の左右パンの振り具合などをランダムに設定することなどができます。
今回はさらに多彩な制御が可能なAISACと呼ばれる機能を利用して再生ピッチをランダムに変更する方法についても紹介してみようと思います。
AISACを使った方法
AISACとは?
AISACはADX2に用意されているゲーム内でのインタラクションに対応してリアルタイムで音声を制御するためのシステムです。音のパラメータをランダムに変更したり、Unity側から渡したパラメータを用いた音声制御が可能です。
AISACを用いて音源のピッチをランダムにする
まずはAISACを作成してみましょう。キューのタイムライン上を右クリックして「新規オブジェクト→AISACの作成」を選択すると、そのキューに紐づいたAISACが作成されます。
AISACの編集はAISACウィンドウを通して行います。
AISACウィンドウが開いていないときは、下のバーの「AISACボタン」をクリックして青色にしましょう。
AISACウィンドウでは、AISACによって制御される音源のパラメータと、0.0~1.0
で表現される数値と紐づけられたグラフが表示されています。
早速ピッチを変更してみましょう。左に表示されたAISACリストから作成したAISACを選択し、右クリックで「グラフの作成→ピッチ」を選択します。作成したピッチのグラフを開いて、「コントロールタイプ」を「ランダム」にします。そうすると、再生するたびにAISACのグラフのパラメータがランダムに選択されて、それに対応したピッチが選択されます。
現在のままだと、ピッチは定数のグラフなのでパラメータがどんな値でも一定の値になってしまいます。グラフをクリックして、傾けてみます。
グラフの例、0.0~1.0の値が一様ランダムに選ばれ、グラフの値が選択されるのでこの場合は「高い値が出やすい」グラフになっている。
試しにAtom Craft上で再生してみると、音源がランダムなピッチで再生されるのがわかると思います。どうでしょうか?同じ音源でもピッチが変わるだけでだいぶ印象が変わるのが確認できたかと思います。
Atom Craftではランダムなパラメータ変更の他にも複数のコントロールタイプが存在します。例えばコントロールタイプを「なし」にすればUnityから変数を渡して音源のパラメータを設定して再生することなどもできます。ADX2のSDKにはキューごとに設定されたAISACのコントロール値を変更するSetAisacControl
関数が存在するので、例えば
//コントロール名(AisacControl_0とか)、変更後の値
source.SetAisacControl("Name",0.5f);
とすることで簡単にピッチやボリュームなどをリアルタイムで変更することができます。
AISACはインタラクティブなサウンド体験を制作するにあたって根本的かつ非常に便利な機能なので、ぜひ使いこなせるようになっておきたいです。
種類を指定して音声を鳴らす
足音の例を考えてみましょう。Unity側で足元と床にColliderをつけて、「今どの材質の床を歩いているのか?」という情報を取得できるようにしたとします。その材質の情報に応じて異なる足音を再生するとなると、例えばC#上でswitch文を用いて再生する音源を切り替える、といった動作が必要になります。今後、鳴らしたい音源の種類を増やしたり、音源を差し替えたりする際にはやはりUnityのインスペクタなどを編集する必要が出てきます。Unity側から何らかのメッセージを送り、それを受け取れるシステムがあるとよさそうです。
Atom Craftでは「ゲーム変数」と呼ばれるゲームエンジン側から編集できるパラメータが存在します。今回はこのゲーム変数を利用してゲーム中からキュー内で再生する音源(トラック)を変更するシステムを作ってみます。
ゲーム変数の追加
まずはAtom Craft側でゲーム変数を追加しましょう。プロジェクトツリー内「全体設定」の下にある「ゲーム変数」を右クリックしてゲーム変数を追加します。ゲーム変数には好きな名前を使えますが、ここで付けた名前はUnity側で更新するゲーム変数を指定するのに用いるのでわかりやすい名前を付けましょう。
キュー(スイッチ)の作成
キュー(ランダム)を作成した時と同様に、キューシートを右クリック→「新しいオブジェクト→キュー「スイッチ」」を選択してキュー(スイッチ)を作成します。このキューはゲーム変数に応じて再生するトラックを変えることができます。トラックに適当な音声を並べてみましょう。
先ほど作成したゲーム変数は数値なので、ゲーム変数のどの範囲がどの音源に対応するのかを設定する必要があります。キューを選択した状態でインスペクタの「スイッチ」ボタンをクリックします。すると、以下のような画面が表示されると思います。
ここに表示されている「スイッチ幅」がそれぞれゲーム変数がどの範囲にある時にどの音を鳴らす、というのを表現しています。上の例だとゲーム変数「Foot」が「0.0~0.5」の時に「Track_shot_gun_2」が、「0.5~1.0」の時に「Track_shot_gun_1」が鳴るような設定になっています。
スイッチ幅を変更すればこの範囲は当然変化します。「等分化」ボタンを押すとスイッチ幅が1を等分したものになります。
ゲーム変数を変更する
ゲーム変数を変更したときの挙動を確認するには表示→変数プレビューウィンドウを選択して変数プレビューウィンドウを表示します。音声を再生しながらプレビューウィンドウから値を変更すればそれに応じて再生される音声が変化するのが確認できたと思います。
これと同等の操作をUnityで行うには、CriAtomEx.SetGameVariable
を利用します。
//ゲーム変数名orゲーム変数ID,変更後の値
CriAtomEx.SetGameVariable("Foot",0.2f);
再生する音声数の制限
爆弾の樽を連続で爆発させると音声が重なって大きくなってしまう問題がありました。複数の音が同時になるようなもの、例えば複数体出てくる雑魚キャラの音声、今回のような連鎖的に反応するオブジェクトなどは同時に再生可能な音声に上限を設けないと、状況によっては非常に大きな音声が鳴ってしまいます。
Unityでこのようなシステムを作ろうと思うと、「音声の再生が開始すると+1、音声の再生が終わると-1するカウンタ」のようなものが必要となります。これを実装するには非同期プログラミングが必要になります。これだけだとそこまで難しい実装ではないですが、これに加えて「再生が中断されることがある」などの要件が重なるとズブズブと非同期沼に沈んでいくことになります。エンジニアとして沼は沼で楽しいものではあるのですが、肝心のゲーム制作がおろそかになっては嫌なので、一旦沼からは上がってこれを楽に解決する方法を考えましょう。
解決といっても、ADX2ではこの再生上限の実装を簡単に行うことができる機能がデフォルトで存在しています。
キューリミット
ADX2では、「キューリミット」と呼ばれるキューの同時再生数を制御するシステムが存在しています。例えば「キューリミットが1の音声は同時に1つの音声のみ再生できる」といった設定が出来ます。設定したキューリミット以上の音声が再生されようとしたときは、設定に応じて一番古い音声の再生が中断されたり、再生しようとした音声が鳴らない、などによって自動的に調整されます。実際にキューリミットを設定してみましょう。
キューリミットを設定したいキューを選択した状態でインスペクターウィンドウを見てみます。
右側の設定画面に「キューリミット」と書かれたカテゴリがあると思うので、ラベルの左側にあるボックスにチェックを入れます。リミット数に最大数を設定すれば完了です。右にある「タイプ」は「先着優先」と「後着優先」の二つがあり、先着優先の場合は上限に達した場合追加の音声が鳴らない仕様になり後着優先の場合は一番古い音声の再生が停止されます。試しに再生上限を小さい数にした状態でAtom Craft上のボタンを連打してみましょう、一定以上の数の音が同時再生されないのが確認できると思います。「後着優先」だとかなり自然な感じで再生音数が制限されてしまうので、先着優先にすると音が制限されているのがわかると思います。
まとめ
今回はCRI ADX2を用いてUnityAudioにおける基礎的な音響システムを置き換えてみる、という試みについて、特に音響にランダム性を持たせるということと、再生する音源の数を制限する、ということについて解説しました。今回紹介したように、ADX2ではAtom Craftを利用してUnityに依存せず様々な音響効果を付与することができます。Atom Craftでは今回設定した項目の他にも多くの制御可能なパラメータが存在するので、ぜひ実際にAtom Craftを触って感覚を掴んでみてください。次回はバスを用いたエフェクトの追加や、3D音響について紹介しようと思います。今後もADX2を上手に利用してよりリッチなゲーム体験を創造していきましょう。