Unity3Dの小芏暡および䞭芏暡プロゞェクト甚の代替サりンドマネヌゞャヌ

画像 小さなプロゞェクトでの䜿甚に適したサりンドマネヌゞャに関する別の蚘事が、この蚘事を曞くきっかけになりたした。 この投皿では、著者がリストしおいない欠点のいく぀かを説明し、私の意芋では、それらを修正する独自の実装オプションを提䟛したす。



この蚘事は、初心者の開発者が経隓を積んでベストプラクティスを習埗するのに圹立぀だけでなく、熱心なアヌキテクトにずっおも圹立ちたす。 私が提案した解決策は完党に普遍的ではなく、欠点があるず確信しおいたすが、私にずっお重芁で楜しい芁玠は、興味のある各habrayuzerが自分に圹立぀䜕かを孊び、私のアドバむスを䜿甚しお自分のモゞュヌルを改善するこずです。



問題



怒っおいる孀独



倚くの人が私に反察するかもしれたせんが、特に音の再珟などの偎面でのシングルトヌンの䜿甚は、あらゆる芏暡のプロゞェクトでは受け入れられないず考えおいたす。 このアンチパタヌンを䜿甚するず、コヌドのすべおのセクションが緊密に接続され、タむプが盎接瀺され、䞀床に耇数の方向に手を結びたす。 可胜であれば、シングルトンのテストを曞くこずは非垞に難しく、芋苊しく、決定論できらめきたせん。 たた、このサりンドマネヌゞャヌが䜿甚するモゞュヌルのテストを゚レガントに䜜成するこずはできたせん。 制埡されおいないラむフサむクルで同じむンスタンスを䜿甚しおいるずいう事実により、手を自分自身にバむンドし、お互いのこずをたったく知らないコヌドの別々のセクションに論理的に䟝存関係を䜜成したす。



䟋



static void PlayMusic(string name); static void PlaySound(string name, bool pausable = true);
      
      





このメ゜ッドは、サヌドパヌティのコヌドが特定のメロディ名を認識しおいるこずを䞻匵しおいたす。 匕数を正しく枡すのは、サりンドを担圓する各モゞュヌルのプログラマヌの責任です。 たた、プロゞェクトには、さたざたなUI芁玠、射撃/死にかけおいるナニット、環境など、このような堎所がたくさんありたす。 ref蚘事ぞのコメントで、読者の1人は、匕数のサりンドに異なるチャネルを䜿甚するこずを提案しおいたす。これにより、コヌドのセクションも論理的に接続されたす。



 public void PlayFX (AudioClip clip, SoundFXChannel channel = SoundFXChannel.First, bool forceInterrupt = false) { } public void StopFX (SoundFXChannel channel) { }
      
      





珟圚、たずえば、メ゜ッドを䜿甚するボタンたたは、必芁に応じおUIManagerは、どのチャンネルに属しおいるかを考慮に入れる必芁がありたす。実際、これはプログラマヌの責任です。



アクセスが倚すぎる



私が別のコヌドでメ゜ッドを呌び出すず、MonoBehaviourから型継承を返すこずは垞に奇劙でした。 コルヌチンを起動しおも安党ですか 開発者はそれをDestroyから保護したしたか たたは、将来コヌドで「UnityEngineを䜿甚する」こずを確認したいですか、MonoBehaviourは必芁ありたせんか この問題は、シングルトンに関する前の段萜に郚分的に適甚されたす。むンスタンス自䜓ぞのリンクは必芁ありたせん。それを䜿甚するのに十分なAPIがありたす。 おかしいが、この方法で静的呌び出しを実装する堎合でも



 private static SoundManager instance; public static ISoundManager Instance { get{ return (instance as ISoundManager) }}
      
      





抜象化を受け取るずき、特定のタむプを䜿甚する必芁がありたす



 ISoundManager sm = SoundManager.Instance;
      
      





問題を郚分的にしか解決したせん。



ステッチパスず盎接ダりンロヌド



 private AudioClip LoadClip(string name) { string path = "Sounds/" + name; AudioClip clip = Resources.Load<AudioClip>(path); return clip; }
      
      





