实现一个音频管理器
学习目标
完成本教程后,你会学会
- 如何统一播放游戏内的音频
操作流程
- 在 Project 视图中,在 Resources 目录下建立文件夹,名为 Sounds
- 实现代码
csharp
using System;
using System.Collections.Generic;
using System.Linq;
using DG.Tweening;
using UnityEngine;
using Random = UnityEngine.Random;
//声音管理器
public class AudioManager : MonoBehaviour
{
[Header("BGM音频源")]
public AudioSource bgmSource;
private List<AudioClip> PlayList = new (); //播放列表
private int bgmIndex = -1;
public static AudioManager Instance; // 单例模式
public float EffectVolume = 1f;
private float totalVolume = 0.7f;
public float bgmVolume = 1f;
private List<AudioSource> effectSources = new();
private Dictionary<object, float> lastPlayedTimes = new ();
private void Awake()
{
Instance = this;
}
public void Init()
{
bgmSource = gameObject.AddComponent<AudioSource>();
}
public void ChangeBgmVolume(float volume)
{
bgmVolume = volume;
bgmSource.volume = volume * totalVolume;
}
public void ChangeEffectVolume(float volume)
{
EffectVolume = volume;
}
private void Update()
{
if (bgmSource == null) return;
if (!bgmSource.isPlaying)
{
PlayGameBgmList(true);
}
}
public void PlayBGMList(List<AudioClip> bgmList)
{
if (bgmList == null || bgmList.Count == 0)
{
return;
}
RandomPlayList(bgmList);
PlayGameBgmList();
}
public void PlayBGM(string name)
{
AudioClip clip = Resources.Load<AudioClip>("Sounds/Music/" + name);
PlayBGMList(new List<AudioClip>(){clip});
}
private void PlayGameBgmList(bool next = true)
{
StopAllCoroutines();
if (PlayList.Count == 0)
{
return;
}
if (bgmSource.isPlaying && bgmIndex != -1)
{
return;
}
if (next)
{
if (tempClip != null && PlayList.Contains(tempClip))
{
PlayList.Remove(tempClip);
tempClip = null;
}
else
{
bgmIndex++;
}
}
if (bgmIndex >= PlayList.Count)
{
bgmIndex = 0;
}
AudioClip clip = PlayList[bgmIndex];
bgmSource.clip = clip;
bgmSource.Play();
}
//播放音效
public void PlayEffect(string name)
{
AudioClip clip = Resources.Load<AudioClip>("Sounds/" + name);
PlayEffect(clip);
}
public void PlayEffect(AudioClip clip)
{
if (clip == null) return;
//检查是否在短时间内重复播放同一音效
if (lastPlayedTimes.TryGetValue(clip, out var lastTime) && Time.time - lastTime < 0.1f)
{
return; // 如果在0.1秒内重复播放,则不播放
}
lastPlayedTimes[clip] = Time.time; // 更新最后播放时间
bgmSource.PlayOneShot(clip);
}
public AudioClip tempClip=null;
private void RandomPlayList(List<AudioClip> BGMList)
{
PlayList = new (BGMList.ToArray());
//随机打乱播放列表
for (int i = 0; i < PlayList.Count; i++)
{
var temp = PlayList[i];
int randomIndex = Random.Range(i, PlayList.Count);
PlayList[i] = PlayList[randomIndex];
PlayList[randomIndex] = temp;
}
PlayList=new List<AudioClip> { PlayList[0] };
bgmSource.Stop();
}
}
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
- 将组件拖入 GameManager 物体上
概念解释
AudioSource
AudioSource 是一个播放音频资源的组件,AudioClip 代表具体的可读取资源。
你可以理解成 AudioSource 是播放器,AudioClip 是音乐文件。
单例模式
由于整个游戏只需要一个音频管理器,需要保证全局唯一性,我们采用单例模式。在类初始化时向外提供其实例的引用,这样保证了在项目中可以随时访问到这个单例。详情请见 设计模式 - 单例模式