立即開始
中級教學

添加 2D 音頻

*此內容是使用 AI(Beta 測試版)翻譯,可能含有錯誤。若要以英文檢視此頁面,請按一下這裡

2D 音頻 是非方向性的聲音,並不來自任何特定位置,無論聽眾在 3D 空間中的位置或方向如何,聽到的效果保持不變。這意味著,隨著玩家在環境中移動他們的聽覺,他們可以隨時聽到來自 2D 音頻的聲音。

Gingerbread House - Start .rbxl 文件作為出發點,以 Gingerbread House - Complete Audio 作為參考,本教程將向您展示如何將循環和單次播放的 2D 音頻添加到您的體驗中,包括以下內容的指導:

  • 隨著玩家連接到伺服器而播放的循環背景音樂。
  • 觸發音頻以根據玩家的遊戲目標向其提供即時反饋。
  • 啟用不同的聲音來指示不同的用戶界面交互。

如果在過程中遇到任何困難,您可以參考 Gingerbread House - Complete Audio 來比較您的進度。

2D 音頻對象

要創建非方向性音頻,了解您在本教程中將使用的音頻對象至關重要。對於 2D 音頻,主要有三種類型的音頻對象:

  • AudioPlayer 對象加載並播放 音頻文件
  • AudioDeviceOutput 對象是一種 物理硬件設備,例如揚聲器或耳機。
  • Wires 將音頻流從一個對象傳遞到另一個對象。

所有這些音頻對象相互協作,將聲音從文件播放傳遞到每位玩家的硬件設備。讓我們通過一個示例來看看這是如何在實踐中運作的:當玩家佩戴耳機並使用筆記本電腦玩體驗時:

  • AudioPlayer127645268874265 音頻資產 ID 加載到體驗中,以便發出貨幣購買聲音。
  • 一條 Wire 將音頻流從 AudioPlayer 傳輸到 AudioDeviceOutput,以便聲音從玩家的耳機中發出。
  • AudioDeviceOutput 對象將聲音傳遞到玩家的物理揚聲器,或在本例中,是他們的耳機。

接下來的部分將深入探討並引用這些對象,讓您學習如何播放循環和單次播放的 2D 音頻。在您審查這些對象與即將介紹的技術時,您可以更準確地預測如何捕獲並從體驗中向玩家輸送聲音。

循環音頻

循環 2D 音頻,或者說是非方向性音頻,當玩家連接到伺服器時無縫地重複播放,是一種常見的聲音設計技術,無論玩家在做什麼或他們在 3D 空間中的位置如何,都能提供一致的聽覺背景。此外,循環 2D 音頻確保您的體驗從來不會完全安靜,這可能會使玩家感到迷失,並分散他們的注意力,讓他們偏離目標。

為了演示這一概念,讓我們回顧以下 2D 音頻如何顯著提升 Hazardous Space Station 示例的質量。沒有背景音樂,太空站感覺空洞無物。然而,當太空站包含背景音樂時,太空站感覺既不祥又危險,增強了整體的詭異氣氛。

沒有循環 2D 音頻
帶有循環 2D 音頻

循環音頻特別有助於建立您希望玩家進入體驗後立即感受到的氣氛。例如,該示例旨在為玩家提供在冬季仙境中愉悅的冒險,因此它使用了一段輕快的音頻曲目,以積極地影響他們進入環境時的心情。要了解這種技術能有多具影響力,考慮一下如果用金屬音軌替換循環音頻,玩家的體驗會發生什麼變化。