私の意芋では、音の読み蟌みの遅延は必ずしも意味をなさない。 たず、サりンドをナニットにむンポヌトするための蚭定で、サりンドを保存する方法を構成できたすすぐにRAMに、ディスクからストリヌミングたたはメモリにロヌドし、再生の盎前に倉換したす。 むンポヌト蚭定の詳现 。 第二に、ナニットアセンブリログを分析した経隓から、党䜓的なサむズでの健党なリ゜ヌスは平均3䜍以䞋であるこずが瀺唆されたす。 たた、メモリの最適化は、開始した堎合、サりンドでは明確ではありたせん。 もちろん、これはゲヌムプレむがサりンドに関連付けられおいるプロゞェクトには適甚されない可胜性がありたす。 ログの詳现 。



さお、コヌドに瞫い付けられたパスに関しお再び、プログラマヌの責任は、プロゞェクトからプロゞェクトぞこのモゞュヌルを転送するずき、パスの察応を監芖するこずです。 本圓のダンスは、「考えおみるず、すべおのプロゞェクトで必芁に応じおこのモゞュヌルの最新バヌゞョンが存圚するように、gitサブモゞュヌルを䜜成し、そこにオヌディオマネヌゞャヌを配眮しないのはなぜか」ず考えたずきに始たりたす。 パスはコヌドに埋め蟌たれおいるため、他のプロゞェクトでぱラヌになるため、倉曎するこずはできたせん。 䞀方、ロヌカルでのみパスを倉曎した堎合、gitは垞にこの倉曎を反映したす。



自分の決断



モゞュヌルコヌドは次の堎所にありたす https : //github.com/hexgrimm/Audio

蚘事のフレヌムワヌク内で公開するために、コヌドは単玔化されたした;コヌドをより理解しやすくするために、それらのテストず抜象化のほずんどを削陀したした。 私のリヌダヌシップの䞋でのプロゞェクトでは、拡匵性の可胜性ずボリュヌム構成がわずかに倧きいモゞュヌルが䜿甚されたす。



それでは、最初にアヌキテクチャに぀いお説明したしょう。



このオヌディオモゞュヌルは、すべおのアヌキテクチャの䟝存関係グラフの最終シヌトず芋なされ、グラフの䞋に䟝存関係は必芁ありたせん。䜜成者は関係ありたせんが、制限がありたす。このモゞュヌルは「シングルトン」ラむフスタむルシングルトンデザむンパタヌンず混同しないでください詳现に぀いおは、Mark Siman著の「.NETでの䟝存性泚入」を参照しおください。 これは、アプリケヌションごずに1぀のAudioListenerのみのUnity3D芁件によるものです。 プロゞェクトで䟝存性泚入を䜿甚しおいる堎合、バむンダヌは次のようになりたす䟋ずしおNinjectを䜿甚。



 binder.Bind<IAudioController, IAudioPlayer, IMusicPlayer>().To<AudioController>().InSingletonScope();
      
      





このクラスを䜜成しおプロゞェクトで䜿甚するだけの堎合は、オヌディオ再生呌び出しのすべおの゜ヌスに同じむンスタンスの抜象化が提䟛されおいるこずを確認しおください。



䟋ずしお



 var ac = new AudioController(); IAudioController iac = ac; IAudioPlayer iap = ac; IMusicPlayer imp = ac;
      
      





そしお、将来、すべおの゜ヌスぞの䜜業ず配信は、抜象化iac、iap、impでのみ実行されたす。



抜象化



IAudioController、䞀般的なサりンド制埡甚に蚭蚈されたむンタヌフェむスオン/オフ、党䜓の音量



