Unityで3D音源の距離と音量からAudioVisualiserを作成する【Unity】

はじめに

前回以下のような 距離から音量を取得する機能を実装しました。こちらをせっかくなら いい感じの見た目にしようと思い、Audio Visualiser の作成を行いました

成果物

動作環境

  • Unity 2021.3.4f1

実装

各音源から距離と音量を取得・表示する

以前に実装について書いたので以下をご覧ください。

ayousanz.hatenadiary.jp

Visualiser UIの作成

今回のAudio Visualiserは 16等分割した円の画像を使って向きと大きさを表現しています。

まずはこちらのUIを作る作業からやっていきます

(完成UIの拡大版)

(完成したUIのパーツUI : 白で塗りつぶししているため、Web上だと見えないため画像をダウンロードして使用してください)

画像作成アプリは、なんでもいいですが 今回は firealpaca を使っていきます。

放射線上の下書きと円形の下書きを書いて、1/16の円を作っていきます。

次に Unity上で、以下の感じで Image の Fill Amountを変更してUIに変化を持たせたいため、画像を横向きに変更します (画像作成時に横向きにしておくとこの作業がスキップできます)

画像の回転は Windows標準機能のフォトアプリの編集機能が優秀だったため、こちらを使っていきます。 回転する角度は 11.25度(360 / 32) ですが、小数点以下は指定できないみたいなので11度回転させてあげます

できた画像を Unity上にimportして、Texture Typeを Spriteにしておきます。 そのあとに 円の中心に回転したいため Sprite Editorを使って Pivotを円の中心に変更します

シーン上に配置すると、以下のようにImageが円の中心を軸にきれいに回転するようになっています。

Audioデータから Visualiser UIをリアルタイで更新する

今回は Visualiserは以下のように要件定義して実装をしていきます

  1. 聞こえている音とVisualiser上の色を同じ色にする(見やすくするため)
  2. 聞こえている方向を 360度の円形上で表現する
  3. 聞こえている音量を 一つの軸の高さで表現する

上を満たすための AudioData は以下のようにしました。

    public class VisualizerAudioData
    {
        public float AudioValue;
        public Vector2 AudioVector;
        public Color32 AudioColor;
    }

1 はほぼ何もしていないので、詳細は省略します。

2.3については以下のようにしています。

簡単に説明すると Visualiserの画像数(表現できる音の種類 = 16個) の配列に その方向の音量で一番大きいデータを入れる 処理をしています。

まずは Player と Audioの位置から 角度を以下のように計算します

        public static float Vector2ToAngle(Vector2 vector2)
        {
            var angle = Mathf.Atan2(vector2.y, vector2.x) * Mathf.Rad2Deg;
            if (0f <= angle) return angle;
            return 360f + angle;
        }

次に上で計算した角度がどの向きグループに属するのかを計算します

例えば 角度が 0度の場合は 配列[0]に属する、角度が 120度の場合は 120 / (360/16) = 5.33 ≒ 6なので 配列[6] に属する。

        private int VisualizerDataIndex(float angle)
        {
            return Mathf.CeilToInt(angle / (360f / _visualiserData.Length)) - 1;
        }

すでに入っているAudioDataの音量よりも大きいかどうかを比較して、大きい場合のみ更新します。

        public void UpdateVisualizerData(VisualizerAudioData data)
        {
            if (data.AudioValue <= _minValue) return;
            var angle = Vector2ToAngle(data.AudioVector);

            var oldValue = _visualiserData[VisualizerDataIndex(angle)].AudioValue;
            if (oldValue < data.AudioValue)
            {
                _visualiserData[VisualizerDataIndex(angle)] = data;
            }
        }

これで データクラスの更新が終わったので、配列データを View側に 渡して Visualiser UIを更新していきます。

MVP の設計にしているため、 PresenterがModelのデータ配列をもらって View側(MonoBehaviour) を更新します

サンプルプロジェクトのクラス図

まとめ

ModelとViewを分割しているため、3D Visualiserとかにも応用できそう??

HoloLensやOculusQuestとかで周りの環境音を取得して、表示したら面白そうですね!(どこかでやりたい)