新增 3D 音訊

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

3D音訊 是從3D空間特定位置發出的方向聲音,根據音訊發射者和聆聽者之間的距離和方向,音量會增加或減少。這意味著聆聽者或發射者在環境中移動時,玩家可以動態聽到不同方向和音量等級的音訊。

使用 薑餅屋 - 開始 .rbxl 檔案作為起點,以及 薑餅屋 - 完成音訊 作為參考,本教學向您展示如何將循環和一次性 3D 音訊添加到您的體驗中,包括指導:

  • 循環播放玩家連接到伺服器時發出的環境聲音。
  • 觸發音響以通知玩家關於對他們的遊遊玩有重要影響的關鍵事件。
  • 啟用音響,在玩家與 3D 物件互動時提供聽覺反饋。
  • 播放角色聲音片段,吸引並引導玩家前往環境內的特定點。

如果在任何時候你被困在過程中,你可以使用 薑餅屋 - 完整音頻 作為參考,來比較你的進度。

音訊對象

要創建方向性音訊,重要的是要理解您在本教學期間將使用的音訊對象。有六種主要類型的音訊對象:

  • AudioPlayer 物件載入並播放 音訊檔案
  • AudioEmitter 對象是一個 虛擬喇叭 ,可以將音頻發射到 3D 環境。
  • AudioListener 對象是一個 虛擬麥克風 ,可從3D環境中拾取音訊。
  • 對象是現實世界內的一個物理硬件裝置,例如扬聲器或耳機。
  • AudioDeviceInput是在現實世界內的 物理麥克風
  • Wires 將音訊流從一個對象傳送到另一個對象。

這些音頻對象皆會協力發出聲音,與其真實世界的對應物一樣。讓我們來看看如何在實踐中使用一個例子來檢視玩家在玩體驗時戴上耳機的情況:

  • The AudioPlayer 載入 1516791621 音頻資產ID到體驗以用於雨軌道。
  • 發出一個雨軌音頻的流到3D環境。
  • A Wire 將流從 AudioPlayer 傳送到 AudioEmitter 以便流從 3D 揚聲器中退出。
  • 角色的子對象 AudioListener 聆聽 3D 環境內的聲音,並將其傳回給他們的耳機。
  • AudioDeviceOutput 對象將聲音從 AudioListener 傳送到玩家的物理喇叭,或在這種情況下,他們的耳機。
  • AudioDeviceInput 對象從真實世界捕捉聲音,並將其傳回到體驗中進行語音聊天。

體驗內的物件代表
>

在現實世界中的物件代表
>

以下部分進一步潛入並參考這些對象,當你學習如何播放循環和一次射擊 3D 音訊時。隨著您使用即將推出的技術檢查這些對象,您可以更準確地預測如何從體驗中捕捉和傳送聲音給玩家,並相反。

循環音訊

循環 3D 音訊 ,或在玩家連接到伺服器時無縫重複的方向性音訊,是一種常見的聲音設計技巧,可以增強 3D 空間的氛圍,讓它感覺活力充沛。此外,循環 3D 音頻會保持環境音源一致,例如電視的靜音或瀑布的咆哮;如果這些聲音突然停止,環境將感覺不寫實。

為了展示此概念,請查看下列 3D 音頻如何在迴圈音頻軌道完成後立即停止到兩個大水瀑布。雖然水的聲音會讓玩家初始化進入戶外環境,但突然的聽覺變化與真實世界中瀑布行為的方式相去甚遠。

相同地,樣本使用此技術來流動巧克力瀑布,並根據玩家與音訊發射器的距離調整其音量。當玩家離開 20 個單位時,發射器以全音量發出聲音。隨著玩家移得越來越遠,沖擊音量每 20 個單位離開音頻發射器之後會減少。這會模擬出實際世界的聲音,當你越遠離來原始碼,聲音的音量就越小。

