ADX for Unityで作る、軒下での雨音表現（発展編）

# はじめに

本記事は「ADX for Unityで作る、軒下での雨音表現（基本編）」の続きとなります。同じプロジェクトファイルを利用しますので、先に該当記事を御覧ください。

[https://qiita.com/nimushiki/items/8d346583632b0cc7eb81](https://qiita.com/nimushiki/items/8d346583632b0cc7eb81)

前回の記事では雨の音は1種類でしたが、通常の音・建物に雨が当たる音の2種類を使ってより表現力を上げる実装をしてみます。
また屋内判定を専用に設置した屋内Colliderから取っていましたが、頭上の障害物から取るようにしてみます。複雑な地形全てに屋内判定を設置するのはかなりの手間になる可能性があるため、プレイヤーオブジェクト自身が判定したほうがより多くの状況に対応でき、実装の効率化が図れます。
ここまでプレイヤーオブジェクトを中心に考えてきましたが、意外にも、発生する音を聞く位置（標準機能だとAudioListener）はカメラを基準にしたほうが自然に感じる場合がありますので、その実装例もご紹介します。

本記事ではUnityとサウンドミドルウェア「ADX for Unity」を用い、雨音を題材に上記のような環境音コントロールの発展的な実装例を紹介します。

※この記事は「UE4+ADX2で作る、軒下での雨音表現（発展編）」のUnity版です。元記事作者の[Sig](https://qiita.com/SigRem )さんの許可を得て作成しています。

[https://qiita.com/SigRem/items/d80cb1d730c5328af216](https://qiita.com/SigRem/items/d80cb1d730c5328af216)

# 前提

### 動作確認環境

Windows 10 Home 20H2
Unity 2020.3.27f1  + ADX LE Unity SDK 3.06.03
CRI ADX LE Tools for Windows 3.46.09

### 前提記事

CRI ADXの導入方法や基本的な操作については、以下の記事を参照して下さい。

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

# やること：雨音の表現を強化する

- AtomCraftでキューに雨の音源バリエーションを追加
- Aisacコントロールを使用し、エフェクトを追加
- Unityにインポート
- テストシーンを修正、必要に応じて雨の音にエフェクトをかける

# 実装

## AtomCraft側
まずは、再生する音データの作成を行います。ADXにおいては、再生用のデータはUnityとは別のツール「AtomCraft」を使います。その手順については、元の記事（UE4向け記事）の同名の項と同じになりますので省略しています。
本記事の手順を試す場合は、まず元の記事の[該当項目](https://qiita.com/SigRem/items/d80cb1d730c5328af216#atomcraft%E5%81%B4)を進めてから本記事を進めてください。UE4版との違いとして1点、「Atomキューシートバイナリのビルド」画面で、一番左下のオプション「UnityAssets出力」にチェックを入れるのを忘れないようにして下さい。

## Unity側の実装

### キューシートのインポート

AtomCraftからエクスポートしたファイルをインポートします。

### テストシーンの修正

テストシーンを以下のように修正します。

![テストシーンの修正](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2576800/423060d7-7689-9c1a-6e86-19be939a7d5c.png)

- BGS_Rain2(CriAtomSource)
    - BGS_Rain(CriAtomSource)と入れ替える
- RainController
    - CriAtomSourceにBGS_Rain2(CriAtomSource)を設定する
    - MaxValueとMinValueをそれぞれ0,1にする
        - AISACの設定が前回から逆転しているため

### テストシーンの実行

出来たら保存し、実行しましょう。
プレイヤーのCubeを屋根下へ動かすと、徐々に雨の音が変化するのが確認できます。

# やること：頭上のオブジェクトで屋内判定をする

基本編では、室内を表すコライダーを用意して屋内を判定していました。この方法は分かりやすいですが、複雑な地形への設置や開発中の修正の際にもれが出やすいと言えるでしょう。

そこで、プレイヤーキャラクターの頭上に物があるかで屋内外の判定をする仕組みを紹介します。
今回はRaycastを利用しますが、これはRay（光線、一筋の光）をcast（放つ）してぶつかった相手の情報を取得することができます。

![判定False](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2576800/fe1503f8-144f-26e3-9c7b-bf9d596d954f.png)

![判定True](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2576800/f785e35a-b461-cc9a-5112-ec45fcc49a7c.png)


# 実装

### テストシーンの修正

屋根のコライダーをデフォルトに戻します。

![テストシーンの修正２](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2576800/159f8f94-9927-a75a-3713-2b0143903b9c.png)

### スクリプトの修正

RainController.csを以下のように変更します。

``` RainController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;

public class RainController : MonoBehaviour
{
    public CriAtomSource criAtomSource;
    public string controlName1;
    public string controlName2;
    public float maxValue;
    public float minValue;
    public float rayDistance;
    private Ray ray;
    private RaycastHit hit;
    private bool Indoor;

    private void Update()
    {
        Debug.DrawRay(transform.position, Vector3.up * rayDistance, Color.red);

        ray = new Ray(transform.position, Vector3.up);
        Physics.Raycast(ray, out hit, rayDistance);
        if (hit.collider?.tag == "Indoor" && Indoor == false)
        {
            OnRaycastHit();
            Indoor = true;
        }
        else if (hit.collider == null && Indoor == true)
        {
            OnRaycastMiss();
            Indoor = false;
        }
    }

    private void OnRaycastHit()
    {
        DOTween.To(() => maxValue,
            (x) => {
                criAtomSource.SetAisacControl(controlName1, x);
                criAtomSource.SetAisacControl(controlName2, x);
            },
            minValue,
            0.5f);
    }

    private void OnRaycastMiss()
    {
        DOTween.To(() => minValue,
            (x) => {
                criAtomSource.SetAisacControl(controlName1, x);
                criAtomSource.SetAisacControl(controlName2, x);
            },
            maxValue,
            0.5f);
    }
}
```

Debug.DrawRayはRayを可視化するためのものです。なくても動作しますが、開発中は見えていたほうが便利なので記述しておきます。
UpdateでRayを飛ばして毎フレーム衝突判定をする必要があります。しかしその度OnRaycastHitかOnRaycastMissを呼んでしまうとDOTweenの動作に問題が起きます。そのためIndoorかそれ以外か、の切り替わりが起きるタイミングに限定するためIndoorフラグを追加しています。
OnRaycast〇〇は前記事のOnTrigger〇〇と内容は同じです。

### テストシーンの実行

修正が出来たら保存し、実行しましょう。
Cubeから赤い線が出ており、この線がCubeのColliderにぶつかると、音の変化が起きることが確認出来ると思います。

![テストシーン実行例](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2576800/52204107-cef8-05bc-59ee-8414d9a80001.png)

### さらなる応用

Rayを複数本用意し何本ぶつかっているかを判定することで、完全に隠れている時とキワにいる時の表現を分けることができるでしょう。

またRayがぶつかるまでの距離を取得しAISAC値の変化具合を調整することによって、障害物の高さを表現できるかもしれません。

# やること：判定の基準をカメラにする

ここまでの実装では全てプレイヤーオブジェクトが判定の基準でした。
しかし、発生する音を聞く位置はプレイヤー自身、つまりカメラを基準にしたほうが自然に感じる場合がありますので、その実装例をご紹介します。

# 実装

### テストシーンの修正

やることは簡単で、以下のように変更するだけです。

![テストシーンの修正３](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2576800/33f7af7e-5805-f04b-7da5-e633f0cde37d.png)

- MainCameraをCubeの子オブジェクトにする
- RainControllerをCubeからMainCameraに移す

### テストシーンの実行

修正出来たら保存し、実行しましょう。
プレイヤーCubeを動かすと、CubeではなくMainCameraが判定の基準になっていることがわかります。