浏览代码

Add a Stop and Finish option for the AudioManager

/release
Marc-Olivier 4 年前
当前提交
45cb9412
共有 10 个文件被更改,包括 301 次插入20 次删除
  1. 26
      UOP1_Project/Assets/Scripts/Audio/AudioCue.cs
  2. 84
      UOP1_Project/Assets/Scripts/Audio/AudioManager.cs
  3. 15
      UOP1_Project/Assets/Scripts/Audio/SoundEmitters/SoundEmitter.cs
  4. 6
      UOP1_Project/Assets/Scripts/Characters/ProtagonistAudio.cs
  5. 2
      UOP1_Project/Assets/Scripts/Characters/StateMachine/Actions/PlayAudioCueActionSO.cs
  6. 57
      UOP1_Project/Assets/Scripts/Events/ScriptableObjects/AudioCueEventChannelSO.cs
  7. 36
      UOP1_Project/Assets/Scripts/Audio/AudioData/AudioCueKey.cs
  8. 11
      UOP1_Project/Assets/Scripts/Audio/AudioData/AudioCueKey.cs.meta
  9. 73
      UOP1_Project/Assets/Scripts/Audio/SoundEmitters/SoundEmitterList.cs
  10. 11
      UOP1_Project/Assets/Scripts/Audio/SoundEmitters/SoundEmitterList.cs.meta

26
UOP1_Project/Assets/Scripts/Audio/AudioCue.cs


[SerializeField] private AudioCueEventChannelSO _audioCueEventChannel = default;
[SerializeField] private AudioConfigurationSO _audioConfiguration = default;
private AudioCueKey controlKey = AudioCueKey.Invalid;
private void Start()
{
if (_playOnStart)

public void PlayAudioCue()
{
_audioCueEventChannel.RaiseEvent(_audioCue, _audioConfiguration, transform.position);
controlKey = _audioCueEventChannel.RaisePlayEvent(_audioCue, _audioConfiguration, transform.position);
}
public void StopAudioCue()
{
if (controlKey != AudioCueKey.Invalid)
{
if (!_audioCueEventChannel.RaiseStopEvent(controlKey))
{
controlKey = AudioCueKey.Invalid;
}
}
}
public void FinishAudioCue()
{
if (controlKey != AudioCueKey.Invalid)
{
if (!_audioCueEventChannel.RaiseFinishEvent(controlKey))
{
controlKey = AudioCueKey.Invalid;
}
}
}
}

84
UOP1_Project/Assets/Scripts/Audio/AudioManager.cs


[Range(0f, 1f)]
[SerializeField] private float _sfxVolume = 1f;
private SoundEmitterList _soundEmitterList;
_soundEmitterList = new SoundEmitterList();
_SFXEventChannel.OnAudioCueRequested += PlayAudioCue;
_musicEventChannel.OnAudioCueRequested += PlayAudioCue; //TODO: Treat music requests differently?
RegisterChannel(_SFXEventChannel);
RegisterChannel(_musicEventChannel); //TODO: Treat music requests differently?
}
private void OnDestroy()
{
UnregisterChannel(_SFXEventChannel);
UnregisterChannel(_musicEventChannel);
}
/// <summary>

}
}
private void RegisterChannel(AudioCueEventChannelSO audioCueEventChannel)
{
audioCueEventChannel.OnAudioCuePlayRequested += PlayAudioCue;
audioCueEventChannel.OnAudioCueStopRequested += StopAudioCue;
audioCueEventChannel.OnAudioCueFinishRequested += FinishAudioCue;
}
private void UnregisterChannel(AudioCueEventChannelSO audioCueEventChannel)
{
audioCueEventChannel.OnAudioCuePlayRequested += PlayAudioCue;
audioCueEventChannel.OnAudioCueStopRequested += StopAudioCue;
audioCueEventChannel.OnAudioCueFinishRequested += FinishAudioCue;
}
// Both MixerValueNormalized and NormalizedToMixerValue functions are used for easier transformations
/// when using UI sliders normalized format
private float MixerValueToNormalized(float mixerValue)