要重新創建樣本 薑餅屋 - 完整音訊 位置檔案中的循環 3D 音訊:

  1. 啟用附於玩家角色的預設聆聽器。

    1. 導航器 窗口中,選擇 聲音服務
    2. 屬性 窗口中,將 預設聆聽位置 設為 角色 。當您執行體驗時,引擎會自動:
  2. 導航器 窗口中,導航到 工作區 > WaterfallAudioObject ,然後:

    1. 插入 音樂播放器 對象來創建瀑布的音訊來源。
    2. 插入 音頻發射器 對象來從 WaterfallAudioObject 發出位置流。
    3. 插入 導線 對象來將音頻從音頻播放器傳送到音頻發射器。
  3. 選擇 音樂播放器 , 然後在 屬性窗口 中,

    1. 資產ID 設為 rbxassetid://1516791621 播放雨天音訊軌道。
    2. 啟用 循環 以便音頻無縫重複。
  4. 選擇 音頻輸出器 ,然後在 屬性 窗口中,將 音頻衰減距離 設置為{0: 1}, {20: 0.8}, {40: 0.4}, {80: 0},以便聲音每20個單位遠離音頻輸出器時逐漸減少音量。

  5. 選擇 電纜 ,然後在 屬性 視窗中,

    1. 來源實例 設置為你的新 音訊播放器 指定你希望線纜從特定音訊播放玩家傳送音訊。
    2. 目標實例 設為您的新 音訊輸出器 指定您希望將音訊傳送到水流中特定的音訊輸出器上。
  6. 回到 導航器 窗口中,將 腳本 插入 WaterfallAudioObject ,重命名為 循環水流音樂 ,將其 運行上下文 設置為 客戶端 ,然後將以下代碼粘貼到腳指令碼中:


    local audioPlayer = script.Parent
    audioPlayer:Play()

    腳本首先宣言一個變量來代表腳指令碼的父AudioPlayer。腳本然後將音頻來源設置為從玩家加入體驗到他們離開體驗的時刻播放。

  7. 測試體驗,聽到你的虛擬人偶在瀑布附近時發出的循環巧克力雨聲音。當您旋轉角色的頭以看向不同的方向時,聲音會根據發射器在 3D 空間中的位置動態切換到您的實世喇叭。

一發音訊

一次射擊3D音訊 ,或向特定時間和位置播放一次的定向音訊,除非玩家再次觸發它,否則能為玩家提供關於他們行動、環境和周圍角色的資訊。在體驗中使用這種類型的聽覺反饋是必須的,因為它讓玩家做出像避免進入敵人或撿起有用的物品等戰略性決定。

以下部分提供玩家需要及時、方向性反回饋的常見遊戲場景的實現細節,包括情況遊戲事件、對象互動和不可玩角色對話。

事件反回饋

隨著玩家在環境內觸發關鍵情況事件,例如解鎖新的遊戲區域或提醒敵人開火,他們必須理解自己在 3D 空間中需要導向和注意力的地方。如果他們沒有立即收到聽覺反回饋,他們可能會錯過對他們的遊玩有重要影響的資訊,導致不知道去哪裡或下一步該做什麼。

為了展示為什麼這很重要,讓我們回顧從每個玩家的發射器播放的 雷射標籤 樣板的一發3D音訊:

  • 深色爆炸聲響應玩家從爆炸器發射的每一次爆炸。
  • 每次玩家重新載入爆破器時,點擊和機器人嗡嗡聲會在每次重新載入時播放。

這兩種聲音都提供了環境意識,通過警告附近的所有玩家來自爆炸的方向,以便他們做出明智的決定,加入歡樂或避免潛在危險。

樣本使用相同的技巧來為玩家提供關於完成體驗主目標的獎勵的情況意識。收集所有三顆橡皮糖後,薑餅屋的門會打開,讓玩家進入內部的禮物。

因為玩家不需要收集特定順序的橡皮糖,所以玩家必須知道門在哪裡打開,無論他們最後收集哪種橡皮糖。位置聲使這成為可能,讓玩家意識到他們的成功和下一步需要去哪裡,無論他們與門的相對距離和方向有多遠。

