249 lines
7.5 KiB
C#
249 lines
7.5 KiB
C#
using System;
|
||
using System.Collections;
|
||
using UnityEngine;
|
||
|
||
public class SoundManager : MonoBehaviour
|
||
{
|
||
public static SoundManager Instance;
|
||
|
||
[Header("Player 1 AudioSources")]
|
||
public AudioSource player1VoiceSource;
|
||
public AudioSource player1EffectsSource;
|
||
|
||
[Header("Player 2 AudioSources")]
|
||
public AudioSource player2VoiceSource;
|
||
public AudioSource player2EffectsSource;
|
||
|
||
[Header("Общий источник (PvP — оба канала)")]
|
||
public AudioSource sharedVoiceSource; // pan=0, для PvP голос
|
||
public AudioSource sharedEffectsSource; // pan=0, для PvP эффекты
|
||
|
||
[Header("Фоновая музыка")]
|
||
public AudioSource musicSource;
|
||
public AudioClip[] backgroundMusic;
|
||
private int currentTrackIndex = -1;
|
||
private Coroutine musicLoopCoroutine;
|
||
private bool isMusicCapturedByVideo;
|
||
|
||
[Header("Звуковые эффекты")]
|
||
public AudioClip[] shootSounds;
|
||
public AudioClip[] explosionSounds;
|
||
public AudioClip[] missSounds;
|
||
|
||
private bool isPvPMode = false;
|
||
|
||
private void Awake()
|
||
{
|
||
if (Instance == null)
|
||
Instance = this;
|
||
else
|
||
{
|
||
Destroy(gameObject);
|
||
return;
|
||
}
|
||
DontDestroyOnLoad(gameObject);
|
||
}
|
||
|
||
private void Start()
|
||
{
|
||
ApplyAudioSettings();
|
||
StartMusicLoopIfNeeded();
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
if (SettingsManager.instance != null)
|
||
SettingsManager.instance.OnSettingsChanged.AddListener(ApplyAudioSettings);
|
||
}
|
||
|
||
private void OnDisable()
|
||
{
|
||
if (SettingsManager.instance != null)
|
||
SettingsManager.instance.OnSettingsChanged.RemoveListener(ApplyAudioSettings);
|
||
}
|
||
|
||
private void ApplyAudioSettings()
|
||
{
|
||
float volumeMusic = (float)Convert.ToInt32(SettingsManager.instance.GetSettingValue("Громкость музыки")) / 10f;
|
||
float volumeEffects = (float)Convert.ToInt32(SettingsManager.instance.GetSettingValue("Громкость эффектов")) / 10f;
|
||
float volumeVoice = (float)Convert.ToInt32(SettingsManager.instance.GetSettingValue("Громкость голоса")) / 10f;
|
||
|
||
if (musicSource != null) musicSource.volume = volumeMusic;
|
||
if (player1VoiceSource != null) player1VoiceSource.volume = volumeVoice;
|
||
if (player1EffectsSource != null) player1EffectsSource.volume = volumeEffects;
|
||
if (player2VoiceSource != null) player2VoiceSource.volume = volumeVoice;
|
||
if (player2EffectsSource != null) player2EffectsSource.volume = volumeEffects;
|
||
if (sharedVoiceSource != null) sharedVoiceSource.volume = volumeVoice;
|
||
}
|
||
|
||
// ───────────── Музыка ─────────────
|
||
|
||
private void StartMusicLoopIfNeeded()
|
||
{
|
||
if (musicLoopCoroutine != null) return;
|
||
if (backgroundMusic == null || backgroundMusic.Length == 0) return;
|
||
if (musicSource == null) return;
|
||
musicLoopCoroutine = StartCoroutine(MusicLoop());
|
||
}
|
||
|
||
private void RestartMusicLoop()
|
||
{
|
||
if (musicLoopCoroutine != null)
|
||
{
|
||
StopCoroutine(musicLoopCoroutine);
|
||
musicLoopCoroutine = null;
|
||
}
|
||
StartMusicLoopIfNeeded();
|
||
}
|
||
|
||
private IEnumerator MusicLoop()
|
||
{
|
||
while (true)
|
||
{
|
||
if (isMusicCapturedByVideo)
|
||
{
|
||
yield return null;
|
||
continue;
|
||
}
|
||
|
||
PlayNextTrack();
|
||
|
||
float wait = (musicSource != null && musicSource.clip != null)
|
||
? musicSource.clip.length
|
||
: 1f;
|
||
|
||
yield return new WaitForSeconds(wait);
|
||
}
|
||
}
|
||
|
||
private void PlayNextTrack()
|
||
{
|
||
if (backgroundMusic.Length == 0 || musicSource == null || isMusicCapturedByVideo) return;
|
||
int num = UnityEngine.Random.Range(0, backgroundMusic.Length);
|
||
if (num == currentTrackIndex && backgroundMusic.Length > 1)
|
||
num = (num + 1) % backgroundMusic.Length;
|
||
currentTrackIndex = num;
|
||
musicSource.clip = backgroundMusic[currentTrackIndex];
|
||
musicSource.Play();
|
||
}
|
||
|
||
public AudioSource CaptureMusicSourceForVideo()
|
||
{
|
||
if (musicSource == null) return null;
|
||
isMusicCapturedByVideo = true;
|
||
|
||
if (musicLoopCoroutine != null)
|
||
{
|
||
StopCoroutine(musicLoopCoroutine);
|
||
musicLoopCoroutine = null;
|
||
}
|
||
|
||
if (musicSource.isPlaying)
|
||
musicSource.Stop();
|
||
|
||
return musicSource;
|
||
}
|
||
|
||
public void ReleaseMusicSourceFromVideo()
|
||
{
|
||
isMusicCapturedByVideo = false;
|
||
|
||
if (musicSource == null || backgroundMusic.Length == 0)
|
||
return;
|
||
|
||
if (musicSource.isPlaying)
|
||
musicSource.Stop();
|
||
|
||
RestartMusicLoop();
|
||
}
|
||
|
||
// ───────────── Режим ─────────────
|
||
|
||
public void SetPvPMode(bool pvp)
|
||
{
|
||
isPvPMode = pvp;
|
||
if (sharedVoiceSource != null) sharedVoiceSource.Stop();
|
||
}
|
||
|
||
// ───────────── Голос ─────────────
|
||
|
||
public void PlayVoice(int playerId, AudioClip clip)
|
||
{
|
||
if (clip == null) return;
|
||
|
||
if (isPvPMode)
|
||
{
|
||
if (sharedVoiceSource == null) return;
|
||
if (sharedVoiceSource.isPlaying) return;
|
||
sharedVoiceSource.panStereo = 0f;
|
||
sharedVoiceSource.spatialBlend = 0f;
|
||
sharedVoiceSource.clip = clip;
|
||
sharedVoiceSource.Play();
|
||
}
|
||
else
|
||
{
|
||
AudioSource src = GetVoiceSource(playerId);
|
||
if (src == null) return;
|
||
src.Stop();
|
||
ApplyPlayerPan(src, playerId);
|
||
src.PlayOneShot(clip);
|
||
}
|
||
}
|
||
|
||
public void StopVoice(int playerId)
|
||
{
|
||
if (isPvPMode)
|
||
{
|
||
if (sharedVoiceSource != null) sharedVoiceSource.Stop();
|
||
}
|
||
else
|
||
{
|
||
AudioSource src = GetVoiceSource(playerId);
|
||
if (src != null) src.Stop();
|
||
}
|
||
}
|
||
|
||
// ───────────── Эффекты ─────────────
|
||
|
||
public void PlayShoot(int playerId)
|
||
{
|
||
if (shootSounds.Length == 0) return;
|
||
PlayEffect(playerId, shootSounds[UnityEngine.Random.Range(0, shootSounds.Length)], 0.8f);
|
||
}
|
||
|
||
public void PlayExplosion(int playerId)
|
||
{
|
||
if (explosionSounds.Length == 0) return;
|
||
PlayEffect(playerId, explosionSounds[UnityEngine.Random.Range(0, explosionSounds.Length)], 1.0f);
|
||
}
|
||
|
||
public void PlayMiss(int playerId)
|
||
{
|
||
if (missSounds.Length == 0) return;
|
||
PlayEffect(playerId, missSounds[UnityEngine.Random.Range(0, missSounds.Length)], 0.5f);
|
||
}
|
||
|
||
private void PlayEffect(int playerId, AudioClip clip, float volume)
|
||
{
|
||
AudioSource src = GetEffectsSource(playerId);
|
||
if (src == null) return;
|
||
ApplyPlayerPan(src, playerId);
|
||
src.PlayOneShot(clip, volume);
|
||
}
|
||
|
||
// ───────────── Утилиты ─────────────
|
||
|
||
private void ApplyPlayerPan(AudioSource src, int playerId)
|
||
{
|
||
if (src == null) return;
|
||
src.spatialBlend = 0f;
|
||
src.panStereo = (playerId == 1) ? -1f : 1f;
|
||
}
|
||
|
||
private AudioSource GetVoiceSource(int playerId)
|
||
=> playerId == 1 ? player1VoiceSource : player2VoiceSource;
|
||
|
||
private AudioSource GetEffectsSource(int playerId)
|
||
=> playerId == 1 ? player1EffectsSource : player2EffectsSource;
|
||
}
|