unity原生声音系统没什么好说的,说一下Wwise音效系统吧
首先现在市面上的排行榜前面的游戏差不多有一半的游戏都在使用这个引擎,这个引擎的功能十分强大,接入也非常简单,最主要的就是他能给音效制作人员提供非常丰富的空间可以去自由的调节,另外还可以让技术人员完全的不需要关心音效的制作细节,也就是说,他能做到让技术人员和音效制作人员完全的脱离,分工比较明确,但是需要注意的一点是,这个引擎需要音效制作人员的技术十分完备
首先要说明一下,两种引擎的使用场景
1.如果你的项目比较小,如果对音效没有什么特殊的需求,比如说你想做一个城市的背景音,或者并不做那种次世代的、3D的,实际上就简简单单的换换音效就可以了,没必要做这么复杂,直接使用unity原生的音效引擎就好了
2.如果你是想做一个音乐类的游戏,并且你的团队足够强大(特指音效制作人员技术方面),项目比较复杂,比如在场景中有丰富的地形,又是次世代的,要求有非常丰富的环境音效(树林、山川、河流、小鸟、优美的风景等等),这样的话,即是说你的团队中并没有这样的技术人员,也应该选择使用Wwise引擎,毕竟人员可以找。
3.共同点:两种引擎都是支持跨平台的,不管是移动设备还是pc,或者其他平台。
现在来说一说,开发人员与音效制作人员是怎样配合的,
实际上,所有的声音都是通过两者约定好的接口,接口实际上很简单,一个是设置一下发送事件,基本上所有声音的播放都是通过事件来传递的,一个事件传过去,至于另一面是怎样播放,声音等等,都是音效制作人员搞定的;
另外一个是变量的通知,比如说你可以定义几个音量的参数,或者其他的参数,包括氛围方面也可以很好的制定,比如说,你可以设置一个紧张感,当敌人出现的时候,紧张感增加,走开的时候,紧张感降低,这样音效同学就可以根据这个紧张度,来改变音效揉合的变化,当然,这样的工作开发人员也可以使用unit原生声音系统做,但是这样堆开发人员素质要求很高,并且需要写各种代码来进行声音的管理。
声音系统
2D音效与3D音效
- 区别:当角色的左面有人靠近的时候,左耳朵应该一种特殊的感觉能够提醒玩家,这样其实就属于3D音效,与之相反的就是2D音效,实际上就是,声音不能带给玩家方位感。
- 使用场景:实际上2D音效在大多数的情况下,都是UI带来的音效.或者背景音乐的音效,而3D音效一般是用在战斗中的音效,一般是和角色,或者物件相关的,总的来说也就是有一个所属物体的声音,比如他是某种物体发出的,而这个物体在空间上有位置,这样的情况一般都要用3D音效。
声音格式
- 一般来说unity可以支持很多格式,但是这里面有一个点是,不要把声音导入的格式和实际运行时的格式区分开,他俩是完全不同的,你导入的格式可以是mp3,mav等等,官网有个清单,支持的格式都可以在上面看,但是这些清单都是属于原始格式,在你导入的一瞬间,在设置里面就已经决定了它在输出的时候是什么格式,是由你的输出平台决定,最直接的可能是体现在他的压缩格式上面,但是一般都不会体现出来,因为这个属性对unity并没有什么用,另一方面,为了提高声音的质量,或者最终空间大小的把握,也是可以设置一下输出的格式的,当然,如果你采用的输出格式是压缩格式的话,可能播放性能就比较差了,所以在一般情况下,背景音乐一定要用压缩格式的。
2.但是对于Wwise来说就灵活的多,在unity大多数情况下只能通过一些滑块等等简简单单的实现,如果你想控制的特别精准,其实用Wwise更好一点,而且对内存方面也比较节省 。
声音的划分
要想来实现整个声音系统的话,有几个组件时必不可少的
AudioSource(音源)->Effect(效果组件)->Mixer(混音器)->AudioListener(监听器),这几个组件的作用在官方文档上面都有,这里就不多说了
下面来看一下我最近设计的一套简易的声音系统
共有3个类分别是
config、soundmanager、uisystemconfig
代码如下
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UISystemConfig : UIWindow {
public Image musicOff;
public Image soundOff;
public Toggle toggleMusic;
public Toggle toggleSound;
public Slider sliderMusic;
public Slider sliderSound;
// Use this for initialization
void Start ()
{
this.toggleMusic.isOn = Config.MusicOn;
this.toggleSound.isOn = Config.SoundOn;
this.sliderMusic.value = Config.MusicVolume;
this.sliderSound.value = Config.SoundVolume;
}
public override void OnYesClick()
{
SoundManager.Instance.PlaySound(SoundDefine.SFX_UI_Click);
PlayerPrefs.Save();
base.OnYesClick();
}
// Update is called once per frame
void Update () {
}
public void SoundToogle(bool on)
{
soundOff.enabled = !on;
Config.MusicOn = on;
SoundManager.Instance.PlaySound(SoundDefine.SFX_UI_Click);
}
public void MusicToogle(bool on)
{
musicOff.enabled = !on;
Config.MusicOn = on;
SoundManager.Instance.PlaySound(SoundDefine.SFX_UI_Click);
}
public void MusicVolume(float vol)
{
Config.MusicVolume = (int)vol;
Debug.Log(vol);
PlaySound();
}
public void SoundVolume(float vol)
{
Config.SoundVolume = (int)vol;
PlaySound();
}
float lastPlay = 0;
private void PlaySound()
{
if(Time.realtimeSinceStartup- lastPlay>0.1f)
{
lastPlay = Time.realtimeSinceStartup;
SoundManager.Instance.PlaySound(SoundDefine.SFX_UI_Click);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
class Config
{
public static bool MusicOn
{
get
{
return PlayerPrefs.GetInt("Music", 1) == 1;//获取Music这个存储变量中的值,如果有的话,返回music,如果没有的话,默认返回1
}
set
{
PlayerPrefs.SetInt("Music", value ? 1 : 0);//判断value的值是真还是假,如果是真,将1存进music中,如果是假,将0存进music中
//相当于对将bool值转换成int
SoundManager.Instance.MusicOn = value;//并且将sundmanager的musicon属性值更改
}
}
public static bool SoundOn//同理
{
get
{
return PlayerPrefs.GetInt("Sound", 1) == 1;
}
set
{
PlayerPrefs.SetInt("Sound", value ? 1 : 0);
SoundManager.Instance.SoundOn = value;
}
}
public static int SoundVolume
{
get
{
return PlayerPrefs.GetInt("SoundVolume");
}
set
{
PlayerPrefs.SetInt("SoundVolum", value);
SoundManager.Instance.SoundVolume = value;
}
}
public static int MusicVolume
{
get
{
return PlayerPrefs.GetInt("MusicVolume");
}
set
{
PlayerPrefs.SetInt("MusicVolum", value);
SoundManager.Instance.MusicVolume = value;
}
}
~Config()
{
PlayerPrefs.Save();//保存修改
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
public class SoundManager : MonoSingleton<SoundManager> {
public AudioSource musicAudioSource;
public AudioSource soundAudioSource;
public AudioMixer audioMixer;
const string MusicPath = "Music/";
const string SoundPath = "Sound/";
private bool musicOn;
public bool MusicOn
{
get { return musicOn; }
set
{
musicOn = value;
this.MusicMute(!musicOn);
}
}
private bool soundOn;
public bool SoundOn
{
get { return SoundOn; }
set
{
soundOn = value;
this.MusicMute(!soundOn);
}
}
private void Start()
{
this.MusicOn = Config.MusicOn;
this.SoundOn = Config.SoundOn;
this.MusicVolume = Config.MusicVolume;
this.SoundVolume = Config.SoundVolume;
}
private int musicVolume;
public int MusicVolume
{
get
{
return musicVolume;
}
set
{
if(musicVolume!=value)
{
musicVolume = value;
if (musicOn)
{
this.SetVolume("MusicVolume", musicVolume);
}
}
}
}
private int soundVolume;
public int SoundVolume
{
get
{
return soundVolume;
}
set
{
if(soundVolume!=value)
{
soundVolume = value;
if (soundOn)
{
this.SetVolume("SoundVolume", soundVolume);
}
}
}
}
private void MusicMute(bool mute)
{
this.SetVolume("MusicVolume", mute ? 1 : 0);
}
private void SoundMute(bool mute)
{
this.SetVolume("SoundVolume", mute ? 1 : 0);
}
private void SetVolume(string name,int value)
{
float volume = value * 0.5f - 50f;
this.audioMixer.SetFloat(name, volume);
}
internal void PlaySound(string name)
{
AudioClip clip = Resloader.Load<AudioClip>(SoundPath + name);
if (clip == null)
{
Debug.LogWarningFormat("Playsound:{0} not existed.", name);
return;
}
soundAudioSource.PlayOneShot(clip);//只播放一次
}
public void PlayMusic(string name)
{
AudioClip clip = Resloader.Load<AudioClip>(MusicPath + name);
if(clip == null)
{
Debug.LogWarningFormat("PlayMusic:{0} not existed.", name);
return;
}
if(musicAudioSource.isPlaying)
{
musicAudioSource.Stop();
}
musicAudioSource.clip = clip;
musicAudioSource.Play();//循环播放
}
}
预制体UISystemconfig
这里有一点要说明一下,不管是toggle还是slider 都有一个Onvaluechange的事件,
On Value Changed 滑动条的当前值已变化时调用的 UnityEvent。该事件可将当前值作为 float 类型动态参数发送。无论是否已启用 Whole Numbers 属性,该值都将作为 float 类型传递。
滑动条的值由控制柄沿其长度的位置确定。该值从 Min Value 增加到 Max Value_,与拖动控制柄的距离成比例。默认行为是滑动条从左向右增加,但也可以使用 Direction_ 属性反转此行为。通过将 Direction 属性设置为 Bottom To Top 或 Top To Bottom,还可以将滑动条设置为垂直增加。
滑动条有一个名为 On Value Changed 的事件,当用户拖动控制柄时会响应。滑动条的当前数值作为 float 参数传递给函数。典型用例包括:
选择游戏难度、光源亮度等。
设置距离、大小、时间或角度。
所以在这个事件上面绑定了一个UISystemConfig的MusicToggle,这个函数有一个float的参数,可以自动获取到其变化值,由于声音系统无法演示,这里就不上传截屏了,写这个的原因就是想以后再做这个系统的时候有个参考,谢谢。