Unityで同じ音が二度と鳴らないゲームの環境音を構築する

こちらの記事はUnity Advent Calender 2019 12/13の記事です。
また、12/7に行われた「UnityおとなのLT大会」で公演した内容をもとに構成しています。
https://www.slideshare.net/takaakiichijo/ss-202740150

# ゲーム内の「環境音」どうしてますか？
ゲームにはその空間、ステージ、情景を表すための「環境音」があります。
BGMやジングルとは違う、その空間から発生しているような音のことです。
たとえば森ならば木々が風で揺れる音、鳥があちこちでさえずる音、などの要素が入った音です。これをゲーム内で流すことによって、ゲームの世界をより豊かに表現できます。

一般的にゲームで効果音を鳴らすときは、一本のステレオ音声データを用意してループ再生を行うことがほとんどです。
しかしながら、それには少々難しい問題があります。

## 環境音再生の難しさ
環境音はプレイヤーが長い時間耳にします。探索時間が長いマップであったり、戦闘画面の滞在時間が長めであるなど、ゲームの構造によってはかなりの長時間聞かせることになります。
そのなかで、環境音を何度も聞いていると無意識のうちに法則性を見つけてしまい、気になってしまうことがあります。
たとえば森の場面で鳥の「ピヨピヨ」という鳴き声がするとき、その鳴き方が何度も何度も繰り返されると、「これ何回も聞いたな」とか、「次にこの音が来るよな」というような予想ができてしまい、没入感を削ぐ可能性があります。

### 音声データのループを長くして、頭出しをランダムにする方法
最もかんたんな解決方法は、ループ再生している環境音の素材をすごく長いものにすることです。しかし、それはゲームのインストールサイズに響きます。
また、ゲームのやりこみが強ければ強いほど、繰り返しは気になります。

ちなみに、ステージを移動するタイミングなどで環境音を再生するとき、プレイヤーがその切替ポイントを行き来すると毎回同じ音がなってしまう問題があります。
これについては、音声データの頭から再生せず、ランダムな途中位置から再生を開始することで改善できます。

### 細かい音声データをランダムに鳴らして環境音を構築する方法
環境音のリピート感をへらすためには、単一のループ素材の再生をやめて、細かい音声データをスクリプトでランダムに再生する方法が考えられます。
たとえば、森の音を「風のループ音」「鳥のさえずり複数種類」「獣の鳴き声複数種類」「木が揺れる音」などの複数種類に分け、それぞれランダムな間隔で再生することを考えます。

Audio Clipを複数読み込んでおき、ランダムな間隔、ランダムなピッチ変更、ランダムな方向性を加えることで自然な環境音を実現できます。
もちろん、これらの制御のスクリプトは新規に書く必要があります。

一見良さそうですが、原理的に複数の音声再生処理が同時に再生されてしまう危険性を考えなくてはなりません。スクリプトで管理しないとすべての音が同時に鳴ってしまって負荷が上がる、という状況がありえます。
加えてこの音をフェードイン・アウトしたいですとか、ポーズしたいなどの要求が出てくると「どの音が現在再生中か」「いま何音鳴っているか」なども管理しなくてはいけません。

## ADX2を使ってノンコーディングかつリッチな環境音を実現する

スクリプトで制御をかけば、少しリッチな環境音を実現はできそうですが、手間は大きそうです。そこで、本記事では統合型サウンドミドルウェア「CRI ADX2」を使って、ノンコーディングでランダムな環境音の再生を実装してみます。

ADX2には音の鳴り方を設計できる外部ツール「CRI Atom Craft」が付属しており、ループやタイミングのランダマイズ設定をデータ側に埋め込むことが可能です。
ゲーム側からは、「この音を再生」というコードを書くだけで、リッチな環境音を使うことができます。

ADX2の概要と導入方法については、次の記事を参照してください。

Unityのサウンド機能をADX2で強化する
https://qiita.com/Takaaki_Ichijo/items/16e6501fc07f5b3b3377

## デモ

「雨の中、軒先で水滴がポタポタおちる音」の環境音デモを用意しました。
10秒のループ素材をUnity Audioで再生する方法と、ADX2を使って2.5秒のループ＋３種類の水滴音をランダムに鳴らす方法を比較します。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ゲームの環境音再生にCRI ADX2を使う　雨音の例<a href="https://t.co/NUepwLGAp0">https://t.co/NUepwLGAp0</a></p>&mdash; ichijo (@Takaaki_Ichijo) <a href="https://twitter.com/Takaaki_Ichijo/status/1205356198779686912?ref_src=twsrc%5Etfw">December 13, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