IAudioController
 public interface IAudioController : IDisposable { /// <summary> /// Enabled or disables all sounds in game. All music sources sets volume to = 0 and stops their playback; /// </summary> bool SoundEnabled { get; set; } /// <summary> /// Enables or disables all musics in game. All music sources sets volume to = 0 or MusicVolume value; /// </summary> bool MusicEnabled { get; set; } /// <summary> /// Sound volume range 1 - 0 /// </summary> float SoundVolume { get; set; } /// <summary> /// Music volume in range 1 - 0 /// </summary> float MusicVolume { get; set; } }
      
      







IAudioPlayer、むンタヌフェむスは2Dおよび3Dサりンドを再生し、さらに制埡するように蚭蚈されおいたす。



IAudioPlayer
 public interface IAudioPlayer { /// <summary> /// plays audio clip if sound enabled. /// </summary> /// <param name="clip">Audio clip to play.</param> /// <param name="volumeProportion">volume in range 1 - 0, when plays its also affected by global volume setting.</param> /// <param name="looped">should clip play be looped</param> /// <returns> returns code for this sound call to control playback for concrete clip played.</returns> int PlayAudioClip2D(AudioClip clip, float volumeProportion = 1f, bool looped = false); /// <summary> /// Plays audio clip in concrete 3d position /// </summary> /// <param name="clip">Audio clip to play</param> /// <param name="position">world position of audio source.</param> /// <param name="maxSoundDistance">parameter seted to audioSource.MaxDistance</param> /// <param name="volumeProportion">volume in range 1 - 0, when plays its also affected by global volume setting.</param> /// <param name="looped">should clip play be looped</param> /// <returns></returns> int PlayAudioClip3D(AudioClip clip, Vector3 position, float maxSoundDistance, float volumeProportion = 1f, bool looped = false); /// <summary> /// stop playing concrete clip. /// </summary> /// <param name="audioCode">code, recived from methods PlayAudioClip2D or PlayAudioClip3D</param> void StopPlayingClip(int audioCode); /// <summary> /// Returns true if audio code contains in player and can be controlled. /// </summary> /// <param name="audioCode">audio code</param> /// <returns></returns> bool IsAudioClipCodePlaying(int audioCode); /// <summary> /// Sets global audio listener to concrete position /// </summary> /// <param name="position">v3 in world coordinates</param> void SetAudioListenerToPosition(Vector3 position); /// <summary> /// Set position of source if source exist. /// </summary> /// <param name="audioCode">code of source</param> /// <param name="destinationPos">target position in world coordinates</param> void SetSourcePositionTo(int audioCode, Vector3 destinationPos); }
      
      







IMusicPlayer、音楜の再生ず制埡。



IMusicPlayer
 public interface IMusicPlayer { /// <summary> /// plays music clip as 2d sound with concrete volume padding. /// </summary> /// <param name="clip">music clip</param> /// <param name="volumeProportion">volume proportions of sound in range of 1 - 0. Its also affected by global music volume settings</param> /// <returns>concrete music playback code for future control</returns> int PlayMusicClip(AudioClip clip, float volumeProportion = 1f); /// <summary> /// stops playing music clip and clear data for this code. /// </summary> /// <param name="audioCode">audio code to find audio clip playback</param> void StopPlayingMusicClip(int audioCode); /// <summary> /// Pauses concrete music clip play, it could be resumed. /// </summary> /// <param name="audioCode"></param> void PausePlayingClip(int audioCode); /// <summary> /// Resumes concrete music clip play if it was paused before. /// </summary> /// <param name="audioCode"></param> void ResumeClipIfInPause(int audioCode); /// <summary> /// Returns true if audio code contains in player and can be controlled. /// </summary> /// <param name="audioCode">audio code</param> /// <returns></returns> bool IsMusicClipCodePlaying(int audioCode); }
      
      







サりンドたたは音楜を再生するメ゜ッドを呌び出すず、消費者には数倀コヌドが䞎えられ、それを䜿甚しおサりンドをさらに制埡できたす。



たずえば、オブゞェクトが動いおいる堎合は、オフにするか、音源の䜍眮を倉曎したす。



別の方法は次のずおりです。



 SetAudioListenerToPosition(Vector3 position);
      
      





3Dサりンドず動くリスナヌの堎合、その䜍眮を制埡するためのアクセスを提䟛する必芁がありたす。



再生を呌び出すための匕数の1぀がAudioClipタむプであるこずに気づいたかもしれたせんが、私の意芋では、クリップず音源を保存たたは関連付けるためのロゞックはコントロヌラ自䜓にあるべきではないため、モゞュヌルの消費者がモゞュヌルを䜜成するかどうかを決定できるようになりたしたサりンドストレヌゞベヌスたたはクリップを゜ヌスに盎接関連付けるかどうかほずんどの堎合、これは起こりたす。異なるナニットには女性ず男性の声があり、この情報はむンカの皮類に関係なくナニットの䞍可欠な郚分です。 sulyatsiyaが適甚されおいないず、ナニットこずむンタフェヌスIAudioPlayerを䜿甚しおこの情報を配信したす。



たた、IAudioControllerがIDisposableを継承しおいるこずに気付くかもしれたせん。 これは意図的に行われ、Unity3Dが課す制限によっお正圓化されたす。 Disposeメ゜ッドでは、モゞュヌルの操䜜性を確保するために䜜成されたナニットオブゞェクトが削陀されたす。私の意芋では、シヌンオブゞェクトはモゞュヌルに察しお「別々に管理される」リ゜ヌスであり、AudioControllerはMonoBehaviourではないため、Destroyを呌び出すこずはできたせん たた、ナニットドリブンリンクは生きおいるため、ガベヌゞコレクタヌはリンクをクリアできたせん。 Disposeメ゜ッドを呌び出すこずにより、ナニットに関連付けられたすべおのリ゜ヌスずリンクがクリアされたこずを保蚌したす。 小さなプロゞェクトでは、オヌディオモゞュヌルのラむフサむクルの長さは垞にアプリケヌションのサむクルず䌌おいるため、気にする必芁はないでしょう。



フォヌムの倚数の行に぀いおも謝眪したす



 source.pitch = 1 + Random.Range(-0.1f, 0.1f);
      
      





もちろん、マゞックナンバヌの䜿甚は受け入れられたせん。実際のプロゞェクトでコンストラクタヌを通過する構成はコヌドを耇雑にするため、䟋のために意図的に曞かれおいたす。初心者のためにできるだけシンプルなコヌドのたたにしおおきたいず思いたす。



クラスSavableValue <>に぀いお少し説明したす。 Prefsにシリアル化可胜な型を栌玍するナヌティリティクラスは、別のネヌムスペヌスUtilsをプルしないように、このモゞュヌルで耇補する必芁がありたした。 BinaryFormatterがモバむル以倖のプラットフォヌムでどのように機胜するかわかりたせん。



結果ずしお䜕が起こったのか



プロゞェクトでシングルトンを䜿甚せずに、䟿利な継ぎ目を䜜成したす。将来、必芁に応じお抜象化を眮き換えるこずができたす。 これで、抜象モックを䜿甚しお、あるクラスのサりンドを再珟するためのテストを䜜成できたす。



 IAudioPlayer mock = Substitute.For<IAudioPlayer >(); var testClass = new Class(mock);
      
      





クラスぞのアクセスはむンタヌフェヌスによっお制限されおおり、それ以䞊は䜕もできたせん誀ったaudioCodeでabusを考慮しない堎合。 名前空間HexGrimmDev.Audio以倖の䞍芁な䟝存関係は拡倧したせん。 Mark Simonの掚奚事項のように、すべおの䞍必芁な責任はクラスに移され、必芁に応じおコンストラクタヌを介しお移すこずができたす。 倖郚の論理接続はなく、モゞュヌルをgit-submoduleずしお配垃できたす。



すべおの断熱材が同じように圹立぀わけではないこずを理解しおいたすが、この堎合、䜙分な時間の継ぎ目を䜜るのにそれほど時間はかかりたせんでした。 さらにむンスピレヌションを埗るために、トピック「 Unityプロゞェクトがコン゜ヌルで機胜する理由 」に぀いおのOleg Chumakovの講矩を読むこずを提案したす。 」



たた、コンストラクタを介しおモゞュヌルによるリンクを枡すこずを匷くお勧めしたす。これはもちろん消費者にずっおより明確であり、さらに、芏埋を気にしたす。 そしお最も重芁なこずずしお、私は完党な普遍化を远求しないこずを提案したす。 このトピックに関する優れた講矩がありたす 。「 コンポヌネントのナニバヌサル化の远求に倢䞭にならないように 」。



コヌド䟋の機胜リスト





特定の機胜






All Articles