2D 音频 是一种无方向的声音,不从任何特定位置发出,无论听众在 3D 空间中的位置或方向如何,听到的音频都保持不变。这意味着当玩家在环境中移动他们的听众时,他们可以从任何转向的地方持续听到 2D 音频。
以 姜饼屋 - 开始 .rbxl 文件为起点,并以 姜饼屋 - 完整音频 作为参考,本教程将向您展示如何将循环和单次 2D 音频添加到您的体验中,包括以下内容的指导:
- 循环背景音乐,玩家连接到服务器时立即播放。
- 触发音频,根据玩家的游戏目标提供即时反馈。
- 启动各种声音以指示不同的用户界面交互。
如果您在过程中遇到困难,可以使用 姜饼屋 - 完整音频 作为参考,以比较您的进度。
2D 音频对象
为了创建无方向音频,理解您在本教程中将要使用的音频对象非常重要。对于 2D 音频,有三种主要类型的音频对象:
- AudioPlayer 对象加载并播放 音频文件。
- AudioDeviceOutput 对象是现实世界中的 物理硬件设备,例如扬声器或耳机。
- Wires 将音频流从一个对象传递到另一个对象。
所有这些音频对象共同工作,将声音从文件播放传递到每个玩家的硬件。让我们通过一个示例来了解其在实践中的工作方式:玩家在使用他们的笔记本电脑玩体验时佩戴耳机:
- AudioPlayer 将 127645268874265 音频资产 ID 加载到体验中,用于货币购买的声音。
- AudioDeviceOutput 对象将音频传送到玩家的物理扬声器,或者在这种情况下,是他们的耳机。
接下来的部分将深入探讨并引用这些对象,帮助您学习如何播放循环和单次 2D 音频。在您回顾这些对象与即将到来的技术时,您可以更准确地预测如何将体验中的声音捕捉并传递给玩家。
循环音频
循环 2D 音频,或在玩家连接到服务器时无方向音频无缝重复的音频,是一种常见的声音设计技术,能够提供一致的听觉背景,无论玩家正在做什么或他们在 3D 空间中的位置如何。此外,循环 2D 音频确保您的体验不会完全安静,这可能会使玩家感到迷失并分散他们的目标。
为了演示这个概念,请回顾以下 2D 音频如何显著提升 有害空间站 示例的质量。没有背景音乐时,空间站显得空洞和无生气。然而,当空间站包含背景音乐时,空间站显得既阴郁又危险,增加了整体的恐怖氛围。
循环音频特别适用于确定您想要玩家在加入体验时的感受。例如,示例旨在为玩家提供在冬季仙境中的愉快冒险,因此它使用一个欢快的音轨,以积极影响他们在进入环境时的情绪。要查看该技术可以产生多大的影响,请考虑如果您将循环音频替换为金属轨道,玩家的体验将会发生怎样变化。
要在示例 姜饼屋 - 完整音频 文件中重现循环 2D 音频:
启用附加到玩家角色的默认监听器。
- 在 资源管理器 窗口中,选择 SoundService。
在 资源管理器 窗口中,导航到 SoundService,然后:
- 插入一个 AudioPlayer 对象以创建音频源。
- 插入一个 AudioDeviceOutput 对象以创建一个在整个体验中播放的扬声器。
- 插入一个 Wire 对象以传递音频流从音频播放器到扬声器。