YouTubeリンク
https://youtu.be/cPs1TQvBjzY

ループの場合はどうしても水滴音の「法則性」が気になってしまいますが、ADX2を使ったランダム再生ではそれを打ち消すことができます。
また、10秒のループより2.5秒ループ＋水滴音３つのほうがデータ容量は小さくすみます。

## 実装手順

それでは、実際に「雨の中、軒先で水滴がポタポタおちる音」を作っていきましょう。

### 水滴部分の設定
CRI Atom Craftを起動し、キューシート内に新しいキューを用意します。
「ポタッ」という水が垂れる音を３種類トラックとしてキュー内に並べます。

![CRI Atom Craft  LE Ver.3.40.17 (built on Jun 10 20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19010/05c85f1c-c43f-67d4-96fe-c4fa015e2c94.png)

キューの再生を行うと、（当然ながら）すべての音が同時に１回鳴って終わります。そこで、それぞれのウェーブフォームが一定間隔で繰り返し再生される設定を行います。

トラック内のウェーブフォームをクリック選択して、インスペクターの「タイミング[シーケンス]」にある「自動繰り返し間隔」項目に数値を入力します。
この項目の単位はミリセカンドです。

![CRI Atom Craft  LE Ver.3.40.17 (built on Jun 10 202.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19010/7bf2523c-7fda-d527-187b-0ac0b5568a75.png)


試しに、2000を入力してキューを再生してみましょう。2秒ごとに繰り返し再生されるようになります。
次に、繰り返しの間隔をランダムにします。ひとつ上の項目の「再生タイミングランダム」に、ランダムの幅を入力します。たとえば0~2秒のランダムを加えて再生するには、2000を入力します。

この設定を３つのウェーブフォームにそれぞれ行います。再生間隔とランダムの値もバラバラの数値がよいでしょう。
設定が終わったら再生してみてください。ランダムに水滴が落ちてくるようになりました。

加えて、ボリュームとピッチのランダマイズをします。インスペクター内の「ボリューム /ピッチ」タブで操作します。

![CRI Atom Craft  LE Ver.3.40.17 (built on Jun 10 203.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19010/338fc201-df45-6828-5ee1-9320e2fc10d6.png)

ボリュームとピッチはランダム設定ができます。スライダーをマウスで使んで、上下に動かすと緑色のエリアが出ます。この幅がランダムの幅です。
「トラック」や「キュー」単位でも同じ項目のランダム設定が可能なのですが、それは「キューの再生が開始されたときにランダムに変化する」設定になってしまいます。今回はウェーブフォームが繰り返し再生されるたびにランダムに変化してほしいので、ウェーブフォームを選択したままこの２項目を編集します。

また、左右のスピーカーから聞こえる音量のランダマイズもします。「パン[5.1]」のタブを選んで「角度」のスライダーをマウスで上に動かします。ボリューム同様、ランダム幅が現れます。

![パンのランダマイズ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19010/f64e1f06-cf04-396f-3cd2-45aa6ccf6ffa.png)

これで、再生タイミング、ボリューム、ピッチ、パンニング４種類のランダムが組み合わさる音になりました。

### ループ部分の設定
雨の「ザー」というループ部分も同じキュー内に設置します。こちらは同じキュー内に新規トラックとして素材をおいて、ループ設定を行うだけです。
ただし、ループはマテリアル側で設定を行います。ウェーブフォーム選択状態で、もう一回ウェーブフォームをクリックすると、中のマテリアルファイルが選択状態になります。
左下のマテリアルツリーから選択しても同じです。

![ループ設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19010/6b32b5e3-c092-037b-866e-cad251a239e1.png)

この状態で、インスペクター内の「エンコード[ループ]」タブで、「ループ情報の上書き」をtrueにした上で「ループタイプ」を「ループ」に設定します。
これで設定は完了です。

## さらなる機能拡張
小さなデータサイズでランダム性を持つ環境音が実現できましたが、さらなる拡張として、音に操作可能なパラメーターをもたせ、ゲーム側の状況と連動させることもできます。
雨の例で言えば、雷がゴロゴロ言う音を加えて、そのボリュームをリアルタイムに操作することで、嵐が近づいている雰囲気を出すことができます。
これにはADX2の「AISAC」機能を使います。Unityのスクリプトから0f~1fの値を受け取ったときにボリュームやピッチ、フィルターがどのように変化するか、グラフで設計できる機能です。
この活用についてはまたの機会にて。