音频控制

音频文件导入

常用格式

wavmp3oggaiff

音频文件参数

  • Force To Mono:多声道转单声道

    • Normalize:强制为单声道时,混合过程中被标准化
  • Load In Background:通常在音效文件较大时使用。在后台加载,不阻塞主线程

  • Ambisonic:立体混响声(360度视频、XR应用程序时使用)

  • LoadType:加载类型

    • Decompress On Load:不压缩形式存在内存,加载快,但是内存占用高。适用于小音效
    • Compress in memory:压缩形式存在内存,加载慢,内存小。适用于较大音效
    • Streaming:以流形式存在,使用时解码。内存占用最小,cpu消耗高。
  • Preload Audio Data:预加载音频。勾选后,进入场景就加载;不勾选,第一次使用时才加载

  • Compression Format:压缩方式

    • PCM:音频以最高质量存储
    • Vorbis:相对PCM压缩的更小,根据质量决定
    • ADPCM:包含噪音,会被多次播放的声音,如打击音效
  • Quality:音频质量

  • Sample Rate Setting:PCM和ADPCM压缩格式允许自动优化或手动降低采样率

    • Preserve Sample Rate:保持采样率不变
    • Optimize Sample Rate:根据分析的最高频率内容自动优化采样率
    • Override Sample Rate:允许手动覆盖采样率,可有效的将其用于丢弃帧率的内容

参数相关

音效系统由两个脚本组成:Audio Source音频源和AudioListener音频监听。

Audio Source 音频源

  • AudioClip:音频文件
  • Output:输出装置。默认输出到场景中的音频监听器(MainCamera上的Audio Listener),可以更改为输出到混音器
  • Mute:静音开关
  • Bypass Effect:开关滤波器效果
  • Bypass Listener Effects:快速开关所有监听器
  • Bypass Reverb Zones:快速开关所有混响区
  • Play On Awake:对象创建时就播放音乐
  • Loop:循环
  • Priority:优先级。很多音效同时播放时,优先级越高越不容易被覆盖
  • Volume:音量大小
  • Pitch:音高。游戏加速时可以使用
  • Stereo Pan:左右声道
  • Spatial Blend:近大远小的效果
  • Reverb Zone Mix:到混响区的输出信号量
  • 3D Sound Settings:和Spatial Blend参数成正比应用

AudioListener 音频监听脚本

默认挂载在主摄像机上一个用来收声的脚本,只用在主摄像机上有就可以了。

代码相关

  1. 得到音频源脚本
1
AudioSource audioSource = this.GetComponent<AudioSource>();
  1. 控制播放停止
  • 播放音效
1
audioSource.Play();

也可以延迟播放,参数填延迟的秒数:

1
audioSource.PlayDelayed(5);
  • 停止音效
1
audioSource.Stop();
  • 暂停音效
1
audioSource.Pause();

停止暂停和暂停后Play效果是一样的,都会继续播放当前的音效。

停止(Stop)会重置播放状态,暂停(Pause)会保留当前播放位置。

  1. 检测音频源播放完毕
1
2
3
4
if(audioSource.isPlaying)
{
// 内部逻辑
}

值得注意的是:一个GameObject可以挂载多个音效源脚本AudioSource,使用时要注意如果挂载多个,一定要自己管理他们,控制他们的播放、停止。

另外,如果你想播放一次性音效,可以采用AudioSource中的静态方法,它的处理逻辑是:Unity帮你创建一个临时的GameObject并为其添加AudioSource组件,播放完音频后自动销毁这个临时对象,全程无需你手动管理。

它的使用API是:

1
2
3
// 参数1:音频文件
// 参数2:要播放音频的位置
AudioSource.PlayClipAtPoint(destoryAudio, transform.position);

麦克风相关

  • 获取麦克风的名字
1
String[] str = MicroPhone.devices;
  • 开始录制
1
2
3
4
5
6
7
8
// 参数1:设备名,传空使用默认设备
// 参数2:超过录制长度后是否重头录制
// 参数3:录制时长
// 参数4:采样率
if(Input.GetKeyDown(KeyCode.Space))
{
clip = Microphone.Start(null, false, 10, 44100);
}
  • 结束录制
1
2
// 参数:设备名
Microphone.End(null);
  • 获取音频数据用于存储或者运输

用于存储数组数据的长度 = 声道数 * 剪辑长度

1
2
3
float[] f = new float[clip.channels * clip.samples];
clip.GetData(f, 0);
print(f.Length);

核心模板

在实际游戏开发中,我们常常采用数据与逻辑分离的架构。

  • SoundEffectLibrary(音效库):专门用来存储和归类音频资源
  • SoundEffectManager(音效管理器):负责实际的播放逻辑

SoundEffectLibrary(音效库)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
using System.Collections.Generic;  
using UnityEngine;

public class SoundEffectLibrary : MonoBehaviour
{
    [SerializeField] private SoundEffectGroup[] soundEffectGroups;
    private Dictionary<string, List<AudioClip>> soundDictionary;
    private void Awake()  
    {  
        InitializeDictionary();  
    }
   
    private void InitializeDictionary()  
    {  
        soundDictionary = new Dictionary<string, List<AudioClip>>();  
        foreach (var soundEffectGroup in soundEffectGroups)  
        {  
            soundDictionary[soundEffectGroup.name] = soundEffectGroup.audioClips;
        }  
    }

    public AudioClip GetRandomClip(string name)  
    {  
        if (soundDictionary.ContainsKey(name))  
        {  
            List<AudioClip> audioClips = soundDictionary[name];
            if(audioClips.Count > 0)
            {
                return audioClips[UnityEngine.Random.Range(0, audioClips.Count)];
            }
        }
        return null;  
    }
}

// 定义一个音效组的结构  
[System.Serializable]  
public struct SoundEffectGroup  
{  
    public string name; // 音效组的名字
    public List<AudioClip> audioClips; // 该组包含的音频文件
}

SoundEffectManager(音效管理器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
using Unity.VisualScripting;
using UnityEngine;  
using UnityEngine.UI;

public class SoundEffectManager : MonoBehaviour
{
    public static SoundEffectManager Instance { get; private set; }
    private static AudioSource audioSource;  
    private static SoundEffectLibrary soundEffectLibrary;
   
    [SerializeField] private Slider sfxSlider;
   
    private void Awake()  
    {  
        if (Instance == null)  
        {  
            Instance = this;  
          // DontDestroyOnLoad(gameObject); // 跨场景不销毁  
        }  
        else  
        {  
            Destroy(gameObject);  
            return;  
        }
        audioSource = GetComponent<AudioSource>();  
        soundEffectLibrary = GetComponent<SoundEffectLibrary>();
    }

    public static void Play(string soundName)  
    {
        AudioClip audioClip = soundEffectLibrary.GetRandomClip(soundName);    
        if (audioClip != null)  
        {  
            // 使用 PlayOneShot 允许音效重叠播放  
            audioSource.PlayOneShot(audioClip);  
        }  
    }

    private void Start()  
    {    
        if (sfxSlider != null)  
        {  
            sfxSlider.onValueChanged.AddListener(delegate { OnValueChanged(); });
        }  
    }

    public static void SetVolume(float volume)  
    {  
        if (audioSource != null)  
        {  
            audioSource.volume = volume;  
        }  
    }
   
    public void OnValueChanged()
    {
        SetVolume(sfxSlider.value);
    }
}