关于Unity中的声音管理模块(专题七)

声音的要素

1: 音频文件AudioClip
2: 音源AudioSource;
3: 耳朵AudioListener;//全局只能有一个
4: 2D/3D音频;//2D只是简单地播放声音,3D可以根据距离衰减音量

怎样听到声音:

创建一个节点,挂载AudioSource组件,AudioSource组件关联AudioClip属性,设置声音是否一开始就加载播放play on awake,是否循环播放,2D还是3D

场景中有一个节点有AudioListener组件,挂载AudioListener组件的节点在挂载AudioSource组件节点的声音范围内。运行,可以听到声音。

 

 

音乐与音效管理

1: 游戏里面一般分为音乐与音效;
2: 音乐指的是游戏的背景音乐;
3: 音效是游戏中的较短的音乐,来配合游戏的动作等;
4: Unity游戏没有音乐和音效之分,都是AudioSource + AudioClip;
5: 2D音效没有衰减,所以与位置没有关系;
6: 3D音效需要有声音所在节点的位置;
7: 游戏需求需要音乐和音效分开设置,开启和关闭;

 

 

sound_manager脚本

首先,声音包括音乐和音效,我们分两大块来管理,音乐为一块,音效为一块,这个块实际上Unity内部是不分的,但是我们来分,

用字典数据类型保存URL和音源AudioSource之间的对应关系,两个表,一个是音乐的,一个是音效的。

有了这两个表,这样如果我们要禁止背景音乐就去遍历音乐表,如果我们要禁止音效就去遍历音效表。

播放一个音效,需要new一个物体,在这个物体上加音源AudioSource组件,关联好音频文件AudioClip,这样就会播放出一个音效,

播放出来的音效会有分2D和3D,2D随便放在哪里,3D需要管理,有一个位置,所以加一个播放3D音效的接口。

1: 全局唯一的sound_manager;
2: 在场景里面创建一个物体(做为声音的根节点),而且设置这个物体场景切换也不会删除;
3: 编写接口播放背景音乐play_music;
4: 编写接口播放背景音效play_effect; // 2D声音
5: 音乐和音效内部实现都是一样的, 只不过要把url分组管理, 音效为一组,音乐为一组;
6: 提供开关音效接口set_mute(),并将值写入本地,下一次打开游戏的时候会读取这个值,维持上一次的设置。
7: 提供开关背景音乐接口switch_music(),并将值写入本地,下一次打开游戏的时候会读取这个值,维持上一次的设置。
8: 添加一个play_effect接口在指定的坐标出访一个声音, 可以用于3D音效;
9: 添加一个接口停止掉背景音乐stop_music(url);
10: 添加一个接口删除掉播放的背景音乐clear_music(url), clear_effect(url);
11: 声音文件来自Resouce还是assetBundle,可以通过资源管理来封装,目前从Resource里面加载;
12: 加一个脚本,每隔0.5秒扫描一次已经播放完的声音组件,将它disable;

 

 

声音管理实例

1.创建Unity项目工程和文件目录,保存场景

2.在Resources文件夹下创建一个sounds文件夹,把背景音乐Login.mp3和音效Close.mp3(第70)放进去

3.为了统一管理游戏中的声音,写一个脚本sound_manager来管理

 打开sound_manager.cs,初始化sound_manager

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class sound_manager {// (1) 声音根节点的物体;// (2) 保证这个节点在场景切换的时候不会删除,这样就不用再初始化一次;// (3) 所有播放声音的生源节点,都是在这个节点下static GameObject sound_play_object;//这个就是根节点// url --> AudioSource 映射, 区分音乐,音效static Dictionary<string, AudioSource> musics=null;//音乐表static Dictionary<string, AudioSource> effects = null;//音效表public static void init(){sound_play_object = new GameObject("sound_play_object");//初始化根节点GameObject.DontDestroyOnLoad(sound_play_object);//场景切换的时候不会删除根节点//初始化音乐表和音效表musics = new Dictionary<string, AudioSource>();effects = new Dictionary<string, AudioSource>();}
}

 

4.创建一个空节点,下面挂载一个脚本game_scene来测试音乐和音效

 打开game_scene.cs,写一个语句测试初始化

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class game_scene : MonoBehaviour {// Use this for initializationvoid Start () {sound_manager.init();//初始化音乐音效管理
    }// Update is called once per framevoid Update () {}
}

 