要重新創建樣本 薑餅屋 - 完整音訊 位置檔案中的一次射擊事件反饋 3D 音訊:

  1. 導航器 窗口中,導航到 工作區 > ,然後:

    1. 插入 音樂播放器 對象來創建音量的音源。
    2. 插入 音頻發射器 對象來從 發出位置流。
    3. 插入 導線 對象來將音頻從音頻播放器傳送到音頻發射器。
  2. 選擇 音響播放器 , 然後在 屬性窗口 中設置 AssetID 到 播放滑動金屬門音頻軌道。

  3. 選擇 電纜 ,然後在 屬性 視窗中,

    1. 來源實例 設置為你的新 音訊播放器 指定你希望線纜從特定音訊播放玩家傳送音訊。
    2. 目標實例 設為您的新 音訊輸出器 以指定您希望將音訊傳送到此特定音訊輸出器內的音量中。
  4. 回到 檢索器 窗口,導航到 ServerScriptService ,然後插入 腳本 ,重命名為 GumdropService ,將其 運行上下文 設置為 服務器 ,然後將以下代碼粘貼到腳指令碼:


    -- 初始化變量
    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("Enabled") then
    local character = otherPart.Parent
    local player = Players:GetPlayerFromCharacter(character)
    if player then
    -- 玩家觸摸了一個軟糖
    local audioPlayer = gumdrop.AudioPlayer
    audioPlayer:Play()
    gumdrop.Transparency = 1
    gumdrop:SetAttribute("Enabled", false)
    updatePlayerGumdrops(player, function(oldGumdropAmount)
    oldGumdropAmount = oldGumdropAmount or 0
    return oldGumdropAmount + GUMDROP_AMOUNT_TO_ADD
    end)
    print("Player collected gumdrop")
    end
    end
    end
    -- 設定事件聆聽器
    for _, gumdrop in gumdrops do
    gumdrop:SetAttribute("Enabled", true)
    gumdrop.Touched:Connect(function(otherPart)
    onGumdropTouched(otherPart, gumdrop)
    end)
    end

    這個腳本首先初始化 WorkspacePlayersServerStorageTweenService 服務,以便能夠引用它們的子孫和功能。然後,需要 排行榜 和 玩家數據 模組在 ;這些模組負責在屏幕右上角創建和更新一個排行榜,記錄玩家在環境中收集的橡皮球數量。

    腳指令碼的 updatePlayerGumdrops 功能是發生在事件反回饋中啟用 3D 音訊的主要地方,需要兩個參數:

    • player - 收集橡皮糖的玩家。
    • updateFunction - 一個回調功能,可更新玩家收集到的橡皮糖數量。

    當玩家與橡皮糖碰撞時,腳指令碼:

    • 通過呼叫 PlayerData.updateValue 函數來獲得玩家的新橡皮糖收集量值。
    • 使用 Leaderboard.setStat 函數來更新排行榜,以此新數量。
    • 檢查是否超過或等於 3 的數量。

    當此值大於或等於 3 時,腳指令碼:

    • 從音訊播放器播放 3D 音訊軌道到音訊發射器。
    • 將門線性地向下移動15學分,低於目前位置。

    剩下的腳本主要負責檢測任何與橡皮糖發生碰撞的東西是玩家,以便它可以觸發收集反饋的非位置聲音。有關此腳指令碼部分的更多信息,請參閱 添加 2D 音頻 - 遊戲回饋

  5. 在環境中收集所有三個橡皮糖後,播放體驗以聽到幻燈片閘聲。當您旋轉相攝影機時,聲音會動態地在您的實世喇叭上切換,以便您根據發射器在 3D 空間中的位置聽到它。

對象互動

當玩家與環境內的 3D 物件互動,例如開啟燈光開關或拿起武器,很重要要提供即時反饋,讓他們直覺地理解 如何 與物件互動。將視覺和聽覺反饋配對強化玩家的行動與環境反應之間的原因和效果關係。

為了擴展這個概念,讓我們檢查一下植物樣本中的下列一發3D音訊,用於用戶種植捲心菜的用戶流程:

  • 當玩家種下種子時,會發出溫和的咕咕聲。
  • 當玩家灌溉他們成長的植物時,會播放一種濕潤、像濺水一樣的聲音。
  • 當玩家收集完全成長的植物時,會播放片段聲音。
  • 當玩家將捲心菜放入車廂時,會發出柔和的衝擊聲。

所有這些聲音都強化玩家的 靠近提示 鍵與物體改變形狀的 3D 空間互動。對於擁有視覺障礙的玩家,其中顏色變更或動畫更難以分辨,提供這些多種形式的感官反饋幫助您的 3D 對象互動仍然對盡可能多的玩家開放和直觀。

為了提供不同的示例,告訴您如何配置對多種感官反回饋的對象互動,樣本會在玩家踩到麵包屋內的 3D 薄荷按鈕時提供視覺和聽覺反饋。當玩家不與按鈕互動時,它看起來像一個典型的薄荷糖,但當他們踩到按鈕時,樣本:

  • 播放慶祝鈴聲音樂軌道。
  • 用綠色色調淡化按鈕的側面。
  • 將按鈕移到地面。

從這裡,您可以將此互動連接到各種獨特的遊戲行動,例如解鎖物品或觸發特殊能力。

預設視圖
>

已壓縮狀態
>