要在示例 Gingerbread House - Complete Audio 場地文件中重建循環 2D 音頻:

  1. 啟用一個默認聆聽器,並將其附加到您的玩家角色上。

    1. Explorer 窗口中,選擇 SoundService
    2. Properties 窗口中,將 DefaultListenerLocation 設置為 Character。當您運行體驗時,引擎會自動在 SoundService 下創建一個 AudioDeviceOutput
  2. Explorer 窗口中,導航到 SoundService,然後:

    1. 插入一個 AudioPlayer 對象以創建音頻源。
    2. 插入一個 AudioDeviceOutput 對象以創建一個在整個體驗中播放的揚聲器。
    3. 插入一個 Wire 對象以攜帶從音頻播放器到揚聲器的音頻流。
  3. 選擇 AudioPlayer,然後在 Properties 窗口中,

    1. AssetID 設置為 rbxassetid://1841461968,以播放一段輕快的音頻曲目。
    2. 啟用 Looping,使音頻無縫重複。
    3. Volume 設置為 0.2,以便在低音量下播放音頻,這樣您還能聽到體驗中的其他音頻源。
  4. 選擇 Wire,然後在 Properties 窗口中,

    1. SourceInstance 設置為您的新 AudioPlayer,以指定希望該電線從此特定音頻播放器攜帶音頻。
    2. TargetInstance 設置為您的新 AudioDeviceOutput,以指定希望該電線將音頻攜帶到此特定揚聲器。
  5. 返回 Explorer 窗口,插入一個 ScriptAudioPlayer,將其重命名為 LoopBackgroundMusic,將其 RunContext 屬性設置為 Client,然後將以下代碼粘貼到腳本中:


    local audioPlayer = script.Parent
    audioPlayer:Play()

    此腳本首先聲明一個變量,用於表示腳本的父級 AudioPlayer。然後,該腳本設置音頻源在玩家進入體驗時播放,並在其退出時停止播放。

  6. 測試該體驗以聆聽循環的背景音樂。

單次音頻

單次 2D 音頻,或在特定時間和位置播放一次的非方向性音頻,除非玩家再次觸發它,否則提供玩家有關他們不想錯過的任何事的重要上下文。在您的體驗中使用這種類型的聽覺反饋至關重要,因為它對關鍵遊戲功能和玩家動作提供了即時、清晰的響應,例如開始首領戰鬥或失去大量健康值。

以下部分提供了實現詳細信息,針對玩家需要即時、非方向性反饋的常見遊戲場景,包括遊戲反饋、用戶界面元素交互和教程的配音旁白。

遊戲反饋

隨著玩家在體驗中的進展,他們需要及時獲得有關影響他們遊戲的任何狀態的聽覺反饋,例如他們的分數、健康值或等級進度。由於這種音頻直接與玩家的目標相關,因此這些聲音必須清晰、可聽且傳達有用且準確的信息。

為了演示這一概念,讓我們回顧以下來自 Platformer 模板的單次 2D 音頻,當玩家跳躍並收集硬幣時播放:

  • 玩家在其裝置上按下跳躍鍵時立即播放一種輕快的風聲。
  • 當玩家的角色觸碰到硬幣時,播放高音的鈴聲和硬幣碰撞聲。

這兩個聲音在 3D 空間的上下文中都是即時可識別的,並且幫助玩家理解他們行動的成功,而不僅僅依賴視覺提示。

當您同時播放多個聲音時,某些聲音可能對玩家而言是更重要的。在此情況下,硬幣收集聲音所傳達的遊戲信息比跳躍聲音更為寶貴,因此體驗中播放的音量稍高。然而,沒有標準或規則可以達成良好的混音,因此請調整您體驗中的音效,以反映您認為最重要的內容。

例如,示例在整個體驗中包含了多個音頻觸發事件,但它們在理解體驗的主要目標(收集糖果以打開薑餅屋)方面的重要程度不一。為了確保玩家知道他們需要做什麼,當玩家收集糖果時,示例會將音頻反饋的幅度提高三倍。