5.运行,初始化,节点如下

 

6.接下来开始写背景音乐和音效的接口

 打开sound_manager.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class sound_manager {// (1) 声音根节点的物体;// (2) 保证这个节点在场景切换的时候不会删除,这样就不用再初始化一次;// (3) 所有播放声音的生源节点,都是在这个节点下static GameObject sound_play_object;//这个就是根节点static bool is_music_mute = false;//存放当前全局背景音乐是否静音的变量static bool is_effect_mute = false;//存放当前音效是否静音的变量// url --> AudioSource 映射, 区分音乐,音效static Dictionary<string, AudioSource> musics=null;//音乐表static Dictionary<string, AudioSource> effects = null;//音效表//初始化public static void init(){sound_play_object = new GameObject("sound_play_object");//初始化根节点sound_play_object.AddComponent<sound_scan>();//把声音检测组件挂载到根节点下GameObject.DontDestroyOnLoad(sound_play_object);//场景切换的时候不会删除根节点//初始化音乐表和音效表musics = new Dictionary<string, AudioSource>();effects = new Dictionary<string, AudioSource>();// 从本地来加载这个开关if (PlayerPrefs.HasKey("music_mute"))//判断is_music_mute有没有保存在本地
        {int value = PlayerPrefs.GetInt("music_mute");is_music_mute = (value == 1);//int转换bool,如果value==1,返回true,否则就是false
        }// 从本地来加载这个开关if (PlayerPrefs.HasKey("effect_mute"))//判断is_effect_mute有没有保存在本地
        {int value = PlayerPrefs.GetInt("effect_mute");is_effect_mute = (value == 1);//int转换bool,如果value==1,返回true,否则就是false
        }}//播放指定背景音乐的接口public static void play_music(string url, bool is_loop = true){AudioSource audio_source = null;if (musics.ContainsKey(url))//判断是否已经在背景音乐表里面了
        {audio_source = musics[url];//是就直接赋值过去
        }else//不是就新建一个空节点,节点下再新建一个AudioSource组件
        {GameObject s = new GameObject(url);//创建一个空节点s.transform.parent = sound_play_object.transform;//加入节点到场景中
            audio_source = s.AddComponent<AudioSource>();//空节点添加组件AudioSourceAudioClip clip = Resources.Load<AudioClip>(url);//代码加载一个AudioClip资源文件audio_source.clip = clip;//设置组件的clip属性为clipaudio_source.loop = is_loop;//设置组件循环播放audio_source.playOnAwake = true;//再次唤醒时播放声音audio_source.spatialBlend = 0.0f;//设置为2D声音
musics.Add(url, audio_source);//加入到背景音乐字典中,下次就可以直接赋值了
        }audio_source.mute = is_music_mute;audio_source.enabled = true;audio_source.Play();//开始播放
    }//停止播放指定背景音乐的接口public static void stop_music(string url){AudioSource audio_source = null;if (!musics.ContainsKey(url))//判断是否已经在背景音乐表里面了
        {return;//没有这个背景音乐就直接返回
        }audio_source = musics[url];//有就把audio_source直接赋值过去audio_source.Stop();//停止播放
    }//停止播放所有背景音乐的接口public static void stop_all_music(){foreach (AudioSource s in musics.Values){s.Stop();}}//删除指定背景音乐和它的节点public static void clear_music(string url){AudioSource audio_source = null;if (!musics.ContainsKey(url))//判断是否已经在背景音乐表里面了
        {return;//没有这个背景音乐就直接返回
        }audio_source = musics[url];//有就把audio_source直接赋值过去musics[url] = null;//指定audio_source组件清空GameObject.Destroy(audio_source.gameObject);//删除掉挂载指定audio_source组件的节点
    }//切换背景音乐静音开关public static void switch_music(){// 切换静音和有声音的状态is_music_mute = !is_music_mute;//把当前是否静音写入本地int value = (is_music_mute) ? 1 : 0;//bool转换intPlayerPrefs.SetInt("music_mute", value);// 遍历所有背景音乐的AudioSource元素foreach (AudioSource s in musics.Values){s.mute = is_music_mute;//设置为当前的状态
        }}//当我的界面的静音按钮要显示的时候,到底是显示关闭,还是开始状态;public static bool music_is_off(){return is_music_mute;}//接下来开始是音效的接口//播放指定音效的接口public static void play_effect(string url, bool is_loop = false){AudioSource audio_source = null;if (effects.ContainsKey(url))//判断是否已经在音效表里面了
        {audio_source = effects[url];//是就直接赋值过去
        }else//不是就新建一个空节点,节点下再新建一个AudioSource组件
        {GameObject s = new GameObject(url);//创建一个空节点s.transform.parent = sound_play_object.transform;//加入节点到场景中
audio_source = s.AddComponent<AudioSource>();//空节点添加组件AudioSourceAudioClip clip = Resources.Load<AudioClip>(url);//代码加载一个AudioClip资源文件audio_source.clip = clip;//设置组件的clip属性为clipaudio_source.loop = is_loop;//设置组件循环播放audio_source.playOnAwake = true;//再次唤醒时播放声音audio_source.spatialBlend = 0.0f;//设置为2D声音
effects.Add(url, audio_source);//加入到音效字典中,下次就可以直接赋值了
        }audio_source.mute = is_effect_mute;audio_source.enabled = true;audio_source.Play();//开始播放
    }//停止播放指定音效的接口public static void stop_effect(string url){AudioSource audio_source = null;if (!effects.ContainsKey(url))//判断是否已经在音效表里面了
        {return;//没有这个背景音乐就直接返回
        }audio_source = effects[url];//有就把audio_source直接赋值过去audio_source.Stop();//停止播放
    }//停止播放所有音效的接口public static void stop_all_effect(){foreach (AudioSource s in effects.Values){s.Stop();}}//删除指定音效和它的节点public static void clear_effect(string url){AudioSource audio_source = null;if (!effects.ContainsKey(url))//判断是否已经在音效表里面了
        {return;//没有这个音效就直接返回
        }audio_source = effects[url];//有就把audio_source直接赋值过去effects[url] = null;//指定audio_source组件清空GameObject.Destroy(audio_source.gameObject);//删除掉挂载指定audio_source组件的节点
    }//切换音效静音开关public static void switch_effect(){// 切换静音和有声音的状态is_effect_mute = !is_effect_mute;//把当前是否静音写入本地int value = (is_effect_mute) ? 1 : 0;//bool转换intPlayerPrefs.SetInt("effect_mute", value);// 遍历所有音效的AudioSource元素foreach (AudioSource s in effects.Values){s.mute = is_effect_mute;//设置为当前的状态
        }}//当我的界面的静音按钮要显示的时候,到底是显示关闭,还是开始状态;public static bool effect_is_off(){return is_effect_mute;}//播放3D的音效public static void play_effect3D(string url, Vector3 pos, bool is_loop = false){AudioSource audio_source = null;if (effects.ContainsKey(url)){audio_source = effects[url];}else{GameObject s = new GameObject(url);s.transform.parent = sound_play_object.transform;s.transform.position = pos;//3D音效的位置