要重新創建樣本 薑餅屋 - 完整音頻 位置檔案中的一次射擊對象互動 3D 音頻:

  1. 導航器 窗口中,導航到 工作區 > 3DAudioButton ,然後:

    1. 插入 音樂播放器 對象來創建按鈕的音訊來源。
    2. 插入 音頻輸出器 對象,從 3DAudioButton 發出位置流。
    3. 插入 導線 對象來將音頻從音頻播放器傳送到音頻發射器。
  2. 選擇 音樂播放器 , 然後在 屬性窗口 中設置 AssetID 到 播放愉快、慶祝的音訊軌道。

  3. 選擇 電纜 ,然後在 屬性 視窗中,

    1. 來源實例 設置為你的新 音訊播放器 指定你希望線纜從特定音訊播放玩家傳送音訊。
    2. 目標實例 設為您的新 音訊輸出器 以指定您希望將音訊傳送到此特定音訊輸出器內的按鈕上。
  4. 回到 導航器 窗口中,插入 腳本3DAudioButton 中,重命名為 播放音頻按一下即可 ,然後將下列代碼粘貼到腳指令碼中:


    local TweenService = game:GetService("TweenService")
    local buttonModel = script.Parent.Parent
    local buttonPart = buttonModel.ButtonPart
    local buttonPressedAudioPlayer = buttonModel.ButtonPressedAudioPlayer
    local tweenInfo = TweenInfo.new(.2, Enum.EasingStyle.Exponential)
    local buttonTweenByIsPressed = {
    -- 已按下
    [true] = TweenService:Create(buttonPart, tweenInfo, {
    Size = buttonPart.Size / Vector3.new(2, 1, 1),
    Color = Color3.fromRGB(75, 151, 75),
    }),
    -- 預設
    [false] = TweenService:Create(buttonPart, tweenInfo, {
    Size = buttonPart.Size,
    Color = Color3.fromRGB(196, 40, 28),
    }),
    }
    local function onIsPlayingChanged()
    local isPlaying = buttonPressedAudioPlayer.IsPlaying
    local tween = buttonTweenByIsPressed[isPlaying]
    tween:Play()
    end
    onIsPlayingChanged()
    buttonPressedAudioPlayer:GetPropertyChangedSignal("IsPlaying"):Connect(onIsPlayingChanged)
    buttonPressedAudioPlayer.Ended:Connect(onIsPlayingChanged)
    buttonPart.Touched:Connect(function(_hit)
    buttonPressedAudioPlayer:Play()
    end)

    腳本首先會獲得:

    • The TweenService 所以它可以動畫彈出地面的按鈕部分。
    • 腳指令碼的父級 3DAudioButton 模型。
    • 按鈕的部分突出地面。
    • 與你的慶祝聲音音訊檔案相關的音訊播放器。

    腳本接下來定義:

    • 一個 TweenInfo 指定按鈕動畫將使用指數動畫風格播放的對象。
    • 代表按鈕狀態的兩個青少年(按下或未按下)。
      • 按下 true 狀態將按鈕稍微向下移動到地面,並將零件的側面染上綠色色調。
      • 未壓縮狀態的 false 將按鈕移回原始位置,並移除以前的染色。

    剩下的腳本是工作對象互動反回饋的主要發生地方,因此讓我們來看看 onIsPlayingChanged 功能和事件聆聽器如何一起工作:

    1. buttonPart.Touched 聆聽玩家觸碰按鈕,然後呼叫 Play() 函數開始播放相關的音訊,從音訊播放器開始播放。此過程會將AudioPlayer.IsPlaying屬性從false切換到true
    2. buttonPressedAudioPlayer:GetPropertyChangedSignal("IsPlaying") 聆聽音樂播放玩家的 IsPlaying 屬性發生變更,然後呼叫 onIsPlayingChanged 函數。
    3. onIsPlayingChanged 功能使用此信息來啟動在 3D 空間中變更其視覺外觀的青少年。
    4. 為了防止玩家誤啓啟音頻,如果他們快速跳到按鈕上,在快速成功之後再次呼叫 功能, 聆聽音頻播放器完成播放,然後再次呼叫 功能。

    要注意的是,onIsPlayingChanged事件只會在它從false變更為true時發生,這意味著它不會在從true變更為false時發生。這是因為從伺服器到客戶端的複製屬性時間複雜而引起的預期行為。因此,在這個例子中提供和聆聽 Ended 事件來覆蓋兩種情況。

  5. 播放體驗,聽到玩家角色在薑餅屋中觸碰 3D 按鈕時發出的慶祝聲音。當您走開按鈕時,音量會降低。

角色對話

從你的無法播放的角色(NPC)提供方向性音頻,有助於引導玩家前往環境中的特定點,並為他們與其他角色的互動增加深度。事實上,在旨在故事驅動的遊戲中,許多遊戲設計師會戰略性地使用角色對話來間接地教導玩家關於他們的角色、盟友和敵人角色,或是世界本身。