选择 AudioPlayer,然后在 属性 窗口中,
- 将 AssetID 设置为 rbxassetid://1841461968 以播放一个欢快的音轨。
- 启用 Looping 使音频无缝重复。
- 将 Volume 设置为 0.2,使音频以较低音量播放,以便您仍然能够听到体验中的其他音频源。
选择 Wire,然后在 属性 窗口中,
- 将 SourceInstance 设置为您的新 AudioPlayer,以指定希望电线从该特定音频播放器传输音频。
- 将 TargetInstance 设置为您的新 AudioDeviceOutput,以指定希望电线将音频传输到该特定扬声器。
返回到 资源管理器 窗口,在 AudioPlayer 中插入一个 Script,将其重命名为 LoopBackgroundMusic,将其 RunContext 属性设置为 Client,然后将以下代码粘贴到脚本中:
local audioPlayer = script.ParentaudioPlayer:Play()代码说明该脚本首先声明一个变量以表示脚本的父级 AudioPlayer。然后脚本设置音频源,以便在玩家加入体验的那一刻播放音频,直到他们退出体验为止。
体验测试播放,以听到循环背景音乐。
单次音频
单次 2D 音频,或在特定时间和位置播放一次的无方向音频,除非玩家再次触发它,为玩家提供重要的上下文,关于任何您希望他们注意的事情。在您的体验中使用这种类型的听觉反馈非常重要,因为它为关键的游戏功能和玩家行为提供了即时且清晰的响应,例如开始一场 Boss 战或失去大量生命值。
以下部分提供了常见游戏场景的实现细节,在这些场景中,玩家需要及时的无方向反馈,包括游戏反馈、用户界面元素交互和教程的旁白。
游戏反馈
当玩家在您的体验中进展时,立即接收关于影响他们游戏的任何事物的 auditory feedback,如他们的分数、生命值或等级进度非常重要。因为这种类型的音频与玩家目标直接相关,所以这些声音必须清晰、可听,并传达有用和准确的信息。
为了演示这个概念,让我们回顾在 平台游戏 模板中,玩家跳跃并收集硬币时播放的以下单次 2D 音频:
- 当玩家按下设备上的跳跃键时,空气中响起一声呼啸声。
- 当玩家的角色触碰到硬币时,会播放高音的叮咚声和硬币的碰撞声。
这两种声音在 3D 空间的上下文中都是瞬时可识别的,它们帮助玩家理解自己行为的成功,而无须仅仅依靠视觉提示。
当您同时播放多个音效时,某些声音可能对玩家而言比其他声音更重要。在这种情况下,收集硬币的声音传达了比跳跃声音更有价值的游戏信息,因此体验会以稍高的音量播放它。不过,没有标准或规则可以实现完美的混音,因此请根据您认为最重要的内容调整您体验中的声音。
例如,该示例在体验中包含多种音频触发器,但并不是所有触发器在理解体验的主要目标时的同等重要性:收集糖豆以打开姜饼屋。为了确保玩家了解他们需要做什么,该示例在玩家每次收集糖豆时将听觉反馈的振幅放大三倍。
要在示例 姜饼屋 - 完整音频 文件中重现单次状态反馈音频:
在 资源管理器 窗口中,导航到 Workspace ⟩ Gumdrops ⟩ Gumdrop,然后:
- 插入一个 AudioPlayer 对象以创建音频源。
- 插入一个 AudioDeviceOutput 对象以创建一个在整个体验中播放的扬声器。
- 插入一个 Wire 对象以传递音频流从音频播放器到扬声器。

