1. 音效管理器主脚本
csharp
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
public enum SoundType
{
// 背景音乐
BackgroundMusic,
// 游戏音效
BlockClick,
BlockMatch,
BlockDestroy,
BlockPlace,
// 道具音效
PowerUpCollect,
PowerUpUse,
PowerUpSpecial,
// UI音效
UIClick,
UIButtonHover,
UIOpenMenu,
UICloseMenu,
UINotification,
UISuccess,
UIFailure,
// 游戏状态音效
GameStart,
GameOver,
LevelComplete,
ScoreIncrease
}
[System.Serializable]
public class Sound
{
public SoundType type;
public AudioClip clip;
[Range(0f, 1f)]
public float volume = 1f;
[Range(0.1f, 3f)]
public float pitch = 1f;
public bool loop = false;
[HideInInspector]
public AudioSource source;
}
public class AudioManager : MonoBehaviour
{
public static AudioManager Instance { get; private set; }
[Header("音频混合器")]
public AudioMixer audioMixer;
[Header("音效列表")]
[SerializeField] private List<Sound> sounds = new List<Sound>();
[Header("音效设置")]
[SerializeField] private float soundFadeDuration = 1f;
private Dictionary<SoundType, Sound> soundDictionary = new Dictionary<SoundType, Sound>();
private Sound currentBackgroundMusic;
private Coroutine fadeCoroutine;
void Awake()
{
// 单例模式
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
return;
}
InitializeSounds();
LoadAudioSettings();
}
void InitializeSounds()
{
foreach (Sound sound in sounds)
{
AudioSource source = gameObject.AddComponent<AudioSource>();
source.clip = sound.clip;
source.volume = sound.volume;
source.pitch = sound.pitch;
source.loop = sound.loop;
sound.source = source;
soundDictionary[sound.type] = sound;
}
}
public void PlaySound(SoundType type, bool oneShot = false)
{
if (soundDictionary.TryGetValue(type, out Sound sound))
{
if (oneShot)
{
sound.source.PlayOneShot(sound.clip, sound.volume);
}
else
{
sound.source.Play();
}
}
else
{
Debug.LogWarning($"音效 {type} 未找到!");
}
}
public void StopSound(SoundType type)
{
if (soundDictionary.TryGetValue(type, out Sound sound))
{
sound.source.Stop();
}
}
public void PlayBackgroundMusic(SoundType type, bool fade = true)
{
if (soundDictionary.TryGetValue(type, out Sound newBackgroundMusic))
{
if (fadeCoroutine != null)
{
StopCoroutine(fadeCoroutine);
}
fadeCoroutine = StartCoroutine(FadeBackgroundMusic(newBackgroundMusic, fade));
}
}
private IEnumerator FadeBackgroundMusic(Sound newBackgroundMusic, bool fade)
{
// 淡出当前背景音乐
if (currentBackgroundMusic != null && currentBackgroundMusic.source.isPlaying)
{
float startVolume = currentBackgroundMusic.source.volume;
float timer = 0f;
while (timer < soundFadeDuration)
{
timer += Time.deltaTime;
currentBackgroundMusic.source.volume = Mathf.Lerp(startVolume, 0f, timer / soundFadeDuration);
yield return null;
}
currentBackgroundMusic.source.Stop();
currentBackgroundMusic.source.volume = startVolume;
}
// 播放新的背景音乐
currentBackgroundMusic = newBackgroundMusic;
if (fade)
{
currentBackgroundMusic.source.volume = 0f;
currentBackgroundMusic.source.Play();
float timer = 0f;
while (timer < soundFadeDuration)
{
timer += Time.deltaTime;
currentBackgroundMusic.source.volume = Mathf.Lerp(0f, currentBackgroundMusic.volume, timer / soundFadeDuration);
yield return null;
}
}
else
{
currentBackgroundMusic.source.volume = currentBackgroundMusic.volume;
currentBackgroundMusic.source.Play();
}
}
public void SetMasterVolume(float volume)
{
audioMixer.SetFloat("MasterVolume", Mathf.Log10(volume) * 20);
PlayerPrefs.SetFloat("MasterVolume", volume);
}
public void SetMusicVolume(float volume)
{
audioMixer.SetFloat("MusicVolume", Mathf.Log10(volume) * 20);
PlayerPrefs.SetFloat("MusicVolume", volume);
}
public void SetSFXVolume(float volume)
{
audioMixer.SetFloat("SFXVolume", Mathf.Log10(volume) * 20);
PlayerPrefs.SetFloat("SFXVolume", volume);
}
private void LoadAudioSettings()
{
// 加载保存的音量设置
SetMasterVolume(PlayerPrefs.GetFloat("MasterVolume", 1f));
SetMusicVolume(PlayerPrefs.GetFloat("MusicVolume", 1f));
SetSFXVolume(PlayerPrefs.GetFloat("SFXVolume", 1f));
}
// 快捷方法
public void PlayBlockClick() => PlaySound(SoundType.BlockClick, true);
public void PlayBlockMatch() => PlaySound(SoundType.BlockMatch, true);
public void PlayBlockDestroy() => PlaySound(SoundType.BlockDestroy, true);
public void PlayUIClick() => PlaySound(SoundType.UIClick, true);
public void PlayPowerUpUse() => PlaySound(SoundType.PowerUpUse, true);
}
2. UI音效触发器脚本
csharp
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class UISoundTrigger : MonoBehaviour, IPointerEnterHandler, IPointerClickHandler
{
[Header("音效设置")]
[SerializeField] private SoundType clickSound = SoundType.UIClick;
[SerializeField] private SoundType hoverSound = SoundType.UIButtonHover;
[SerializeField] private bool enableHoverSound = true;
private Button button;
private Toggle toggle;
private Slider slider;
void Start()
{
button = GetComponent<Button>();
toggle = GetComponent<Toggle>();
slider = GetComponent<Slider>();
// 为UI组件添加声音
if (button != null)
{
button.onClick.AddListener(OnButtonClick);
}
if (toggle != null)
{
toggle.onValueChanged.AddListener(OnToggleValueChanged);
}
if (slider != null)
{
slider.onValueChanged.AddListener(OnSliderValueChanged);
}
}
private void OnButtonClick()
{
if (AudioManager.Instance != null)
{
AudioManager.Instance.PlaySound(clickSound, true);
}
}
private void OnToggleValueChanged(bool isOn)
{
if (AudioManager.Instance != null && isOn)
{
AudioManager.Instance.PlaySound(clickSound, true);
}
}
private void OnSliderValueChanged(float value)
{
// 可以为滑动条添加特殊音效
if (AudioManager.Instance != null)
{
AudioManager.Instance.PlaySound(SoundType.UIClick, true);
}
}
public void OnPointerClick(PointerEventData eventData)
{
// 备用点击检测
if (button == null && toggle == null && slider == null)
{
if (AudioManager.Instance != null)
{
AudioManager.Instance.PlaySound(clickSound, true);
}
}
}
public void OnPointerEnter(PointerEventData eventData)
{
if (enableHoverSound && AudioManager.Instance != null)
{
AudioManager.Instance.PlaySound(hoverSound, true);
}
}
}
3. 游戏块音效脚本
csharp
using UnityEngine;
public class BlockSoundController : MonoBehaviour
{
[Header("音效设置")]
[SerializeField] private SoundType clickSound = SoundType.BlockClick;
[SerializeField] private SoundType matchSound = SoundType.BlockMatch;
[SerializeField] private SoundType destroySound = SoundType.BlockDestroy;
[SerializeField] private SoundType placeSound = SoundType.BlockPlace;
[Header("音效触发")]
[SerializeField] private bool playOnClick = true;
[SerializeField] private bool playOnDestroy = true;
private Collider blockCollider;
void Start()
{
blockCollider = GetComponent<Collider>();
if (blockCollider == null)
{
blockCollider = gameObject.AddComponent<BoxCollider>();
}
}
void OnMouseDown()
{
if (playOnClick && AudioManager.Instance != null)
{
AudioManager.Instance.PlaySound(clickSound, true);
}
}
void OnDestroy()
{
if (playOnDestroy && AudioManager.Instance != null)
{
AudioManager.Instance.PlaySound(destroySound, true);
}
}
public void PlayMatchSound()
{
if (AudioManager.Instance != null)
{
AudioManager.Instance.PlaySound(matchSound, true);
}
}
public void PlayPlaceSound()
{
if (AudioManager.Instance != null)
{
AudioManager.Instance.PlaySound(placeSound, true);
}
}
}
4. 音效设置UI面板
csharp
using UnityEngine;
using UnityEngine.UI;
public class AudioSettingsPanel : MonoBehaviour
{
[Header("音量控制滑块")]
[SerializeField] private Slider masterVolumeSlider;
[SerializeField] private Slider musicVolumeSlider;
[SerializeField] private Slider sfxVolumeSlider;
[Header("测试按钮")]
[SerializeField] private Button testSFXButton;
[SerializeField] private Button testMusicButton;
void Start()
{
InitializeSliders();
SetupTestButtons();
}
void InitializeSliders()
{
if (masterVolumeSlider != null)
{
masterVolumeSlider.value = PlayerPrefs.GetFloat("MasterVolume", 1f);
masterVolumeSlider.onValueChanged.AddListener(OnMasterVolumeChanged);
}
if (musicVolumeSlider != null)
{
musicVolumeSlider.value = PlayerPrefs.GetFloat("MusicVolume", 1f);
musicVolumeSlider.onValueChanged.AddListener(OnMusicVolumeChanged);
}
if (sfxVolumeSlider != null)
{
sfxVolumeSlider.value = PlayerPrefs.GetFloat("SFXVolume", 1f);
sfxVolumeSlider.onValueChanged.AddListener(OnSFXVolumeChanged);
}
}
void SetupTestButtons()
{
if (testSFXButton != null)
{
testSFXButton.onClick.AddListener(() => {
if (AudioManager.Instance != null)
{
AudioManager.Instance.PlaySound(SoundType.UIClick, true);
}
});
}
if (testMusicButton != null)
{
testMusicButton.onClick.AddListener(() => {
if (AudioManager.Instance != null)
{
AudioManager.Instance.PlaySound(SoundType.UISuccess, true);
}
});
}
}
void OnMasterVolumeChanged(float value)
{
if (AudioManager.Instance != null)
{
AudioManager.Instance.SetMasterVolume(value);
}
}
void OnMusicVolumeChanged(float value)
{
if (AudioManager.Instance != null)
{
AudioManager.Instance.SetMusicVolume(value);
}
}
void OnSFXVolumeChanged(float value)
{
if (AudioManager.Instance != null)
{
AudioManager.Instance.SetSFXVolume(value);
}
}
}
5. 音频混合器设置(AudioMixer)
创建一个AudioMixer并命名为"MainMixer",设置以下参数:
- MasterVolume (暴露参数)
- MusicVolume (暴露参数)
- SFXVolume (暴露参数)
6. 音效文件建议
-
背景音乐:
- 游戏配乐:可在pixabay.com的"游戏配乐"分类查找
- 休闲背景音乐:适合消除类游戏
-
方块音效:
- 点击:清脆的"click"或"pop"声
- 消除:短促的"ding"或"ping"声
- 放置:轻微的"thud"或"tap"声
-
UI音效:
- 点击:界面按钮音效
- 悬停:轻微的"hover"声
- 通知:短促提示音
-
道具音效:
- 收集:悦耳的"collect"声
- 使用:特殊效果音
7. 使用示例脚本
csharp
using UnityEngine;
public class GameController : MonoBehaviour
{
void Start()
{
// 播放背景音乐
AudioManager.Instance.PlayBackgroundMusic(SoundType.BackgroundMusic, true);
// 游戏开始音效
AudioManager.Instance.PlaySound(SoundType.GameStart, true);
}
public void OnBlockMatched()
{
// 方块匹配音效
AudioManager.Instance.PlaySound(SoundType.BlockMatch, true);
// 如果连续匹配,可以播放特殊音效
if (IsCombo())
{
AudioManager.Instance.PlaySound(SoundType.PowerUpSpecial, true);
}
}
public void OnPowerUpCollected()
{
// 收集道具音效
AudioManager.Instance.PlaySound(SoundType.PowerUpCollect, true);
}
public void OnLevelComplete()
{
// 停止背景音乐
AudioManager.Instance.StopSound(SoundType.BackgroundMusic);
// 播放完成音效
AudioManager.Instance.PlaySound(SoundType.LevelComplete, true);
}
private bool IsCombo()
{
// 判断连击逻辑
return false;
}
}
安装步骤:
- 创建一个空GameObject,命名为"AudioManager"
- 将AudioManager脚本附加到该对象
- 在Inspector面板中,为每种SoundType分配对应的AudioClip
- 为UI按钮添加UISoundTrigger脚本
- 为游戏块添加BlockSoundController脚本
- 创建音量设置面板并附加AudioSettingsPanel脚本