When working with sound designers and composers, I found myself having to needlessly implement the same basic functionalities over and over, such as being able to have a random pitch range to break up frequently-used sounds. Eventually I began working on a reusable package to give me the complete functionality I wanted, that I could import into any project. Here are some of the key features that I've developed over time to provide myself and sound designers / composers the tools for efficient audio playback and setup for rapid prototyping or game jams.
To reduce coupling and make more maintainable code, Scriptable Objects were used as a means to implement the mediator pattern.
The heart and soul of the system, these allow configuration of a variety of settings and easy setup for robust playback. In addition, to assist sound designers in quick setup, there are editor features to allow selection of one or more audio clips and, using the asset menu, provides a fast way to generate a new instance of a sound object with the selected clips injected, and named based on the audio clip's name - or the first clip's name, if multiple are selected.
Audio Event Channel
Allows customized playback logic within the Audio Manager. This is particularly useful, at its most basic level, for handling the way music vs sound effects are played. Typically I have the music set to alternate between two cached audio sources dedicated for music, and will cross-fade the volume levels between the current and incoming audio.
To provide sound designers a clean, accessible, and informative interface to setup and tweak sound files, I created a custom inspector, partitioned into several sections, to help keep the information organized. As well, a play test button to allow rapid-fire testing and adjusting without needing to set up a sound board or having to repeatedly play-test and hit specific circumstances to be able to play them.
Provides the basic configuration expected from Unity's AudioSource component, as well as several additional features.
Pitch Range provides a min-max slider to allow sound designers the ability to set a specific range in which a sound should randomize their pitch within
Fade-In Time will make the Audio Manager lerp the sound's volume up when playing
Fade-Out Time will make the Audio Manager lerp the sound's volume down within a specified time before the audio clip's playback ends
Here you can specify the audio mixer group, audio clips and their playback order settings, and audio channel, are to be used. Helpful information about the way each playback order setting are natively displayed for developers to quickly understand their functionality when needing a randomized playback order.
Features two simple text areas to save personal notes for things like tweaks that may want to be done later, as well as a description for the audio directory class, so sound designers can leave readily-accessible notes for when programmers are using references to the sound objects via code.
When requesting a sound to be played, a custom class called a Sound Request is returned. You can cache this request to be used later for playback functionality. For example, an enemy may have a long wind-up sound before a big attack, but you don't want it to keep playing once the enemy has died, so you can cache the request and send a request to stop the sound upon the enemy's death. You can also pause or resume sounds as well.
Although a controversial design decision to some, the audio system was built with ease-of-use in mind, first and foremost. It utilizes a static initialization method with the RuntimeInitializationOnLoad attribute, to ensure that an instance is always available, and avoids many of the more common potential issues that other implementations, such as the singleton pattern, can come with.
To make playing dedicated sounds easier to play and reference, a custom script was made to generate a static helper class to access the Sound files that are loaded from the Resources folder at runtime. Although caution and thoughtful planning ahead of time are necessary to avoid a large amount of cleanup needed if naming is needed to changed, this was more of a personal challenge to try and learn how to generate code via the StreamWriter class in C#. It pulls the previously mentioned descriptions to inject as summary comments for quick references and communication between sound designers and programmers.