Second commit
This commit is contained in:
commit
36840f3ad4
999 files changed
+61364
No files matched your search
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Code.Infrastructure.Configs;
|
||||
using Code.Infrastructure.Utils;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
namespace Code.Infrastructure.Audio
|
||||
{
|
||||
public interface IAudioFactory
|
||||
{
|
||||
void UnloadAudioClips(AudioPartition groupPartition);
|
||||
UniTask LoadAudioClips(AudioPartition groupPartition);
|
||||
public Dictionary<AudioPartition, Dictionary<int, AudioReference>> ReferenceDictionary { get; }
|
||||
}
|
||||
|
||||
public interface IAudioFactoryInitializer
|
||||
{
|
||||
UniTask LoadConfigs();
|
||||
}
|
||||
|
||||
public class AudioFactory : IAudioFactory, IDisposable, IAudioFactoryInitializer
|
||||
{
|
||||
private string _label;
|
||||
private AsyncOperationHandle<IList<AudioInfoConfig>> _configsHandle;
|
||||
|
||||
public Dictionary<AudioPartition, Dictionary<int, AudioReference>> ReferenceDictionary { get; } = new();
|
||||
|
||||
public AudioFactory(IConfigLocator configLocator)
|
||||
{
|
||||
_label = configLocator.TryGetManyConfigAssetReferences<AudioInfoConfig>();
|
||||
|
||||
}
|
||||
|
||||
public async UniTask LoadConfigs()
|
||||
{
|
||||
if(!await AddressablesUtilities.CheckLabelExists(_label)) return;
|
||||
|
||||
_configsHandle = Addressables.LoadAssetsAsync<AudioInfoConfig>(_label);
|
||||
|
||||
await _configsHandle.ToUniTask();
|
||||
|
||||
foreach (AudioInfoConfig config in _configsHandle.Result)
|
||||
{
|
||||
if(!ReferenceDictionary.ContainsKey(config.GroupPartition))
|
||||
ReferenceDictionary.Add(config.GroupPartition, new());
|
||||
ReferenceDictionary[config.GroupPartition].Add(config.id, config.Reference);
|
||||
}
|
||||
}
|
||||
|
||||
public async UniTask LoadAudioClips(AudioPartition groupPartition)
|
||||
{
|
||||
foreach (var reference in ReferenceDictionary[groupPartition])
|
||||
{
|
||||
var load = reference.Value.Load();
|
||||
if (load.Status == UniTaskStatus.Pending)
|
||||
await load;
|
||||
}
|
||||
}
|
||||
|
||||
public void UnloadAudioClips(AudioPartition groupPartition)
|
||||
{
|
||||
foreach (var reference in ReferenceDictionary[groupPartition])
|
||||
{
|
||||
reference.Value.Unload();
|
||||
}
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var partition in ReferenceDictionary)
|
||||
{
|
||||
foreach (var reference in partition.Value)
|
||||
{
|
||||
reference.Value.Unload();
|
||||
}
|
||||
}
|
||||
|
||||
if(_configsHandle.IsValid()) _configsHandle.Release();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc45c23918ed446681564b467668304b
|
||||
timeCreated: 1769344269
|
||||
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace Code.Infrastructure.Audio
|
||||
{
|
||||
[Serializable]
|
||||
public abstract class AudioReference
|
||||
{
|
||||
public abstract UniTask Load();
|
||||
public abstract void Unload();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class AudioReferenceLODed : AudioReference
|
||||
{
|
||||
private AsyncOperationHandle<AudioClip> _interiorClipHandle;
|
||||
private AsyncOperationHandle<AudioClip> _closeClipHandle;
|
||||
private AsyncOperationHandle<AudioClip> _distantClipHandle;
|
||||
private AsyncOperationHandle<AudioClip> _farClipHandle;
|
||||
|
||||
public override async UniTask Load()
|
||||
{
|
||||
if (_interiorClipHandle.IsValid()) return;
|
||||
|
||||
_interiorClipHandle = Addressables.LoadAssetAsync<AudioClip>(interior);
|
||||
_closeClipHandle = Addressables.LoadAssetAsync<AudioClip>(close);
|
||||
_distantClipHandle = Addressables.LoadAssetAsync<AudioClip>(distant);
|
||||
_farClipHandle = Addressables.LoadAssetAsync<AudioClip>(far);
|
||||
|
||||
await UniTask.WhenAll(
|
||||
_interiorClipHandle.ToUniTask(),
|
||||
_closeClipHandle.ToUniTask(),
|
||||
_distantClipHandle.ToUniTask(),
|
||||
_farClipHandle.ToUniTask()
|
||||
);
|
||||
|
||||
Interior = _interiorClipHandle.Result;
|
||||
Close = _closeClipHandle.Result;
|
||||
Distant = _distantClipHandle.Result;
|
||||
Far = _farClipHandle.Result;
|
||||
}
|
||||
|
||||
public override void Unload()
|
||||
{
|
||||
if (!_interiorClipHandle.IsValid()) return;
|
||||
|
||||
_interiorClipHandle.Release();
|
||||
_closeClipHandle.Release();
|
||||
_distantClipHandle.Release();
|
||||
_farClipHandle.Release();
|
||||
|
||||
Interior = null;
|
||||
Close = null;
|
||||
Distant = null;
|
||||
Far = null;
|
||||
}
|
||||
|
||||
[SerializeField] private AssetReference interior;
|
||||
[SerializeField] private AssetReference close;
|
||||
[SerializeField] private AssetReference distant;
|
||||
[SerializeField] private AssetReference far;
|
||||
|
||||
public AudioClip Interior { get; private set; }
|
||||
public AudioClip Close { get; private set; }
|
||||
public AudioClip Distant { get; private set; }
|
||||
public AudioClip Far { get; private set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class AudioReferenceSingle : AudioReference
|
||||
{
|
||||
private AsyncOperationHandle<AudioClip> _clipHandle;
|
||||
|
||||
public override async UniTask Load()
|
||||
{
|
||||
if (_clipHandle.IsValid()) return;
|
||||
|
||||
_clipHandle = Addressables.LoadAssetAsync<AudioClip>(clip);
|
||||
|
||||
await _clipHandle.ToUniTask();
|
||||
|
||||
Clip = _clipHandle.Result;
|
||||
}
|
||||
|
||||
public override void Unload()
|
||||
{
|
||||
if (!_clipHandle.IsValid()) return;
|
||||
|
||||
_clipHandle.Release();
|
||||
|
||||
Clip = null;
|
||||
}
|
||||
|
||||
[SerializeField] private AssetReference clip;
|
||||
|
||||
public AudioClip Clip { get; private set; }
|
||||
}
|
||||
[Serializable]
|
||||
public class AudioReferenceRandom : AudioReference
|
||||
{
|
||||
private AsyncOperationHandle<AudioClip>[] _clipHandles;
|
||||
|
||||
public override async UniTask Load()
|
||||
{
|
||||
if (_clipHandles != null) return;
|
||||
|
||||
_clipHandles = new AsyncOperationHandle<AudioClip>[clips.Length];
|
||||
|
||||
for (var i = 0; i < clips.Length; i++)
|
||||
{
|
||||
_clipHandles[i] = Addressables.LoadAssetAsync<AudioClip>(clips[i]);
|
||||
await _clipHandles[i].ToUniTask();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Unload()
|
||||
{
|
||||
if (_clipHandles == null) return;
|
||||
|
||||
for (var i = 0; i < clips.Length; i++) Addressables.Release(_clipHandles[i]);
|
||||
}
|
||||
|
||||
[SerializeField] private AssetReference[] clips;
|
||||
|
||||
public AudioClip Clip => _clipHandles[Random.Range(0, _clipHandles.Length)].Result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4423af0d471f60ef89f8df279e82af2
|
||||
@@ -0,0 +1,14 @@
|
||||
using Code.Infrastructure.Generators;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.Infrastructure.Audio
|
||||
{
|
||||
[Config("Configs/Audio/AudioInfo", true, true)]
|
||||
public abstract class AudioInfoConfig : ScriptableObject
|
||||
{
|
||||
public AudioPartition GroupPartition;
|
||||
public int id;
|
||||
|
||||
public abstract AudioReference Reference { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24ed33a7f788429eb32b93b79fcb84bd
|
||||
timeCreated: 1785871082
|
||||
@@ -0,0 +1,11 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.Infrastructure.Audio
|
||||
{
|
||||
[CreateAssetMenu(fileName = "AudioInfoLODed", menuName = "Configs/Audio/AudioInfoLODed")]
|
||||
public class AudioInfoLODedConfig : AudioInfoConfig
|
||||
{
|
||||
public AudioReferenceLODed reference;
|
||||
public override AudioReference Reference => reference;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97e55393d63f871ac8472894d1d9f627
|
||||
@@ -0,0 +1,11 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.Infrastructure.Audio
|
||||
{
|
||||
[CreateAssetMenu(fileName = "AudioInfoRandom", menuName = "Configs/Audio/AudioInfoRandom")]
|
||||
public class AudioInfoRandomConfig : AudioInfoConfig
|
||||
{
|
||||
public AudioReferenceRandom reference;
|
||||
public override AudioReference Reference => reference;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb3ca31ee67afb7818583cdd1dc6a43d
|
||||
@@ -0,0 +1,11 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.Infrastructure.Audio
|
||||
{
|
||||
[CreateAssetMenu(fileName = "AudioInfoSingle", menuName = "Configs/Audio/AudioInfoSingle")]
|
||||
public class AudioInfoSingleConfig : AudioInfoConfig
|
||||
{
|
||||
public AudioReferenceSingle reference;
|
||||
public override AudioReference Reference => reference;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3a6a54ee0524ae96905e8f5293922fd
|
||||
@@ -0,0 +1,13 @@
|
||||
using Code.Infrastructure.Generators;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Audio;
|
||||
|
||||
namespace Code.Infrastructure.Audio
|
||||
{
|
||||
[Config("Configs/Audio/AudioMixer")]
|
||||
[CreateAssetMenu(fileName = "AudioMixerSettings", menuName = "Configs/Audio/AudioMixer")]
|
||||
public class AudioMixerSettings : ScriptableObject
|
||||
{
|
||||
public AudioMixer mixerAsset;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 89e2ff4d31a24b78b40d9c4e20e1f1bc
|
||||
timeCreated: 1785871073
|
||||
@@ -0,0 +1,366 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Code.Infrastructure.Configs;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using LitMotion;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.Audio;
|
||||
|
||||
namespace Code.Infrastructure.Audio
|
||||
{
|
||||
public interface IAudioService
|
||||
{
|
||||
UniTaskVoid PlayOneShot(AudioPartition groupPartition, int id, AudioGroupType audioGroupType,
|
||||
Vector3 position = new Vector3(),
|
||||
AudioSourceType sourceType = AudioSourceType.NoBlend,
|
||||
AudioSource audioSource = null, bool isLOD = false);
|
||||
|
||||
UniTaskVoid PlayRepeatedly(AudioPartition groupPartition, int id, AudioGroupType audioGroupType,
|
||||
Vector3 position = new Vector3(),
|
||||
AudioSourceType sourceType = AudioSourceType.NoBlend, AudioSource audioSource = null,
|
||||
CancellationTokenSource cancellationTokenSource = null);
|
||||
|
||||
void CancelMusic(AudioPartition groupPartition, int id);
|
||||
void ReleaseCancellationSource(AudioPartition groupPartition, int id);
|
||||
|
||||
UniTask ApplyParameter(string effectName, float value, float time, Action callback = null,
|
||||
CancellationTokenSource cts = null);
|
||||
|
||||
UniTask ChangeGroupVolume(AudioGroupType groupType, float value, float time, Action callback = null,
|
||||
CancellationTokenSource cts = null);
|
||||
|
||||
void SetAudioListener(AudioListener listener);
|
||||
|
||||
UniTaskVoid PlayRepeatedly(AudioPartition groupPartition, int id, AudioGroupType audioGroupType,
|
||||
Transform parent,
|
||||
AudioSourceType sourceType = AudioSourceType.NoBlend, AudioSource audioSource = null,
|
||||
CancellationTokenSource cancellationTokenSource = null);
|
||||
|
||||
UniTaskVoid PlayOneShot(AudioPartition groupPartition, int id, AudioGroupType audioGroupType,
|
||||
Transform parent,
|
||||
AudioSourceType sourceType = AudioSourceType.NoBlend,
|
||||
AudioSource audioSource = null);
|
||||
}
|
||||
|
||||
public interface IAudioServiceInitializer
|
||||
{
|
||||
UniTask LoadMixerAsset();
|
||||
}
|
||||
|
||||
public partial class AudioService : IAudioService, IAudioServiceInitializer
|
||||
{
|
||||
private IAudioFactory _audioFactory;
|
||||
private IAudioSourcePool _pool;
|
||||
private AudioMixer _mixer;
|
||||
private AudioListener _listener;
|
||||
private Dictionary<AudioGroupType, AudioMixerGroup> _audioMixerGroups = new();
|
||||
|
||||
public AudioService(IAudioFactory audioFactory, IAudioSourcePool pool,
|
||||
IConfigLocator configLocator)
|
||||
{
|
||||
_audioFactory = audioFactory;
|
||||
_pool = pool;
|
||||
|
||||
_mixerConfig = configLocator.TryGetConfigAssetReference<AudioMixerSettings>();
|
||||
}
|
||||
|
||||
public void SetAudioListener(AudioListener listener)
|
||||
{
|
||||
if(_listener)
|
||||
_listener.enabled = false;
|
||||
|
||||
_listener = listener;
|
||||
_listener.enabled = true;
|
||||
}
|
||||
private partial void InitializeMixer();
|
||||
|
||||
public async UniTask LoadMixerAsset()
|
||||
{
|
||||
var audioMixer = await Addressables.LoadAssetAsync<AudioMixerSettings>(_mixerConfig);
|
||||
|
||||
_mixer = audioMixer.mixerAsset;
|
||||
|
||||
InitializeMixer();
|
||||
}
|
||||
|
||||
public async UniTaskVoid PlayOneShot(AudioPartition groupPartition, int id, AudioGroupType audioGroupType,
|
||||
Vector3 position = new Vector3(),
|
||||
AudioSourceType sourceType = AudioSourceType.NoBlend,
|
||||
AudioSource audioSource = null, bool isLOD = false)
|
||||
{
|
||||
if (!CheckClip(groupPartition, id)) return;
|
||||
|
||||
if (!audioSource) audioSource = _pool.GetSource(sourceType);
|
||||
|
||||
audioSource.transform.position = position;
|
||||
audioSource.outputAudioMixerGroup = _audioMixerGroups[audioGroupType];
|
||||
|
||||
if (sourceType == AudioSourceType.NoBlend)
|
||||
audioSource.PlayOneShot(GetSingleClip(groupPartition, id));
|
||||
if (sourceType == AudioSourceType.Full3D)
|
||||
{
|
||||
audioSource.PlayOneShot(isLOD
|
||||
? GetLODClip(groupPartition, id, GetLODLevelOnPosition(position))
|
||||
: GetSingleClip(groupPartition, id));
|
||||
}
|
||||
|
||||
await UniTask.NextFrame();
|
||||
await UniTask.WaitUntilValueChanged(audioSource, _ => !audioSource.isPlaying);
|
||||
|
||||
_pool.ReleaseSource(audioSource);
|
||||
}
|
||||
|
||||
public async UniTaskVoid PlayOneShot(AudioPartition groupPartition, int id, AudioGroupType audioGroupType,
|
||||
Transform parent,
|
||||
AudioSourceType sourceType = AudioSourceType.NoBlend,
|
||||
AudioSource audioSource = null)
|
||||
{
|
||||
if (!CheckClip(groupPartition, id)) return;
|
||||
|
||||
if (!audioSource) audioSource = _pool.GetSource(sourceType);
|
||||
|
||||
audioSource.GetComponent<AudioSourceFollower>().FollowTarget = parent;
|
||||
|
||||
audioSource.outputAudioMixerGroup = _audioMixerGroups[audioGroupType];
|
||||
|
||||
if (sourceType == AudioSourceType.NoBlend)
|
||||
audioSource.PlayOneShot(GetSingleClip(groupPartition, id));
|
||||
if (sourceType == AudioSourceType.Full3D)
|
||||
audioSource.PlayOneShot(GetLODClip(groupPartition, id, GetLODLevelOnPosition(parent.position)));
|
||||
|
||||
await UniTask.NextFrame();
|
||||
await UniTask.WaitUntilValueChanged(audioSource, _ => !audioSource.isPlaying);
|
||||
|
||||
_pool.ReleaseSource(audioSource);
|
||||
}
|
||||
private Dictionary<AudioPartitionIdPair, CancellationTokenSource> _cancellationTokenSources = new();
|
||||
private AssetReference _mixerConfig;
|
||||
|
||||
public void CancelMusic(AudioPartition groupPartition, int id)
|
||||
{
|
||||
var key = new AudioPartitionIdPair(groupPartition, id);
|
||||
|
||||
if (!_cancellationTokenSources.ContainsKey(key)) return;
|
||||
|
||||
_cancellationTokenSources[key].Cancel();
|
||||
_cancellationTokenSources[key].Dispose();
|
||||
_cancellationTokenSources.Remove(key);
|
||||
}
|
||||
|
||||
public void ReleaseCancellationSource(AudioPartition groupPartition, int id)
|
||||
{
|
||||
_cancellationTokenSources.Remove(new AudioPartitionIdPair(groupPartition, id));
|
||||
}
|
||||
|
||||
public async UniTaskVoid PlayRepeatedly(AudioPartition groupPartition, int id, AudioGroupType audioGroupType,
|
||||
Vector3 position = new(),
|
||||
AudioSourceType sourceType = AudioSourceType.NoBlend, AudioSource audioSource = null,
|
||||
CancellationTokenSource cancellationTokenSource = null)
|
||||
{
|
||||
if (!CheckClip(groupPartition, id)) return;
|
||||
|
||||
if (cancellationTokenSource == null)
|
||||
{
|
||||
cancellationTokenSource = new CancellationTokenSource();
|
||||
_cancellationTokenSources[new AudioPartitionIdPair(groupPartition, id)] = cancellationTokenSource;
|
||||
}
|
||||
|
||||
if (!audioSource) audioSource = _pool.GetSource(sourceType);
|
||||
|
||||
if (!audioSource.resource)
|
||||
{
|
||||
if (sourceType == AudioSourceType.NoBlend)
|
||||
audioSource.resource = GetSingleClip(groupPartition, id);
|
||||
if (sourceType == AudioSourceType.Full3D)
|
||||
audioSource.resource = GetLODClip(groupPartition, id, GetLODLevelOnPosition(position));
|
||||
}
|
||||
|
||||
audioSource.outputAudioMixerGroup = _audioMixerGroups[audioGroupType];
|
||||
audioSource.Play();
|
||||
|
||||
var (isCanceled, _) = await UniTask.WaitUntilValueChanged(audioSource,
|
||||
_ => !audioSource.isPlaying,
|
||||
cancellationToken: cancellationTokenSource.Token).SuppressCancellationThrow();
|
||||
|
||||
if (isCanceled)
|
||||
{
|
||||
cancellationTokenSource.Dispose();
|
||||
|
||||
_pool.ReleaseSource(audioSource);
|
||||
|
||||
ReleaseCancellationSource(groupPartition, id);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
PlayRepeatedly(groupPartition, id, AudioGroupType.Music, position, sourceType, audioSource,
|
||||
cancellationTokenSource)
|
||||
.Forget();
|
||||
}
|
||||
|
||||
public async UniTaskVoid PlayRepeatedly(AudioPartition groupPartition, int id, AudioGroupType audioGroupType,
|
||||
Transform parent,
|
||||
AudioSourceType sourceType = AudioSourceType.NoBlend, AudioSource audioSource = null,
|
||||
CancellationTokenSource cancellationTokenSource = null)
|
||||
{
|
||||
if (!CheckClip(groupPartition, id)) return;
|
||||
|
||||
if (cancellationTokenSource == null)
|
||||
{
|
||||
cancellationTokenSource = new CancellationTokenSource();
|
||||
_cancellationTokenSources[new AudioPartitionIdPair(groupPartition, id)] = cancellationTokenSource;
|
||||
}
|
||||
|
||||
if (!audioSource)
|
||||
{
|
||||
audioSource = _pool.GetSource(sourceType);
|
||||
audioSource.GetComponent<AudioSourceFollower>().FollowTarget = parent;
|
||||
}
|
||||
|
||||
if (!audioSource.resource)
|
||||
{
|
||||
if (sourceType == AudioSourceType.NoBlend)
|
||||
audioSource.resource = GetSingleClip(groupPartition, id);
|
||||
if (sourceType == AudioSourceType.Full3D)
|
||||
audioSource.resource = GetLODClip(groupPartition, id, GetLODLevelOnPosition(parent.position));
|
||||
}
|
||||
|
||||
audioSource.outputAudioMixerGroup = _audioMixerGroups[audioGroupType];
|
||||
audioSource.Play();
|
||||
|
||||
var (isCanceled, _) = await UniTask.WaitUntilValueChanged(audioSource,
|
||||
_ => !audioSource.isPlaying,
|
||||
cancellationToken: cancellationTokenSource.Token).SuppressCancellationThrow();
|
||||
|
||||
if (isCanceled)
|
||||
{
|
||||
cancellationTokenSource.Dispose();
|
||||
|
||||
_pool.ReleaseSource(audioSource);
|
||||
|
||||
ReleaseCancellationSource(groupPartition, id);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
PlayRepeatedly(groupPartition, id, AudioGroupType.Music, parent, sourceType, audioSource,
|
||||
cancellationTokenSource)
|
||||
.Forget();
|
||||
}
|
||||
private AudioLODLevel GetLODLevelOnPosition(Vector3 position)
|
||||
{
|
||||
var distance = Vector3.Distance(position, _listener.transform.position);
|
||||
AudioLODLevel lodLevel;
|
||||
if (distance < 5f) lodLevel = AudioLODLevel.Interior;
|
||||
else if (distance < 20f) lodLevel = AudioLODLevel.Close;
|
||||
else if (distance < 50f) lodLevel = AudioLODLevel.Distant;
|
||||
else lodLevel = AudioLODLevel.Far;
|
||||
return lodLevel;
|
||||
}
|
||||
public AudioClip GetSingleClip(AudioPartition groupPartition, int id)
|
||||
{
|
||||
var reference = _audioFactory.ReferenceDictionary[groupPartition][id];
|
||||
var asSingle = reference as AudioReferenceSingle;
|
||||
|
||||
if(asSingle != null) return asSingle.Clip;
|
||||
return ((AudioReferenceRandom)reference).Clip;
|
||||
}
|
||||
|
||||
public AudioClip GetLODClip(AudioPartition groupPartition, int id, AudioLODLevel lodLevel)
|
||||
{
|
||||
switch (lodLevel)
|
||||
{
|
||||
case AudioLODLevel.Interior:
|
||||
return ((AudioReferenceLODed)_audioFactory.ReferenceDictionary[groupPartition][id]).Interior;
|
||||
case AudioLODLevel.Close:
|
||||
return ((AudioReferenceLODed)_audioFactory.ReferenceDictionary[groupPartition][id]).Close;
|
||||
case AudioLODLevel.Distant:
|
||||
return ((AudioReferenceLODed)_audioFactory.ReferenceDictionary[groupPartition][id]).Distant;
|
||||
case AudioLODLevel.Far:
|
||||
return ((AudioReferenceLODed)_audioFactory.ReferenceDictionary[groupPartition][id]).Far;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool CheckClip(AudioPartition groupPartition, int id)
|
||||
{
|
||||
return _audioFactory.ReferenceDictionary.ContainsKey(groupPartition)
|
||||
&& _audioFactory.ReferenceDictionary[groupPartition].ContainsKey(id);
|
||||
}
|
||||
|
||||
public async UniTask ApplyParameter(string effectName,
|
||||
float value,
|
||||
float time,
|
||||
Action callback = null,
|
||||
CancellationTokenSource cts = null)
|
||||
{
|
||||
_mixer.GetFloat(effectName, out float startValue);
|
||||
if (time == 0f) _mixer.SetFloat(effectName, value);
|
||||
else
|
||||
{
|
||||
// TODO Проверить отмену
|
||||
if (cts != null)
|
||||
await LMotion.Create(startValue, value, time)
|
||||
.WithOnComplete(callback)
|
||||
.WithOnCancel(() => _mixer.SetFloat(effectName, value))
|
||||
.Bind(x => _mixer.SetFloat(effectName, x))
|
||||
.ToUniTask(cts.Token);
|
||||
else
|
||||
await LMotion.Create(startValue, value, time)
|
||||
.WithOnComplete(callback)
|
||||
.WithOnCancel(() => _mixer.SetFloat(effectName, value))
|
||||
.Bind(x => _mixer.SetFloat(effectName, x))
|
||||
.ToUniTask();
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<AudioGroupType, CancellationTokenSource> _volumeCancellationSources = new();
|
||||
|
||||
// TODO Очередь принятия параметров
|
||||
public async UniTask ChangeGroupVolume(
|
||||
AudioGroupType groupType,
|
||||
float value,
|
||||
float time,
|
||||
Action callback = null,
|
||||
CancellationTokenSource cts = null)
|
||||
{
|
||||
if (_volumeCancellationSources.ContainsKey(groupType)) _volumeCancellationSources[groupType].Cancel();
|
||||
|
||||
_volumeCancellationSources[groupType] = cts ?? new CancellationTokenSource();
|
||||
|
||||
await ApplyParameter(_audioMixerGroups[groupType].name, Mathf.Log10(value == 0f ? 0.001f : value) * 20,
|
||||
time,
|
||||
callback, cts);
|
||||
}
|
||||
}
|
||||
|
||||
public struct AudioPartitionIdPair : IEquatable<AudioPartitionIdPair>
|
||||
{
|
||||
public AudioPartition AudioPartition;
|
||||
public int ID;
|
||||
|
||||
public AudioPartitionIdPair(AudioPartition audioPartition, int id)
|
||||
{
|
||||
AudioPartition = audioPartition;
|
||||
ID = id;
|
||||
}
|
||||
|
||||
public bool Equals(AudioPartitionIdPair other)
|
||||
{
|
||||
return AudioPartition == other.AudioPartition && ID == other.ID;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is AudioPartitionIdPair other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine((int)AudioPartition, ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8426c9e925a431aa73069c49d6a71c0
|
||||
timeCreated: 1769446252
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.Infrastructure.Audio
|
||||
{
|
||||
public class AudioSourceFollower : MonoBehaviour
|
||||
{
|
||||
[NonSerialized]
|
||||
public Transform FollowTarget;
|
||||
private void Update()
|
||||
{
|
||||
if(FollowTarget) transform.SetPositionAndRotation(FollowTarget.position, FollowTarget.rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f6debe328864a5a88509519354fcfc7
|
||||
timeCreated: 1776704631
|
||||
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Code.Infrastructure.Configs;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.Pool;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Code.Infrastructure.Audio
|
||||
{
|
||||
public interface IAudioSourcePoolInitializer
|
||||
{
|
||||
UniTask LoadConfigs();
|
||||
}
|
||||
|
||||
public interface IAudioSourcePool
|
||||
{
|
||||
AudioSource GetSource(AudioSourceType sourceType);
|
||||
void ReleaseSource(AudioSource source);
|
||||
}
|
||||
|
||||
public class AudioSourcePool : IAudioSourcePool, IAudioSourcePoolInitializer, IDisposable
|
||||
{
|
||||
// The pool holds plain GameObjects (you can swap this for any component type).
|
||||
private IObjectPool<AudioSource> _pool;
|
||||
private GameObject _sourcePrefab;
|
||||
|
||||
private Dictionary<AudioSourceType, AudioSourceSettings> _settings;
|
||||
|
||||
private string _configsLabel;
|
||||
private AsyncOperationHandle<IList<AudioSourceSettings>> _handles;
|
||||
private AssetReference _sourcePrefabConfigReference;
|
||||
private AsyncOperationHandle<AudioSourcePrefab> _prefabConfigHandle;
|
||||
private AsyncOperationHandle<GameObject> _prefabHandle;
|
||||
|
||||
public AudioSourcePool(IConfigLocator configLocator)
|
||||
{
|
||||
_configsLabel = configLocator.TryGetManyConfigAssetReferences<AudioSourceSettings>();
|
||||
_sourcePrefabConfigReference = configLocator.TryGetConfigAssetReference<AudioSourcePrefab>();
|
||||
}
|
||||
|
||||
public async UniTask LoadConfigs()
|
||||
{
|
||||
_prefabConfigHandle = Addressables.LoadAssetAsync<AudioSourcePrefab>(_sourcePrefabConfigReference);
|
||||
_handles = Addressables.LoadAssetsAsync<AudioSourceSettings>(_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
|
||||
);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (!source) return;
|
||||
|
||||
source.Stop();
|
||||
source.resource = null;
|
||||
source.GetComponent<AudioSourceFollower>().FollowTarget = 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);
|
||||
if (_sourcePrefab) Addressables.Release(_sourcePrefab);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 246ae406fabc4ca791673d3f38cfe1c5
|
||||
timeCreated: 1769448041
|
||||
@@ -0,0 +1,13 @@
|
||||
using Code.Infrastructure.Generators;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
|
||||
namespace Code.Infrastructure.Audio
|
||||
{
|
||||
[Config("Configs/Audio/AudioSourcePrefab")]
|
||||
[CreateAssetMenu(fileName = "AudioSourcePrefab", menuName = "Configs/Audio/AudioSourcePrefab")]
|
||||
public partial class AudioSourcePrefab : ScriptableObject
|
||||
{
|
||||
public AssetReference sourcePrefab;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e812cfdf0544504b47cfe48b621ac4e
|
||||
timeCreated: 1785852790
|
||||
@@ -0,0 +1,15 @@
|
||||
using Code.Infrastructure.Generators;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.Infrastructure.Audio
|
||||
{
|
||||
[CreateAssetMenu(fileName = "AudioSourceSettings", menuName = "Configs/Audio/AudioSourceSettings")]
|
||||
[Config("Configs/Audio/AudioSourceSettings", true)]
|
||||
public class AudioSourceSettings : ScriptableObject
|
||||
{
|
||||
// Дополнять по мере необходимости
|
||||
public float spatialBlend;
|
||||
|
||||
public AudioSourceType sourceType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fdbfd61d34d228d088f8be38588cd571
|
||||
Reference in new issue
Block a user