Second commit
This commit is contained in:
commit
36840f3ad4
999 files changed
+61364
No files matched your search
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 03ec58f5292de68cb94cf053c242d44a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,33 @@
|
||||
using Code.Infrastructure.Audio;
|
||||
using Code.Infrastructure.Camera;
|
||||
using Code.Infrastructure.Input;
|
||||
using Code.Infrastructure.Particles;
|
||||
using Code.Infrastructure.SceneLoader;
|
||||
using Code.Infrastructure.UI;
|
||||
using Code.Infrastructure.Volume;
|
||||
using Reflex.Attributes;
|
||||
|
||||
namespace Code.Infrastructure.App
|
||||
{
|
||||
public class AppStateMachineContext
|
||||
{
|
||||
[Inject] public ISceneContainerManager SceneContainerManager { get; set; }
|
||||
[Inject] public ISceneLoader SceneLoader { get; set; }
|
||||
[Inject] public IUIFactory UIFactory { get; set; }
|
||||
[Inject] public CameraStateMachine Camera { get; set; }
|
||||
[Inject] public ICameraFactory CameraFactory { get; set; }
|
||||
[Inject] public IAudioService AudioService { get; set; }
|
||||
[Inject] public IAudioFactory AudioFactory { get; set; }
|
||||
[Inject] public IInputService InputService { get; set; }
|
||||
[Inject] public IVolumeService VolumeService { get; set; }
|
||||
[Inject] public IParticleLoader ParticleLoader { get; set; }
|
||||
public AppStateMachine StateMachine { get; set; }
|
||||
}
|
||||
|
||||
[Generators.StateMachine(typeof(AppStateMachineContext),typeof(AppStates))]
|
||||
public partial class AppStateMachine
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5627248405434f028be921cb7916f823
|
||||
timeCreated: 1765572329
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc39950ba97e90dc09676d793e810207
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f30c588256ece023ad9a01d0f51d747
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using Code.Infrastructure.Configs;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Code.Infrastructure.Camera
|
||||
{
|
||||
public interface ICameraFactory
|
||||
{
|
||||
UniTask<CameraStateMachineContext.CameraLoadedData> CreateCameraStateMachine();
|
||||
void CreateCameraStateMachineRunner(CameraStateMachine cameraStateMachine);
|
||||
}
|
||||
|
||||
public class CameraFactory : IDisposable, ICameraFactory
|
||||
{
|
||||
private AsyncOperationHandle<GameCamera> _configHandle;
|
||||
private GameCamera _config;
|
||||
private AssetReference _gameCameraConfigReference;
|
||||
|
||||
private UnityEngine.Camera _camera;
|
||||
|
||||
public CameraFactory(IConfigLocator configLocator)
|
||||
{
|
||||
_gameCameraConfigReference = configLocator.TryGetConfigAssetReference<GameCamera>();
|
||||
}
|
||||
|
||||
public async UniTask<CameraStateMachineContext.CameraLoadedData> CreateCameraStateMachine()
|
||||
{
|
||||
_configHandle = Addressables.LoadAssetAsync<GameCamera>(_gameCameraConfigReference);
|
||||
await _configHandle.ToUniTask();
|
||||
_config = _configHandle.Result;
|
||||
|
||||
var cameraObject = new GameObject { name = "Camera" };
|
||||
|
||||
cameraObject.AddComponent<AudioListener>();
|
||||
_camera = cameraObject.AddComponent<UnityEngine.Camera>();
|
||||
|
||||
var universalCameraData = _camera.GetUniversalAdditionalCameraData();
|
||||
universalCameraData.renderPostProcessing = _config.PostProcessing;
|
||||
|
||||
_camera.fieldOfView = _config.FieldOfView;
|
||||
_camera.nearClipPlane = _config.NearClip;
|
||||
_camera.farClipPlane = _config.FarClip;
|
||||
_camera.clearFlags = _config.ClearFlags;
|
||||
|
||||
Object.DontDestroyOnLoad(cameraObject);
|
||||
|
||||
return new CameraStateMachineContext.CameraLoadedData(_camera);
|
||||
}
|
||||
|
||||
public void CreateCameraStateMachineRunner(CameraStateMachine cameraStateMachine)
|
||||
{
|
||||
var runner = _camera.AddComponent<CameraStateMachineRunner>();
|
||||
runner.SetStateMachine(cameraStateMachine);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Addressables.Release(_configHandle);
|
||||
Addressables.ReleaseInstance(_camera.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c05e01628b742be94dfd0bbbdfb87fa
|
||||
timeCreated: 1767302222
|
||||
@@ -0,0 +1,36 @@
|
||||
using Code.Infrastructure.Camera.CameraStates;
|
||||
using Code.Infrastructure.Input;
|
||||
using Reflex.Attributes;
|
||||
|
||||
namespace Code.Infrastructure.Camera
|
||||
{
|
||||
public enum CameraMode
|
||||
{
|
||||
None,
|
||||
Cleaner
|
||||
}
|
||||
|
||||
public class CameraStateMachineContext
|
||||
{
|
||||
public class CameraLoadedData
|
||||
{
|
||||
public UnityEngine.Camera Camera;
|
||||
|
||||
public CameraLoadedData(UnityEngine.Camera camera)
|
||||
{
|
||||
Camera = camera;
|
||||
}
|
||||
}
|
||||
public CameraLoadedData cameraLoadedData;
|
||||
[Inject] public IInputService InputService;
|
||||
}
|
||||
|
||||
[Generators.StateMachine(typeof(CameraStateMachineContext), typeof(CameraMode))]
|
||||
public partial class CameraStateMachine
|
||||
{
|
||||
public UnityEngine.Camera Camera => StateMachineContext.cameraLoadedData.Camera;
|
||||
|
||||
public void Update() => ((BaseCameraState)CurrentBaseState).Update();
|
||||
public void LateUpdate() => ((BaseCameraState)CurrentBaseState).LateUpdate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b220b02510c34ea08257cc951d949fd7
|
||||
timeCreated: 1761671720
|
||||
@@ -0,0 +1,15 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.Infrastructure.Camera
|
||||
{
|
||||
public class CameraStateMachineRunner : MonoBehaviour
|
||||
{
|
||||
private CameraStateMachine _cameraStateMachine;
|
||||
|
||||
public void SetStateMachine(CameraStateMachine cameraStateMachine) => _cameraStateMachine = cameraStateMachine;
|
||||
|
||||
private void Update() => _cameraStateMachine.Update();
|
||||
|
||||
private void LateUpdate() => _cameraStateMachine.LateUpdate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fc7c5cfabb3c4d60bb283444a1c9a53b
|
||||
timeCreated: 1775490818
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d7399e433dff42a185f62d79b546b059
|
||||
timeCreated: 1761671908
|
||||
@@ -0,0 +1,14 @@
|
||||
using Code.Infrastructure.StateMachine;
|
||||
|
||||
namespace Code.Infrastructure.Camera.CameraStates
|
||||
{
|
||||
public abstract class BaseCameraState : BaseState<CameraMode, CameraStateMachineContext>
|
||||
{
|
||||
protected BaseCameraState(CameraStateMachineContext context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
public abstract void Update();
|
||||
public abstract void LateUpdate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7901175f2c0646fda8741c4c269ce54b
|
||||
timeCreated: 1767127681
|
||||
@@ -0,0 +1,22 @@
|
||||
using Code.Infrastructure.Generators;
|
||||
|
||||
namespace Code.Infrastructure.Camera.CameraStates
|
||||
{
|
||||
[State(typeof(CameraMode),typeof(CameraStateMachineContext), ManualInherit = true)]
|
||||
public partial class NoneCameraState : BaseCameraState
|
||||
{
|
||||
public NoneCameraState(CameraStateMachineContext context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
public override CameraMode MyState => CameraMode.None;
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
}
|
||||
|
||||
public override void LateUpdate()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb14de48ff13488c8edb4431ac34e5be
|
||||
timeCreated: 1767301090
|
||||
@@ -0,0 +1,19 @@
|
||||
using Code.Infrastructure.Generators;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
namespace Code.Infrastructure.Camera
|
||||
{
|
||||
[CreateAssetMenu(fileName = "GameCamera", menuName = "Configs/Camera/CameraConfig")]
|
||||
[Config("Configs/Camera/CameraConfig")]
|
||||
public class GameCamera : ScriptableObject
|
||||
{
|
||||
public float FieldOfView;
|
||||
public float NearClip;
|
||||
public float FarClip;
|
||||
public bool PostProcessing;
|
||||
public bool RenderShadows;
|
||||
public CameraClearFlags ClearFlags;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8bbd78d2335f4e55945f1e56ff536200
|
||||
timeCreated: 1785830560
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 89adaad2de8d3daf1858a612b6dbedea
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
|
||||
namespace Code.Infrastructure.Configs
|
||||
{
|
||||
public interface IConfigLocator
|
||||
{
|
||||
AssetReference TryGetConfigAssetReference<T>();
|
||||
string TryGetManyConfigAssetReferences<T>();
|
||||
}
|
||||
public partial class ConfigLocator : IConfigLocator
|
||||
{
|
||||
private readonly Dictionary<Type, AssetReference> _singleConfigReferences = new();
|
||||
private readonly Dictionary<Type, string> _multipleConfigReferences = new();
|
||||
|
||||
private ConfigReferences _configReferences;
|
||||
|
||||
public AssetReference TryGetConfigAssetReference<T>() => _singleConfigReferences.GetValueOrDefault(typeof(T), null);
|
||||
public string TryGetManyConfigAssetReferences<T>() => _multipleConfigReferences.GetValueOrDefault(typeof(T), null);
|
||||
|
||||
partial void LoadConfigs();
|
||||
|
||||
public ConfigLocator()
|
||||
{
|
||||
LoadConfigReferences();
|
||||
|
||||
LoadConfigs();
|
||||
}
|
||||
|
||||
private void LoadConfigReferences()
|
||||
{
|
||||
_configReferences = Resources.Load<ConfigReferences>("ConfigReferences");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 23ccbd5d4d27ba1bdadaa94c0b564ff9
|
||||
@@ -0,0 +1,9 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.Infrastructure.Configs
|
||||
{
|
||||
[CreateAssetMenu(fileName = "ConfigReferences", menuName = "Configs/ConfigReferences")]
|
||||
public partial class ConfigReferences : ScriptableObject
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02095449ead629e7490547f6fa161935
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: daf23f61a4d244d5aec0ffcc9b9c8ac4
|
||||
timeCreated: 1785767213
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
|
||||
namespace Code.Infrastructure.Generators
|
||||
{
|
||||
public class StateMachine : Attribute
|
||||
{
|
||||
public Type StateMachineContextType;
|
||||
public Type StateMachineEnumType;
|
||||
|
||||
public StateMachine(Type stateMachineContextType, Type stateMachineEnumType)
|
||||
{
|
||||
StateMachineContextType = stateMachineContextType;
|
||||
StateMachineEnumType = stateMachineEnumType;
|
||||
}
|
||||
}
|
||||
|
||||
public class State : Attribute
|
||||
{
|
||||
public Type StateEnum;
|
||||
public Type StateMachineContext;
|
||||
public bool ManualInherit;
|
||||
|
||||
public State(Type stateEnum,Type stateMachineContext, bool manualInherit = false)
|
||||
{
|
||||
StateEnum = stateEnum;
|
||||
StateMachineContext = stateMachineContext;
|
||||
ManualInherit = manualInherit;
|
||||
}
|
||||
}
|
||||
public class Config : Attribute
|
||||
{
|
||||
public bool Multiple;
|
||||
public bool OnlyLocator;
|
||||
public string MenuName;
|
||||
public Config(string menuName, bool multiple = false, bool onlyLocator = false)
|
||||
{
|
||||
Multiple = multiple;
|
||||
MenuName = menuName;
|
||||
OnlyLocator = onlyLocator;
|
||||
}
|
||||
}
|
||||
public class SceneIdEnum : Attribute
|
||||
{
|
||||
}
|
||||
public class AudioMixerEnum : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f1a9e94d27945afad92fd36ee148c7d
|
||||
timeCreated: 1785767311
|
||||
@@ -0,0 +1,53 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityCodeGen;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Code.Infrastructure.Generators
|
||||
{
|
||||
[Generator]
|
||||
public class AudioMixerGenerator : ICodeGenerator
|
||||
{
|
||||
public void Execute(GeneratorContext context)
|
||||
{
|
||||
var Enum = TypeCache.GetTypesWithAttribute<AudioMixerEnum>();
|
||||
|
||||
var initializedCode = "";
|
||||
var serviceCode = "";
|
||||
var interfacesUsingsCode = $@"
|
||||
// using Code.Infrastructure.Audio;
|
||||
";
|
||||
|
||||
foreach (var en in Enum)
|
||||
{
|
||||
if (!en.IsEnum) return;
|
||||
|
||||
var members = en.GetFields();
|
||||
|
||||
foreach (var member in members)
|
||||
{
|
||||
if (member.FieldType.FullName != en.FullName) continue;
|
||||
initializedCode += $@"
|
||||
_audioMixerGroups.Add({en.FullName}.{member.Name}, _mixer.FindMatchingGroups(""{member.Name}"")[0]);
|
||||
|
||||
";
|
||||
}
|
||||
|
||||
serviceCode += $@"
|
||||
#pragma warning disable CS0105
|
||||
{interfacesUsingsCode}
|
||||
#pragma warning restore CS0105
|
||||
namespace Code.Infrastructure.Audio{{
|
||||
public partial class AudioService
|
||||
{{
|
||||
private partial void InitializeMixer(){{
|
||||
{initializedCode}
|
||||
}}
|
||||
}}
|
||||
}}";
|
||||
}
|
||||
|
||||
context.AddCode($"AudioMixer.Generated.cs", serviceCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d4380f9fef404347be28b7e95548c008
|
||||
timeCreated: 1785767222
|
||||
@@ -0,0 +1,79 @@
|
||||
#if UNITY_EDITOR
|
||||
using System.Linq;
|
||||
using Unity.VisualScripting;
|
||||
using UnityCodeGen;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Code.Infrastructure.Generators
|
||||
{
|
||||
[Generator]
|
||||
public class ConfigGenerator : ICodeGenerator
|
||||
{
|
||||
public void Execute(GeneratorContext context)
|
||||
{
|
||||
var configs = TypeCache.GetTypesWithAttribute<Config>();
|
||||
|
||||
var configsCode = "";
|
||||
var configAddCode = "";
|
||||
var configReferencesCode = "";
|
||||
var configReferencesUsingsCode = "";
|
||||
var configOfConfigsCode = "";
|
||||
|
||||
foreach (var cfg in configs.OrderByDescending(o => o.Name))
|
||||
{
|
||||
var attribute = cfg.GetAttribute<Config>();
|
||||
|
||||
configReferencesUsingsCode += $"using {cfg.Namespace};\n";
|
||||
|
||||
if (attribute.Multiple)
|
||||
{
|
||||
configReferencesCode += $"public string {cfg.Name};\n";
|
||||
configAddCode += $@"
|
||||
_multipleConfigReferences.Add(typeof({cfg.FullName}), _configReferences.{cfg.Name});
|
||||
";
|
||||
}
|
||||
else
|
||||
{
|
||||
configReferencesCode += $"public AssetReference {cfg.Name};\n";
|
||||
configAddCode += $@"
|
||||
_singleConfigReferences.Add(typeof({cfg.FullName}), _configReferences.{cfg.Name});
|
||||
";
|
||||
}
|
||||
}
|
||||
configsCode += $@"
|
||||
#pragma warning disable CS0105
|
||||
{configReferencesUsingsCode}
|
||||
#pragma warning restore CS0105
|
||||
|
||||
namespace Code.Infrastructure.Configs
|
||||
{{
|
||||
|
||||
public partial class ConfigLocator
|
||||
{{
|
||||
partial void LoadConfigs()
|
||||
{{
|
||||
{configAddCode}
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
";
|
||||
context.AddCode($"ConfigLocator.Generated.cs", configsCode);
|
||||
|
||||
configOfConfigsCode += $@"
|
||||
#pragma warning disable CS0105
|
||||
{configReferencesUsingsCode}
|
||||
using UnityEngine.AddressableAssets;
|
||||
#pragma warning restore CS0105
|
||||
|
||||
namespace Code.Infrastructure.Configs{{
|
||||
public partial class ConfigReferences
|
||||
{{
|
||||
{configReferencesCode}
|
||||
}}
|
||||
}}
|
||||
";
|
||||
context.AddCode($"ConfigReferences.Generated.cs", configOfConfigsCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4dd23f7e41734b9d86b74032d5f7f71d
|
||||
timeCreated: 1785767609
|
||||
@@ -0,0 +1,105 @@
|
||||
#if UNITY_EDITOR
|
||||
using System.Linq;
|
||||
using Unity.VisualScripting;
|
||||
using UnityCodeGen;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Code.Infrastructure.Generators
|
||||
{
|
||||
[Generator]
|
||||
public class StateMachineGenerator : ICodeGenerator
|
||||
{
|
||||
public void Execute(GeneratorContext context)
|
||||
{
|
||||
GenerateStates(context);
|
||||
GenerateStateMachine(context);
|
||||
}
|
||||
|
||||
private void GenerateStateMachine(GeneratorContext context)
|
||||
{
|
||||
var stateMachines = TypeCache.GetTypesWithAttribute<StateMachine>();
|
||||
|
||||
foreach (var sm in stateMachines)
|
||||
{
|
||||
var attribute = sm.GetAttribute<StateMachine>();
|
||||
|
||||
var states = TypeCache.GetTypesWithAttribute<State>()
|
||||
.Where(st =>
|
||||
st.GetAttribute<State>().StateMachineContext.FullName ==
|
||||
attribute.StateMachineContextType.FullName);
|
||||
|
||||
var statesCode = "";
|
||||
var statesNamespacesCode = "";
|
||||
|
||||
foreach (var st in states)
|
||||
{
|
||||
statesNamespacesCode += $@"
|
||||
using {st.Namespace};
|
||||
";
|
||||
statesCode += $@"
|
||||
var state{st.Name} = new {st.Name}(StateMachineContext);
|
||||
States.Add(state{st.Name}.MyState, state{st.Name});
|
||||
";
|
||||
|
||||
}
|
||||
|
||||
var stateMachineCode =
|
||||
$@"
|
||||
// <auto-generated/>
|
||||
#pragma warning disable CS0105
|
||||
using Code.Infrastructure.StateMachine;
|
||||
{statesNamespacesCode}
|
||||
#pragma warning restore CS0105
|
||||
namespace {sm.Namespace}
|
||||
{{
|
||||
|
||||
public partial class {sm.Name} : AbstractStateMachine<{attribute.StateMachineEnumType.FullName},{attribute.StateMachineContextType.FullName}>
|
||||
{{
|
||||
|
||||
public {sm.Name}({attribute.StateMachineContextType.FullName} context) : base(context)
|
||||
{{
|
||||
{statesCode}
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
|
||||
";
|
||||
|
||||
context.AddCode($"{sm.FullName}.Generated.cs", stateMachineCode);
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateStates(GeneratorContext context)
|
||||
{
|
||||
var states = TypeCache.GetTypesWithAttribute<State>();
|
||||
|
||||
foreach (var st in states)
|
||||
{
|
||||
var attribute = st.GetAttribute<State>();
|
||||
|
||||
var genetics = "";
|
||||
if (!attribute.ManualInherit)
|
||||
genetics =
|
||||
$@" : BaseState<{attribute.StateEnum.FullName}, {attribute.StateMachineContext.FullName}>";
|
||||
|
||||
var statesCode = $@"
|
||||
|
||||
// retard-generated
|
||||
|
||||
using Code.Infrastructure.StateMachine;
|
||||
|
||||
namespace {st.Namespace}
|
||||
{{
|
||||
public partial class {st.Name} {genetics}
|
||||
{{
|
||||
}}
|
||||
}}
|
||||
";
|
||||
|
||||
|
||||
context.AddCode($"{st.FullName}.Generated.cs", statesCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20bfbd0b7c5840c8a5f351cff2be58e0
|
||||
timeCreated: 1785767485
|
||||
@@ -0,0 +1,28 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Code.Infrastructure.Generators
|
||||
{
|
||||
public static class TypeExtensions
|
||||
{
|
||||
public static string GetFriendlyName(this Type type)
|
||||
{
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
if (type.FullName != null)
|
||||
{
|
||||
string name = type.FullName.Split('`')[0];
|
||||
|
||||
var genericArgs = type.GetGenericArguments()
|
||||
.Select(t => t.GetFriendlyName());
|
||||
|
||||
return $"{name}<{string.Join(", ", genericArgs)}>";
|
||||
}
|
||||
}
|
||||
|
||||
return type.FullName ?? type.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9ad74e45abc4260bee92ebe33d28602
|
||||
timeCreated: 1785767387
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bcb18c4105d206e10b66ddcac7f7eee7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using Code.Infrastructure.Configs;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
namespace Code.Infrastructure.Input
|
||||
{
|
||||
public interface IInputService
|
||||
{
|
||||
void EnableMap(ActionMappings actionMap);
|
||||
void DisableMap(ActionMappings actionMap);
|
||||
void BindActionPerformed(int mapId, int actionId, Action<InputAction.CallbackContext> action);
|
||||
void BindActionCancelled(int mapId, int actionId, Action<InputAction.CallbackContext> action);
|
||||
void UnbindActionPerformed(int mapId, int actionId, Action<InputAction.CallbackContext> action);
|
||||
void UnbindActionCancelled(int mapId, int actionId, Action<InputAction.CallbackContext> action);
|
||||
}
|
||||
|
||||
public interface IInputServiceInitializer
|
||||
{
|
||||
UniTask LoadConfigs();
|
||||
}
|
||||
|
||||
public class InputService : IInputService, IInputServiceInitializer
|
||||
{
|
||||
private InputActionAsset _inputActionAsset;
|
||||
|
||||
private AsyncOperationHandle<InputServiceConfig> _configHandle;
|
||||
private readonly AssetReference _configReference;
|
||||
|
||||
public InputService(IConfigLocator configLocator)
|
||||
{
|
||||
_configReference = configLocator.TryGetConfigAssetReference<InputServiceConfig>();
|
||||
}
|
||||
|
||||
public async UniTask LoadConfigs()
|
||||
{
|
||||
_configHandle = Addressables.LoadAssetAsync<InputServiceConfig>(_configReference);
|
||||
|
||||
await _configHandle.ToUniTask();
|
||||
|
||||
_inputActionAsset = _configHandle.Result.inputActionAsset;
|
||||
}
|
||||
|
||||
public void EnableMap(ActionMappings actionMap) => _inputActionAsset.actionMaps[(int)actionMap].Enable();
|
||||
public void DisableMap(ActionMappings actionMap) => _inputActionAsset.actionMaps[(int)actionMap].Disable();
|
||||
|
||||
public void BindActionPerformed(int mapId, int actionId, Action<InputAction.CallbackContext> action) => _inputActionAsset
|
||||
.actionMaps[mapId].actions[actionId].performed += action;
|
||||
|
||||
public void BindActionCancelled(int mapId, int actionId, Action<InputAction.CallbackContext> action) => _inputActionAsset
|
||||
.actionMaps[mapId].actions[actionId].canceled += action;
|
||||
|
||||
public void UnbindActionPerformed(int mapId, int actionId, Action<InputAction.CallbackContext> action) =>
|
||||
_inputActionAsset
|
||||
.actionMaps[mapId].actions[actionId].performed -= action;
|
||||
|
||||
public void UnbindActionCancelled(int mapId, int actionId, Action<InputAction.CallbackContext> action) =>
|
||||
_inputActionAsset
|
||||
.actionMaps[mapId].actions[actionId].canceled -= action;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2755e684d68a4f79994f0f92e8327a73
|
||||
timeCreated: 1766612399
|
||||
@@ -0,0 +1,13 @@
|
||||
using Code.Infrastructure.Generators;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace Code.Infrastructure.Input
|
||||
{
|
||||
[Config("Configs/InputService")]
|
||||
[CreateAssetMenu(fileName = "InputService", menuName = "Configs/InputService")]
|
||||
public class InputServiceConfig : ScriptableObject
|
||||
{
|
||||
public InputActionAsset inputActionAsset;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a3fe014c5e7645708654c530a664a11d
|
||||
timeCreated: 1785871062
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3135ae3e535d681ecb827013e589ed05
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.Infrastructure.Loadings
|
||||
{
|
||||
public class Loading
|
||||
{
|
||||
private int _progress;
|
||||
public bool Completed { get; private set; }
|
||||
private Action _completed;
|
||||
private Action<Loading> _loadProgress;
|
||||
|
||||
public Loading(int externalMaxProgress, Action completed, Action<Loading> loadProgress = null)
|
||||
{
|
||||
_progress = externalMaxProgress;
|
||||
MaxProgress = externalMaxProgress;
|
||||
|
||||
_completed = completed;
|
||||
_loadProgress = loadProgress;
|
||||
}
|
||||
/// <summary>
|
||||
/// Получаем текущее значение прогресса
|
||||
/// </summary>
|
||||
public int CurrentProgress => _progress;
|
||||
public int MaxProgress { get; private set; }
|
||||
/// <summary>
|
||||
/// Сдвигаем прогресс загрузки на 1
|
||||
/// </summary>
|
||||
public void DoProgress()
|
||||
{
|
||||
_progress--;
|
||||
_loadProgress?.Invoke(this);
|
||||
|
||||
if(_progress == 0) // Зачем инвертировать, если так читается лучше, райдер соси
|
||||
{
|
||||
Completed = true;
|
||||
_completed?.Invoke();
|
||||
|
||||
_cts?.Dispose();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Ждём заполнения шкалы прогресса
|
||||
/// </summary>
|
||||
public async UniTask FreezeTillDone()
|
||||
{
|
||||
_cts ??= new CancellationTokenSource();
|
||||
try
|
||||
{
|
||||
await UniTask.WaitUntil(this, _ => Completed)
|
||||
.AttachExternalCancellation(_cts.Token)
|
||||
.SuppressCancellationThrow();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Привязать асинхронный процесс к выполнению загрузки. Сдвигает MaxProgress на 1
|
||||
/// </summary>
|
||||
public Loading AttachProcess(Func<UniTask> process)
|
||||
{
|
||||
AddLoadProcess(process).Forget();
|
||||
return this;
|
||||
}
|
||||
private CancellationTokenSource _cts;
|
||||
/// <summary>
|
||||
/// Привязать асинхронный процесс к выполнению загрузки. Сдвигает MaxProgress на 1
|
||||
/// </summary>
|
||||
// Originally written by MrTorex https://github.com/MrTorex
|
||||
public async UniTaskVoid AddLoadProcess(Func<UniTask> loader)
|
||||
{
|
||||
_cts ??= new CancellationTokenSource();
|
||||
|
||||
MaxProgress++;
|
||||
_progress++;
|
||||
try
|
||||
{
|
||||
await loader().AttachExternalCancellation(_cts.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
DoProgress();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1707a55b78d4218b1557d42adb7d961
|
||||
timeCreated: 1766483205
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5403b8aeab91da411a1a1d04dc34d392
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using Code.Infrastructure.Generators;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
|
||||
namespace Code.Infrastructure.Particles
|
||||
{
|
||||
[CreateAssetMenu(fileName = "ParticleInfo", menuName = "Configs/Particles/ParticleInfo")]
|
||||
[Config("Configs/Particles/ParticleInfo", true)]
|
||||
public class ParticleInfo : ScriptableObject
|
||||
{
|
||||
public AssetReference particleSystem;
|
||||
public int particleId;
|
||||
public ParticlePartition particlePartition;
|
||||
public int CountInPool;
|
||||
public ParticlePartitionIDPair PartitionIDPair;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct ParticlePartitionIDPair : IEquatable<ParticlePartitionIDPair>
|
||||
{
|
||||
public ParticlePartition ParticlePartition;
|
||||
public int ID;
|
||||
|
||||
public ParticlePartitionIDPair(ParticlePartition particlePartition, int id)
|
||||
{
|
||||
ParticlePartition = particlePartition;
|
||||
ID = id;
|
||||
}
|
||||
|
||||
public bool Equals(ParticlePartitionIDPair other)
|
||||
{
|
||||
return ParticlePartition == other.ParticlePartition && ID == other.ID;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ParticlePartitionIDPair other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine((int)ParticlePartition, ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b7578a949deb96ef808e8b285d8e68e
|
||||
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Code.Infrastructure.Configs;
|
||||
using Code.Infrastructure.Utils;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
namespace Code.Infrastructure.Particles
|
||||
{
|
||||
public interface IParticleLoader
|
||||
{
|
||||
UniTask LoadParticleSystems(ParticlePartition partition);
|
||||
void ReleaseParticles(ParticlePartition partition);
|
||||
void Dispose();
|
||||
}
|
||||
|
||||
public interface IParticleLoaderInitializer
|
||||
{
|
||||
UniTask LoadConfigs();
|
||||
}
|
||||
|
||||
public class ParticleLoader : IParticleLoader, IParticleLoaderInitializer, IDisposable
|
||||
{
|
||||
private readonly string _label;
|
||||
private Dictionary<ParticlePartition, Dictionary<int, ParticleInfo>> ParticleInfos { get; } = new();
|
||||
|
||||
private readonly Dictionary<ParticlePartition, Dictionary<int, AsyncOperationHandle<GameObject>>>
|
||||
_particleSystemHandles = new();
|
||||
|
||||
private AsyncOperationHandle<IList<ParticleInfo>> _configsHandle;
|
||||
|
||||
private readonly IParticlePoolManagerForFabric _poolManagerForFabric;
|
||||
|
||||
public ParticleLoader(IConfigLocator configLocator, IParticlePoolManagerForFabric poolManagerForFabric)
|
||||
{
|
||||
_poolManagerForFabric = poolManagerForFabric;
|
||||
_label = configLocator.TryGetManyConfigAssetReferences<ParticleInfo>();
|
||||
|
||||
for (var i = 0; i < typeof(ParticlePartition).GetEnumValues().Length; i++)
|
||||
{
|
||||
ParticleInfos.Add((ParticlePartition)i, new Dictionary<int, ParticleInfo>());
|
||||
_particleSystemHandles.Add((ParticlePartition)i,
|
||||
new Dictionary<int, AsyncOperationHandle<GameObject>>());
|
||||
}
|
||||
}
|
||||
|
||||
public async UniTask LoadConfigs()
|
||||
{
|
||||
if (!await AddressablesUtilities.CheckLabelExists(_label)) return;
|
||||
|
||||
_configsHandle = Addressables.LoadAssetsAsync<ParticleInfo>(_label);
|
||||
|
||||
await _configsHandle.ToUniTask();
|
||||
|
||||
foreach (var config in _configsHandle.Result)
|
||||
ParticleInfos[config.particlePartition].Add(config.particleId, config);
|
||||
}
|
||||
|
||||
public async UniTask LoadParticleSystems(ParticlePartition partition)
|
||||
{
|
||||
foreach (var particleInfo in ParticleInfos[partition])
|
||||
if (!_particleSystemHandles[partition].ContainsKey(particleInfo.Key))
|
||||
_particleSystemHandles[partition].Add(particleInfo.Key,
|
||||
Addressables.LoadAssetAsync<GameObject>(particleInfo.Value.particleSystem));
|
||||
else
|
||||
_particleSystemHandles[partition][particleInfo.Key] =
|
||||
Addressables.LoadAssetAsync<GameObject>(particleInfo.Value.particleSystem);
|
||||
|
||||
foreach (var handle in _particleSystemHandles[partition])
|
||||
await handle.Value.ToUniTask();
|
||||
|
||||
foreach (var particleInfo in ParticleInfos[partition])
|
||||
_poolManagerForFabric.CreatePoolOfParticles(
|
||||
particleInfo.Value,
|
||||
_particleSystemHandles[partition][particleInfo.Key].Result.GetComponent<ParticleSystem>()
|
||||
);
|
||||
}
|
||||
|
||||
public void ReleaseParticles(ParticlePartition partition)
|
||||
{
|
||||
if (_particleSystemHandles[partition].Count == 0) return;
|
||||
|
||||
foreach (var handle in _particleSystemHandles[partition])
|
||||
{
|
||||
_poolManagerForFabric.DeletePoolOfParticles(partition, handle.Key);
|
||||
Addressables.Release(handle.Value);
|
||||
}
|
||||
|
||||
_particleSystemHandles[partition].Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var handles in _particleSystemHandles)
|
||||
{
|
||||
foreach (var handle in handles.Value)
|
||||
{
|
||||
_poolManagerForFabric.DeletePoolOfParticles(handles.Key, handle.Key);
|
||||
handle.Value.Release();
|
||||
}
|
||||
}
|
||||
|
||||
_particleSystemHandles.Clear();
|
||||
|
||||
if (_configsHandle.IsValid()) _configsHandle.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e0558ddc52d6d69e825887b8be94f10
|
||||
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Reflex.Attributes;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Pool;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Code.Infrastructure.Particles
|
||||
{
|
||||
public class ParticlePool
|
||||
{
|
||||
public IObjectPool<ParticleSystem> Pool;
|
||||
|
||||
private ParticleInfo _particleInfoConfig;
|
||||
private ParticleSystem _particleSystemPrefab;
|
||||
|
||||
public ParticlePool(ParticleInfo particleInfoConfig, ParticleSystem particleSystemPrefab)
|
||||
{
|
||||
_particleSystemPrefab = particleSystemPrefab;
|
||||
_particleInfoConfig = particleInfoConfig;
|
||||
|
||||
Pool = new ObjectPool<ParticleSystem>(
|
||||
createFunc: CreateItem,
|
||||
// actionOnGet: OnGet,
|
||||
actionOnRelease: OnRelease,
|
||||
actionOnDestroy: OnDestroyItem,
|
||||
collectionCheck: true, // helps catch double-release mistakes
|
||||
defaultCapacity: particleInfoConfig.CountInPool,
|
||||
maxSize: 20
|
||||
);
|
||||
}
|
||||
|
||||
public ParticleSystem Get() => Pool.Get();
|
||||
public void Release(ParticleSystem particleSystem) => Pool.Release(particleSystem);
|
||||
|
||||
// Creates a new pooled GameObject the first time (and whenever the pool needs more).
|
||||
private ParticleSystem CreateItem()
|
||||
{
|
||||
var particleSystem = Object
|
||||
.Instantiate(_particleSystemPrefab) // Он уже загружен
|
||||
.GetComponent<ParticleSystem>();
|
||||
particleSystem.name = "PooledParticleSystem";
|
||||
Object.DontDestroyOnLoad(particleSystem);
|
||||
return particleSystem;
|
||||
}
|
||||
|
||||
// Called when an item is taken from the pool.
|
||||
private void OnGet(ParticleSystem source)
|
||||
{
|
||||
// Настроить если надо
|
||||
}
|
||||
|
||||
// Called when an item is returned to the pool.
|
||||
private void OnRelease(ParticleSystem particleSystem)
|
||||
{
|
||||
if (!particleSystem) return;
|
||||
|
||||
particleSystem.Stop();
|
||||
|
||||
// Настроить если надо
|
||||
|
||||
}
|
||||
|
||||
// Called when the pool decides to destroy an item (e.g., above max size).
|
||||
private void OnDestroyItem(ParticleSystem source)
|
||||
{
|
||||
Object.Destroy(source.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IParticlePoolManagerForService
|
||||
{
|
||||
ParticleSystem GetParticleSystem(ParticlePartition partition, int id);
|
||||
void ReleaseParticleSystem(ParticlePartition partition, int id, ParticleSystem particleSystem);
|
||||
}
|
||||
|
||||
public interface IParticlePoolManagerForFabric
|
||||
{
|
||||
void CreatePoolOfParticles(ParticleInfo particleInfoConfig, ParticleSystem particleSystemPrefab);
|
||||
void DeletePoolOfParticles(ParticlePartition partition, int id);
|
||||
}
|
||||
|
||||
public class ParticlePoolManager : IParticlePoolManagerForService, IParticlePoolManagerForFabric
|
||||
{
|
||||
// The pool holds plain GameObjects (you can swap this for any component type).
|
||||
private Dictionary<ParticlePartition, Dictionary<int, ParticlePool>> _poolDictionary = new();
|
||||
|
||||
public void CreatePoolOfParticles(ParticleInfo particleInfoConfig, ParticleSystem particleSystemPrefab)
|
||||
{
|
||||
if(!_poolDictionary.ContainsKey(particleInfoConfig.particlePartition))
|
||||
_poolDictionary[particleInfoConfig.particlePartition] = new();
|
||||
|
||||
_poolDictionary[particleInfoConfig.particlePartition]
|
||||
.Add(particleInfoConfig.particleId, new ParticlePool(particleInfoConfig, particleSystemPrefab));
|
||||
}
|
||||
public void DeletePoolOfParticles(ParticlePartition partition, int id)
|
||||
{
|
||||
_poolDictionary[partition][id].Pool.Clear();
|
||||
_poolDictionary[partition].Remove(id);
|
||||
}
|
||||
|
||||
public ParticleSystem GetParticleSystem(ParticlePartition partition, int id)
|
||||
{
|
||||
var source = _poolDictionary[partition][id].Get();
|
||||
|
||||
// Настроить если надо
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
public void ReleaseParticleSystem(ParticlePartition partition, int id, ParticleSystem particleSystem)
|
||||
{
|
||||
if(_poolDictionary[partition].ContainsKey(id)) _poolDictionary[partition][id].Release(particleSystem);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21ce106fcb48566dc92ae832b09fa268
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Reflex.Attributes;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.Infrastructure.Particles
|
||||
{
|
||||
public interface IParticleService
|
||||
{
|
||||
UniTaskVoid EmitOneShot(ParticlePartitionIDPair pair, int particleCount, Vector3 position, Quaternion rotation);
|
||||
UniTaskVoid EmitTillCancelled(ParticlePartitionIDPair pair, CancellationToken token, Vector3 position, Quaternion rotation);
|
||||
}
|
||||
|
||||
public class ParticleService : IParticleService
|
||||
{
|
||||
[Inject] private IParticlePoolManagerForService _poolManagerForService;
|
||||
[Inject] private IParticleLoader _particleLoader;
|
||||
|
||||
public async UniTaskVoid EmitOneShot(ParticlePartitionIDPair pair, int particleCount, Vector3 position, Quaternion rotation)
|
||||
{
|
||||
var particleSystem = _poolManagerForService.GetParticleSystem(pair.ParticlePartition, pair.ID);
|
||||
|
||||
particleSystem.transform.position = position;
|
||||
particleSystem.transform.rotation = rotation;
|
||||
|
||||
particleSystem.Emit(particleCount);
|
||||
|
||||
await UniTask.WaitWhile(() => particleSystem.particleCount > 0);
|
||||
|
||||
_poolManagerForService.ReleaseParticleSystem(pair.ParticlePartition, pair.ID, particleSystem);
|
||||
}
|
||||
public async UniTaskVoid EmitTillCancelled(ParticlePartitionIDPair pair, CancellationToken token, Vector3 position, Quaternion rotation)
|
||||
{
|
||||
var particleSystem = _poolManagerForService.GetParticleSystem(pair.ParticlePartition, pair.ID);
|
||||
|
||||
particleSystem.transform.position = position;
|
||||
particleSystem.transform.rotation = rotation;
|
||||
|
||||
particleSystem.Play();
|
||||
try
|
||||
{
|
||||
await UniTask.WaitUntilCanceled(cancellationToken: token);
|
||||
}
|
||||
catch
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
particleSystem.Stop();
|
||||
|
||||
_poolManagerForService.ReleaseParticleSystem(pair.ParticlePartition, pair.ID, particleSystem);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a4f2bf7e8fd80581b714c3d32e47f20
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e1152e782dc9c46c5bc317f1ba499fd6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Code.Infrastructure.Configs;
|
||||
using Code.Infrastructure.Utils;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using ObservableCollections;
|
||||
using Reflex.Core;
|
||||
using UnityEngine;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using UnityEngine.ResourceManagement.ResourceProviders;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Code.Infrastructure.SceneLoader
|
||||
{
|
||||
using UnityEngine.AddressableAssets;
|
||||
|
||||
public interface ISceneLoader
|
||||
{
|
||||
UniTask LoadSceneAsync(SceneId id, LoadSceneMode loadMode, bool setActive, Action<Scene, ContainerBuilder> extraInstall = null);
|
||||
Scene GetScene(SceneId id);
|
||||
UniTask UnloadSceneAsync(SceneId id);
|
||||
}
|
||||
|
||||
public interface ISceneContainerManager
|
||||
{
|
||||
ObservableDictionary<SceneId, Container> ScenesContainers { get; }
|
||||
}
|
||||
|
||||
public interface ICustomSceneManagerInitializer
|
||||
{
|
||||
UniTask LoadConfigs();
|
||||
}
|
||||
|
||||
public class CustomCustomSceneManager : IDisposable, ISceneLoader, ISceneContainerManager,
|
||||
ICustomSceneManagerInitializer
|
||||
{
|
||||
private AsyncOperationHandle<IList<SceneInfo>> _loadHandle;
|
||||
private AsyncOperationHandle<IList<SceneInfo>> _configHandle;
|
||||
private readonly string _configsLabel;
|
||||
|
||||
public ObservableDictionary<SceneId, Container> ScenesContainers { get; } = new();
|
||||
|
||||
private Dictionary<SceneId, SceneInfo> _sceneInfos = new();
|
||||
private Dictionary<SceneId, AsyncOperationHandle<SceneInstance>> _loadedScenes = new();
|
||||
|
||||
public CustomCustomSceneManager(IConfigLocator configLocator)
|
||||
{
|
||||
_configsLabel = configLocator.TryGetManyConfigAssetReferences<SceneInfo>();
|
||||
}
|
||||
|
||||
// Киська
|
||||
// TODO При загрузке нескольких сцен сразу может быть неприятность
|
||||
public async UniTask LoadSceneAsync(
|
||||
SceneId id,
|
||||
LoadSceneMode loadMode,
|
||||
bool setActive,
|
||||
Action<Scene, ContainerBuilder> extraInstall = null)
|
||||
{
|
||||
if (loadMode == LoadSceneMode.Single)
|
||||
for (var i = 0; i < _loadedScenes.Count; i++)
|
||||
UnloadSceneAsync(_loadedScenes.Keys.ToList()[i]).Forget();
|
||||
|
||||
if (extraInstall != null) ContainerScope.OnSceneContainerBuilding += extraInstall;
|
||||
|
||||
_loadedScenes.Add(id, Addressables.LoadSceneAsync(_sceneInfos[id].SceneAsset, LoadSceneMode.Additive));
|
||||
|
||||
await _loadedScenes[id];
|
||||
|
||||
if (extraInstall != null) ContainerScope.OnSceneContainerBuilding -= extraInstall;
|
||||
|
||||
if (setActive) SceneManager.SetActiveScene(_loadedScenes[id].Result.Scene);
|
||||
}
|
||||
|
||||
public Scene GetScene(SceneId id) => _loadedScenes[id].Result.Scene;
|
||||
|
||||
public async UniTask UnloadSceneAsync(SceneId id)
|
||||
{
|
||||
await Addressables.UnloadSceneAsync(_loadedScenes[id]);
|
||||
_loadedScenes.Remove(id);
|
||||
}
|
||||
|
||||
|
||||
public async UniTask LoadConfigs()
|
||||
{
|
||||
if (!await AddressablesUtilities.CheckLabelExists(_configsLabel)) return;
|
||||
|
||||
_configHandle =
|
||||
Addressables.LoadAssetsAsync<SceneInfo>(_configsLabel, OnSceneConfigLoaded,
|
||||
Addressables.MergeMode.Union);
|
||||
|
||||
await _configHandle.ToUniTask();
|
||||
}
|
||||
|
||||
private void OnSceneConfigLoaded(SceneInfo sceneInfoConfig)
|
||||
{
|
||||
SceneInfo sceneInfo = ScriptableObject.CreateInstance<SceneInfo>();
|
||||
sceneInfo.SceneId = sceneInfoConfig.SceneId;
|
||||
sceneInfo.SceneAsset = sceneInfoConfig.SceneAsset;
|
||||
|
||||
_sceneInfos.Add(sceneInfoConfig.SceneId, sceneInfo);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if(_configHandle.IsValid()) Addressables.Release(_configHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 760817ac86164799ac58327509052257
|
||||
timeCreated: 1766325358
|
||||
@@ -0,0 +1,14 @@
|
||||
using Code.Infrastructure.Generators;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
|
||||
namespace Code.Infrastructure.SceneLoader
|
||||
{
|
||||
[Config("Configs/SceneConfig", true)]
|
||||
[CreateAssetMenu(fileName = "SceneInfo", menuName = "Configs/SceneConfig")]
|
||||
public class SceneInfo : ScriptableObject
|
||||
{
|
||||
public AssetReference SceneAsset;
|
||||
public SceneId SceneId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f8b44b2bf5414b47a94fc1633e021e94
|
||||
timeCreated: 1785871561
|
||||
@@ -0,0 +1,9 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.Infrastructure.SceneLoader
|
||||
{
|
||||
[CreateAssetMenu(fileName = "ScenesAssetReferences", menuName = "Configs/ScenesAssetReferences")]
|
||||
public class ScenesAssetReferences : ScriptableObject
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9759979f397044a5bcb03ded0d1c9561
|
||||
timeCreated: 1766325404
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7364161b6b6945d3866be9f0445777a5
|
||||
timeCreated: 1785766193
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace Code.Infrastructure.StateMachine
|
||||
{
|
||||
public class AbstractStateMachine<TStateEnum, TStateMachineContext>
|
||||
where TStateMachineContext : new()
|
||||
{
|
||||
protected AbstractStateMachine(TStateMachineContext stateMachineContext)
|
||||
{
|
||||
StateMachineContext = stateMachineContext;
|
||||
}
|
||||
|
||||
public TStateEnum CurrentState { get; protected set; }
|
||||
public TStateEnum NextState { get; protected set; }
|
||||
public BaseState<TStateEnum, TStateMachineContext> CurrentBaseState { get; private set; }
|
||||
private BaseState<TStateEnum, TStateMachineContext> _previousBaseState;
|
||||
|
||||
protected Dictionary<TStateEnum, BaseState<TStateEnum, TStateMachineContext>> States = new();
|
||||
protected Dictionary<TStateEnum, Type> StatesTypes = new();
|
||||
protected TStateMachineContext StateMachineContext { get; }
|
||||
|
||||
private readonly SemaphoreSlim _stateLock = new (1, 1);
|
||||
|
||||
public virtual async UniTask SwitchState(TStateEnum nextState)
|
||||
{
|
||||
await _stateLock.WaitAsync();
|
||||
|
||||
try
|
||||
{
|
||||
NextState = nextState;
|
||||
|
||||
if (CurrentBaseState != null) await CurrentBaseState.ExitState();
|
||||
|
||||
var newState = States[nextState];
|
||||
await newState.EnterState();
|
||||
|
||||
CurrentBaseState = newState;
|
||||
CurrentState = nextState;
|
||||
NextState = default;
|
||||
|
||||
// Logger
|
||||
}
|
||||
finally
|
||||
{
|
||||
_stateLock.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b58aba24383545149cd08a2a0511bf67
|
||||
timeCreated: 1785766207
|
||||
@@ -0,0 +1,19 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace Code.Infrastructure.StateMachine
|
||||
{
|
||||
public abstract class BaseState<TEnum, TStateMachineContext>
|
||||
{
|
||||
public abstract TEnum MyState { get; }
|
||||
|
||||
protected BaseState(TStateMachineContext context)
|
||||
{
|
||||
Context = context;
|
||||
}
|
||||
|
||||
protected TStateMachineContext Context { get; }
|
||||
|
||||
public virtual UniTask EnterState() => UniTask.CompletedTask;
|
||||
public virtual UniTask ExitState() => UniTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dbb4ded54baa4a58b4f7b77f84fd57ed
|
||||
timeCreated: 1785766328
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d4a5df853da427b2870df717e157028
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,18 @@
|
||||
using Code.Infrastructure.Generators;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Code.Infrastructure.UI
|
||||
{
|
||||
[CreateAssetMenu(fileName = "CanvasSettings", menuName = "Configs/UI/CanvasSettings")]
|
||||
[Config("Configs/UI/CanvasSettings")]
|
||||
public class CanvasSettings : ScriptableObject
|
||||
{
|
||||
public RenderMode RenderMode;
|
||||
public int OrderInLayer;
|
||||
public CanvasScaler.ScaleMode ScaleMode;
|
||||
public int ReferenceResolutionX;
|
||||
public int ReferenceResolutionY;
|
||||
public float PlaneDistance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d23873b1c0034bf89886915f997b2f9a
|
||||
timeCreated: 1785859025
|
||||
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Code.Infrastructure.Configs;
|
||||
using Code.Infrastructure.Utils;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Code.Infrastructure.UI
|
||||
{
|
||||
public interface IUIFactory
|
||||
{
|
||||
Canvas GameplayCanvas { get; }
|
||||
Canvas Canvas { get; }
|
||||
UniTask<UIPanel> LoadPanel(UIPanelType panelType, bool gameplayCanvas, Transform parent = null);
|
||||
}
|
||||
|
||||
public interface IUIFactoryInitializer
|
||||
{
|
||||
UniTask LoadConfigs();
|
||||
void SetCanvas(Canvas canvas);
|
||||
void SetCanvasCamera(UnityEngine.Camera camera);
|
||||
}
|
||||
|
||||
public class UIFactory : IDisposable, IUIFactory, IUIFactoryInitializer
|
||||
{
|
||||
private Dictionary<UIPanelType, UIPanelSettings> _configs;
|
||||
private readonly AssetReference _canvasConfigReference;
|
||||
private readonly string _configsLabel;
|
||||
|
||||
private AsyncOperationHandle<IList<UIPanelSettings>> _configsHandle;
|
||||
private AsyncOperationHandle<CanvasSettings> _canvasSettingsHandle;
|
||||
private CanvasSettings _canvasConfig;
|
||||
|
||||
private GameObject _gameplayCanvas;
|
||||
|
||||
public Canvas GameplayCanvas { get; private set; }
|
||||
public Canvas Canvas { get; private set; }
|
||||
|
||||
public UIFactory(IConfigLocator configLocator)
|
||||
{
|
||||
_configsLabel = configLocator.TryGetManyConfigAssetReferences<UIPanelSettings>();
|
||||
_canvasConfigReference = configLocator.TryGetConfigAssetReference<CanvasSettings>();
|
||||
}
|
||||
|
||||
public void SetCanvas(Canvas canvas)
|
||||
{
|
||||
Canvas = canvas;
|
||||
UnityEngine.Object.DontDestroyOnLoad(canvas.gameObject);
|
||||
}
|
||||
|
||||
public void SetCanvasCamera(UnityEngine.Camera camera)
|
||||
{
|
||||
Canvas.worldCamera = camera;
|
||||
Canvas.planeDistance = 0.1f;
|
||||
|
||||
GameplayCanvas.worldCamera = camera;
|
||||
}
|
||||
|
||||
public async UniTask<UIPanel> LoadPanel(UIPanelType panelType, bool gameplayCanvas, Transform parent = null)
|
||||
{
|
||||
var panel = (await Addressables.InstantiateAsync(_configs[panelType].Prefab,
|
||||
parent ?? gameplayCanvas ? _gameplayCanvas.transform : Canvas.transform).ToUniTask())
|
||||
.GetComponent<UIPanel>();
|
||||
|
||||
panel.PanelType = panelType;
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
public async UniTask LoadConfigs()
|
||||
{
|
||||
if (await AddressablesUtilities.CheckLabelExists(_configsLabel)) await LoadPanelConfigs();
|
||||
|
||||
_canvasSettingsHandle = Addressables.LoadAssetAsync<CanvasSettings>(_canvasConfigReference);
|
||||
|
||||
await _canvasSettingsHandle.ToUniTask();
|
||||
|
||||
_canvasConfig = _canvasSettingsHandle.Result;
|
||||
|
||||
_gameplayCanvas = new GameObject { name = "GameplayCanvas" };
|
||||
UnityEngine.Object.DontDestroyOnLoad(_gameplayCanvas);
|
||||
|
||||
GameplayCanvas = _gameplayCanvas.AddComponent<Canvas>();
|
||||
GameplayCanvas.renderMode = _canvasConfig.RenderMode;
|
||||
GameplayCanvas.sortingOrder = _canvasConfig.OrderInLayer;
|
||||
GameplayCanvas.planeDistance = _canvasConfig.PlaneDistance;
|
||||
var scaler = GameplayCanvas.AddComponent<CanvasScaler>();
|
||||
|
||||
scaler.uiScaleMode = _canvasConfig.ScaleMode;
|
||||
scaler.referenceResolution =
|
||||
new Vector2(_canvasConfig.ReferenceResolutionX, _canvasConfig.ReferenceResolutionY);
|
||||
|
||||
GameplayCanvas.AddComponent<GraphicRaycaster>();
|
||||
}
|
||||
|
||||
private async UniTask LoadPanelConfigs()
|
||||
{
|
||||
_configsHandle = Addressables.LoadAssetsAsync<UIPanelSettings>(_configsLabel);
|
||||
await _configsHandle.ToUniTask();
|
||||
_configs = _configsHandle.Result.ToDictionary(c => c.UIPanelType);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_configsHandle.IsValid())
|
||||
Addressables.Release(_configsHandle);
|
||||
if (_canvasSettingsHandle.IsValid())
|
||||
Addressables.Release(_canvasSettingsHandle);
|
||||
if (_gameplayCanvas)
|
||||
Addressables.ReleaseInstance(_gameplayCanvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35696d0a97ac9023daa173dc582e7057
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
|
||||
namespace Code.Infrastructure.UI
|
||||
{
|
||||
public abstract class UIPanel : MonoBehaviour, IDisposable
|
||||
{
|
||||
public UIPanelType PanelType { get; set; }
|
||||
|
||||
public abstract UniTask Open(CancellationToken cts = default);
|
||||
public abstract UniTask Close(CancellationToken cts = default);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Addressables.ReleaseInstance(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 87687716d22d15d189146c86f059f922
|
||||
@@ -0,0 +1,14 @@
|
||||
using Code.Infrastructure.Generators;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
|
||||
namespace Code.Infrastructure.UI
|
||||
{
|
||||
[Config("Configs/UI/UIPanelSettings", true)]
|
||||
[CreateAssetMenu(fileName = "UIPanelSettings", menuName = "Configs/UI/UIPanelSettings")]
|
||||
public class UIPanelSettings : ScriptableObject
|
||||
{
|
||||
public AssetReference Prefab;
|
||||
public UIPanelType UIPanelType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 263960ff516f55c6686b3c63c7a6a28e
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9252caa218789b530ad841271890c0f7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
namespace Code.Infrastructure.Utils
|
||||
{
|
||||
public static class AddressablesUtilities
|
||||
{
|
||||
public static async UniTask<bool> CheckLabelExists(string labelName)
|
||||
{
|
||||
if (labelName is null) return false;
|
||||
|
||||
var handle = Addressables.LoadResourceLocationsAsync(labelName);
|
||||
|
||||
await handle.ToUniTask();
|
||||
|
||||
if (handle.Status == AsyncOperationStatus.Succeeded)
|
||||
{
|
||||
if (handle.Result.Count > 0)
|
||||
{
|
||||
Addressables.Release(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Addressables.Release(handle);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 607f50d5faeb41f585a2b4079902fbc6
|
||||
timeCreated: 1774778444
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Code.Infrastructure.Configs;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Reflex.Core;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
namespace Code.Infrastructure.Utils
|
||||
{
|
||||
public class MultipleConfig<TConfig> : IDisposable where TConfig : ScriptableObject
|
||||
{
|
||||
private AsyncOperationHandle<IList<TConfig>> _configsHandle;
|
||||
|
||||
public IList<TConfig> Configs => _configsHandle.Result;
|
||||
|
||||
public async UniTask<IList<TConfig>> LoadConfigs()
|
||||
{
|
||||
if (_configsHandle.IsValid()) return _configsHandle.Result;
|
||||
var configLocator = Container.RootContainer.Resolve<IConfigLocator>();
|
||||
|
||||
var label = configLocator.TryGetManyConfigAssetReferences<TConfig>();
|
||||
|
||||
_configsHandle = Addressables.LoadAssetsAsync<TConfig>(label);
|
||||
|
||||
await _configsHandle;
|
||||
|
||||
return _configsHandle.Result;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if(_configsHandle.IsValid()) _configsHandle.Release();
|
||||
}
|
||||
}
|
||||
public class SingleConfig<TConfig> : IDisposable where TConfig : ScriptableObject
|
||||
{
|
||||
private AsyncOperationHandle<TConfig> _configHandle;
|
||||
public TConfig Config => _configHandle.IsDone ? _configHandle.Result : null;
|
||||
|
||||
public async UniTask<TConfig> LoadConfig()
|
||||
{
|
||||
if (_configHandle.IsValid()) return _configHandle.Result;
|
||||
var configLocator = Container.RootContainer.Resolve<IConfigLocator>();
|
||||
|
||||
var label = configLocator.TryGetConfigAssetReference<TConfig>();
|
||||
|
||||
_configHandle = Addressables.LoadAssetAsync<TConfig>(label);
|
||||
|
||||
await _configHandle;
|
||||
|
||||
return _configHandle.Result;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if(_configHandle.IsValid()) _configHandle.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 999 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user