184 lines
5.6 KiB
C#
184 lines
5.6 KiB
C#
using System.Collections.Generic;
|
|
using Cysharp.Threading.Tasks;
|
|
using Unity.Mathematics;
|
|
using UnityEngine;
|
|
using UnityEngine.Audio;
|
|
using Object = UnityEngine.Object;
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
#endif
|
|
|
|
namespace DanieleMarotta.RiversongCodeShowcase
|
|
{
|
|
[Service(typeof(ISoundPlayer))]
|
|
[GameSystemGroup(typeof(AudioSystemGroup))]
|
|
[InitializeAfter(typeof(BackgroundMusicSystem))]
|
|
public class SoundPlayerSystem : GameSystem, IInitializable, IUpdatable, ISoundPlayer, IDrawGizmos
|
|
{
|
|
private const int ChannelCount = 16;
|
|
|
|
[InjectService]
|
|
private ITileSpace _tileSpace;
|
|
|
|
[InjectService]
|
|
private IScene _scene;
|
|
|
|
[InjectService]
|
|
private ICameraProperties _cameraProperties;
|
|
|
|
[InjectService]
|
|
private GameConfig _config;
|
|
|
|
private AudioSourcePool _poolNonSpatial;
|
|
|
|
private AudioSourcePool _poolSpatial;
|
|
|
|
private SystemSoundLibrary _systemSoundLibrary;
|
|
|
|
public SoundPlayerSystem(IServiceLocator serviceLocator) : base(serviceLocator)
|
|
{
|
|
}
|
|
|
|
public async UniTask InitializeAsync()
|
|
{
|
|
var systemSoundLibraryTask = _config.Audio.SystemSoundLibrary.LoadAssetAsync<SystemSoundLibrary>().ToUniTask();
|
|
var audioSourcePrefabTask = _config.Audio.AudioSourcePrefab.LoadAssetAsync().ToUniTask();
|
|
|
|
_systemSoundLibrary = await systemSoundLibraryTask;
|
|
|
|
var audioSourcePrefab = (await audioSourcePrefabTask).GetComponent<AudioSource>();
|
|
await InitializePoolAsync(audioSourcePrefab);
|
|
}
|
|
|
|
private async UniTask InitializePoolAsync(AudioSource audioSourcePrefab)
|
|
{
|
|
_poolNonSpatial = InitializePool(audioSourcePrefab, "Sound_{0:00} (2D)");
|
|
await UniTask.NextFrame();
|
|
|
|
_poolSpatial = InitializePool(audioSourcePrefab, "Sound_{0:00} (3D)");
|
|
await UniTask.NextFrame();
|
|
}
|
|
|
|
private AudioSourcePool InitializePool(AudioSource audioSourcePrefab, string nameFormat)
|
|
{
|
|
var audioSources = new AudioSource[ChannelCount];
|
|
|
|
for (var i = 0; i < ChannelCount; i++)
|
|
{
|
|
var source = Object.Instantiate(audioSourcePrefab, _scene.SceneFolders.AudioSources);
|
|
source.name = string.Format(nameFormat, i);
|
|
|
|
audioSources[i] = source;
|
|
}
|
|
|
|
return new AudioSourcePool(audioSources);
|
|
}
|
|
|
|
public void Update()
|
|
{
|
|
UpdateSpatialAudioSources();
|
|
}
|
|
|
|
private void UpdateSpatialAudioSources()
|
|
{
|
|
var cameraPosition = ((float3)_scene.MainCamera.transform.position).xz;
|
|
|
|
var horizontalDistanceRange = _config.Audio.SpatialAudioHorizontalDistanceRange;
|
|
var zoomRange = _config.Audio.SpatialAudioZoomRange;
|
|
|
|
foreach (var audioSource in _poolSpatial.AudioSources)
|
|
{
|
|
#if !UNITY_EDITOR
|
|
if (!audioSource.isPlaying) continue;
|
|
#endif
|
|
|
|
var p = ((float3)audioSource.transform.position).xz;
|
|
|
|
var horizontalDistance = math.distance(cameraPosition, p);
|
|
|
|
var t = math.saturate(math.unlerp(horizontalDistanceRange.x, horizontalDistanceRange.y, horizontalDistance));
|
|
var attenuation = 1 - t * t;
|
|
|
|
var zoomFactor = 1 - math.unlerp(zoomRange.x, zoomRange.y, _cameraProperties.Zoom);
|
|
|
|
audioSource.volume = attenuation * zoomFactor;
|
|
}
|
|
}
|
|
|
|
public void Play(AudioResource resource)
|
|
{
|
|
var source = _poolNonSpatial.GetAudioSource();
|
|
|
|
source.resource = resource;
|
|
|
|
source.Play();
|
|
}
|
|
|
|
public void PlayAt(AudioResource resource, float3 position)
|
|
{
|
|
var source = _poolSpatial.GetAudioSource();
|
|
|
|
source.resource = resource;
|
|
source.transform.position = position;
|
|
|
|
source.Play();
|
|
}
|
|
|
|
public void PlayAt(AudioResource resource, int2 tile)
|
|
{
|
|
PlayAt(resource, _tileSpace.TileToWorld(tile));
|
|
}
|
|
|
|
public AudioResource GetSystemSound(SystemSoundId soundId)
|
|
{
|
|
return _systemSoundLibrary.Sounds.GetValueOrDefault(soundId);
|
|
}
|
|
|
|
public void DrawGizmos(bool selected)
|
|
{
|
|
#if UNITY_EDITOR
|
|
const int width = 60;
|
|
const int height = 8;
|
|
|
|
var camera = _scene.MainCamera;
|
|
|
|
foreach (var audioSource in _poolSpatial.AudioSources)
|
|
{
|
|
var screenPoint = camera.WorldToScreenPoint(audioSource.transform.position);
|
|
if (screenPoint.z < 0) continue;
|
|
|
|
var p = new Vector2(screenPoint.x - width * 0.5f, camera.pixelHeight - screenPoint.y);
|
|
|
|
Handles.BeginGUI();
|
|
|
|
EditorGUI.DrawRect(new Rect(p.x, p.y, width, height), new Color(0, 0, 0, 0.6f));
|
|
|
|
var volume = audioSource.volume;
|
|
var color = Color.Lerp(Color.red, Color.green, volume);
|
|
EditorGUI.DrawRect(new Rect(p.x + 0.5f * (width * (1 - volume)), p.y, width * volume, height), color);
|
|
|
|
Handles.EndGUI();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
private class AudioSourcePool
|
|
{
|
|
private int _nextChannel;
|
|
|
|
public AudioSourcePool(AudioSource[] audioSources)
|
|
{
|
|
AudioSources = audioSources;
|
|
}
|
|
|
|
public AudioSource[] AudioSources { get; }
|
|
|
|
public AudioSource GetAudioSource()
|
|
{
|
|
var source = AudioSources[_nextChannel];
|
|
_nextChannel = (_nextChannel + 1) % ChannelCount;
|
|
return source;
|
|
}
|
|
}
|
|
}
|
|
} |