要在示例 Gingerbread House - Complete Audio 場地文件中重建單次狀態反饋音頻:

  1. Explorer 窗口中,導航到 WorkspaceGumdropsGumdrop,然後:

    1. 插入一個 AudioPlayer 對象以創建音頻源。
    2. 插入一個 AudioDeviceOutput 對象以創建一個在整個體驗中播放的揚聲器。
    3. 插入一個 Wire 對象以攜帶從音頻播放器到揚聲器的音頻流。
  2. 選擇 AudioPlayer,然後在 Properties 窗口中,

    1. AssetID 設置為 rbxassetid://9113723699,以播放柔和的咀嚼音頻曲目。
    2. TimePosition 設置為 0.15,以便音頻曲目更接近實際的咀嚼聲音。
    3. Volume 設置為 3,以便在高音量下播放音頻,確保您聽到的糖果聲音優於體驗中的其他音頻源。
  3. 選擇 Wire,然後在 Properties 窗口中,

    1. SourceInstance 設置為您的新 AudioPlayer,以指定希望該電線從此特定音頻播放器攜帶音頻。
    2. TargetInstance 設置為您的新 AudioDeviceOutput,以指定希望該電線將音頻攜帶到此特定揚聲器。
  4. 複製 Gumdrop 直到您有三個糖果,然後把它們散佈在環境中。

  5. 返回 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.Gumdrops
    local gumdrops = gumdropsFolder:GetChildren()
    local GUMDROP_KEY_NAME = PlayerData.GUMDROP_KEY_NAME
    local GUMDROP_AMOUNT_TO_ADD = 1
    local 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.AudioPlayer
    audioPlayer:Play()
    -- 動畫門向下移動
    local doorPart = Workspace.Door
    local tweenInfo = TweenInfo.new(2, Enum.EasingStyle.Linear)
    local tween = TweenService:Create(doorPart, tweenInfo, {Position = doorPart.Position + Vector3.new(0, -15, 0)})
    tween:Play()
    end
    end
    -- 定義事件處理函數
    local function onGumdropTouched(otherPart, gumdrop)
    if gumdrop:GetAttribute("Active") then
    local character = otherPart.Parent
    local player = Players:GetPlayerFromCharacter(character)
    if player then
    -- 玩家觸碰了一顆糖果
    local audioPlayer = gumdrop.AudioPlayer
    audioPlayer:Play()
    gumdrop.Transparency = 1
    gumdrop:SetAttribute("Active", false)
    updatePlayerGumdrops(player, function(oldGumdropAmount)
    oldGumdropAmount = oldGumdropAmount or 0
    return oldGumdropAmount + GUMDROP_AMOUNT_TO_ADD
    end)
    end
    end
    end
    -- 設置事件監聽器
    for _, gumdrop in gumdrops do
    gumdrop:SetAttribute("Active", true)
    gumdrop.Touched:Connect(function(otherPart)
    onGumdropTouched(otherPart, gumdrop)
    end)
    end

    該腳本首先初始化 WorkspacePlayersServerStorageTweenService 服務,以便引用它們的子項和功能。然後,在 ServerStorage 中要求 LeaderboardPlayerData 模組;這些模組負責創建和更新顯示在螢幕右上角的排行榜,跟蹤玩家在環境中收集的糖球數量。

    腳本的 updatePlayerGumdrops 函數基本上負責在玩家收集每顆糖果時更新排行榜,並等待他們收集到三顆糖果的瞬間,以便觸發糖果屋門的位音效和動動畫。要獲得有關這部分腳本的更多信息,請參閱 添加 3D 音頻 - 事件反饋

    腳本的 onGumdropTouched 函數是在觸發 2D 音頻以提供遊戲反饋時的主要運作場所,它接受兩個參數:

    • otherPart - 與糖果發生碰撞的零件。
    • gumdrop - 代表糖果的球形零件。

    當任何東西與糖果零件發生碰撞時,腳本首先檢查糖果是否仍然有效。如果是,它會檢查碰撞物是否為玩家。如果是,則腳本會:

    • 播放來自音頻播放器的 2D 音頻曲目。
    • 使糖果變得不可見。
    • 停用糖果,這樣玩家就無法與不可見的糖果互動。
    • 更新觸碰糖果的玩家的排行榜。

    最後,腳本設置事件監聽器,為每顆糖果連接一個函數到 Touched 事件,當玩家觸碰糖果時調用 onGumdropTouched 函數。

  6. 測試該體驗以在收集糖果後聽到咀嚼聲。

用戶界面互動

當玩家與您的用戶界面 (UI) 元素交互時,例如將鼠標懸停在復選框上或選擇購買按鈕,提供即時的反饋至關重要,以便他們直觀地理解他們如何與 UI 交互。例如,許多設計師在玩家啟用或禁用設置時改變 UI 的視覺特徵,以便傳達設置的狀態。

