Unityの小さなゲームとプロトタイプ用のサウンドマネージャー

Unityでのサウンドの再生は簡単です。 AudioSourceコンポーネントを作成し、AudioClipの形式でオーディオファイルをアタッチし、スクリプトからaudioSource.Play()を呼び出す必要があります。 または、オブジェクトの作成時に自動再生をオンに設定することもできます(「目覚めで再生」)。



困難は、ゲームにたくさんの音があるときに始まります。 優先順位を設定するには、すべて設定する必要があります。 別々に聞こえ、音楽は別々に聞こえます。 サウンドと音楽の音量を個別に調整する場合も、困難があります。 もちろん、AudioMixerでさまざまなチャンネルの音量を調整できますが、WebGLでは機能しません。 そして、Webplayerは今では時代遅れと見なされています。



また、ある種の音が連続して数回繰り返される場合(たとえば、プレーヤーがボタンをすばやく押してクリック音を再生する場合)、途中で途切れず、前の音と干渉することなく新しい音が開始されると便利です。 はい。一時停止をオンにすると、ゲームの音は一時停止する必要がありますが、メニューの音は一時停止しません。 すぐに使用できるUnityにはこのような機会がありますが、何らかの理由でスクリプトからのみアクセスでき、誰もがそれについて知っているわけではありません。



一般的に、シンプルで便利なSoundManagerが必要です。その作成について説明します。 大規模なプロジェクトには適していませんが、プロトタイプや小さなゲームには適しています。









それでは、SoundManagerはどうあるべきでしょうか? まあ、まず、使用するのが便利であるべきです。 つまり、「ステージ上でオブジェクトを見つける」、「コンポーネントを接続する」などのユーザー向けのものはなく、すべてが内部にあります。 したがって、すぐにそれをシングルトンにします(本質を強調するためにコードが短縮されます)。