/// <summary>
/// Plays an AudioCue by requesting the appropriate number of SoundEmitters from the pool.
/// </summary>
public void PlayAudioCue(AudioCueSO audioCue, AudioConfigurationSO settings, Vector3 position = default)
public AudioCueKey PlayAudioCue(AudioCueSO audioCue, AudioConfigurationSO settings, Vector3 position = default)
int nOfClips = clipsToPlay.Length;
SoundEmitter[] soundEmitterArray = new SoundEmitter[clipsToPlay.Length];
int nOfClips = clipsToPlay.Length;
SoundEmitter soundEmitter = _pool.Request();
if (soundEmitter != null)
soundEmitterArray[i] = _pool.Request();
if (soundEmitterArray[i] != null)
soundEmitter.PlayAudioClip(clipsToPlay[i], settings, audioCue.looping, position);
soundEmitterArray[i].PlayAudioClip(clipsToPlay[i], settings, audioCue.looping, position);
soundEmitter.OnSoundFinishedPlaying += OnSoundEmitterFinishedPlaying;
soundEmitterArray[i].OnSoundFinishedPlaying += OnSoundEmitterFinishedPlaying;
//TODO: Save the SoundEmitters that were activated, to be able to stop them if needed
return _soundEmitterList.Add(audioCue, soundEmitterArray);
}
public bool FinishAudioCue(AudioCueKey emitterKey)
{
bool isFound = _soundEmitterList.Get(emitterKey, out SoundEmitter[] soundEmitters);
if (isFound)
{
for (int i = 0; i < soundEmitters.Length; i++)
{
soundEmitters[i].Finish();
soundEmitters[i].OnSoundFinishedPlaying += OnSoundEmitterFinishedPlaying;
}
}
return isFound;
}
public bool StopAudioCue(AudioCueKey emitterKey)
{
bool isFound = _soundEmitterList.Get(emitterKey, out SoundEmitter[] soundEmitters);
if (isFound)
{
for (int i = 0; i < soundEmitters.Length; i++)
{
StopAndCleanEmitter(soundEmitters[i]);
}
_soundEmitterList.Remove(emitterKey);
}
return isFound;
soundEmitter.OnSoundFinishedPlaying -= OnSoundEmitterFinishedPlaying;
StopAndCleanEmitter(soundEmitter);
}
private void StopAndCleanEmitter(SoundEmitter soundEmitter)
{
if(soundEmitter.IsFinishing())
soundEmitter.OnSoundFinishedPlaying -= OnSoundEmitterFinishedPlaying;
soundEmitter.Stop();
_pool.Return(soundEmitter);
}

15
UOP1_Project/Assets/Scripts/Audio/SoundEmitters/SoundEmitter.cs


_audioSource.Stop();
}
public void Finish()
{
if (_audioSource.loop)
{
_audioSource.loop = false;
float timeRemaining = _audioSource.clip.length - _audioSource.time;
StartCoroutine(FinishedPlaying(timeRemaining));
}
}
public bool IsInUse()
{
return _audioSource.isPlaying;

{
return _audioSource.loop;
}
public bool IsFinishing()
{
return !_audioSource.loop;
}
IEnumerator FinishedPlaying(float clipLength)

6
UOP1_Project/Assets/Scripts/Characters/ProtagonistAudio.cs


[SerializeField] private AudioCueSO caneSwing, objectPickup, footstep;
public void PlayFootstep() => _sfxEventChannel.RaiseEvent(footstep, _audioConfig, transform.position);
public void PlayCaneSwing() => _sfxEventChannel.RaiseEvent(caneSwing, _audioConfig, transform.position);
public void PlayObjectPickup() => _sfxEventChannel.RaiseEvent(objectPickup, _audioConfig, transform.position);
public void PlayFootstep() => _sfxEventChannel.RaisePlayEvent(footstep, _audioConfig, transform.position);
public void PlayCaneSwing() => _sfxEventChannel.RaisePlayEvent(caneSwing, _audioConfig, transform.position);
public void PlayObjectPickup() => _sfxEventChannel.RaisePlayEvent(objectPickup, _audioConfig, transform.position);
}

2
UOP1_Project/Assets/Scripts/Characters/StateMachine/Actions/PlayAudioCueActionSO.cs


public override void OnStateEnter()
{
_originSO.audioCueEventChannel.RaiseEvent(_originSO.audioCue, _originSO.audioConfiguration, _stateMachineTransform.position);
_originSO.audioCueEventChannel.RaisePlayEvent(_originSO.audioCue, _originSO.audioConfiguration, _stateMachineTransform.position);
}
}

57
UOP1_Project/Assets/Scripts/Events/ScriptableObjects/AudioCueEventChannelSO.cs


using UnityEngine;
using System.Collections;
using UnityEngine.Events;
using System.Collections.Generic;
/// <summary>
/// Event on which <c>AudioCue</c> components send a message to play SFX and music. <c>AudioManager</c> listens on these events, and actually plays the sound.

