This commit is contained in:
ThomasCharester committed 2026-02-26 11:21:27 +03:00
1 parent a73338916a
commit fbc1cc8d3e
207 files changed
+10327 -2

No files matched your search

@@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Code.Infrastructure.Configs;
using Code.Infrastructure.EventBus;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
namespace Code.Infrastructure.Audio
{
public interface IAudioFactory
{
UniTaskVoid LoadAudioClips(AudioNames groupName);
void ReleaseAudios(AudioNames groupName);
}
public class AudioFactory : IAudioFactory, IDisposable
{
private string _label;
private Dictionary<AudioNames, Dictionary<int, AssetReference>> _audioInfos = new();
private Dictionary<AudioNames, Dictionary<int, AsyncOperationHandle<AudioClip>>> _clipsHandles = new();
private AsyncOperationHandle<IList<AudioInfoConfig>> _configsHandle;
private EventBus.EventBus _eventBus;
public AudioFactory(IConfigLocator configLocator, EventBus.EventBus eventBus)
{
_eventBus = eventBus;
_label = configLocator.TryGetManyConfigAssetReferences<AudioInfoConfig>();
for (var i = 0; i < typeof(AudioNames).GetEnumValues().Length; i++)
{
_audioInfos.Add((AudioNames)i, new Dictionary<int, AssetReference>());
_clipsHandles.Add((AudioNames)i, new Dictionary<int, AsyncOperationHandle<AudioClip>>());
}
LoadConfigs().Forget();
}
private async UniTaskVoid LoadConfigs()
{
_configsHandle = Addressables.LoadAssetsAsync<AudioInfoConfig>(_label);
await _configsHandle.ToUniTask();
foreach (AudioInfoConfig config in _configsHandle.Result)
_audioInfos[config.GroupName].Add(config.id, config.fileReference);
_eventBus.RaiseEvent<IAudioConfigsLoadedHandle>(ui => ui.AudioConfigsLoaded());
}
public async UniTaskVoid LoadAudioClips(AudioNames groupName)
{
UnityEngine.Debug.LogWarning($"Audios loaded {groupName}");
foreach (var audioInfo in _audioInfos[groupName])
if (!_clipsHandles[groupName].ContainsKey(audioInfo.Key))
_clipsHandles[groupName].Add(audioInfo.Key, Addressables.LoadAssetAsync<AudioClip>(audioInfo.Value));
else
_clipsHandles[groupName][audioInfo.Key] = Addressables.LoadAssetAsync<AudioClip>(audioInfo.Value);
foreach (var handle in _clipsHandles[groupName])
await handle.Value.ToUniTask();
_eventBus.RaiseEvent<IAudioClipsEventHandle>(ui =>
ui.AudioClipsLoaded(groupName, _clipsHandles[groupName].ToDictionary(
x => x.Key,
x => x.Value.Result)));
}
public void ReleaseAudios(AudioNames groupName)
{
UnityEngine.Debug.LogWarning($"Audios unloaded {groupName}");
_eventBus.RaiseEvent<IAudioClipsEventHandle>(x=> x.AudioClipsUnLoaded(groupName));
foreach (var handle in _clipsHandles)
Addressables.Release(handle.Value);
}
public void Dispose()
{
foreach (var handles in _clipsHandles)
{
foreach (var handle in handles.Value)
{
handle.Value.Release();
}
}
_configsHandle.Release();
}
}
internal interface IAudioConfigsLoadedHandle : IGlobalSubscriber
{
void AudioConfigsLoaded();
}
internal interface IAudioClipsEventHandle : IGlobalSubscriber
{
void AudioClipsLoaded(AudioNames groupName, Dictionary<int, AudioClip> toDictionary);
void AudioClipsUnLoaded(AudioNames groupName);
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cc45c23918ed446681564b467668304b
timeCreated: 1769344269
@@ -0,0 +1,11 @@
using Code.Infrastructure.Generators;
namespace Code.Infrastructure.Audio
{
[AudioMixerEnum]
public enum AudioGroupType
{
Master,
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 754a72a6b2c140a49a23bf6d39654c94
timeCreated: 1769609499
@@ -0,0 +1,19 @@
using Code.Infrastructure.Generators;
using UnityEngine;
using UnityEngine.AddressableAssets;
namespace Code.Infrastructure.Audio
{
[Config("Configs/Audio/AudioInfo", true)]
public class AudioInfo
{
[ToConfig] public AudioNames GroupName;
[ToConfig] public int id;
[ToConfig] public AssetReference fileReference;
}
[Config("Configs/Audio/AudioSourceSettings")]
public class AudioSourcePrefab
{
[ToConfig] public AssetReference sourcePrefab;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 821f8c193f8846568335e415bf1e7ced
timeCreated: 1769437653
@@ -0,0 +1,8 @@
namespace Code.Infrastructure.Audio
{
public enum AudioNames
{
UISounds,
EnemySFX
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 88f7326e5bec4c97b2a70228dcd9f899
timeCreated: 1769437552
@@ -0,0 +1,133 @@
using System.Collections.Generic;
using System.Threading;
using Code.Infrastructure.EventBus;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.Audio;
namespace Code.Infrastructure.Audio
{
public interface IAudioService
{
AudioClip GetClip(AudioNames groupName, int id);
void PlayTest();
UniTaskVoid PlayOneShot(AudioNames groupName, int id, Vector3 position = new Vector3(),
AudioSourceType sourceType = AudioSourceType.NoBlend,
AudioSource audioSource = null);
UniTaskVoid PlayRepeatedly(AudioNames groupName, int id,
AudioSourceType sourceType = AudioSourceType.NoBlend, AudioSource audioSource = null, CancellationTokenSource cancellationTokenSource = null);
bool CheckClip(AudioNames groupName, int id);
void CancelMusic(AudioNames groupName, int id);
void ReleaseCancellationSource(AudioNames groupName, int id);
}
public partial class AudioService : IAudioClipsEventHandle, IAudioService
{
private EventBus.EventBus _eventBus;
private IAudioFactory _audioFactory;
private IAudioSourcePool _pool;
private Dictionary<AudioNames, Dictionary<int, AudioClip>> _audioClips = new();
private AudioMixer _mixer;
private Dictionary<AudioGroupType, AudioMixerGroup> _audioMixerGroups = new();
public AudioService(EventBus.EventBus eventBus, IAudioFactory audioFactory, IAudioSourcePool pool)
{
_eventBus = eventBus;
_audioFactory = audioFactory;
_pool = pool;
_eventBus.Subscribe(this);
for (int i = 0; i < typeof(AudioNames).GetEnumValues().Length; i++)
_audioClips.Add((AudioNames)i, new Dictionary<int, AudioClip>());
//InitializeMixer();
}
private partial void InitializeMixer();
public void PlayTest()
{
_pool.GetSource(AudioSourceType.NoBlend).PlayOneShot(GetClip(AudioNames.UISounds, 0));
}
public async UniTaskVoid PlayOneShot(AudioNames groupName, int id, Vector3 position = new Vector3(),
AudioSourceType sourceType = AudioSourceType.NoBlend,
AudioSource audioSource = null)
{
if (!CheckClip(groupName, id)) return;
if (!audioSource) audioSource = _pool.GetSource(sourceType);
audioSource.transform.position = position;
audioSource.PlayOneShot(GetClip(groupName, id));
await UniTask.NextFrame();
await UniTask.WaitUntilValueChanged(audioSource, _ => !audioSource.isPlaying);
_pool.ReleaseSource(audioSource);
}
private Dictionary<KeyValuePair<AudioNames, int>, CancellationTokenSource> _cancellationTokenSources = new();
public void CancelMusic(AudioNames groupName, int id) => _cancellationTokenSources[new KeyValuePair<AudioNames, int>(groupName, id)].Cancel();
public void ReleaseCancellationSource(AudioNames groupName, int id) => _cancellationTokenSources[new KeyValuePair<AudioNames, int>(groupName, id)] = null;
public async UniTaskVoid PlayRepeatedly(AudioNames groupName, int id,
AudioSourceType sourceType = AudioSourceType.NoBlend, AudioSource audioSource = null, CancellationTokenSource cancellationTokenSource = null)
{
if (!CheckClip(groupName, id)) return;
if(cancellationTokenSource == null)
{
cancellationTokenSource = new CancellationTokenSource();
_cancellationTokenSources[new KeyValuePair<AudioNames, int>(groupName, id)] = cancellationTokenSource;
}
if (!audioSource) audioSource = _pool.GetSource(sourceType);
if(!audioSource.resource) audioSource.resource = GetClip(groupName, id);
audioSource.Play();
await UniTask.NextFrame();
var (isCanceled, _) = await UniTask.WaitUntilValueChanged(audioSource,
_ => !audioSource.isPlaying,
cancellationToken: cancellationTokenSource.Token).SuppressCancellationThrow();
UnityEngine.Debug.LogError("Music skipping");
if (isCanceled)
{
cancellationTokenSource.Dispose();
_pool.ReleaseSource(audioSource);
ReleaseCancellationSource(groupName,id);
return;
}
PlayRepeatedly(groupName, id, sourceType, audioSource, cancellationTokenSource).Forget();
}
public AudioClip GetClip(AudioNames groupName, int id) => _audioClips[groupName][id];
public bool CheckClip(AudioNames groupName, int id) => _audioClips[groupName].ContainsKey(id);
public void AudioClipsLoaded(AudioNames groupname, Dictionary<int, AudioClip> clips)
{
_audioClips[groupname] = clips;
_eventBus.RaiseEvent<IReadyToPlayHandle>(x => x.OnReadyToPlay(groupname));
}
public void AudioClipsUnLoaded(AudioNames groupName)
{
_audioClips[groupName].Clear();
_audioClips[groupName] = null;
}
}
public interface IReadyToPlayHandle : IGlobalSubscriber
{
void OnReadyToPlay(AudioNames groupName);
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e8426c9e925a431aa73069c49d6a71c0
timeCreated: 1769446252
@@ -0,0 +1,129 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Code.Infrastructure.Configs;
using Code.Infrastructure.EventBus;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Audio;
using UnityEngine.Pool;
using UnityEngine.ResourceManagement.AsyncOperations;
using Object = UnityEngine.Object;
namespace Code.Infrastructure.Audio
{
public interface IAudioSourcePool
{
AudioSource GetSource(AudioSourceType sourceType);
void ReleaseSource(AudioSource source);
}
public class AudioSourcePool : IAudioSourcePool, IDisposable
{
// The pool holds plain GameObjects (you can swap this for any component type).
private IObjectPool<AudioSource> _pool;
private GameObject _sourcePrefab;
private Dictionary<AudioSourceType, AudioSourceSettingsConfig> _settings;
public AudioSourcePool(Dictionary<AudioSourceType, AudioSourceSettingsConfig> settings) => _settings = settings;
private string _configsLabel;
private AsyncOperationHandle<IList<AudioSourceSettingsConfig>> _handles;
private AssetReference _sourcePrefabConfigReference;
private AsyncOperationHandle<AudioSourcePrefabConfig> _prefabConfigHandle;
private AsyncOperationHandle<GameObject> _prefabHandle;
private EventBus.EventBus _eventBus;
public AudioSourcePool(IConfigLocator configLocator, EventBus.EventBus eventBus)
{
_eventBus = eventBus;
_configsLabel = configLocator.TryGetManyConfigAssetReferences<AudioSourceSettingsConfig>();
_sourcePrefabConfigReference = configLocator.TryGetConfigAssetReference<AudioSourcePrefabConfig>();
LoadConfigs().Forget();
}
private async UniTaskVoid LoadConfigs()
{
_prefabConfigHandle = Addressables.LoadAssetAsync<AudioSourcePrefabConfig>(_sourcePrefabConfigReference);
_handles = Addressables.LoadAssetsAsync<AudioSourceSettingsConfig>(_configsLabel);
await UniTask.WhenAll(
_prefabConfigHandle.ToUniTask(),
_handles.ToUniTask());
_prefabHandle = Addressables.LoadAssetAsync<GameObject>(_prefabConfigHandle.Result.sourcePrefab);
_settings = _handles.Result.ToDictionary(x => x.sourceType);
await _prefabHandle.ToUniTask();
_pool = new ObjectPool<AudioSource>(
createFunc: CreateItem,
// actionOnGet: OnGet,
actionOnRelease: OnRelease,
actionOnDestroy: OnDestroyItem,
collectionCheck: true, // helps catch double-release mistakes
defaultCapacity: 10,
maxSize: 20
);
_eventBus.RaiseEvent<IAudioSourcePoolInitializedHandle>(x => x.AudioSourcePoolInitialized());
}
// Creates a new pooled GameObject the first time (and whenever the pool needs more).
private AudioSource CreateItem()
{
var audioSource = Object.Instantiate(_prefabHandle.Result).GetComponent<AudioSource>();
audioSource.name = "PooledAudioSource";
Object.DontDestroyOnLoad(audioSource);
return audioSource;
}
// Called when an item is taken from the pool.
private void OnGet(AudioSource source)
{
//source.gameObject.SetActive(true);
}
// Called when an item is returned to the pool.
private void OnRelease(AudioSource source)
{
source.Stop();
source.resource = null;
//source.gameObject.SetActive(false);
}
// Called when the pool decides to destroy an item (e.g., above max size).
private void OnDestroyItem(AudioSource source)
{
Object.Destroy(source.gameObject);
}
public AudioSource GetSource(AudioSourceType sourceType)
{
var source = _pool.Get();
source.spatialBlend = _settings[sourceType].spatialBlend;
return source;
}
public void ReleaseSource(AudioSource source) => _pool.Release(source);
public void Dispose()
{
Addressables.Release(_handles);
Addressables.Release(_prefabConfigHandle);
Addressables.Release(_sourcePrefab);
}
}
public interface IAudioSourcePoolInitializedHandle : IGlobalSubscriber
{
void AudioSourcePoolInitialized();
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 246ae406fabc4ca791673d3f38cfe1c5
timeCreated: 1769448041
@@ -0,0 +1,14 @@
using Code.Infrastructure.Generators;
namespace Code.Infrastructure.Audio
{
[Config("Configs/Audio/AudioSourceSettings", true)]
public class AudioSourceSettings
{
// Дополнять по мере необходимости
[ToConfig] public float spatialBlend;
[ToConfig] public AudioSourceType sourceType;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 76d9dd39cbe641dab0cfa8ed1f2007f4
timeCreated: 1769448198
@@ -0,0 +1,8 @@
namespace Code.Infrastructure.Audio
{
public enum AudioSourceType
{
Full3D,
NoBlend
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 85f9cb99599f4399a1fd66496b5184d0
timeCreated: 1769448254