private static SoundManager _instance; public static SoundManager Instance { get { if (_instance != null) { return _instance; } // Do not modify _instance here. It will be assigned in awake return new GameObject("(singleton) SoundManager").AddComponent<SoundManager>(); } } void Awake() { // Only one instance of SoundManager at a time! if (_instance != null) { Destroy(gameObject); return; } _instance = this; DontDestroyOnLoad(gameObject); }
      
      







これで、マネージャー自身がステージ上で自分自身を作成するため、自分で追加する必要はありません(推奨されません)。 これは、コードに記述されているパスであるプレハブによって作成されるため、プレハブを移動しないでください。 必要に応じて、新しいGameObject()およびAddComponent()を使用して作成することもできますUPD。 プレハブではなく作成されました。 さらに、オブジェクトはDontDestroyOnLoadを使用してすぐにフラグが立てられます。 これは、シーンをリロードするときに音楽とサウンドが中断せずに再生され続けるために必要です。

SoundManager.Instance.Method()を記述するだけで、どのメソッドにもアクセスできるようになりました。 すべてのメソッドでこのエントリをさらに減らすために、静的ラッパーを追加しました。



  public static void PlayMusic(string name) { Instance.PlayMusicInternal(name); }
      
      







したがって、さらに短いSoundManager.Method()を記述できます。



オブジェクトがあり、それを操作すると便利です。 次に、機能を追加します。 最も重要な機能はPlaySoundです。



  void PlaySoundInternal(string soundName, bool pausable) { if (string.IsNullOrEmpty(soundName)) { Debug.Log("Sound null or empty"); return; } int sameCountGuard = 0; foreach (AudioSource audioSource in _sounds) { if (audioSource.clip.name == soundName) sameCountGuard++; } if (sameCountGuard > 8) { Debug.Log("Too much duplicates for sound: " + soundName); return; } if (_sounds.Count > 16) { Debug.Log("Too much sounds"); return; } StartCoroutine(PlaySoundInternalSoon(soundName, pausable)); } IEnumerator PlaySoundInternalSoon(string soundName, bool pausable) { ResourceRequest request = LoadClipAsync("Sounds/" + soundName); while (!request.isDone) { yield return null; } AudioClip soundClip = (AudioClip)request.asset; if (null == soundClip) { Debug.Log("Sound not loaded: " + soundName); } GameObject sound = (GameObject)Instantiate(soundPrefab); sound.transform.parent = transform; AudioSource soundSource = sound.GetComponent<AudioSource>(); soundSource.mute = _mutedSound; soundSource.volume = _volumeSound * DefaultSoundVolume; soundSource.clip = soundClip; soundSource.Play(); soundSource.ignoreListenerPause = !pausable; _sounds.Add(soundSource); }
      
      







まず、いくつかのサウンドチェック。 空ではなく、そのような音が多すぎないこと(サイクルのどこかで誤って呼び出された場合)。 その後、リソースからサウンドをロードし、ダウンロードを待って、ステージ上に新しいオブジェクトを作成し、AudioSourceを追加し、構成して起動します。 LoadClipAsync関数は、名前からリソースからサウンドファイルの非同期ロードを開始します。 そのため、ファイルは「Resources / Sounds / Sounds」フォルダに配置する必要があります。 オブジェクトの作成は、リソースからロードされるプレハブによって行われます。 そのため、パラメータの一部(サウンドの優先度など)、インスペクタからプレハブを設定できます。 ボリュームは、オブジェクトごとに個別に設定されます。 AudioListenerボリュームの設定とは異なり、これにより、サウンドと音楽のボリュームを個別に調整できます。 オブジェクトを_soundsサウンドリストに保存して、ボリュームを調整し、最後に破棄できるようにします。



一時停止可能なパラメーターは、UIサウンドとゲームサウンドを分離するために必要です。 最初のものは常に再生され、一時停止されることはありません。 2番目のものは一時停止され、ゲームが再開されると続行されます。 これは、サウンドソース.ignoreListenerPauseフラグを使用して自動的に行われますが、何らかの理由でインスペクタから利用できません。



次に、ゲームに音楽を追加する方法が必要です。 一般に、コードはサウンドの追加に似ていますが、別のプレハブが使用されます(別の優先度とループ設定)。



  void PlayMusicInternal(string musicName) { if (string.IsNullOrEmpty(musicName)) { Debug.Log("Music empty or null"); return; } if (_currentMusicName == musicName) { Debug.Log("Music already playing: " + musicName); return; } StopMusicInternal(); _currentMusicName = musicName; AudioClip musicClip = LoadClip("Music/" + musicName); GameObject music = (GameObject)Instantiate(musicPrefab); if (null == music) { Debug.Log("Music not found: " + musicName); } music.transform.parent = transform; AudioSource musicSource = music.GetComponent<AudioSource>(); musicSource.mute = _mutedMusic; musicSource.ignoreListenerPause = true; musicSource.clip = musicClip; musicSource.Play(); musicSource.volume = 0; StartFadeMusic(musicSource, MusicFadeTime, _volumeMusic * DefaultMusicVolume, false); _currentMusicSource = musicSource; }
      
      







ほとんどの小さなプロジェクトでは、現在再生中の1つのトラックで十分なので、新しい音楽を開始すると前のトラックが自動的に停止するため、SoundManager.PlayMusic(“ MusicForCurrentScene”)だけで各シーンを呼び出すことができます。 さらに、音楽を作成および停止するときに、音量の滑らかな増加と滑らかなフェードアウトが追加されます。 これにより、移行がスムーズになり、耳に当たらないようになります。 ボリュームの最もスムーズな変更はTweenで行うことができますが、ハンドルを使用して依存関係を少なくすることもできます。



次に、一時停止する機会が必要です。 AudioListenerが一時停止されたときにすべてのサウンドが一時停止されているかどうかはすでにチェックされているため、メソッドは非常に簡単です。



  public static void Pause() { AudioListener.pause = true; } public static void UnPause() { AudioListener.pause = false; }
      
      







または、一時停止を自動的に含めるように構成できます。



  void Update() { if (AutoPause) { bool curPause = Time.timeScale < 0.1f; if (curPause != AudioListener.pause) { AudioListener.pause = curPause; } } }
      
      







次に、ボリュームを設定および取得するためのメソッドが必要です。



  void SetSoundVolumeInternal(float volume) { _volumeSound = volume; SaveSettings(); ApplySoundVolume(); } float GetSoundVolumeInternal() { return _volumeSound; } void SaveSettings() { PlayerPrefs.SetFloat("SM_SoundVolume", _volumeSound); } void LoadSettings() { _volumeSound = PlayerPrefs.GetFloat("SM_SoundVolume", 1); ApplySoundVolume(); } void ApplySoundVolume() { foreach (AudioSource sound in _sounds) { sound.volume = _volumeSound * DefaultSoundVolume; } }
      
      







ここではすべてが簡単です。 PlayerPrefsを使用して設定を保存および読み取り、変更する場合、サウンドを確認して新しいボリュームを適用します。 同様に、音楽に対してもミュートの調整を行うことができます。



まあ、それだけです。 SoundManagerは使いやすいです。 サウンドと音楽のテンプレートをプレハブに持ってきたので、AudioMixerからの出力をそれらに簡単に接続できます。 さらに、アニメーション、ボタンハンドラなどからの必要なメソッドの呼び出しを簡素化する小さなクラスがまだあるため、1行のためにスクリプトを記述する必要はありません。







受け取ったマネージャーの長所:

+使いやすい

+きれいなコードとシーンオブジェクト。 サウンドコンポーネントをどこにでも掛けて、コードから探して呼び出す必要はありません

+シーンの読み込み時に中断せず、スムーズに変化する音楽

+ゲームプレイとUIサウンド

+一時停止サポート

+ AudioMixerをサポート

+ AudioMixerをサポートしないプラットフォーム(例:WebGL)を含む、すべてのプラットフォームで動作します

+ナレーターの声のサポート(記事には記載されていませんが、完全なコードで実装されています)



現在の実装の制限(まだ):

-位置的な3Dサウンドはありませんが

-同じ音を何度も繰り返しても気にならないように音のピッチを変更する

-使用中のサウンドの読み込みが遅れる可能性があります(小さなプロジェクトや小さなサウンドでは気付かれません)

-単一のサウンドのボリュームコントロールなし

-アンビエントのようなループ音はありません



完全なマネージャーコードは、私のGitHubで表示できます。

https://github.com/Gasparfx/SoundManager



GreenLightでこのマネージャーを使用するプロジェクト:

http://steamcommunity.com/sharedfiles/filedetails/?id=577337491



All Articles