关于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,一经查实,立即删除!

相关文章

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: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 :…

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

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

算法入门经典 第三章

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.*…

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

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

Halcon:手眼标定——眼在手外与眼在手上

为什么需要九点标定&#xff1f; 为了得到机械和相机的关系&#xff0c;就好比人的手和眼的关系。我们用手将一个物体放到空间的一个位置&#xff0c;用眼看到这个物体&#xff0c;这也存在两个坐标系&#xff0c;一个是手所在的运动空间的坐标系&#xff0c;一个是视网膜上成像…

grep 正则匹配

\{0,n\}&#xff1a;至多n次 \{\ 匹配/etc/passwd文件中数字出现只是数字1次到3次 匹配/etc/grub2.cfg文件以一个空格开头匹配一个字符的文件的所有行 显示以LISTEN结尾的行 显示匹配右边以LISTEN结尾匹配一个或者多个空格的所有输出 分组及引用&#xff1a;讲一个或者多个字符…

C#中调用halcon引擎来执行hdev程序

调用halcon引擎有两个直接的好处&#xff1a; 避免C# 与halcon代码混编时可能产生的内存泄露问题 修改halcon程序时不用重新编译C# 勇哥写了一个示例&#xff0c;详细的应用感受和缺点限制勇哥会持续做相关的总结给大家分享。 对于halcon17来说&#xff0c;要运行下面的程序…

ASP.NET Core部署到Linux服务器(CentOS7 x64)

前言 本文主要讲解如何一步步将ASP.NET Core网站发布到Linux服务器&#xff0c;文中会讲解具体步骤及需要避免的各种问题。 目录 一、环境介绍 二、创建及发布ASP.NET Core网站项目 三、服务器软件安装&#xff08;.NET Core SDK&#xff09; 四、在服务器上部署ASP.NET Core网…

Python3.5以上版本lxml导入etree报错Unresolved reference

Web抓取Web站点使用HTML描述&#xff0c;这意味着每个web页面是一个结构化的文档。有时从中 获取数据同时保持它的结构是有用的。web站点不总是以容易处理的格式&#xff0c; 如 csv 或者 json 提供它们的数据。 这正是web抓取出场的时机。Web抓取是使用计算机程序将web页面数据…

其它综合-CentOS7 忘记root密码

CentOS7 忘记root密码 长时间不用的 CentOS 机器再次开机的时候忽然忘记了密码&#xff0c;总不能就重装一台吧&#xff0c;还有好多服务在机器上&#xff0c;于是决定重置root的密码。   如果是已经开启的机器&#xff0c;需要进行关闭&#xff0c;重新启动。在启动选择内核…

spring boot高性能实现二维码扫码登录(中)——Redis版

前言 本打算用CountDownLatch来实现&#xff0c;但有个问题我没有考虑&#xff0c;就是当用户APP没有扫二维码的时候&#xff0c;线程会阻塞5分钟&#xff0c;这反而造成性能的下降。好吧&#xff0c;现在回归传统方式&#xff1a;前端ajax每隔1秒或2秒发一次请求&#xff0c;去…

C# :socket 通讯基础使用实例

们在讲解Socket编程前&#xff0c;先看几个和Socket编程紧密相关的概念&#xff1a; TCP/IP层次模型当然这里我们只讨论重要的四层 01&#xff0c;应用层(Application)&#xff1a;应用层是个很广泛的概念&#xff0c;有一些基本相同的系统级TCP/IP应用以及应用协议&#xff0…

PHP + NGINX 控制视频文件播放,并防止文件下载

最简单的方法是使用NGINX的 internal 功能 server { listen 80; server_name www.xxx.com;  location / { index index.php index.html index.htm; root /xxx; if (!-e $request_filename) { rewrite ^/index.php(.*)$ /index.php?s$…

C#:委托基础与事件

通过以下思维导图&#xff0c;学习委托的基本概念&#xff0c;后面着重讲解委托的运用&#xff0c;希望通过最简单的方式收获更多的知识。 1.委托的各种写法 1、委托 委托名new 委托&#xff08;会调用的方法名); 委托名&#xff08;参数&#xff09;; 2、委托 委托名 会调用…

Django基本命令

Django基本命令 1.创建一个Django 项目 django_admin.py startproject mysite当前目录下会生成mysite的工程&#xff0c;目录结构如下&#xff1a; manage.py ----- Django项目里面的工具&#xff0c;通过它可以调用django shell和数据库等。settings.py ---- 包含了项目的默认…

reactor模式:多线程的reactor模式

上文说到单线程的reactor模式 reactor模式&#xff1a;单线程的reactor模式 单线程的reactor模式并没有解决IO和CPU处理速度不匹配问题&#xff0c;所以多线程的reactor模式引入线程池的概念&#xff0c;把耗时的IO操作交由线程池处理&#xff0c;处理完了之后再同步到selecti…