audio_source = s.AddComponent<AudioSource>();AudioClip clip = Resources.Load<AudioClip>(url);audio_source.clip = clip;audio_source.loop = is_loop;audio_source.playOnAwake = true;audio_source.spatialBlend = 1.0f; // 3D音效
effects.Add(url, audio_source);}audio_source.mute = is_effect_mute;audio_source.enabled = true;audio_source.Play();}//优化策略接口public static void disable_over_audio(){//遍历背景音乐表foreach(AudioSource s in musics.Values){if (!s.isPlaying)//判断是否在播放
            {s.enabled = false;//不在播放就直接隐藏
            }}//遍历音效表foreach (AudioSource s in effects.Values){if (!s.isPlaying)//判断是否在播放
            {s.enabled = false;//不在播放就直接隐藏
            }}}}

 

7.在game_scene脚本里面写测试语句,测试接口是否可以使用

 打开game_scene.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class game_scene : MonoBehaviour {// Use this for initializationvoid Start () {sound_manager.init();//初始化音乐音效管理//sound_manager.play_music("sounds/Login");//播放背景音乐//this.InvokeRepeating("test_music_mute", 1, 3);
sound_manager.play_effect("sounds/Close");//播放音效if (sound_manager.effect_is_off())//如果当前是静音,就切换成有声音的状态
        {sound_manager.switch_effect();}this.InvokeRepeating("again", 3, 3);//每隔3秒调用一次//this.InvokeRepeating("test_effect_mute", 1, 3);
    }//背景音乐静音切换测试函数void test_music_mute(){Debug.Log("test_music_mute");sound_manager.switch_music();}//音效静音切换测试函数void test_effect_mute(){Debug.Log("test_effect_mute");sound_manager.switch_effect();}//隐藏AudioSource组件优化测试函数void again(){sound_manager.play_effect("sounds/Close");}// Update is called once per framevoid Update () {}
}

 

