当前位置: 首页 > news >正文

Unity 简易音乐播放系统

前言

众所周知, Unity自带音效播放没有回调,不能自动播放clip列表; 所以简单实现一个带自动播放功能的接口,用以实现音乐列表的逐个播放.

一. 功能分析

  1. 首先要求切换场景时音乐不停,只在需要时播放
  2. 其次即传入音乐名和播放次数,即可将该音乐循环播放指定次数
  3. 可以直接传入一个包含音乐名和播放次数的列表, 将所有列表中的音乐全部播放指定次数
  • 第一点很好解决,只要将挂载AudioListenerAudioSource的节点设置为切换场景不销毁即可
  • 第二个功能则需要通过开启协程,通过协程来判断音乐的播放状态, 再传入回调函数, 递归调用播放音乐函数
  • 第三个功能在第二个功能的基础上实现,通过传入回调函数并递归实现

二.实现

  1. 初始化
    这里的选择是创建一个AudioSystem节点挂在Listener,下面挂着两个节点分别挂载音乐节点和音效节点.
    在这里插入图片描述
    将父节点设置为不因场景跳转销毁的模式.
    注意在这里我将Loop属性关闭, 因为我是通过音乐是否在播放来实现协程检测的.
public class AudioSystem : MonoBehaviour
{
	// 单例
    static AudioSystem audioSys;
	// 音乐播放
    static AudioSource musicSource;
	// 音效播放
    static AudioSource soundSource;

	void Awake()
	{
		DontDestroyOnLoad(gameObject);
	}
    public static AudioSystem Instance
    {
        get
        {
            if(audioSys == null)
            {
                audioSys = new GameObject("AudioSystem").AddComponent<AudioSystem>();
                audioSys.gameObject.AddComponent<AudioListener>();
                audioSys.Init();
            }
            return audioSys;
        }
    }

    void Init()
    {
        musicSource = new GameObject("musicSource").AddComponent<AudioSource>();
        musicSource.transform.SetParent(audioSys.transform);
        musicSource.playOnAwake = false;
        musicSource.loop = false;
        soundSource = new GameObject("soundSource").AddComponent<AudioSource>();
        soundSource.transform.SetParent(audioSys.transform);
        soundSource.playOnAwake = false;
        soundSource.loop = false;
    }
}
  1. 音乐播放
    函数传入name音乐名, loopTime循环次数, action回调函数
public void PlayMusic(string name, int loopTime, Action action = null)

通过协程实际上很好实现,AudioSource中有个属性为IsPlaying,通过这个属性我们可以随时知道当前是否有音频在播放; 所以直接开一个协程等待即可.

  • 主要分为几步:
  • 判断是否正在播放该音频,如果是,那么重启一个协程即可,
  • 如果不是当前正在播放的音频,则加载需要播放的资源并播放.
  • 开启协程时,将循环次数减少,代表还剩多少遍播放.

协程中:

  • 先等待音乐播放完
  • 播完后判断是否需要循环
  • 循环的话就调用PlayMusic
  • 不循环则执行回调
	static string nowName = null;
	static Coroutine coroutine;
    // 播放完成执行acion
    public void PlayMusic(string name, int loopTime, Action action = null)
    {
        if (coroutine != null)
            StopCoroutine(coroutine);
        
        if(nowName == name)
        {
        	//正在播放当前资源
            if(!musicSource.isPlaying)
                musicSource.Play();
        }
        else
        {
        	//加载资源
        	AudioClip clip = Resources.Load<AudioClip>(PATH + fname + ".wav");
            if(clip == null)
                return;
            if (musicSource.isPlaying)
                musicSource.Stop();
            musicSource.clip = clip;
            nowName = name;
            musicSource.Play();
        }
        // 开启协程,并将循环次数 - 1
        coroutine = StartCoroutine(MusicLoop(name, loopTime - 1, action));
    }

    IEnumerator MusicLoop(string path, int loopTime, Action action = null)
    {
    	//等待播放完毕
        while (musicSource.isPlaying) yield return null;
		//播放次数不为0就继续循环
        if (loopTime !=  0)
            PlayMusic(path, loopTime, action);
        //播放完毕且有回调则执行
        else if(action != null)
            action();
    }
  1. 播放列表
    这里只要利用回调函数来移动下标就可以了
    //播放列表音乐
    public void PlayMusicList(List<KeyValuePair<string, int>> list, int index = 0)
    {
        if (list == null || index >= list.Count) return;
        PlayMusic(list[index].Key, list[index].Value, () =>
        {
            PlayMusicList(list, index + 1);
        });
    }

