Design patterns in use
- Singleton:
AudioManagerexposes a staticInstance. InAwake, the first instance is kept withDontDestroyOnLoad, and duplicates are destroyed. - Object pool:
AudioSourcePoolcreates a fixed set ofAudioSourceGameObjects at construction. It hands out inactive sources and reactivates them when needed. - Template method:
AudioManagerdefines the fixed steps, and hands project-specific parts to abstract hooks (such asHandleButtonClickedandGetVolumePreset), the injectedIAudioTweener, and the injectedILayeredMusicPlayer. - Strategy through injection:
AudioManageraccepts anIAudioTweenerthroughSetAudioTweenerand anILayeredMusicPlayerthroughSetLayeredMusicPlayer. Backends and fading implementations can be swapped without changingAudioManager. - Static factory:
AudioCommandFactorybuilds Audio-domain commands with the correct verb, scope, subject, and parameters, then sends them throughBusUtility. - Two-tier lookup:
AudioClipLookupmaps clip keys to holders. Each holder maps clip keys toAudioClipData. Localized holders add a third level, language. - Publish and subscribe:
AudioFlowControllerregisters with the event bus for the UI domain and forwards received events toAudioManager.ProcessEvent. - Service locator:
FlowControllerandAudioFlowControllerget the bus registry throughServiceLocatorinstead of having it injected.
Connections to dependency systems
- Contracts:
Domainroutes commands and events toAudioFlowController.CoreParamKeys.ObjectValuecarries the float volume value.Command.Createis used by the factory. These types are defined outside this folder. - Params: Each command carries typed values in a
Paramscontainer, read and written withParamKey<T>keys defined inAudioParamKeys.Getthrows when a key is missing, so the system usesHasandTryGetfor optional values such as the clip key or callback. - Flow:
AudioFlowControllerextendsFlowController.FlowController.Initregisters for the Audio domain on the command bus.AudioFlowControlleralso callsbase.Init()and then registers for UI events.HandleCommandis declared abstract inFlowController, so a project subclass must implement it and forward commands toAudioManager.HandleCommand. - Bus:
AudioCommandFactorysends commands throughBusUtility.Send, which uses theCommandBus. The event bus delivers UI events toAudioFlowController.OnEvent. - Initialization:
InitializationManagerrunsFlowControllersubclasses throughIInitializable.AudioFlowControlleris classified as a sync initializable event receiver, because it implementsIEventReceiver. ItsInitruns before ordinary sync systems. - Application:
ServiceLocator.GetBusRegistrysupplies the buses toFlowControllerandAudioFlowController.
Key concepts
- Audio command: A command in the Audio domain. Its verb is an
AudioCommandVerb. Its scope is anAudioScope, used for volume and progress. Its subject is anAudioSubject, such as Music, SFX, Preset, or Master. - Clip key: A string that identifies a clip. Clip keys are hashed with
GetHashCodeforAudioStatelookups. - Audio state: An
AudioStaterecords the playback status, linkedAudioSource, clip key, and reserved flag for each tracked music clip.AudioManagerowns the list of states. - Reserved state: A paused music state is marked reserved, so it is kept and can be resumed. A non-reserved state is removed when released.
- Volume mix: A
VolumeMixstruct holds SFX, Music, and Master decibel values. AVolumeSourceselects whether values come from saved settings, runtime values, or a preset. AVolumeChannelselects which mixer groups to apply. - Volume context: A
VolumeContextis stored bySetAudioContext. - Layered music: Music played through an
ILayeredMusicPlayerbackend. Progress is set as a value, and the backend interprets it.ILayeredMusicDataStoresaves and loads progress. - Tweener: An
IAudioTweenerfadesAudioSourcevolume over time and can cancel a fade.
Event and data flow
Commands:
- A caller builds a command with
AudioCommandFactory. For example,PlayMusicsets the clip key and sets the subject to Music. BusUtility.Sendplaces the command on theCommandBus, routed byDomain.Audio.- The project's
AudioFlowControllersubclass receives the command inHandleCommand, and callsAudioManager.HandleCommand. HandleCommandpasses the command toHandleStandardAudioCommand. That method reads the verb, scope, and subject, then calls the matchingAudioManagermethod.- For sound effects,
AudioManagergets a pooled source and callsAudioPlaybackUtility.Play. That method applies the clip data, fades in through the tweener, and, for non-looping clips, waits until playback ends before deactivating the source and invoking the optional callback. - For music,
RequestMusicchecks the existingAudioState. It resumes a paused clip, does nothing if the clip is already playing, or starts new playback. When layering is off, it releases other tracked states first.
UI events:
- The event bus delivers a Domain.UI event to
AudioFlowController.OnEvent. OnEventcallsAudioManager.ProcessEvent.ProcessEventroutes UI-domain events toHandleUIEvent. For button clicks on the button subject, it calls the abstractHandleButtonClicked, which the project implements.
Clip resolution:
AudioManager.Awakebuilds anAudioClipLookupfrom its holder lists, then callsBuildHolderHashmap.- The lookup builds each holder's internal map, then maps clip keys to holders. The first occurrence of a duplicate key wins, and later duplicates are logged.
GetHashedClipfinds the holder for a key, then asks it for the clip. Localized holders use the language key, and fall back to any available language.