{
public UnityAction<AudioCueSO, AudioConfigurationSO, Vector3> OnAudioCueRequested;
public AudioCuePlayAction OnAudioCuePlayRequested;
public AudioCueStopAction OnAudioCueStopRequested;
public AudioCueFinishAction OnAudioCueFinishRequested;
public AudioCueKey RaisePlayEvent(AudioCueSO audioCue, AudioConfigurationSO audioConfiguration, Vector3 positionInSpace)
{
AudioCueKey audioCueKey = AudioCueKey.Invalid;
if (OnAudioCuePlayRequested != null)
{
audioCueKey = OnAudioCuePlayRequested.Invoke(audioCue, audioConfiguration, positionInSpace);
}
else
{
Debug.LogWarning("An AudioCue play event was requested, but nobody picked it up. " +
"Check why there is no AudioManager already loaded, " +
"and make sure it's listening on this AudioCue Event channel.");
}
return audioCueKey;
}
public bool RaiseStopEvent(AudioCueKey audioCueKey)
{
bool requestSucceed = false;
if (OnAudioCueStopRequested != null)
{
requestSucceed = OnAudioCueStopRequested.Invoke(audioCueKey);
}
else
{
Debug.LogWarning("An AudioCue stop event was requested, but nobody picked it up. " +
"Check why there is no AudioManager already loaded, " +
"and make sure it's listening on this AudioCue Event channel.");
}
public void RaiseEvent(AudioCueSO audioCue, AudioConfigurationSO audioConfiguration, Vector3 positionInSpace)
return requestSucceed;
}
public bool RaiseFinishEvent(AudioCueKey audioCueKey)
if (OnAudioCueRequested != null)
bool requestSucceed = false;
if (OnAudioCueStopRequested != null)
OnAudioCueRequested.Invoke(audioCue, audioConfiguration, positionInSpace);
requestSucceed = OnAudioCueFinishRequested.Invoke(audioCueKey);
Debug.LogWarning("An AudioCue was requested, but nobody picked it up. " +
Debug.LogWarning("An AudioCue finish event was requested, but nobody picked it up. " +
return requestSucceed;
public delegate AudioCueKey AudioCuePlayAction(AudioCueSO audioCue, AudioConfigurationSO audioConfiguration, Vector3 positionInSpace);
public delegate bool AudioCueStopAction(AudioCueKey emitterKey);
public delegate bool AudioCueFinishAction(AudioCueKey emitterKey);

36
UOP1_Project/Assets/Scripts/Audio/AudioData/AudioCueKey.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public struct AudioCueKey
{
public static AudioCueKey Invalid = new AudioCueKey(-1, null);
internal int Value;
internal AudioCueSO AudioCue;
internal AudioCueKey(int value, AudioCueSO audioCue)
{
Value = value;
AudioCue = audioCue;
}
public override bool Equals(Object obj)
{
return obj is AudioCueKey x && Value == x.Value && AudioCue == x.AudioCue;
}
public override int GetHashCode()
{
return Value.GetHashCode() ^ AudioCue.GetHashCode();
}
public static bool operator ==(AudioCueKey x, AudioCueKey y)
{
return x.Value == y.Value && x.AudioCue == y.AudioCue;
}
public static bool operator !=(AudioCueKey x, AudioCueKey y)
{
return !(x == y);
}
}

11
UOP1_Project/Assets/Scripts/Audio/AudioData/AudioCueKey.cs.meta


fileFormatVersion: 2
guid: 325f637f904634344bb44ab93b38e81e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

73
UOP1_Project/Assets/Scripts/Audio/SoundEmitters/SoundEmitterList.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine.Assertions;
public class SoundEmitterList
{
private int _nextUniqueKey = 0;
private List<AudioCueKey> _emittersKey;
private List<SoundEmitter[]> _emittersList;
public SoundEmitterList()
{
_emittersKey = new List<AudioCueKey>();
_emittersList = new List<SoundEmitter[]>();
}
public AudioCueKey GetKey(AudioCueSO cue)
{
return new AudioCueKey(_nextUniqueKey++, cue);
}
public void Add(AudioCueKey key, SoundEmitter[] emitter)
{
_emittersKey.Add(key);
_emittersList.Add(emitter);
}
public AudioCueKey Add(AudioCueSO cue, SoundEmitter[] emitter)
{
AudioCueKey emitterKey = GetKey(cue);
_emittersKey.Add(emitterKey);
_emittersList.Add(emitter);
return emitterKey;
}
public bool Get(AudioCueKey key, out SoundEmitter[] emitter)
{
int index = _emittersKey.FindIndex(x => x == key);
if (index < 0)
{
emitter = null;
return false;
}
emitter = _emittersList[index];
return true;
}
public bool Remove(AudioCueKey key)
{
int index = _emittersKey.FindIndex(x => x == key);
return RemoveAt(index);
}
private bool RemoveAt(int index)
{
if (index < 0)
{
return false;
}
_emittersKey.RemoveAt(index);
_emittersList.RemoveAt(index);
return true;
}
}

11
UOP1_Project/Assets/Scripts/Audio/SoundEmitters/SoundEmitterList.cs.meta


fileFormatVersion: 2
guid: 9d1a0455c3126ea4a991e9fd36520393
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存