這種技術的常見例子包括:

  • 對話風格 來設置遊戲的調調。
  • 嘲諷 來教導玩家關於角色關係。
  • 敵對對話 承認動機或與玩家相關的位置。
  • 玩家角色大聲說出他們的想法 來輕輕引導玩家做下一步,例如治療自己、移動到另一個地點或尋找物道具。
  • 盟友角色與玩家角色對話 來揭露經驗世界的細節,例如歷史、文化和社會問題。

為了展示這在實踐中會是什麼樣子,讓我們回顧一下來自 Beyond the Dark 展示的下列一次射擊 3D 音頻,該音頻會在玩家在太空站主大廳區域時定期播放。

使用太空站作為角色,此對話片段為玩家提供關於整體設設定的重要上下文和傳說。例如,從這個單一句子玩家學到:

  • 他們在外空間,特別是在名為「Kerr-Newman Deep Space Relay 14」的太空站上。
  • 他們的環境是未來派且歡迎的。
  • 他們是一位訪客,可能很快就會離開。

這些細節一起將玩家置身於環境中,並為他們的任務增添緊迫感。然而,如果玩家不知道他們的主要任務是什麼,你也可以使用角色對話來通知玩家你希望他們在你的體驗中做什麼。

為了說明,樣本使用 音量 或 3D 空間內的隱形區域來啟動從雪人到玩家的角色對話,以引導玩家收集三個橡皮糖以開啟他的首頁門。作為玩家加入體驗的第一件事之一,玩家更有可能喚起對話並知道他們需要做什麼才能成功。

雪人周圍的音量使用衝突反饋來播放音頻,當玩家進入 3D 區域時。

要重新創建樣本 薑餅屋 - 完整音訊 位置檔案中的一發射角色對話 3D 音訊:

  1. 導航器 窗口中,導航到 工作區 > 對話音量 ,然後:

    1. 插入 音樂播放器 對象來創建音量的音源。
    2. 插入 音訊輸出器 對象,從 對話音量 發出位置流。
    3. 插入 導線 對象來將音頻從音頻播放器傳送到音頻發射器。
  2. 選擇 音訊播放器 ,然後在 屬性 視窗中,將 資產ID 設為rbxassetid://92917410841704

  3. 選擇 電纜 ,然後在 屬性 視窗中,

    1. 來源實例 設置為你的新 音訊播放器 指定你希望線纜從特定音訊播放玩家傳送音訊。
    2. 目標實例 設為您的新 音訊輸出器 以指定您希望將音訊傳送到此特定音訊輸出器內的音量中。
  4. 回到 探索器 窗口,導航到 StarterPlayer > StarterCharacterScripts ,插入 本地腳本 ,重命名為 播放音響時在音量中 ,然後將下列代碼粘貼到本地腳指令碼:


    local Workspace = game:GetService("Workspace")
    local Players = game:GetService("Players")
    local humanoid = script.Parent:WaitForChild("Humanoid")
    local volumeDetector = Workspace.DialogueVolume
    local trigger = humanoid:WaitForChild("Animator")
    local debounce = false
    local localPlayer = Players.LocalPlayer
    volumeDetector.Touched:Connect(function(hit)
    if debounce then
    return
    end
    local hitCharacter = hit:FindFirstAncestorWhichIsA("Model")
    local hitPlayer = Players:GetPlayerFromCharacter(hitCharacter)
    if hitPlayer ~= localPlayer then
    return
    end
    debounce = true
    local audioPlayer = Workspace.DialogueVolume.AudioPlayer
    audioPlayer:Play()
    audioPlayer.Ended:Wait()
    debounce = false
    end)

    此腳本首先獲得工作區和玩家服務,以便它可以引用其子服務和功能。對於每個玩家角色載入或重生到體驗中,腳本等待:

    • 角色的 HumanoidAnimator 對象。
    • 工作區名為 對話音量 的音量對象。

    當任何東西與音量發生碰撞時,Touched會獲得第一個祖先,即Model,這應該是角色,如果碰撞的BasePart與音量相關的字模是角色模型的子孫。如果是,那麼功能會:

    • 將延遲設為 true
    • 播放並等待音訊結束。
    • 將延遲設為 false

    將從 設置到 的延遲設置重置為 後,音頻播放完成後再將延遲設置重置為 的模式,這是防止音頻反覆發生的模式,因為玩家不斷碰撞音量。有關此缓冲模式的更多信息,請參閱偵測碰撞

  5. 播放體驗,聽到玩家角色觸碰雪人周圍的音量時聽到教學角色對話。