选择 AudioPlayer,然后在 属性 窗口中,
- 将 AssetID 设置为 rbxassetid://9113723699 以播放一段温和的咀嚼音轨。
- 将 TimePosition 设置为 0.15,以便音频轨道更接近实际的咀嚼声音。
- 将 Volume 设置为 3,以在体验中以较高音量播放音频,确保您能听到糖豆的声音。
选择 Wire,然后在 属性 窗口中,
- 将 SourceInstance 设置为您的新 AudioPlayer,以指定希望电线从该特定音频播放器传输音频。
- 将 TargetInstance 设置为您的新 AudioDeviceOutput,以指定希望电线将音频传输到该特定扬声器。
复制 Gumdrop,直到您有三个糖豆,然后将它们散布在环境中。
返回到 资源管理器 窗口,导航到 ServerScriptService,然后插入一个 Script,将其重命名为 GumdropService,将其 RunContext 属性设置为 Server,然后将以下代码粘贴到脚本中:
-- 初始化服务local Workspace = game:GetService("Workspace")local Players = game:GetService("Players")local ServerStorage = game:GetService("ServerStorage")local TweenService = game:GetService("TweenService")-- 模块local Leaderboard = require(ServerStorage.Leaderboard)local PlayerData = require(ServerStorage.PlayerData)-- 变量local gumdropsFolder = Workspace.Gumdropslocal gumdrops = gumdropsFolder:GetChildren()local GUMDROP_KEY_NAME = PlayerData.GUMDROP_KEY_NAMElocal GUMDROP_AMOUNT_TO_ADD = 1local function updatePlayerGumdrops(player, updateFunction)-- 更新糖豆表local newGumdropAmount = PlayerData.updateValue(player, GUMDROP_KEY_NAME, updateFunction)-- 更新糖豆排行榜Leaderboard.setStat(player, GUMDROP_KEY_NAME, newGumdropAmount)-- 检查玩家是否收集了三个糖豆if newGumdropAmount >= 3 then-- 当玩家收集三个糖豆时播放门的事件音频local audioPlayer = Workspace.Door.AudioPlayeraudioPlayer:Play()-- 动画门向下移动local doorPart = Workspace.Doorlocal tweenInfo = TweenInfo.new(2, Enum.EasingStyle.Linear)local tween = TweenService:Create(doorPart, tweenInfo, {Position = doorPart.Position + Vector3.new(0, -15, 0)})tween:Play()endend-- 定义事件处理程序local function onGumdropTouched(otherPart, gumdrop)if gumdrop:GetAttribute("Active") thenlocal character = otherPart.Parentlocal player = Players:GetPlayerFromCharacter(character)if player then-- 玩家碰到糖豆local audioPlayer = gumdrop.AudioPlayeraudioPlayer:Play()gumdrop.Transparency = 1gumdrop:SetAttribute("Active", false)updatePlayerGumdrops(player, function(oldGumdropAmount)oldGumdropAmount = oldGumdropAmount or 0return oldGumdropAmount + GUMDROP_AMOUNT_TO_ADDend)endendend-- 设置事件监听器for _, gumdrop in gumdrops dogumdrop:SetAttribute("Active", true)gumdrop.Touched:Connect(function(otherPart)onGumdropTouched(otherPart, gumdrop)end)end代码说明该脚本首先初始化 Workspace、Players、ServerStorage 和 TweenService 服务,以便引用它们的子项和功能。然后,它在 ServerStorage 中要求 Leaderboard 和 PlayerData 模块;这些模块负责在屏幕右上角创建和更新一个排行榜,跟踪玩家在环境中收集的糖果数量。
脚本的 updatePlayerGumdrops 函数主要负责在玩家收集糖豆时更新排行榜,并等待他们收集所有三个糖豆的时刻,以便触发姜饼屋门的定位声音和动画。有关脚本此部分的更多信息,请参见 添加 3D 音频 - 事件反馈。
脚本的 onGumdropTouched 函数是触发游戏反馈的 2D 音频的主要工作发生的地方,并接受两个参数:
- otherPart - 与糖豆碰撞的部件。
- gumdrop - 代表糖豆的球形部件。
当任何东西与糖豆部件碰撞时,脚本首先检查糖豆是否处于激活状态。如果是,脚本会验证碰撞对象是否为玩家。如果是,脚本然后:
- 播放来自音频播放器的 2D 音频轨道。
- 使糖豆不可见。
- 禁用糖豆,以便玩家不能与不可见的糖豆交互。
- 更新触碰糖豆的玩家的排行榜。
最后,脚本设置事件监听器,以便对于每个糖豆,连接一个函数到 Touched 事件,以便在玩家触碰糖豆时调用 onGumdropTouched 函数。
体验测试播放,以在收集糖豆后听到咀嚼声。
用户界面交互
当玩家与您的用户界面 (UI) 元素交互,例如悬停在复选框上或选择购买按钮时,提供即时反馈很重要,以便他们直观地理解 如何 与您的 UI 进行交互。例如,许多设计师会在玩家启用或禁用设置时更改 UI 的视觉特征,以传达该设置的状态。
但是,对于在视觉上有障碍的玩家来说,颜色变化或动画更难以独立识别,提供多种形式的感觉反馈对于您的 UI 交互是非常有用的,以便尽可能多的玩家都能保持可及性和直观性。为了扩展这一概念,让我们考虑对 UGC 家居商店 模板购买按钮的重要 UI 状态:
- 获取焦点 - 表示玩家正在准备选择 UI 元素。
- 悬停 - 表示玩家的光标正在悬停在 UI 元素上。
- 已选择 - 表示玩家通过输入选择了某个 UI 元素。
这三种交互对于模板的主要用户流程(浏览、选择和购买玩家的头像服装和配饰)至关重要,因此确保任何人都不会对如何与 UI 进行交互感到困惑是至关重要的。为了确保玩家成功,模板为每个 UI 交互提供了视觉和听觉反馈。




举个例子,展示您可以如何配置多种形式的感觉反馈,示例在玩家按下屏幕上的薄荷按钮时提供了视觉和听觉反馈。当玩家没有与按钮交互时,它看起来就像一颗典型的薄荷糖,但当他们按下按钮时,示例:
- 播放一段令人愉快的音乐音轨。
- 将按钮的颜色染成蓝绿色。
- 将按钮稍微向下移动。
从这里,您可以将此交互与各种有用的游戏动作联系起来,例如打开菜单或购买物品。


