FPSプレイヤーからターゲットまでの距離に応じてBGMをクロスフェードさせてみました。
クロスフェードさせるときだけ距離を計算したいので、そのためのオブジェクトを作ってプレハブ化しました。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DistanceChecker : MonoBehaviour
{
List<Transform> targets;
Transform center;
AudioManager audioManager;
// 中心とターゲット、音管理オブジェクトを設定
public void SetUp(Transform center, List<Transform> targets, AudioManager audioManager)
{
this.center = center;
this.targets = targets;
this.audioManager = audioManager;
}
public void SetUp(Transform center, Transform target, AudioManager audioManager)
{
this.center = center;
targets.Clear();
targets.Add(target);
this.audioManager = audioManager;
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
// 初めのターゲットの距離
float distance = Vector3.Distance(center.position, targets[0].position);
for (int i = 1; i < targets.Count; i++)
{
// 次のターゲットの距離
float d = Vector3.Distance(center.position, targets[i].position);
// 小さい方を保存
distance = distance > d ? d : distance;
}
// 音管理オブジェクトのメソッドを呼ぶ
audioManager.SetCrossFadeRatio(distance);
}
}
スクリプトでは、距離を計算する対象のオブジェクトとオーディオ管理のオブジェクトを設定し、Updateメソッドで距離を計算して、管理オブジェクトのメソッドを呼ぶことで距離を知らせます。
計算するのは中心からターゲットまでの距離です。中心はプレイヤーにして、複数のターゲットを設定できるようにしました。ターゲットは変更できるようにしました。
オーディオ管理のスクリプトでは、クロスフェードさせたいときにこれをインスタンス化します。オブジェクトにはスクリプトの他に2つのオーディオソースが付いていて、距離の値によってボリュームを変更します。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AudioManager : MonoBehaviour
{
[SerializeField] AudioClip bgm1;
[SerializeField] AudioClip bgm2;
[SerializeField, Range(0f, 1f)] float bgmVolume = 0.5f;
[SerializeField, Range(0f, 1f)] float maxBgmVolume = 0.5f;
AudioSource bgmAudioSource1;
AudioSource bgmAudioSource2;
// 距離を計算するゲームオブジェクト
[SerializeField] DistanceChecker distanceCheckerPrefab;
DistanceChecker distanceChecker;
static AudioManager instance;
public static AudioManager GetInstance()
{
return instance;
}
// Start is called before the first frame update
void Start()
{
instance = this;
bgmAudioSource1 = GetComponents<AudioSource>()[0];
bgmAudioSource2 = GetComponents<AudioSource>()[1];
}
// クロスフェードを開始
public void StartCrossFade(Transform center, List<Transform> targets)
{
// 距離計算オブジェクトがなければインスタンス化
if (distanceChecker == null) distanceChecker = Instantiate(distanceCheckerPrefab);
// 設定
distanceChecker.SetUp(center, targets, this);
// BGMを再生
SetUpBgmAudioSource(bgm1, bgm2);
}
// クロスフェードのボリュームを変更
public void SetCrossFadeRatio(float distance)
{
float crossFadeRatio = Mathf.Clamp(Mathf.Pow(2f / distance, 2f), 0f, 1f);
// ボリュームを設定
bgmAudioSource1.volume = (1 - crossFadeRatio) * bgmVolume;
bgmAudioSource2.volume = crossFadeRatio * bgmVolume;
}
// 2つのオーディオソースでBGMを再生
void SetUpBgmAudioSource(AudioClip bgm1, AudioClip bgm2)
{
bgmAudioSource1.clip = bgm1;
bgmAudioSource1.loop = true;
bgmAudioSource1.Play();
bgmAudioSource2.clip = bgm2;
bgmAudioSource2.loop = true;
bgmAudioSource2.Play();
}
// クロスフェードを終了
public void StopCrossFade()
{
Destroy(distanceChecker.gameObject);
}
WaitForSeconds wait = new WaitForSeconds(0.1f);
// フェードイン
public void StartFadeInBgm()
{
StopAllCoroutines();
bgmVolume = 0f;
StartCoroutine("FadeInBgm");
}
IEnumerator FadeInBgm()
{
while (true)
{
yield return wait;
bgmVolume += 0.01f;
if (bgmVolume >= maxBgmVolume)
{
bgmVolume = maxBgmVolume;
yield break;
}
}
}
// フェードアウト
public void StartFadeOutBgm()
{
StopAllCoroutines();
StartCoroutine("FadeOutBgm");
}
IEnumerator FadeOutBgm()
{
while (true)
{
yield return wait;
bgmVolume -= 0.01f;
if (bgmVolume <= 0f)
{
bgmVolume = 0f;
yield break;
}
}
}
}
距離を計算するオブジェクトから渡された距離で、ある値を割ることで、距離がその値以下のときは1以上、それより遠いときは値が小さくなります。それを2乗することで、近づくと急激にボリュームが上がるようにしてみました。最後に0~1の範囲に固定しています。
float crossFadeRatio = Mathf.Clamp(Mathf.Pow(2f / distance, 2f), 0f, 1f);
そして、2つのオーディオソースのボリュームの一方をこの値、もう一方を1から引いた値にしました。この時に両方とも0~1の値をかけて、この値を増減させることで、クロスフェードさせながら全体の音量を変えることができます。
bgmAudioSource1.volume = (1 - crossFadeRatio) * bgmVolume;
bgmAudioSource2.volume = crossFadeRatio * bgmVolume;
そこで、コルーチンを使って全体の音量を増減させてフェードイン/アウトさせてみました。
これで、距離によってクロスフェードさせながら全体の音量を変えられました。距離を計算するオブジェクトがオーディオ管理のメソッドを呼んでボリュームを変えているだけなので、クロスフェードを終了するときは距離を計算するオブジェクトを破壊します。
public void StopCrossFade()
{
Destroy(distanceChecker.gameObject);
}
今回はオーディオ管理のクロスフェードのメソッドを呼ぶ時に、ターゲットのオブジェクトをタグで検索してリストを作っています。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Player3 : MonoBehaviour
{
[SerializeField] Text text;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.LeftShift))
{
if (Input.GetKeyDown(KeyCode.Alpha1))
{
// タグでオブジェクトを検索
GameObject[] obj = GameObject.FindGameObjectsWithTag("Target");
// ターゲットのリスト
List<Transform> targets = new List<Transform>();
// Transformをリストに追加
for (int i = 0; i < obj.Length; i++)
{
targets.Add(obj[i].transform);
}
// クロスフェードを開始
AudioManager.GetInstance().StartCrossFade(transform, targets);
text.text = "クロスフェード";
}
else if (Input.GetKeyDown(KeyCode.Alpha2))
{
// ターゲットのリスト
List<Transform> targets = new List<Transform>();
// 別のタグで検索してリストに追加
targets.Add(GameObject.FindGameObjectWithTag("Target2").transform);
// クロスフェードを開始
AudioManager.GetInstance().StartCrossFade(transform, targets);
text.text = "ターゲット変更";
}
else if (Input.GetKeyDown(KeyCode.Alpha3))
{
AudioManager.GetInstance().StartFadeOutBgm();
text.text = "フェードアウト";
}
else if (Input.GetKeyDown(KeyCode.Alpha4))
{
AudioManager.GetInstance().StartFadeInBgm();
text.text = "フェードイン";
}
else if (Input.GetKeyDown(KeyCode.Alpha5))
{
AudioManager.GetInstance().StopCrossFade();
text.text = "クロスフェード終了";
}
}
}
}