updated AudioManager to handle multiple sublayers

godot-4.2
John Montagu, the 4th Earl of Sandvich 2023-09-09 15:14:53 -07:00
parent 96eac1c640
commit ffe6c50e93
Signed by: sandvich
GPG Key ID: 9A39BE37E602B22D
5 changed files with 167 additions and 194 deletions

View File

@ -0,0 +1,118 @@
using Godot;
using System.Collections.Generic;
namespace SupaLidlGame.Audio;
public sealed partial class AudioManager : Node
{
private ManagedAudioPlayer _ambientPlayer;
private List<ManagedAudioPlayer> _bgPlayers;
public enum Layer
{
Ambient,
Background
}
public AudioManager()
{
_bgPlayers = new();
for (int i = 0; i < 4; i++)
{
_bgPlayers.Add(null);
}
}
public ManagedAudioPlayer CreateAudioPlayer(AudioStream stream)
{
var player = new ManagedAudioPlayer();
player.Stream = stream;
AddChild(player);
return player;
}
/// <summary>
/// Plays ambient audio. Ambient audio will play alongside the current
/// background audio and will replace the current ambient audio.
/// </summary>
public void PlayAmbient(AudioStream stream, float fade = 1)
{
if (IsPlayerValid(_ambientPlayer))
{
_ambientPlayer.FadeOut(fade, true);
}
_ambientPlayer = CreateAudioPlayer(stream);
_ambientPlayer.Play();
}
/// <summary>
/// Plays background audio. Background audio will play if no sublayer above
/// is playing, pause all sublayers below, and replace the current sublayer
/// if it is playing.
/// </summary>
public void PlayBackground(AudioStream stream, int sublayer = 0, float fade = 1)
{
// stop current sublayer if playing
if (IsPlayerValid(_bgPlayers[sublayer]))
{
_bgPlayers[sublayer].FadeOut(fade, true);
}
_bgPlayers[sublayer] = CreateAudioPlayer(stream);
// pause all sublayers below this
for (int i = 0; i < sublayer; i++)
{
if (IsPlayerValid(_bgPlayers[i]))
{
if (!_bgPlayers[i].StreamPaused)
{
_bgPlayers[i]?.FadeOut(fade, pause: true);
}
}
}
// if a sublayer above is not playing then play
if (!IsSublayerAboveValid(sublayer))
{
_bgPlayers[sublayer].Play();
}
}
public void StopBackground(int sublayer, float fade = 1)
{
// kill current sublayer
_bgPlayers[sublayer]?.FadeOut(fade, true);
// if sublayer above is not plyaing, play lowest sublayer
if (!IsSublayerAboveValid(sublayer))
{
for (int i = sublayer - 1; i >= 0; i--)
{
if (IsPlayerValid(_bgPlayers[i]))
{
_bgPlayers[i].FadeIn(fade, unpause: true);
break;
}
}
}
}
private bool IsSublayerAboveValid(int sublayer)
{
for (int i = sublayer + 1; i < 4; i++)
{
if (IsPlayerValid(_bgPlayers[i]))
{
return true;
}
}
return false;
}
private bool IsPlayerValid(ManagedAudioPlayer player)
{
return player is not null && IsInstanceValid(player) && !player.IsDead;
}
}

View File

@ -0,0 +1,45 @@
using Godot;
namespace SupaLidlGame.Audio;
public sealed partial class ManagedAudioPlayer : AudioStreamPlayer
{
private Tween _tween;
public bool IsDead { get; set; }
private Tween GetNewTween()
{
if (_tween is not null && IsInstanceValid(_tween))
{
_tween.Kill();
}
return _tween = GetTree().CreateTween().BindNode(this);
}
public void FadeOut(float time, bool kill = false, bool pause = false)
{
GetNewTween();
_tween.TweenProperty(this, "volume_db", -80, time);
if (kill)
{
IsDead = true;
_tween.TweenCallback(Callable.From(QueueFree));
}
else if (pause)
{
_tween.TweenCallback(Callable.From(() => StreamPaused = true));
}
}
public void FadeIn(float time, bool unpause = false)
{
GetNewTween();
_tween.TweenProperty(this, "volume_db", 0, time);
if (unpause)
{
_tween.TweenCallback(Callable.From(() => StreamPaused = false));
}
}
}

View File