要在示例 姜饼屋 - 完整音频 文件中重现单次 UI 交互音频:
在 资源管理器 窗口中,导航到 StarterGui ⟩ 2DAudioButton,然后:
- 插入一个 AudioPlayer 对象以创建音频源。
- 插入一个 AudioDeviceOutput 对象以创建一个在整个体验中播放的扬声器。
- 插入一个 Wire 对象以传递音频流从音频播放器到扬声器。

选择 AudioPlayer,然后在 属性 窗口中,将 AssetID 设置为 rbxassetid://3422389728 以播放一段复古的音乐音轨。
选择 Wire,然后在 属性 窗口中,
- 将 SourceInstance 设置为您的新 AudioPlayer,以指定希望电线从该特定音频播放器传输音频。
- 将 TargetInstance 设置为您的新 AudioDeviceOutput,以指定希望电线将音频传输到该特定扬声器。
返回到 资源管理器 窗口,在 2DAudioButton 中插入一个 LocalScript,将其重命名为 PlayAudioWhenPressed,然后将以下代码粘贴到脚本中:
local TweenService = game:GetService("TweenService")local buttonGui = script.Parentlocal buttonImageButton = buttonGui.ButtonFrame.ButtonImageButtonlocal buttonAudioPlayer = buttonGui.AudioPlayerlocal tweenInfo = TweenInfo.new(.2, Enum.EasingStyle.Exponential)local buttonTweenByIsPressed = {-- 按下[true] = TweenService:Create(buttonImageButton, tweenInfo, {Position = buttonImageButton.Position + UDim2.fromScale(0, .1),ImageColor3 = Color3.fromRGB(117, 255, 255),}),-- 默认[false] = TweenService:Create(buttonImageButton, tweenInfo, {Position = buttonImageButton.Position,ImageColor3 = Color3.fromRGB(255, 255, 255),}),}local function onIsPlayingChanged()local isPlaying = buttonAudioPlayer.IsPlayinglocal tween = buttonTweenByIsPressed[isPlaying]tween:Play()endonIsPlayingChanged()buttonAudioPlayer:GetPropertyChangedSignal("IsPlaying"):Connect(onIsPlayingChanged)buttonAudioPlayer.Ended:Connect(onIsPlayingChanged)buttonImageButton.Activated:Connect(function(_hit)buttonAudioPlayer:Play()end)代码说明该脚本首先获取:
- TweenService,以便可以动画 UI 元素。
- 脚本的父级 2DAudioButton ScreenGui 对象。
- 薄荷糖的 ImageButton。
- 拥有您的音乐音轨的相关音频播放器。
然后脚本定义:
- 一个指定按钮动画将以指数动画样式播放的 TweenInfo 对象。
- 两个表示按钮 pressed 和 unpressed 状态的 tween。
- true 按下状态使按钮在屏幕上稍微向下移动,并将其染成蓝绿色。
- false 未按下状态将按钮移回其在屏幕上的原始位置,移除之前的染色。
剩下的脚本是触发 2D UI 交互反馈的主要工作发生的地方,让我们回顾 onIsPlayingChanged 函数和事件监听器在 UI 用户流程中的协同工作:
- buttonImageButton.Activated 监听玩家点击按钮,然后调用 Play() 函数开始播放音频播放器中的相关音频。该过程将 AudioPlayer.IsPlaying 属性从 false 切换为 true。
- buttonAudioPlayer:GetPropertyChangedSignal("IsPlaying") 监听音频播放器的 IsPlaying 属性变化,然后调用 onIsPlayingChanged 函数。
- onIsPlayingChanged 函数利用此信息触发会改变其在屏幕上视觉外观的 tween。
- 为了防止玩家在快速点击按钮时意外重新启动音频,buttonAudioPlayer.Ended 监听音频播放器完成播放,然后再次调用 onIsPlayingChanged 函数。
值得注意的是,onIsPlayingChanged 事件仅在从 false 变为 true 时触发,意味着在从 true 变为 false 时不会触发。这是由于从服务器到客户端复制属性的时序问题而导致的预期行为。因此在此示例中提供并监听 Ended 事件,以涵盖这两种情况。
体验测试播放,以在每次按下屏幕按钮时听到音效。