2D 音頻 是非方向性的聲音,並不來自任何特定位置,無論聽眾在 3D 空間中的位置或方向如何,聽到的效果保持不變。這意味著,隨著玩家在環境中移動他們的聽覺,他們可以隨時聽到來自 2D 音頻的聲音。
以 Gingerbread House - Start .rbxl 文件作為出發點,以 Gingerbread House - Complete Audio 作為參考,本教程將向您展示如何將循環和單次播放的 2D 音頻添加到您的體驗中,包括以下內容的指導:
- 隨著玩家連接到伺服器而播放的循環背景音樂。
- 觸發音頻以根據玩家的遊戲目標向其提供即時反饋。
- 啟用不同的聲音來指示不同的用戶界面交互。
如果在過程中遇到任何困難,您可以參考 Gingerbread House - Complete Audio 來比較您的進度。
2D 音頻對象
要創建非方向性音頻,了解您在本教程中將使用的音頻對象至關重要。對於 2D 音頻,主要有三種類型的音頻對象:
- AudioPlayer 對象加載並播放 音頻文件。
- AudioDeviceOutput 對象是一種 物理硬件設備,例如揚聲器或耳機。
- Wires 將音頻流從一個對象傳遞到另一個對象。
所有這些音頻對象相互協作,將聲音從文件播放傳遞到每位玩家的硬件設備。讓我們通過一個示例來看看這是如何在實踐中運作的:當玩家佩戴耳機並使用筆記本電腦玩體驗時:
- AudioPlayer 將 127645268874265 音頻資產 ID 加載到體驗中,以便發出貨幣購買聲音。
- AudioDeviceOutput 對象將聲音傳遞到玩家的物理揚聲器,或在本例中,是他們的耳機。
接下來的部分將深入探討並引用這些對象,讓您學習如何播放循環和單次播放的 2D 音頻。在您審查這些對象與即將介紹的技術時,您可以更準確地預測如何捕獲並從體驗中向玩家輸送聲音。
循環音頻
循環 2D 音頻,或者說是非方向性音頻,當玩家連接到伺服器時無縫地重複播放,是一種常見的聲音設計技術,無論玩家在做什麼或他們在 3D 空間中的位置如何,都能提供一致的聽覺背景。此外,循環 2D 音頻確保您的體驗從來不會完全安靜,這可能會使玩家感到迷失,並分散他們的注意力,讓他們偏離目標。
為了演示這一概念,讓我們回顧以下 2D 音頻如何顯著提升 Hazardous Space Station 示例的質量。沒有背景音樂,太空站感覺空洞無物。然而,當太空站包含背景音樂時,太空站感覺既不祥又危險,增強了整體的詭異氣氛。
循環音頻特別有助於建立您希望玩家進入體驗後立即感受到的氣氛。例如,該示例旨在為玩家提供在冬季仙境中愉悅的冒險,因此它使用了一段輕快的音頻曲目,以積極地影響他們進入環境時的心情。要了解這種技術能有多具影響力,考慮一下如果用金屬音軌替換循環音頻,玩家的體驗會發生什麼變化。
要在示例 Gingerbread House - Complete Audio 場地文件中重建循環 2D 音頻:
啟用一個默認聆聽器,並將其附加到您的玩家角色上。
- 在 Explorer 窗口中,選擇 SoundService。
- 在 Properties 窗口中,將 DefaultListenerLocation 設置為 Character。當您運行體驗時,引擎會自動在 SoundService 下創建一個 AudioDeviceOutput。
在 Explorer 窗口中,導航到 SoundService,然後:
- 插入一個 AudioPlayer 對象以創建音頻源。
- 插入一個 AudioDeviceOutput 對象以創建一個在整個體驗中播放的揚聲器。
- 插入一個 Wire 對象以攜帶從音頻播放器到揚聲器的音頻流。

