【Unity】Cinemachineと3Dオーディオの問題と対処

Cinemachine は 3D オーディオを考慮していないようで、カメラが切り替わる時に意図しない風切り音がなってしまう。という問題。


<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Cinemachine は3Dオーディオを考慮していないようで、カメラが切り替わる時に意図しない風切り音がなってしまう。という問題。<a href="https://twitter.com/hashtag/unity3d?src=hash&amp;ref_src=twsrc%5Etfw">#unity3d</a> <a href="https://t.co/ZioPKIqPQu">pic.twitter.com/ZioPKIqPQu</a></p>&mdash; サトー (@sator_imaging) <a href="https://twitter.com/sator_imaging/status/1433224354242908162?ref_src=twsrc%5Etfw">September 2, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>



<!--
https://qiita.com/sator_imaging/items/041159d1b236ea27227f
-->

### もくじ

- [問題の原因](#問題の原因)
- [対処方法](#対処方法)
    - [CinemachineVirtualCamera](#cinemachinevirtualcamera)
    - [CinemachineBrain](#cinemachinebrain)
- [おわりに](#おわりに)



## 問題の原因



Cinemachine は設定に従ってメインカメラを瞬間移動／高速移動させる、という機能。

結果としてカメラにくっついている Audio Listener も高速移動することになる、というのが風切り音が鳴ってしまう問題の原因。（正しいと言えば正しい結果）


※ Cinemachine によってメインカメラが瞬間移動している様子
<img src="https://dl.dropbox.com/s/gwjk7gprrojzlsf/CinemachineDoesntSupport3DAudio.gif" />




最初に示した動画は Blend を0.1秒に設定しているのでまだ聞くに堪えるが、↓ のように Blend 方法が Cut の場合、瞬間移動による破裂音が鳴る。完全にエラー。


<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">3Dオーディオはカメラ本体が動かないと変化が生まれないから、ズームで画角が変わった時にも音量が変わるようにした<br>そしたらズームしたときに破裂音が出るように、、、消し方が分からん<br><br>OnAudioFilterRead で音鳴らしてるせいかなーー？<a href="https://twitter.com/hashtag/realtimevfx?src=hash&amp;ref_src=twsrc%5Etfw">#realtimevfx</a> <a href="https://twitter.com/hashtag/generativeArt?src=hash&amp;ref_src=twsrc%5Etfw">#generativeArt</a> <a href="https://twitter.com/hashtag/madewithunity?src=hash&amp;ref_src=twsrc%5Etfw">#madewithunity</a> <a href="https://t.co/csnZB0zbqi">pic.twitter.com/csnZB0zbqi</a></p>&mdash; サトー (@sator_imaging) <a href="https://twitter.com/sator_imaging/status/1431226535147540483?ref_src=twsrc%5Etfw">August 27, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


風切り音は 3D オーディオ設定の Doppler Level をゼロにすると消すことが出来るが、鳴って欲しい風切り音も消えてしまうし、そもそもそういう問題じゃない。



## 対処方法

Audio Listener の瞬間移動／高速移動が原因なので、メインカメラからオーディオリスナーを剥がしてやれば解決できる。


### CinemachineVirtualCamera

Cinemachine を使う際には Main Camera についている Audio Listener は削除し、Cinemachine Virtual Camera すべてに Audio Listener をアタッチして高速移動が起きないようにする。

手動でも構わないが、面倒なのでスクリプトで生成すると良い。


```csharp
void OnEnable()
{
    // add audio listener to virtual camera.
    foreach (var vc in FindObjectsOfType<CinemachineVirtualCamera>())
    {
        var al = vc.GetComponentInChildren<AudioListener>();
        if (al) continue;

        al = vc.gameObject.AddComponent<AudioListener>();
        al.enabled = false;
    }

}
```

※ `Start()` は後述の Activated イベントより後に実行されるので `OnEnable()` で行うこと。



### CinemachineBrain

リスナーを追加しただけだとダメで、`CinemachineBrain` の Camera Activated イベントでリスナーの有効・無効を切り替える必要がある。

```csharp
public void OnCinemachineCut(ICinemachineCamera nextcut, ICinemachineCamera prevcut)
{
    // sometimes CinemachineBrain is passed, why?
    if (nextcut.GetType() != typeof(CinemachineVirtualCamera)) return;

    // disable first.
    if (null != prevcut)
    {
        foreach (var al in prevcut.VirtualCameraGameObject.GetComponentsInChildren<AudioListener>())
        {
            al.enabled = false;
        }
    }

    var listener = nextcut.VirtualCameraGameObject.GetComponentInChildren<AudioListener>();
    if (listener) listener.enabled = true;

}
```


## おわりに


破裂音の原因なんなんだと探ったら、すげー単純な問題。気付けばなんてことないが、バグといっても差し支えない感じ。

メインカメラを移動させる手法はパラメーター管理が楽でいいけど、時間軸に依存したエフェクト（モーションブラーや3Dオーディオ）なんかに悪影響があるんで、Cinemachine には標準でそれらに対処する機能を追加してほしいところ。



以上です。お疲れ様でした。