Unity 音效模块(二)

Unity3D 音效模块(二)


前言

解决了 音效模块一 的缺点(只有一个播放器audiosource)
利用有限个播放器(audiosource)管理无限个音效片段(clip)

基本思路

Unity 音效模块(二)_第1张图片

代码实现

注释:

  • 从设计思路考虑 首先实现简单的SourceManager
  • 用对象池设计模式方便动态扩容
  • 大概有以下几个功能:
    1. 在众多播放器里拿出空闲的播放器( AudioSource
    2. 播放一个音效(Clip
    3. 停止播放一个音效(Clip
    4. 释放多余的播放器(AudioSource
using System.Collections.Generic;
using UnityEngine;

public class SourceManager {

    //-----------------------------字段,属性,引用---------------------------------
    //播放器个数 初始化为两个
    private const int AudioSourceCount = 2;

    /// 
    /// 所有播放器的集合
    /// 
    List<AudioSource> m_allSources;

    /// 
    /// 用于保存释放的临时list
    /// 
    /// 实际类型是播放器
    List<AudioSource> m_tmpSources = new List<AudioSource> ();

    GameObject go;
    //=============================================================================

    //---------------------------------构造函数-------------------------------------
    /// 
    /// 构造函数
    /// 
    /// /// 游戏对象
    public SourceManager (GameObject _tmpGo) {
        this.go = _tmpGo;
        Initial ();
    }
    //=============================================================================

    //---------------------------------初始化播放器----------------------------------
    /// 
    /// 初始化allSources集合
    /// 
    public void Initial () {
        m_allSources = new List<AudioSource> ();
        for (int i = 0; i < AudioSourceCount; i++) {
            AudioSource tmpSource = go.AddComponent<AudioSource> (); //对象挂载音效组件
            m_allSources.Add (tmpSource); //存2个音效组件
        }
    }
    //=============================================================================

    //---------------------------------获取空闲播放器---------------------------------
    /// 
    /// 获取空闲播放器
    /// 
    /// 返回 AudioSource
    public AudioSource GetFreeAudio () {
        //循环找到空闲的
        for (int i = 0; i < m_allSources.Count; i++) {
            if (!m_allSources[i].isPlaying)
                return m_allSources[i];
        }
        //如果上面循环完没找到就动态创建一个
        AudioSource tmpSource = go.AddComponent<AudioSource> ();
        m_allSources.Add (tmpSource);
        return tmpSource;
    }
    //=============================================================================

    //--------------------------------释放空闲的播放器--------------------------------
    /// 
    /// 释放空闲的播放器
    /// 
    public void ReleaseFreeAudio () {
        int tmpCount = 0;

        /*从m_allSources里找空闲放到tmp里准备删除
          这里必须要单独存出来 不能直接在循环里面删*/
        for (int i = 0; i < m_allSources.Count; i++) {
            if (!m_allSources[i].isPlaying) { //空闲一个就计数
                tmpCount++;
                if (tmpCount > AudioSourceCount) { //默认是2个多了就删 
                    m_tmpSources.Add (m_allSources[i]);
                }
            }
        }
        //删all里面播放器 数据是从上面那个循环拿到的 
        for (int i = 0; i < m_tmpSources.Count; i++) {
            AudioSource tmpSource = m_tmpSources[i];
            //从m_allSources中移除
            m_allSources.Remove (tmpSource);
            //从场景中移除组件
            GameObject.Destroy (tmpSource);
        }
        //本次函数走完会清空所有数据 ,确保没有对象再引用 防止没被删完
        m_tmpSources.Clear ();
    }
    //=============================================================================

    //----------------------------------停止播放------------------------------------
    public void Stop (string _audioName) {
        for (int i = 0; i < m_allSources.Count; i++) {
            if (m_allSources[i].isPlaying && m_allSources[i].clip.name.Equals (_audioName))
                m_allSources[i].Stop ();
        }
    }
    //=============================================================================
}

接下来实现ClipManager
大概功能有:

  1. 存储所有的Clip - - 这里可以单独写一个类来存
  2. 从配置文件读取音效名
  3. 动态添加所有音效并存储
using System.IO;
using UnityEngine;

public class ClipManager {

    string[] m_clipName;
    SingleClip[] m_allSingleClip;//用单独一个类来存储

    public ClipManager () {
        ReadConfig ();
        LoadClips ();
    }

    //---------------------------------根据名字找音源片段----------------------------
    /// 
    /// 更具名字找到SingleClip
    /// 
    /// 音效名字
    /// 返回SingleClip
    public SingleClip FindClipByName (string _clipName) {
        for (int i = 0; i < m_clipName.Length; i++) {
            if (m_clipName[i].Equals (_clipName)) //判断两个名字是否相等
                return m_allSingleClip[i];
        }
        return null;
    }
    //=============================================================================

    //---------------------------------加载音效到内存---------------------------------
    /// 
    /// 加载音效片段
    /// 
    public void LoadClips () {
        m_allSingleClip = new SingleClip[m_clipName.Length];
        //将Resources里的音效加载出来,并用一个类数组存起来
        for (int i = 0; i < m_clipName.Length; i++) {
            var tempClip = Resources.Load<AudioClip> ("AudioAssets/" + m_clipName[i]);

            SingleClip tempSingle = new SingleClip (tempClip);

            m_allSingleClip[i] = tempSingle;
        }
    }
    //=============================================================================

    //----------------------------------配置文件读取---------------------------------
    //这里读取的是本地 一般情况是从服务器读取
    /// 
    /// 从配置文件读取
    /// 
    public void ReadConfig () {
        //加载地址
        string fileAddress = System.IO.Path.Combine (Application.streamingAssetsPath, "AudioConfig");
        //打开文件
        FileInfo fileInfo = new FileInfo (fileAddress);
        //如果文件存在
        if (fileInfo.Exists) {
            //用文件系统来读取 
            StreamReader r = new StreamReader (fileAddress);
            var tempStr = r.ReadLine ();
            //从配置文件第一行读取总数
            int lineCount = 0;
            if (int.TryParse (tempStr, out lineCount)) {

                m_clipName = new string[lineCount];

                for (int i = 0; i < lineCount; i++) {

                    tempStr = r.ReadLine ();

                    //以 “空格” 分割 存在splits里
                    string[] splits = tempStr.Split (" ".ToCharArray ());

                    m_clipName[i] = splits[0];

                }
                r.Close ();
            }
        }
    }
    //=============================================================================
}

SingleClip类 这里为了区分单独写出来了 也可以直接写在ClipManager里面

/**
此类用来存clip片段 和 播放对应片段
**/
using UnityEngine;

public class SingleClip {

    AudioClip clip;

    public SingleClip (AudioClip _clip) {
        this.clip = _clip;
    }
    /// 
    /// 播放一个音效片段
    /// 
    /// 播放器
    public void Play (AudioSource _audioSource) {
        _audioSource.clip = clip;
        _audioSource.Play ();
    }
}

最后AudioManager 把左右管理者结合

Unity 音效模块(二)_第2张图片

using UnityEngine;

public class AudioManager : MonoBehaviour {

    public static AudioManager Instance;

    SourceManager m_sourceManager;

    ClipManager m_clipManager;

    private void Start () {

        Instance = this;

        m_sourceManager = new SourceManager (gameObject);

        m_clipManager = new ClipManager ();
    }

    public void Play (string _audioName) {
        //找空闲播放器
        var tempSorce = m_sourceManager.GetFreeAudio ();
        //找对应片段 返回SingleClip
        var tempClip = m_clipManager.FindClipByName (_audioName);

        tempClip.Play (tempSorce);

        m_sourceManager.ReleaseFreeAudio ();
    }

    public void Stop (string _audioName) {
        m_sourceManager.Stop (_audioName);
    }
    
	public void Free (){
        m_sourceManager.ReleaseFreeAudio ();
    }
}

测试

  • 首先在Assets文件夹创建StreamingAssets文件夹
    • StreamingAssets作为保留文件夹其作用和Resources 相似他们都是只读,区别在于:
      • 前者会将内容原封不动的打入包中 主要用于存放一些二进制文件
      • 后者会在打包时将内容压缩和加密 可以存放一些Prefab
  • 接着创建一个配置文件放到StreamingAssets
    • Windows 是 txt文件
      • 可以直接右键创建 内容和下图一样
    • Mac 是 不带后缀名文本文件,在linux中文本文件是不区分格式的(acsii)。
      • 可以在终端用 touch命令创建文件 ,用vim来编辑,写入音效名 注意第一行为音效个数
        Unity 音效模块(二)_第3张图片 Unity 音效模块(二)_第4张图片
  • 拖几个音效到以下文件夹 音效名必须和配置文件中一样
    Unity 音效模块(二)_第5张图片
  • 在工程中创建两个按钮用于测试 结构如下图

  • 编写Test脚本并拖入panel 如图

    这里用Find匿名函数 只是为了方便演示 实际运用可以根据情况而定
    释放的时机更具情况而定,可以达到某个条件释放,也可以做定时器释放

using UnityEngine;
using UnityEngine.UI;

public class Test : MonoBehaviour {
    void Start () {
        GameObject.Find ("Button1").GetComponent<Button> ().onClick.AddListener (() => {
            AudioManager.Instance.Play ("asd");//使用方法
        });
        GameObject.Find ("Button2").GetComponent<Button> ().onClick.AddListener (() => {
            AudioManager.Instance.Play ("qwe");
        });
    }

    float t, freeTime = 5f;
    void Update () {
        t += Time.deltaTime;
        if (t > freeTime) {
            AudioManager.Instance.Free ();//释放方法
            t = 0;
            print ("释放");
        }
    }
}

最后

学习笔记,仅供参考,如有不正,欢迎指正

你可能感兴趣的:(Unity3D)