Newer
Older
DungeonShooting / DungeonShooting_Godot / src / game / manager / SoundManager.cs
@小李xl 小李xl on 9 Jun 2023 6 KB 完善音效
using System;
using System.Collections.Generic;
using Godot;

/// <summary>
/// 音频总线 区分不同的声音 添加声音效果 目前只有背景音乐 和音效 两个bus
/// </summary>
public enum BUS
{
    BGM = 0,
    SFX = 1
}

/// <summary>
/// 声音管理 背景音乐管理 音效
/// </summary>
public partial class SoundManager
{
    private static Stack<AudioPlayer2D> _streamPlayer2DStack = new Stack<AudioPlayer2D>();
    private static Stack<AudioPlayer> _streamPlayerStack = new Stack<AudioPlayer>();

    /// <summary>
    /// 2D音频播放节点
    /// </summary>
    private partial class AudioPlayer2D : AudioStreamPlayer2D
    {
        private float _delayTimer = -1;
        
        public override void _Ready()
        {
            Finished += OnPlayFinish;
        }

        /// <summary>
        /// 延时播放
        /// </summary>
        public void DelayPlay(float time)
        {
            _delayTimer = time;
        }

        /// <summary>
        /// 停止播放, 并回收节点
        /// </summary>
        public void StopPlay()
        {
            Stop();
            OnPlayFinish();
        }
        
        public override void _Process(double delta)
        {
            if (_delayTimer > 0)
            {
                _delayTimer -= (float)delta;
                if (_delayTimer <= 0)
                {
                    Play();
                }
            }
        }
        
        private void OnPlayFinish()
        {
            _delayTimer = -1;
            RecycleAudioPlayer2D(this);
        }
    }

    /// <summary>
    /// 音频播放节点
    /// </summary>
    private partial class AudioPlayer : AudioStreamPlayer
    {
        public override void _Ready()
        {
            Finished += OnPlayFinish;
        }

        /// <summary>
        /// 停止播放, 并回收节点
        /// </summary>
        public void StopPlay()
        {
            Stop();
            OnPlayFinish();
        }
        
        private void OnPlayFinish()
        {
            GetParent().RemoveChild(this);
            Stream = null;
            Playing = false;
            RecycleAudioPlayer(this);
        }
    }

    /// <summary>
    /// 播放声音 用于bgm
    /// </summary>
    /// <param name="soundName">bgm路径</param>
    /// <param name="volume">音量</param>
    public static AudioStreamPlayer PlayMusic(string soundName, float volume = 0.5f)
    {
        var sound = ResourceManager.Load<AudioStream>(soundName);
        var soundNode = GetAudioPlayerInstance();
        GameApplication.Instance.GlobalNodeRoot.AddChild(soundNode);
        soundNode.Stream = sound;
        soundNode.Bus = Enum.GetName(typeof(BUS), 0);
        soundNode.VolumeDb = volume;
        soundNode.Play();
        return soundNode;
    }

    /// <summary>
    /// 添加并播放音效 用于音效
    /// </summary>
    /// <param name="soundName">音效文件路径</param>
    /// <param name="volume">音量</param>
    public static AudioStreamPlayer PlaySoundEffect(string soundName, float volume = 0.5f)
    {
        var sound = ResourceManager.Load<AudioStream>(soundName);
        var soundNode = GetAudioPlayerInstance();
        GameApplication.Instance.GlobalNodeRoot.AddChild(soundNode);
        soundNode.Stream = sound;
        soundNode.Bus = Enum.GetName(typeof(BUS), 1);
        soundNode.VolumeDb = volume;
        soundNode.Play();
        return soundNode;
    }

    /// <summary>
    /// 在指定的节点下播放音效 用于音效
    /// </summary>
    /// <param name="soundName">音效文件路径</param>
    /// <param name="pos">发声节点所在全局坐标</param>
    /// <param name="volume">音量</param>
    /// <param name="target">挂载节点, 为null则挂载到房间根节点下</param>
    public static AudioStreamPlayer2D PlaySoundEffectPosition(string soundName, Vector2 pos, float volume = 0.5f, Node2D target = null)
    {
        var sound = ResourceManager.Load<AudioStream>(soundName);
        var soundNode = GetAudioPlayer2DInstance();
        if (target != null)
        {
            target.AddChild(soundNode);
        }
        else
        {
            GameApplication.Instance.GlobalNodeRoot.AddChild(soundNode);
        }
        
        soundNode.GlobalPosition = pos;
        soundNode.Stream = sound;
        soundNode.Bus = Enum.GetName(typeof(BUS), 1);
        soundNode.VolumeDb = volume;
        soundNode.Play();
        return soundNode;
    }

    /// <summary>
    /// 在指定的节点下延时播放音效 用于音效
    /// </summary>
    /// <param name="soundName">音效文件路径</param>
    /// <param name="pos">发声节点所在全局坐标</param>
    /// <param name="delayTime">延时时间</param>
    /// <param name="volume">音量</param>
    /// <param name="target">挂载节点, 为null则挂载到房间根节点下</param>
    public static AudioStreamPlayer2D PlaySoundEffectPositionDelay(string soundName, Vector2 pos, float delayTime, float volume = 0.5f, Node2D target = null)
    {
        var sound = ResourceManager.Load<AudioStream>(soundName);
        var soundNode = GetAudioPlayer2DInstance();
        if (target != null)
        {
            target.AddChild(soundNode);
        }
        else
        {
            GameApplication.Instance.GlobalNodeRoot.AddChild(soundNode);
        }
        
        soundNode.GlobalPosition = pos;
        soundNode.Stream = sound;
        soundNode.Bus = Enum.GetName(typeof(BUS), 1);
        soundNode.VolumeDb = volume;
        soundNode.DelayPlay(delayTime);
        return soundNode;
    }

    /// <summary>
    /// 获取2D音频播放节点
    /// </summary>
    private static AudioPlayer2D GetAudioPlayer2DInstance()
    {
        if (_streamPlayer2DStack.Count > 0)
        {
            return _streamPlayer2DStack.Pop();
        }

        var inst = new AudioPlayer2D();
        inst.AreaMask = 0;
        return inst;
    }

    /// <summary>
    /// 获取音频播放节点
    /// </summary>
    private static AudioPlayer GetAudioPlayerInstance()
    {
        if (_streamPlayerStack.Count > 0)
        {
            return _streamPlayerStack.Pop();
        }

        return new AudioPlayer();
    }

    /// <summary>
    /// 回收2D音频播放节点
    /// </summary>
    private static void RecycleAudioPlayer2D(AudioPlayer2D inst)
    {
        var parent = inst.GetParent();
        if (parent != null)
        {
            parent.RemoveChild(inst);
        }

        inst.Stream = null;
        _streamPlayer2DStack.Push(inst);
    }

    /// <summary>
    /// 回收音频播放节点
    /// </summary>
    private static void RecycleAudioPlayer(AudioPlayer inst)
    {
        _streamPlayerStack.Push(inst);
    }
}