三. 完整代码

  • 注意销毁协程

public class AudioSystem : MonoBehaviour
{
	// 单例
    static AudioSystem audioSys;
	// 音乐播放
    static AudioSource musicSource;
	// 音效播放
    static AudioSource soundSource;
	// 当前音乐名
    static string nowName = null;

	void Awake()
	{
		DontDestroyOnLoad(gameObject);
	}
	// 单例
    public static AudioSystem Instance
    {
        get
        {
            if(audioSys == null)
            {
                audioSys = new GameObject("AudioSystem").AddComponent<AudioSystem>();
                audioSys.gameObject.AddComponent<AudioListener>();
                audioSys.Init();
            }
            return audioSys;
        }
    }

	// 创建两个子节点
    void Init()
    {
        musicSource = new GameObject("musicSource").AddComponent<AudioSource>();
        musicSource.transform.SetParent(audioSys.transform);
        musicSource.playOnAwake = false;
        musicSource.loop = false;
        soundSource = new GameObject("soundSource").AddComponent<AudioSource>();
        soundSource.transform.SetParent(audioSys.transform);
        soundSource.playOnAwake = false;
        soundSource.loop = false;
    }

    //播放列表音乐
    public void PlayMusicList(List<KeyValuePair<string, int>> list, int index = 0)
    {
        if (list == null || index >= list.Count) return;
        PlayMusic(list[index].Key, list[index].Value, () =>
        {
            PlayMusicList(list, index + 1);
        });
    }

    static Coroutine coroutine;
    // 播放完成执行acion
    public void PlayMusic(string name, int loopTime, Action action = null)
    {
        if (coroutine != null)
            StopCoroutine(coroutine);
        
        if(nowName == name)
        {
        	//正在播放当前资源
            if(!musicSource.isPlaying)
                musicSource.Play();
        }
        else
        {
        	//加载资源
        	AudioClip clip = Resources.Load<AudioClip>(PATH + fname + ".wav");
            if(clip == null)
                return;
            if (musicSource.isPlaying)
                musicSource.Stop();
            musicSource.clip = clip;
            nowName = name;
            musicSource.Play();
        }
        // 开启协程,并将循环次数 - 1
        coroutine = StartCoroutine(MusicLoop(name, loopTime - 1, action));
    }

    IEnumerator MusicLoop(string path, int loopTime, Action action = null)
    {
    	//等待播放完毕
        while (musicSource.isPlaying) yield return null;
		//播放次数不为0就继续循环
        if (loopTime !=  0)
            PlayMusic(path, loopTime, action);
        //播放完毕且有回调则执行
        else if(action != null)
            action();
    }

    public void SetMusicVolume(float volume)
    {
        musicSource.volume = Mathf.Clamp(volume, 0, 1);
    }

    public void PlayOneShot(string name)
    {

    }

    private void OnDestroy()
    {
        if (coroutine != null)
            StopCoroutine(coroutine);
    }
}

相关文章:

  • wordpress添加喜欢or分享按钮/百度指数的数值代表什么
  • wordpress sql server/直播回放老卡怎么回事
  • 糗事百科 wordpress/关键词优化的技巧
  • 广州网站推广公司/广州短视频代运营
  • 衡水网站制/我想做电商
  • 个人游戏网站备案/南昌百度搜索排名优化
  • Android | Fragment
  • 代码随想录算法训练营第13天 239.滑动窗口最大值、347. 前 K 个高频元素
  • 2023-01-16 Dubbo+Zookeeper集成
  • 我的周刊(第074期)
  • WuThreat身份安全云-TVD每日漏洞情报-2023-01-16
  • 【Python学习】条件和循环
  • 寒假集训一期总结(一)–––思维训练
  • Rpc了解
  • 【开发心得】Spring Mail发送邮件
  • 编译基于armV8架构的opencv,并在rock3a开发板上运行
  • Unity | 序列化(Serialized)和反序列化(NonSerialized)是什么意思
  • C/C++路面导航系统[2023-01-16]