然而,對於視覺障礙的玩家來說,顏色變化或動畫難以自行識別,因此提供多種感官反饋形式對於您的 UI 互動非常有用,使其對盡可能多的玩家保持可及性和直觀性。為了擴展這一概念,讓我們考慮以下對於 UGC Homestore 模板的購買按鈕重要的 UI 狀態:

  • 聚焦中 - 表示玩家正在高亮顯示準備選擇的 UI 元素。
  • 懸停中 - 表示玩家正在用鼠標懸停在 UI 元素上。
  • 已選擇 - 表示玩家通過其輸入選擇了一個 UI 元素。

這三種互動對於模板中玩家瀏覽、選擇和購買虛擬角色服裝和配件的主要用戶流程至關重要,因此沒有任何人對如何與 UI 交互感到困惑至關重要。為了使玩家成功,模板為每個 UI 互動提供了視覺和聽覺反饋。

視覺反饋
默認視圖
聚焦狀態
懸停狀態
已選擇狀態

為了提供如何配置多種感官反饋的示例,示例在玩家點擊屏幕上的薄荷糖按鈕時,提供了視覺和聽覺反饋。當玩家沒有與按鈕交互時,它看起來像一顆典型的薄荷糖,但當他們按下按鈕時,示例會:

  • 播放一段愉快的音樂音頻曲目。
  • 用青色調色按鈕。
  • 將按鈕稍微向下移動。

從這裡開始,您可以將這一交互關聯到各種有用的遊戲操作,例如打開菜單或購買物品。

默認視圖
已按下狀態

要在示例 Gingerbread House - Complete Audio 場地文件中重建單次 UI 交互音頻:

  1. Explorer 窗口中,導航到 StarterGui2DAudioButton,然後:

    1. 插入一個 AudioPlayer 對象以創建音頻源。
    2. 插入一個 AudioDeviceOutput 對象以創建一個在整個體驗中播放的揚聲器。
    3. 插入一個 Wire 對象以攜帶從音頻播放器到揚聲器的音頻流。
  2. 選擇 AudioPlayer,然後在 Properties 窗口中,將 AssetID 設置為 rbxassetid://3422389728,以播放復古的音樂音頻曲目。

  3. 選擇 Wire,然後在 Properties 窗口中,

    1. SourceInstance 設置為您的新 AudioPlayer,以指定希望該電線從此特定音頻播放器攜帶音頻。
    2. TargetInstance 設置為您的新 AudioDeviceOutput,以指定希望該電線將音頻攜帶到此特定揚聲器。
  4. 返回 Explorer 窗口,插入一個 LocalScript2DAudioButton,將其重命名為 PlayAudioWhenPressed,然後將以下代碼粘貼到腳本中:


    local TweenService = game:GetService("TweenService")
    local buttonGui = script.Parent
    local buttonImageButton = buttonGui.ButtonFrame.ButtonImageButton
    local buttonAudioPlayer = buttonGui.AudioPlayer
    local 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.IsPlaying
    local tween = buttonTweenByIsPressed[isPlaying]
    tween:Play()
    end
    onIsPlayingChanged()
    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 用戶流程:

    1. buttonImageButton.Activated 監聽玩家點擊按鈕,然後調用 Play() 函數開始播放相關音頻。此過程將 AudioPlayer.IsPlaying 屬性的值從 false 切換到 true
    2. buttonAudioPlayer:GetPropertyChangedSignal("IsPlaying") 監聽音頻播放器的 IsPlaying 屬性發生變化,然後調用 onIsPlayingChanged 函數。
    3. onIsPlayingChanged 函數根據這個信息觸發動畫,使其在屏幕上的視覺外觀發生改變。
    4. 為了防止玩家快速點擊按鈕而意外重啟音頻,buttonAudioPlayer.Ended 監聽音頻播放器完成播放後,才再次調用 onIsPlayingChanged 函數。

    需要注意的是,onIsPlayingChanged 事件只有在從 false 變化到 true 時才會觸發,這意味著在從 true 變化到 false 并不會觸發。這是預期的行為,因為在伺服器與客戶端之間複製屬性的時間可能會產生複雜問題。因此,在本示例中提供並監聽 Ended 事件以涵蓋這兩種情況。

  5. 測試該體驗,每次按下螢幕上的按鈕時,聽到音樂。

©2026 Roblox Corporation、Roblox、Roblox 標誌及 Powering Imagination 是我們在美國及其他國家地區的部分註冊與未註冊商標。