選擇 AudioPlayer,然後在 Properties 窗口中,
- 將 AssetID 設置為 rbxassetid://1841461968,以播放一段輕快的音頻曲目。
- 啟用 Looping,使音頻無縫重複。
- 將 Volume 設置為 0.2,以便在低音量下播放音頻,這樣您還能聽到體驗中的其他音頻源。
選擇 Wire,然後在 Properties 窗口中,
- 將 SourceInstance 設置為您的新 AudioPlayer,以指定希望該電線從此特定音頻播放器攜帶音頻。
- 將 TargetInstance 設置為您的新 AudioDeviceOutput,以指定希望該電線將音頻攜帶到此特定揚聲器。
返回 Explorer 窗口,插入一個 Script 到 AudioPlayer,將其重命名為 LoopBackgroundMusic,將其 RunContext 屬性設置為 Client,然後將以下代碼粘貼到腳本中:
local audioPlayer = script.ParentaudioPlayer:Play()代碼解釋此腳本首先聲明一個變量,用於表示腳本的父級 AudioPlayer。然後,該腳本設置音頻源在玩家進入體驗時播放,並在其退出時停止播放。
測試該體驗以聆聽循環的背景音樂。
單次音頻
單次 2D 音頻,或在特定時間和位置播放一次的非方向性音頻,除非玩家再次觸發它,否則提供玩家有關他們不想錯過的任何事的重要上下文。在您的體驗中使用這種類型的聽覺反饋至關重要,因為它對關鍵遊戲功能和玩家動作提供了即時、清晰的響應,例如開始首領戰鬥或失去大量健康值。
以下部分提供了實現詳細信息,針對玩家需要即時、非方向性反饋的常見遊戲場景,包括遊戲反饋、用戶界面元素交互和教程的配音旁白。
遊戲反饋
隨著玩家在體驗中的進展,他們需要及時獲得有關影響他們遊戲的任何狀態的聽覺反饋,例如他們的分數、健康值或等級進度。由於這種音頻直接與玩家的目標相關,因此這些聲音必須清晰、可聽且傳達有用且準確的信息。
為了演示這一概念,讓我們回顧以下來自 Platformer 模板的單次 2D 音頻,當玩家跳躍並收集硬幣時播放:
- 玩家在其裝置上按下跳躍鍵時立即播放一種輕快的風聲。
- 當玩家的角色觸碰到硬幣時,播放高音的鈴聲和硬幣碰撞聲。
這兩個聲音在 3D 空間的上下文中都是即時可識別的,並且幫助玩家理解他們行動的成功,而不僅僅依賴視覺提示。
當您同時播放多個聲音時,某些聲音可能對玩家而言是更重要的。在此情況下,硬幣收集聲音所傳達的遊戲信息比跳躍聲音更為寶貴,因此體驗中播放的音量稍高。然而,沒有標準或規則可以達成良好的混音,因此請調整您體驗中的音效,以反映您認為最重要的內容。
例如,示例在整個體驗中包含了多個音頻觸發事件,但它們在理解體驗的主要目標(收集糖果以打開薑餅屋)方面的重要程度不一。為了確保玩家知道他們需要做什麼,當玩家收集糖果時,示例會將音頻反饋的幅度提高三倍。
要在示例 Gingerbread House - Complete Audio 場地文件中重建單次狀態反饋音頻:
在 Explorer 窗口中,導航到 Workspace ⟩ Gumdrops ⟩ Gumdrop,然後:
- 插入一個 AudioPlayer 對象以創建音頻源。
- 插入一個 AudioDeviceOutput 對象以創建一個在整個體驗中播放的揚聲器。
- 插入一個 Wire 對象以攜帶從音頻播放器到揚聲器的音頻流。

選擇 AudioPlayer,然後在 Properties 窗口中,
- 將 AssetID 設置為 rbxassetid://9113723699,以播放柔和的咀嚼音頻曲目。
- 將 TimePosition 設置為 0.15,以便音頻曲目更接近實際的咀嚼聲音。
- 將 Volume 設置為 3,以便在高音量下播放音頻,確保您聽到的糖果聲音優於體驗中的其他音頻源。
選擇 Wire,然後在 Properties 窗口中,
- 將 SourceInstance 設置為您的新 AudioPlayer,以指定希望該電線從此特定音頻播放器攜帶音頻。
- 將 TargetInstance 設置為您的新 AudioDeviceOutput,以指定希望該電線將音頻攜帶到此特定揚聲器。
複製 Gumdrop 直到您有三個糖果,然後把它們散佈在環境中。
返回 Explorer 窗口,導航到 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 Homestore 模板的購買按鈕重要的 UI 狀態:
- 聚焦中 - 表示玩家正在高亮顯示準備選擇的 UI 元素。
- 懸停中 - 表示玩家正在用鼠標懸停在 UI 元素上。
- 已選擇 - 表示玩家通過其輸入選擇了一個 UI 元素。
這三種互動對於模板中玩家瀏覽、選擇和購買虛擬角色服裝和配件的主要用戶流程至關重要,因此沒有任何人對如何與 UI 交互感到困惑至關重要。為了使玩家成功,模板為每個 UI 互動提供了視覺和聽覺反饋。




