
音频
Tutorial
Beginner
+0XP
30 mins
(369)
Unity Technologies

现在,您可以通过引人入胜的音频为游戏添加画龙点睛的效果。
在本教程中,您将:
- 探索游戏中可以使用的各种音频
- 添加音轨和一系列音频效果
- 编写脚本来控制游戏中的音频使用
学完本教程后,您的游戏将完成,然后便可以开始创建游戏的构建版本了!
Languages available:
1. 音频
现在,您在关卡中已经填入了一系列敌人使 JohnLemon 能够逃生,但是当您测试游戏时,发现游戏有点安静。在本教程中,您将使用一系列诡异的音频来增强气氛,为游戏添加画龙点睛的效果。
本教程将使用两种不同的音频:
- 非剧情声音,这种声音没有可识别的音源(比如音轨)
- 剧情声音,这种声音具有可识别的音源(比如枪击声)
让我们首先给游戏添加非剧情音频!
2. Unity 音频的入门介绍
在开始向游戏中添加音频之前,让我们快速探究一下音频在 Unity 中的工作方式。主要包括三个部分:音频剪辑 (Audio Clips)、音频源 (Audio Sources) 和音频监听器 (Audio Listener)。
- 音频剪辑 (Audio Clips) 是诸如 MP3 之类的资源,其中包含与特定声音相关的所有数据。
- 音频源 (Audio Sources) 是在游戏世界中充当声音起源的组件。在游戏中发出声音的大多数事物都应具有音频源 (Audio Source) 组件,以便声音具有位置。
- 音频监听器 (Audio Listener) 作为场景中的单个组件,其功能类似于玩家的虚拟耳朵(就像 Camera 组件的作用也类似于玩家的虚拟眼睛一样)。默认情况下,音频监听器 (Audio Listener) 组件位于主摄像机 (Main Camera) 上。
这些不同的部件通常以如下方式协同工作:音频源播放音频剪辑,如果音频监听器离音频源足够近,则可以听到声音。
特定音频源的空间混合决定了音频听起来是来自游戏世界中的某个特定点,还是无论音频源与音频监听器之间的距离如何都是同等大小的音量。
您将从非剧情音频开始。由于这种声音没有起源,因此其空间混合设置为 2D。当空间混合设置为纯 2D 时,音频源和音频监听器之间的距离不会影响音量。之所以称为 2D,是因为仍然可以使用 Stereo Pan 设置来左右平移。如果音频源的空间混合设置为纯 3D,则音量将随着与音频监听器的距离不同而变化。2D 和 3D 之间的值会改变此效果的强度。
3. 什么是非剧情音频?
非剧情音频(没有可识别的音源的音频)可能会对您的游戏和玩家体验产生重大影响。想一想您喜欢玩的游戏,它们如何利用这些声音来营造特殊的氛围,让您有什么感觉?这些声音特别有效吗?
您将在自己的游戏中添加三个非剧情声音:
- 一个是循环播放的环境音轨,为鬼屋带来一般性的氛围
- 一个是抓住 JohnLemon 时播放的音效
- 一个是 JohnLemon 设法逃脱时播放的音效
4. 为您的游戏创建音频源
首先,为您的游戏创建一些音频源:
1.在 Hierarchy 窗口中,单击 Create 菜单并选择 Create Empty。将游戏对象重命名为“Ambient”。
2.在 Inspector 中,将 Ambient 的位置设置为 (0, 0, 0)。
该游戏对象的位置在技术上并不重要,因为无论在哪里,音频的音量都是相同的。但是,为了防以后发生问题,让游戏对象的位置保持井然有序总是有用的。
3.接下来,需要添加一个 Audio Source 组件。通常情况下可以使用 Add Component 按钮来执行此操作,但是当您还要分配音频剪辑时,可以使用一种快捷的方式。
在 Project 窗口中,选择 Assets > Audio。将 SFXHouseAmbience 音频剪辑从 Project 窗口拖到 Inspector 窗口上即可创建 Audio Source 组件并自动将其分配为音频剪辑 (AudioClip)。
4.Spatial Blend 属性已经完全设置为 2D,因此您无需对其进行更改。默认情况下还已经启用 Play On Awake。这意味着,一旦关卡开始,音频就会开始播放。
5.但是,默认情况下,音频将在单次完整运行后停止。启用 Loop 复选框可以循环播放游戏的环境音轨。
6.现在,您的鬼屋有了一些令人毛骨悚然的背景声音!请进入播放模式并测试音频是否正确播放。
7.您应该会听到音频,但是与本教程中将要添加的其他声音相比,声音有点大。请退出播放模式以便对此进行调整。
8.在 Inspector 中,将 Audio Source 组件的 Volume 属性设置为 0.5。
5. 复制音频源
在这里我们不再针对两个游戏结束音效重复执行上述过程,而是采用以更高效的方式,即复制游戏对象,然后根据需要更改设置。
1.在 Hierarchy 窗口中,选择 Ambient 游戏对象。使用 Ctrl + D (Windows) 或 CMD + D (macOS) 快捷方式复制两次该游戏对象。将第一个副本重命名为“Escape”,将第二个重命名为“Caught”。
2.这两个音效之间的唯一区别是使用的音频剪辑 (AudioClip);它们的设置完全相同。
在 Hierarchy 窗口中,按 Ctrl (Windows) 或 CMD (macOS),并单击 Escape 和 Caught 游戏对象。这样一来,您便可以一起编辑这两个游戏对象。
3.这些声音不应立即播放,也不应循环播放。此外,需要在游戏中的其他声音之外听到这些声音,因此它们应该比环境音频响亮。
同时选择 Escape 和 Caught 游戏对象,然后:
- 禁用 Play On Awake 复选框
- 禁用 Loop 复选框
将 Volume 的值设置为 1
现在,您已经调整了这两个游戏对象的通用设置,接下来便可以设置每个游戏对象的音频剪辑 (AudioClip)。
4.选择 Escape 游戏对象,并将其 Audio Source 组件的 AudioClip 设置为 SFXWin;要执行此操作,可以将其从 Project 窗口进行拖动或使用圈选按钮。
5. 选择 Caught 游戏对象,并将其 Audio Source 组件的 AudioClip 设置为 SFXGameOver;要执行此操作,可以将其从 Project 窗口进行拖动或使用圈选按钮。
现在,您已经设置了非剧情音频源,但是在继续实现 Escape 和 Caught 声音之前,让我们先将音频源整理到单个父项下。
6. 整理音频源
要整理音频源,请执行以下操作:
1.在 Hierarchy 窗口中,创建一个空游戏对象。将其重命名为“Audio”。
2.在 Hierarchy 窗口中,按住 Ctrl (Windows) 或 CMD (macOS) 并分别单击 Ambient、Escape 和 Caught 游戏对象以选择这些游戏对象。
3. 将选定的游戏对象拖到 Audio 游戏对象上以使 Audio 游戏对象成为它们的父项。
7. 返回到 GameEnding 脚本
现在您已经整理好了,接下来让我们继续在 GameEnding 脚本中实现游戏结束音效。
在 Project 窗口中,打开 Assets > Scripts 文件夹,然后双击 GameEnding 脚本以将其打开进行编辑。
当您最后完成 GameEnding 脚本时,该脚本如下所示:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameEnding : MonoBehaviour
{
public float fadeDuration = 1f;
public float displayImageDuration = 1f;
public GameObject player;
public CanvasGroup exitBackgroundImageCanvasGroup;
public CanvasGroup caughtBackgroundImageCanvasGroup;
bool m_IsPlayerAtExit;
bool m_IsPlayerCaught;
float m_Timer;
void OnTriggerEnter (Collider other)
{
if (other.gameObject == player)
{
m_IsPlayerAtExit = true;
}
}
public void CaughtPlayer ()
{
m_IsPlayerCaught = true;
}
void Update ()
{
if (m_IsPlayerAtExit)
{
EndLevel (exitBackgroundImageCanvasGroup, false);
}
else if (m_IsPlayerCaught)
{
EndLevel (caughtBackgroundImageCanvasGroup, true);
}
}
void EndLevel (CanvasGroup imageCanvasGroup, bool doRestart)
{
m_Timer += Time.deltaTime;
imageCanvasGroup.alpha = m_Timer / fadeDuration;
if (m_Timer > fadeDuration + displayImageDuration)
{
if (doRestart)
{
SceneManager.LoadScene (0);
}
else
{
Application.Quit ();
}
}
}
}
8. 更新 GameEnding 脚本以播放音频
您需要添加在调用 EndLevel 方法时播放音频源的功能。可以播放两种不同的音频源,因此您需要分别引用它们。请记住:在 Update 中的每一帧都会调用 EndLevel 方法。您不希望音频源一直播放,因此您需要通过某种方式在第一次播放之后停止播放。
让我们开始添加音频源引用:
1. 在 exitBackgroundImageCanvasGroup 变量声明下面添加以下代码:
public AudioSource exitAudio;2.在 caughtBackgroundImageCanvasGroup 变量声明下面添加以下代码:
public AudioSource caughtAudio;3.接下来,您需要创建一个变量以确保音频仅播放一次。一个布尔变量默认情况下为 false,当您要播放音频时,可以确认该变量为 false 并播放音频,并在播放完成时将该变量设置为 true。
在 m_Timer 变量声明下面添加以下代码:
bool m_HasAudioPlayed;
9. 调整您的方法以使用新变量
现在,让我们使用以下新变量:
1. 由于游戏应根据 JohnLemon 被抓还是逃脱而播放不同的音频源,因此您需要向 EndLevel 方法添加另一个参数。将 EndLevel 方法签名更改为如下所示:
void EndLevel (CanvasGroup imageCanvasGroup, bool doRestart, AudioSource audioSource)2.您对 EndLevel 的调用不再具有正确的参数,现在还需要 AudioSource。将第一个 EndLevel 方法调用更改为如下所示:
EndLevel (exitBackgroundImageCanvasGroup, false, exitAudio);3.将第二个 EndLevel 方法调用更改为如下所示:
EndLevel (caughtBackgroundImageCanvasGroup, true, caughtAudio);这些方法调用现在又正确了,但是当前您的 EndLevel 方法对其新参数不执行任何操作。
10. 确保音频仅播放一次
无论计时器经过多久,音频都应该播放,因此在 EndLevel 方法的开头包含音频代码是合理的。您还只希望音频在尚未播放的情况下才播放,因此需要放入 if 语句中进行检查。
1.在 EndLevel 方法的开头添加以下代码:
if(!m_HasAudioPlayed)
{
}感叹号表示否定其右边的任何内容。这意味着 if 语句中的代码仅在音频未播放时才会执行。
2.在这条 if 语句中需要做的第一件事就是播放音频。要播放分配给音频源的音频剪辑,请按以下方式调用音频源的 Play 方法:
audioSource.Play();4.因为只希望音频播放一次,所以现在需要将 m_HasAudioPlayed 的值设置为 true。这意味着 if 语句中的代码不会再被调用。在 if 语句中的 Play 方法调用下面,添加以下代码:
m_HasAudioPlayed = true;5.您现在已经完成了 GameEnding 脚本的编辑。结果应如下所示:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameEnding : MonoBehaviour
{
public float fadeDuration = 1f;
public float displayImageDuration = 1f;
public GameObject player;
public CanvasGroup exitBackgroundImageCanvasGroup;
public AudioSource exitAudio;
public CanvasGroup caughtBackgroundImageCanvasGroup;
public AudioSource caughtAudio;
bool m_IsPlayerAtExit;
bool m_IsPlayerCaught;
float m_Timer;
bool m_HasAudioPlayed;
void OnTriggerEnter (Collider other)
{
if (other.gameObject == player)
{
m_IsPlayerAtExit = true;
}
}
public void CaughtPlayer ()
{
m_IsPlayerCaught = true;
}
void Update ()
{
if (m_IsPlayerAtExit)
{
EndLevel (exitBackgroundImageCanvasGroup, false, exitAudio);
}
else if (m_IsPlayerCaught)
{
EndLevel (caughtBackgroundImageCanvasGroup, true, caughtAudio);
}
}
void EndLevel (CanvasGroup imageCanvasGroup, bool doRestart, AudioSource audioSource)
{
if (!m_HasAudioPlayed)
{
audioSource.Play();
m_HasAudioPlayed = true;
}
m_Timer += Time.deltaTime;
imageCanvasGroup.alpha = m_Timer / fadeDuration;
if (m_Timer > fadeDuration + displayImageDuration)
{
if (doRestart)
{
SceneManager.LoadScene (0);
}
else
{
Application.Quit ();
}
}
}
}
6.保存脚本,然后返回到 Unity。
7.最后一步是设置向 GameEnding 脚本添加的引用。在 Hierarchy 窗口中,选择 GameEnding 游戏对象。
8.在 Inspector 中,使用圈选按钮将 Exit Audio 字段设置为 Escape,并将 Caught Audio 字段设置为 Caught。
就是这样,您已经完成了非剧情音频的设置!
11. 添加脚步声音频
现在,您需要实现可以识别来源的音频。让我们先从脚步声开始吧:
1.在 Hierarchy 窗口中,选择 JohnLemon 游戏对象。
2.在 Inspector 中,添加一个 AudioSource 组件。
3.使用圈选按钮将 AudioClip 属性设置为 SFXFootstepsLooping。
4.尽管这是剧情声音,但仍应使用完全 2D 的空间混合 (Spatial Blend),以便音量不会随着 JohnLemon 的移动而变化,默认设置就很好。
5.由于 JohnLemon 在开始时处于静止状态,因此您不希望在场景开始后立即播放音频。禁用 Play On Awake 复选框。
6.您希望声音在您命令其停止之前一直播放,因此它需要循环播放。启用 Loop 复选框。
和以前一样,您现在需要编辑脚本,以便可以随时播放音频。
12. 返回到 PlayerMovement 脚本
为了启用脚步声音频,需要编辑 PlayerMovement 脚本。双击 PlayerMovement 组件的 Script 属性以打开 PlayerMovement 脚本进行编辑。
当您最后完成 PlayerMovement 脚本时,该脚本如下所示:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public float turnSpeed = 20f;
Animator m_Animator;
Rigidbody m_Rigidbody;
Vector3 m_Movement;
Quaternion m_Rotation = Quaternion.identity;
void Start ()
{
m_Animator = GetComponent<Animator> ();
m_Rigidbody = GetComponent<Rigidbody> ();
}
void FixedUpdate ()
{
float horizontal = Input.GetAxis ("Horizontal");
float vertical = Input.GetAxis ("Vertical");
m_Movement.Set(horizontal, 0f, vertical);
m_Movement.Normalize ();
bool hasHorizontalInput = !Mathf.Approximately (horizontal, 0f);
bool hasVerticalInput = !Mathf.Approximately (vertical, 0f);
bool isWalking = hasHorizontalInput || hasVerticalInput;
m_Animator.SetBool ("IsWalking", isWalking);
Vector3 desiredForward = Vector3.RotateTowards (transform.forward, m_Movement, turnSpeed * Time.deltaTime, 0f);
m_Rotation = Quaternion.LookRotation (desiredForward);
}
void OnAnimatorMove ()
{
m_Rigidbody.MovePosition (m_Rigidbody.position + m_Movement * m_Animator.deltaPosition.magnitude);
m_Rigidbody.MoveRotation (m_Rotation);
}
}
13. 更新 PlayerMovement 脚本以播放音频
现在,您需要编辑脚本,以便 JohnLemon 走路时播放脚步声,而当他静止时停止播放脚步声。
您需要做的第一件事是引用刚刚添加到 JohnLemon 的 Audio Source 组件
1. 在 m_Rigidbody 变量声明下面,添加以下代码:
AudioSource m_AudioSource;由于这不是公共变量,因此您无法在 Inspector 窗口中分配该变量。相反,您需要像对 Animator 和 Rigidbody 组件一样在代码中分配该变量。
2.在 Start 方法中的 m_Animator 和 m_Rigidbody 变量后面分配它们的引用,并添加以下代码:
m_AudioSource = GetComponent<AudioSource>();就是这样!接下来,您需要使用 Audio Source 组件的这个引用。
14. 调整您的方法以使用新变量
幸运的是,您已经在 FixedUpdate 方法中创建了一个名为 isWalking 的变量。这个变量非常适合用于播放和停止脚步声:
1. 在 Animator 组件上的 SetBool 方法调用下方,添加以下 if-else 语句:
if(isWalking)
{
}
else
{
}现在,如果 isWalking 为 true,则可以在音频源上调用 Play,否则如果为 false,则可以在音频源上调用 Stop。
2.除非音频源尚未处于播放状态,否则您不希望持续在每一帧都调用 Play。为确保不会在每一帧都进行调用,请在刚添加的 if 语句中添加以下 if 语句:
if(!m_AudioSource.isPlaying)
{
}3.现在,向 if 语句中添加对 Play 方法的调用:
m_AudioSource.Play ();4.要停止播放音频源,请将以下代码添加到 else 语句中:
m_AudioSource.Stop ();5.确认您完成的 PlayerMovement 脚本如下所示:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public float turnSpeed = 20f;
Animator m_Animator;
Rigidbody m_Rigidbody;
AudioSource m_AudioSource;
Vector3 m_Movement;
Quaternion m_Rotation = Quaternion.identity;
void Start ()
{
m_Animator = GetComponent<Animator> ();
m_Rigidbody = GetComponent<Rigidbody> ();
m_AudioSource = GetComponent<AudioSource> ();
}
void FixedUpdate ()
{
float horizontal = Input.GetAxis ("Horizontal");
float vertical = Input.GetAxis ("Vertical");
m_Movement.Set(horizontal, 0f, vertical);
m_Movement.Normalize ();
bool hasHorizontalInput = !Mathf.Approximately (horizontal, 0f);
bool hasVerticalInput = !Mathf.Approximately (vertical, 0f);
bool isWalking = hasHorizontalInput || hasVerticalInput;
m_Animator.SetBool ("IsWalking", isWalking);
if (isWalking)
{
if (!m_AudioSource.isPlaying)
{
m_AudioSource.Play();
}
}
else
{
m_AudioSource.Stop ();
}
Vector3 desiredForward = Vector3.RotateTowards (transform.forward, m_Movement, turnSpeed * Time.deltaTime, 0f);
m_Rotation = Quaternion.LookRotation (desiredForward);
}
void OnAnimatorMove ()
{
m_Rigidbody.MovePosition (m_Rigidbody.position + m_Movement * m_Animator.deltaPosition.magnitude);
m_Rigidbody.MoveRotation (m_Rotation);
}
}
6.保存脚本,然后返回到 Unity。
15. 将音频监听器 (Audio Listener) 移动到 JohnLemon
游戏的最后一段音频是幽灵在通过时发出的一些诡异声音。此音频不仅要营造气氛,还将帮助玩家识别危险离自己有多远。
有一个难题是,幽灵的声音在幽灵靠近 JohnLemon 时不会变大,但在靠近摄像机时会变大。这是因为默认情况下,音频监听器 (Audio Listener) 位于主摄像机 (Main Camera) 上。第一步就是要更改此设置。
要将 Audio Listener 组件移到 JohnLemon 游戏对象,请执行以下操作:
1.在 Hierarchy 中,选择 Main Camera 游戏对象。
2.在 Inspector 中,单击 Audio Listener 组件的上下文菜单,然后选择 Remove Component。
3.在 Hierarchy 中,选择 JohnLemon 游戏对象。
4.在 Inspector 中,将 Audio Listener 组件添加到 JohnLemon。
16. 更新 JohnLemon 预制件
您在本教程中对 JohnLemon 所做的所有更改都已对 JohnLemon 预制件的实例做过了。
您添加的新组件在图标上带有 + 号,以表明这些组件已添加到预制件实例中。若是要实例化另一个 JohnLemon,这些音频组件将不存在。
您可以通过将以下更改应用于 JohnLemon 预制件来解决此问题:
1.在 Hierarchy 中,选择 JohnLemon 游戏对象。
2.在 Inspector 中,找到窗口顶部的 Overrides 下拉菜单。
Overrides 下拉菜单会显示 JohnLemon 实例与 JohnLemon 预制件不同的所有方式。
3.单击该下拉菜单以查看更改。
您添加了两个组件:Audio Source 和 Audio Listener。让我们使这些更改成为 JohnLemon 预制件的一部分。
4.单击 Overrides 下拉菜单底部的 Apply All 按钮。
17. 将音频源添加到幽灵
现在,JohnLemon 预制件具有音频监听器 (Audio Listener),所有幽灵音频都将具有相对于 JohnLemon 位置的音量。这将使玩家更容易识别与幽灵之间的距离。现在,让我们将音频添加到幽灵:
1.在 Hierarchy 窗口中,展开 Enemies 游戏对象。
2.选择 Ghost 游戏对象之一。使用名称旁边的箭头快捷方式可打开预制件以进行编辑。
3.在 Project 窗口中,展开 Assets > Audio 文件夹,然后找到 SFXGhostMove 音频剪辑。
4.将 SFXGhostMove 音频剪辑从 Project 窗口拖到 Hierarchy 窗口中的 Ghost 游戏对象上。
5.在 Inspector 窗口中,找到 Audio Source 组件。
6.为防止音量过大并确保音量取决于与 JohnLemon 之间的距离,请执行以下操作:
- 启用 Loop 属性复选框
- 将 Volume 属性设置为 0.4
- 将 Spatial Blend 设置为 1,使其完全为 3D
以上便是基础知识!
18. 调整 3D Sound Settings 功能
让我们调整几项高级功能,使幽灵具有出色的音效:
1.在 Audio Source 组件中,展开 3D Sound Settings 部分。
3D Sound Settings 用于控制音频如何随着与音频监听器 (Audio Listener) 之间的距离不同而变化。
2.将 Max Distance 属性更改为 10。将 Max Distance 设置为 10 意味着,当幽灵在 10 米外时,玩家将可以非常轻微地听到它们的声音。
3.音量随距离而变化的方式由 Volume Rolloff 控制。目前,此设置为 Logarithmic Rolloff,非常适合用于较远的距离。由于您的 Max Distance 现在仅为 10,请将 Volume Rolloff 更改为 Custom Rolloff。
4.默认的自定义曲线正好适合该游戏,因此无需调整此项。
但是,在完成之前,还有最后一个修复操作。
19. 纠正幽灵音效的方向
您已经将 Audio Listener 组件放在 JohnLemon 上,但有一个小问题:当 JohnLemon 转动时,音频监听器 (Audio Listener) 会也随之转动。这意味着,当 JohnLemon 面向屏幕时,玩家的虚拟眼睛(摄像机)和玩家的虚拟耳朵(音频监听器)将面向不同的方向。因此,听起来,鬼魂就像在对面。
让我们结合使用两个属性,使幽灵的声音似乎毫无方向性,但随着幽灵的靠近,声音仍会变大:
1. SFXGhostMove 音频剪辑已设置为播放单声道声音 (Mono) 而不是立体声 (Stereo)。这意味着通过左右声道的声音是相同的。(如果要查看 Import Settings,请在 Assets > Audio 中查找此部分,然后选择剪辑以查看其设置。)
2.在幽灵 (Ghost) 预制件的 Audio Source 组件中,找到 3D Sound Settings 部分。Spread 属性用于控制声音听起来的发生范围(以度为单位)。
3.将 Spread 属性设置为 180。这意味着一半的声音将来自每个声道,而由于这些声道相同,因此音频听起来将无方向性。
4.保存幽灵 (Ghost) 预制件,然后返回到场景中。
5.保存场景。
您的游戏现在已完成!恭喜!确保对游戏进行测试,以了解玩家的体验情况。
20. 总结
在本教程中,您通过向游戏中添加环境音轨和音效来探索了 Unity 中的音频基础知识。您的游戏现在已完成!还有另一件事要做:在最后一个教程中,您将创建游戏的构建版本,以便可以分发游戏。