8.还有一个问题就是,在某个声音播放完成的时候,可以隐藏AudioSource组件,这样就不会去调用组件的相关接口,游戏性能就能有所提升,这个接口在第6步已经写好了,测试语句也在第7步显现出来了,现在就写一个扫描的脚本

 可以创建一个脚本sound_scan来检测哪些声音已经播放完成,播完的节点就把它的AudioSource组件隐藏,这个脚本0.5s启动一次去检测声音节点。  

 打开sound_scan.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class sound_scan : MonoBehaviour {// Use this for initializationvoid Start () {//固定一个节奏去扫描,每隔0.5s扫描一次this.InvokeRepeating("scan",0, 0.5f);}// Update is called once per framevoid Update () {}//定时器函数void scan(){sound_manager.disable_over_audio();//调用隐藏AudioSource组件接口
    }
}

9.运行时的节点

播放背景音乐时

 

播放音效时

 

 

 

注意:

Unity代码加载资源文件的时候是没有文件后缀名的,看到什么名字就是什么名字

转载于:https://www.cnblogs.com/HangZhe/p/7326308.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/252145.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

重启唯一的窗体实例,以及调用系统重启函数失败解决办法

1、修改Program.cs内的程序启动函数 static class Program{public static System.Threading.Mutex Instance;/// <summary>/// 应用程序的主入口点。/// </summary>[STAThread]static void Main(){Application.EnableVisualStyles();Application.SetCompatibleTe…

ThreadLocal可能引起的内存泄露

threadlocal里面使用了一个存在弱引用的map,当释放掉threadlocal的强引用以后,map里面的value却没有被回收.而这块value永远不会被访问到了. 所以存在着内存泄露. 最好的做法是将调用threadlocal的remove方法. 在threadlocal的生命周期中,都存在这些引用. 看下图: 实线代表强引…

codevs 1576 最长严格上升子序列

题目链接&#xff1a;http://codevs.cn/problem/1576/题目描述 Description给一个数组a1, a2 ... an&#xff0c;找到最长的上升降子序列ab1<ab2< .. <abk&#xff0c;其中b1<b2<..bk。 输出长度即可。 输入描述 Input Description第一行&#xff0c;一个整数N。…

nginx服务器开启缓存、反向代理

一、反向代理配置 1、反向代理服务器配置如下 反向代理就是需要这一行proxy_pass来完成。当我们要访问后端web服务器的时候&#xff0c;我们只需要访问代理服务器就可以了&#xff0c;此时代理服务器就充当后端web服务器的角色。proxy_pass依赖的模块是&#xff1a; 至于后两行…

Halcon:区域特征:select_shape(Regions : SelectedRegions : Features, Operation, Min, Max : )

Region特征一览&#xff1a; 特征 英 译 备注 area Area of the object 对象的面积 row Row index of the center 中心点的行坐标 column Column index of the center 中心点的列坐标 width Width of the region 区域的宽度 height Height of the…

Web应用主动侦测工具Skipfish

Web应用主动侦测工具SkipfishSkipfish是Kali Linux附带的一个主动Web应用侦测工具。该工具会首先尽可能获取所有网站路径&#xff0c;进行访问&#xff0c;然后根据返回的内容&#xff0c;检测是否存在漏洞。该工具采用字典爆破和网页爬行两种方式获取网站。一旦获取网页内容&a…

7步让你get首个数据科学实习

由于数据科学的庞大和复杂&#xff0c;如果你没有相关的实习经历的话&#xff0c;成为数据科学家的道路将会更加艰巨和困难。即使是经验丰富的人&#xff0c;实习也是转型进入数据科学领域的一种有效方式。 那么&#xff0c;寻找数据科学实习有哪些技巧&#xff1f;本文总结了数…

Halcon:Image、region、xld常用的处理

一、读取文件夹中的所有图片 list_files (C:/Users/fuping.liu/Desktop/槟榔有无头/有头, [files,follow_links], ImageFiles) tuple_regexp_select (ImageFiles, [\(tif|tiff|gif|bmp|jpg|jpeg|jp2|png|pcx|pgm|ppm|pbm|xwd|ima|hobj)$,ignore_case], ImageFiles)for Index :…

赛码网算法: 上台阶 ( python3实现 、c实现)

上台阶 题目描述 有一楼梯共m级&#xff0c;刚开始时你在第一级&#xff0c;若每次只能跨上一级或二级&#xff0c;要走上第m级&#xff0c;共有多少走法&#xff1f;注&#xff1a;规定从一级到一级有0种走法。 输入…

Halcon: 畸变矫正与标定(1)

1、 Halcon相机标定和图像矫正 对于相机采集的图片&#xff0c;会由于相机本身和透镜的影响产生形变&#xff0c;通常需要对相机进行标定&#xff0c;获取相机的内参或内外参&#xff0c;然后矫正其畸变。相机畸变主要分为径向畸变和切向畸变&#xff0c;其中径向畸变是由透…

conda install 出错

在下载包时出现下面的错误&#xff1a; userdeMBP:pytorch user$ conda install -n deeplearning matplotlib Solving environment: failedCondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://repo.anaconda.com/pkgs/main/osx-64/repodata.json.bz2> Elapsed…

算法入门经典 第三章

scanf 遇到tab或空格或换行符停下来1.例题2-1 7744问题 从数本身看 从个位数的数字看#include <iostream>#include<math.h>using namespace std; int main(){ for(int a1;a<9;a) { for(int b1;b<9;b) { int n1100*a11*b;//floor x 等于1的区间为[1,2),florr(…

Halcon :畸变矫正与标定(2)

相机标定1.相机标定是什么2.怎么使用halcon进行相机内外参标定&#xff1f; &#xff08;1&#xff09;搭建硬件1.**相机连好电脑&#xff0c;用相机厂家软件打开相机&#xff0c;检查一下相机是否正常。**2.**接下来使用halcon连接相机**&#xff08;2&#xff09;开始标定1.*…

jQuery2

一、层次选择器 1、后代选择器$("div p"):div中所有的p标签元素 2、自带选择器$("div>p")&#xff1a;div中的子代是p的第一层元素 3、兄弟选择器$("divp")和div是兄弟的p标签 4、相邻兄弟选择器$("div~p")与div相邻的p标签 二、jQ…

HTTP协议详解(转载)

http://www.cnblogs.com/TankXiao/archive/2012/02/13/2342672.html 转载于:https://www.cnblogs.com/youmei11/p/8608007.html

bzoj1016 [JSOI2008]最小生成树计数

1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 6032 Solved: 2452[Submit][Status][Discuss]Description 现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树&#xff0c;而希望知道这个图中有多少个不同的最小生成树。&…

http请求概述

当浏览器输入网址后 浏览器首先向DNS域名解析服务器发送请求。DNS反解析&#xff1a;根据浏览器请求地址中的域名&#xff0c;到DNS服务器中找到对应的服务器外网IP地址通过找到外网IP&#xff0c;向对应的服务器发请求&#xff08;首先访问服务器的WEB站点管理工具&#xff1a…

Halcon:二维仿射变换实例探究

二维仿射变换&#xff0c;顾名思义就是在二维平面内&#xff0c;对对象进行平移、旋转、缩放等变换的行为&#xff08;当然还有其他的变换&#xff0c;这里仅论述这三种最常见的&#xff09;。 Halcon中进行仿射变换的常见步骤如下&#xff1a; ① 通过hom_mat2d_identity算子…

剑指Offer-数组中重复的数字

题目描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的&#xff0c;但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如&#xff0c;如果输入长度为7的数组{2,3,1,0,2,5,3}&#xff0c;那么对应的…

CSS2--字体样式

## CSS2 字体样式 ##### font-family 字体族 - 规定元素的字体系列 - 把多个字体作为一个"回退"系统保存.保证浏览器的支持 - Microsoft YaHei, tahoma, arial, Hiragino Sans GB, sans-serif ##### font 字体类型 - 衬线字体(serif)&#xff1a;在字的笔划开始及结束…