為了提供如何配置多種感官反饋的示例,示例在玩家點擊屏幕上的薄荷糖按鈕時,提供了視覺和聽覺反饋。當玩家沒有與按鈕交互時,它看起來像一顆典型的薄荷糖,但當他們按下按鈕時,示例會:
- 播放一段愉快的音樂音頻曲目。
- 用青色調色按鈕。
- 將按鈕稍微向下移動。
從這裡開始,您可以將這一交互關聯到各種有用的遊戲操作,例如打開菜單或購買物品。


要在示例 Gingerbread House - Complete Audio 場地文件中重建單次 UI 交互音頻:
在 Explorer 窗口中,導航到 StarterGui ⟩ 2DAudioButton,然後:
- 插入一個 AudioPlayer 對象以創建音頻源。
- 插入一個 AudioDeviceOutput 對象以創建一個在整個體驗中播放的揚聲器。
- 插入一個 Wire 對象以攜帶從音頻播放器到揚聲器的音頻流。

選擇 AudioPlayer,然後在 Properties 窗口中,將 AssetID 設置為 rbxassetid://3422389728,以播放復古的音樂音頻曲目。
選擇 Wire,然後在 Properties 窗口中,
- 將 SourceInstance 設置為您的新 AudioPlayer,以指定希望該電線從此特定音頻播放器攜帶音頻。
- 將 TargetInstance 設置為您的新 AudioDeviceOutput,以指定希望該電線將音頻攜帶到此特定揚聲器。
返回 Explorer 窗口,插入一個 LocalScript 到 2DAudioButton,將其重命名為 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)代碼解釋該腳本首先獲取:
- 以便可以對 UI 元素進行動畫的 TweenService。
- 腳本的父級 2DAudioButton ScreenGui 對象。
- 薄荷糖 ImageButton。
- 相關的音頻播放器,搭配您的音樂音頻曲目。
然後腳本定義了:
- 一個 TweenInfo 對象,指定按鈕的動畫將使用指數動畫風格播放。
- 兩個代表按鈕被按下或未按下狀態的動畫。
- true 被按下狀態使按鈕稍微向下移動並用青色調色。
- false 未按下狀態將按鈕移回其原始位置並移除之前的顏色。
剩下的腳本則是在觸發 2D UI 互動反饋時發生大量工作的地方,所以讓我們回顧一下 onIsPlayingChanged 函數和事件監聽器如何協作以實現 UI 用戶流程:
- buttonImageButton.Activated 監聽玩家點擊按鈕,然後調用 Play() 函數開始播放相關音頻。此過程將 AudioPlayer.IsPlaying 屬性的值從 false 切換到 true。
- buttonAudioPlayer:GetPropertyChangedSignal("IsPlaying") 監聽音頻播放器的 IsPlaying 屬性發生變化,然後調用 onIsPlayingChanged 函數。
- onIsPlayingChanged 函數根據這個信息觸發動畫,使其在屏幕上的視覺外觀發生改變。
- 為了防止玩家快速點擊按鈕而意外重啟音頻,buttonAudioPlayer.Ended 監聽音頻播放器完成播放後,才再次調用 onIsPlayingChanged 函數。
需要注意的是,onIsPlayingChanged 事件只有在從 false 變化到 true 時才會觸發,這意味著在從 true 變化到 false 并不會觸發。這是預期的行為,因為在伺服器與客戶端之間複製屬性的時間可能會產生複雜問題。因此,在本示例中提供並監聽 Ended 事件以涵蓋這兩種情況。
測試該體驗,每次按下螢幕上的按鈕時,聽到音樂。