@ -1,191 +0,0 @@
using Godot;
using System.Collections.Generic;
namespace SupaLidlGame.Utils;
public sealed partial class AudioManager : Node
{
public enum Layer
{
Ambient,
BackgroundMusic,
ActiveMusic,
};
//private Array<AudioStreamPlayer> _players;
private Dictionary<Layer, AudioStreamPlayer> _players;
public override void _Ready()
{
_players = new Dictionary<Layer, AudioStreamPlayer>();
_players.Add(Layer.Ambient, null);
_players.Add(Layer.BackgroundMusic, null);
_players.Add(Layer.ActiveMusic, null);
}
public void PlayAmbient(AudioStream stream)
{
StopPlayback(Layer.Ambient);
_players[Layer.Ambient] = CreateAudioStreamPlayer(stream);
FadeIn(_players[Layer.Ambient]);
}
public void PlayBackground(AudioStream stream)
{
StopPlayback(Layer.BackgroundMusic);
_players[Layer.BackgroundMusic] = CreateAudioStreamPlayer(stream);
if (!IsLayerValid(Layer.ActiveMusic))
{
FadeIn(_players[Layer.BackgroundMusic]);
}
}
public void StopBackground()
{
StopPlayback(Layer.BackgroundMusic);
}
public void PlayActive(AudioStream stream)
{
_players[Layer.ActiveMusic] = CreateAudioStreamPlayer(stream);
_players[Layer.ActiveMusic].Play();
if (IsLayerValid(Layer.BackgroundMusic))
{
Pause(Layer.BackgroundMusic);
}
}
public void StopActive()
{
StopPlayback(Layer.ActiveMusic);
if (IsLayerValid(Layer.BackgroundMusic))
{
if (_players[Layer.BackgroundMusic].StreamPaused)
{
Unpause(Layer.BackgroundMusic);
}
else
{
_players[Layer.BackgroundMusic].Play();
}
}
}
public void Play(AudioStream stream, Layer layer)
{
switch (layer)
{
case Layer.Ambient:
PlayAmbient(stream);
break;
case Layer.BackgroundMusic:
PlayBackground(stream);
break;
case Layer.ActiveMusic:
PlayActive(stream);
break;
}
}
public void Stop(Layer layer)
{
switch (layer)
{
case Layer.Ambient:
case Layer.BackgroundMusic:
StopPlayback(layer);
break;
case Layer.ActiveMusic:
StopActive();
break;
}
}
private void Pause(Layer layer)
{
var player = _players[layer];
if (player is null || !IsInstanceValid(player))
{
return;
}
FadeOut(player, pause: true);
}
private void Unpause(Layer layer)
{
var player = _players[layer];
if (player is null || !IsInstanceValid(player))
{
return;
}
FadeIn(player, unpause: true);
}
private void StopPlayback(Layer layer)
{
var player = _players[layer];
if (player is null || !IsInstanceValid(player))
{
return;
}
FadeOut(player, kill: true);
_players[layer] = null;
}
private AudioStreamPlayer CreateAudioStreamPlayer(AudioStream stream)
{
var player = new AudioStreamPlayer();
AddChild(player);
player.Stream = stream;
return player;
}
private void FadeOut(
AudioStreamPlayer player,
double time = 1.0,
bool kill = false,
bool pause = false)
{
var tween = player.GetTree().CreateTween().BindNode(player);
tween.TweenProperty(player, "volume_db", 0, time);
if (kill)
{
tween.TweenCallback(Callable.From(player.QueueFree));
}
else if (pause)
{
tween.TweenCallback(
Callable.From(() => player.StreamPaused = true));
}
}
private void FadeIn(AudioStreamPlayer player,
double time = 1.0,
bool unpause = false)
{
if (unpause)
{
player.StreamPaused = false;
}
else
{
player.Play();
}
player.VolumeDb = 0;
var tween = player.GetTree().CreateTween().BindNode(player);
tween.TweenProperty(player, "volume_db", 1, time);
}
public bool IsLayerValid(Layer layer)
{
var player = _players[layer];
return player is not null && IsInstanceValid(player);
}
}

View File

@ -1,4 +1,5 @@
using Godot; using Godot;
using SupaLidlGame.Audio;
using SupaLidlGame.Characters; using SupaLidlGame.Characters;
using SupaLidlGame.Extensions; using SupaLidlGame.Extensions;
using SupaLidlGame.Scenes; using SupaLidlGame.Scenes;
@ -123,13 +124,13 @@ public partial class World : Node
return; return;
} }
CurrentBoss = boss; CurrentBoss = boss;
GetNode<AudioManager>("/root/AudioManager").PlayActive(boss.Music); GetNode<AudioManager>("/root/AudioManager").PlayBackground(boss.Music, 2);
} }
private void DeregisterBoss(Boss boss) private void DeregisterBoss(Boss boss)
{ {
CurrentBoss = null; CurrentBoss = null;
GetNode<AudioManager>("/root/AudioManager").StopActive(); GetNode<AudioManager>("/root/AudioManager").StopBackground(2);
} }
private void LoadMap(Map map) private void LoadMap(Map map)

View File

@ -22,7 +22,7 @@ GlobalState="*res://State/Global/GlobalState.cs"
EventBus="*res://Events/EventBus.cs" EventBus="*res://Events/EventBus.cs"
BaseUI="*res://UI/Base.tscn" BaseUI="*res://UI/Base.tscn"
World="*res://Scenes/Level.tscn" World="*res://Scenes/Level.tscn"
AudioManager="*res://Utils/AudioManager.cs" AudioManager="*res://Audio/AudioManager.cs"
DebugConsole="*res://Debug/DebugConsole.cs" DebugConsole="*res://Debug/DebugConsole.cs"
Panku="*res://addons/panku_console/console.tscn" Panku="*res://addons/panku_console/console.tscn"