文字轉語音是一種輔助技術,它使用人工語音將文字字串轉換為語音音效。除了提高視覺、行動或認知障礙玩家的體驗可及性之外,TTS還允許您動態生成語音,這樣您就不必為所有可能的敘事場景預錄音頻。
使用 Gingerbread House - Start 的 .rbxl 文件作為起點,並以 Gingerbread House - Text-to-Speech 作為參考,本教程向您展示如何為您的體驗添加基本和上下文感知的TTS音頻,包括指導:
- 觸發TTS以應對不會改變的常見遊戲場景,例如UI交互和教程。
- 配置TTS,使其根據玩家動作、環境狀態或靈活目標進行調整。
如果在過程中您有任何困難,您可以使用 Gingerbread House - Text-to-Speech 作為參考,以比較您的進度。
音頻對象
要創建TTS音頻,重要的是要了解您在整個教程中將要使用的音頻對象。有五種主要類型的TTS音頻對象:
- AudioTextToSpeech 對象將文字字串轉換為語音音效。
- AudioEmitter 對象是一個虛擬揚聲器,將音頻發射到3D環境中。
- AudioListener 對象是一個虛擬麥克風,從3D環境中接收音頻。
- AudioDeviceOutput 對象是一個實體硬件設備,例如揚聲器或耳機。
- Wires 將音頻流從一個對象傳遞到另一個對象。
所有這些音頻對象協同工作,以響應玩家的動作發出TTS聲音。讓我們來看看這在使用耳機的玩家在筆記本電腦上玩體驗時如何運作:
- AudioTextToSpeech 在玩家觸摸非可Playable角色(NPC)附近的某個部分時,會加載並將文字轉換為音頻。
- AudioEmitter 從NPC向3D環境發射TTS音頻流。
- 角色的子 AudioListener 對象在3D環境中聆聽該聲音並將其回饋到他們的耳機中。
- AudioDeviceOutput 對象將聲音從AudioListener傳遞到玩家的物理揚聲器或在這種情況下,他們的耳機。
接下來的部分將深入探討並引用這些對象,同時您將學習如何播放基本和上下文感知的音頻。在您使用即將推出的技術查看這些對象時,您可以更準確地預測如何捕捉並將聲音從體驗回饋給玩家。
基本 TTS
基本TTS是最常見的文字轉語音形式,其中人工語音無論玩家或環境上下文如何都會朗讀文字字串。這意味著每當玩家觸發TTS音頻時,無論玩家的狀態、他們的行動還是環境狀態如何,單詞及該人工語音朗讀單詞的方式都會保持一致。
這種形式的TTS在大多數遊戲場景中非常有用,例如玩家與UI菜單、教程或例行NPC交互(如供應商報價或敵人叫喊)的互動。Roblox提供了以下類型的聲音,您可以在這些互動中進行實驗:
| VoiceID | 語音描述 | 音頻範例 |
|---|---|---|
| 1 | 英國男性 | |
| 2 | 英國女性 | |
| 3 | 美國男性 #1 | |
| 4 | 美國女性 #1 | |
| 5 | 美國男性 #2 | |
| 6 | 美國女性 #2 | |
| 7 | 澳大利亞男性 | |
| 8 | 澳大利亞女性 | |
| 9 | 復古聲音 #1 | |
| 10 | 復古聲音 #2 | |
| 11 | 主持人聲音 | |
| 101 | 西班牙男性 | |
| 102 | 西班牙女性 | |
| 201 | 德國男性 | |
| 202 | 德國女性 | |
| 301 | 義大利男性 | |
| 302 | 義大利女性 | |
| 401 | 法國男性 | |
| 402 | 法國女性 |
要在示例 Gingerbread House - Text-to-Speech 樓層文件中重建基本的3D TTS音頻:
- 啟用附加到您的玩家角色的默認聆聽器。
- 在 Explorer 窗口中,選擇 SoundService。
- 在 Properties 窗口中,將 DefaultListenerLocation 設置為 Character。當您運行體驗時,引擎會自動:
- 在每個玩家角色的 Humanoid.RootPart 下創建一個 AudioListener,以便您可以根據體驗中的音源位置和音量感受到聲音在您現實世界揚聲器中的變化。
- 在 SoundService 下創建一個 AudioDeviceOutput。
- 在 Explorer 窗口中,導航到 Workspace ⟩ DialogueVolume,然後:
- 插入一個 AudioTextToSpeech 對象,以創建雪人的音頻發聲器。
- 插入一個 AudioEmitter 對象,從 DialogueVolume 發出位置流。
- 插入一個 Wire 對象,以將流從音頻發聲器傳遞到音頻發射器。
- 選擇 AudioTextToSpeech 對象,然後在 Properties 窗口中:
- 將 Text 設置為 "Collect every single last gumpdrop to open my home!"
- 將 VoiceId 設置為 2,以設置人工語音模仿英國女性。
- 將 Volume 設置為 3,以便您可以以高音量播放音頻,讓TTS聲音在體驗中的其他音源上更加明顯。
- 選擇 Wire,然後在 Properties 窗口中:
- 將 SourceInstance 設置為您新創建的 AudioTextToSpeech,以指定您希望這條電線從這個特定的音頻發聲器傳輸音頻。
- 將 TargetInstance 設置為您新的 AudioEmitter,以指定您希望這條電線向這個特定的音頻發射器傳輸音頻。
- 在 Explorer 窗口中導航至 StarterPlayer ⟩ StarterCharacterScripts,然後插入一個 LocalScript,將其重新命名為 PlayBasicTTSAudioWhenInVolume,並將以下代碼粘貼到本地腳本中:
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 audioTextToSpeech = Workspace.DialogueVolume.AudioTextToSpeech
audioTextToSpeech:Play()
audioTextToSpeech.Ended:Wait()
debounce = false
end)
此腳本首先獲取 Workspace 和 Players 服務,以便引用其子項和功能。對於每個玩家角色加載或重生進入體驗,該腳本會等待:
- 工作區中名為 DialogueVolume 的音量對象。
當任何東西與此音量碰撞時,Touched 事件處理函數獲取第一個祖先是 Model,如果遇到的 BasePart 是角色模型的子代,則應是角色。如果是,則函數將:
- 將防抖設置為 true。
- 播放並等待TTS音頻結束。
- 將防抖設置回 false。
將防抖從 false 設置為 true 然後再次設置為 false,以防止音頻在玩家不斷碰撞音量時重複觸發。若要獲得有關此防抖模式的更多信息,請參見 防抖 - 偵測碰撞。
測試體驗,當您的玩家角色觸摸圍繞雪人的音量時,聽到指導角色的對話。
您可以進一步通過修改 Text、VoiceID、Pitch 和 Speed 屬性為新值來實驗此音頻。生成的語音會完全不同,而不需要為每個場景錄製和上傳新的音頻文件。
上下文感知 TTS
上下文感知的TTS是一種更高級的文字轉語音形式,其中人工語音根據玩家、環境狀態或遊戲狀態來朗讀文字字串。這意味著每當玩家觸發TTS音頻時,單詞及人工語音朗讀單詞的方式會相應調整。
這種形式的TTS在不斷變化的遊戲場景中非常有用,例如方向音頻提示、目標狀態或獨特的NPC交互。因此,由於上下文感知的TTS需要轉變以保持準確性,您必須配置遊戲元素,以便在玩家導航環境並完成遊戲目標時跟踪其狀態。
雖然有許多方法可以完成這項任務,但示例使用自定義屬性來跟蹤玩家必須收集的每個糖果的顏色和位置,以便進入薑餅屋。如需有關屬性的更多信息,請參見 屬性和屬性。



要在示例 Gingerbread House - Text-to-Speech 樓層文件中重建上下文感知的3D TTS音頻:
在 Explorer 窗口中,導航到 Workspace ⟩ GumDrops。
配置三個自定義屬性來跟蹤黃色糖果。
- 選擇黃色糖果,然後在 Properties 窗口中,導航到 Attributes 部分,然後單擊加號圖標。彈出對話框顯示。
- 在 Name 欄位中,輸入 ColorDescription。
- 在 Type 下拉菜單中,選擇 string。
- 單擊 Save 按鈕。
- 將新的 ColorDescription 屬性設置為 yellow。
- 使用此過程,使用以下值創建另外兩個屬性。
名稱 類型 值 HintOrder number 0 LocationDescription string by the waterfall 配置三個自定義屬性來跟蹤綠色糖果。
- 在 Explorer 窗口中,選擇綠色糖果。
- 在 Properties 窗口中,使用以下值創建三個屬性。
名稱 類型 值 ColorDescription string green HintOrder number 1 LocationDescription string on the ledge 配置三個自定義屬性來跟蹤紅色糖果。
- 在 Explorer 窗口中,選擇紅色糖果。
- 在 Properties 窗口中,使用以下值創建三個屬性。
名稱 類型 值 ColorDescription string red HintOrder number 2 LocationDescription string behind the fence 在 Explorer 窗口中,導航到 Workspace ⟩ HintVolume,然後:
- 插入一個 AudioTextToSpeech 對象,以創建圍繞馴鹿的音頻發聲器。
- 插入一個 AudioEmitter 對象,從 HintVolume 發出位置流。
- 插入一個 Wire 對象,以將流從音頻發聲器傳遞到音頻發射器。
選擇 Wire,然後在 Properties 窗口中:
- 將 SourceInstance 設置為您的新 AudioTextToSpeech,以指定您希望這條電線從這個特定的音頻發聲器傳輸音頻。
- 將 TargetInstance 設置為您的新 AudioEmitter,以指定您希望這條電線向這個特定的音頻發射器傳輸音頻。
在 Explorer 窗口中導航至 StarterPlayer ⟩ StarterCharacterScripts,然後插入一個 LocalScript,將其重新命名為 PlayContextTTSAudioWhenInVolume,並將以下代碼粘貼到本地腳本中:
local Workspace = game:GetService("Workspace")
local Players = game:GetService("Players")
local humanoid = script.Parent:WaitForChild("Humanoid")
local volumeDetector = Workspace.HintVolume
local trigger = humanoid:WaitForChild("Animator")
local debounce = false
local localPlayer = Players.LocalPlayer
function getRemainingGumdrops(): {Part}
local gumdropsFolder = Workspace.Gumdrops
local gumdrops = gumdropsFolder:GetChildren()
local remainingGumdrops = {};
for _, gumdrop in gumdrops do
if gumdrop:GetAttribute("Active") then
remainingGumdrops[#remainingGumdrops + 1] = gumdrop
end
end
table.sort(remainingGumdrops, function(a, b)
return a:GetAttribute("HintOrder") < b:GetAttribute("HintOrder")
end)
return remainingGumdrops
end
function getReindeerHint(remainingGumdrops: {Part}): string
local remainingGumdrops = getRemainingGumdrops()
if (#remainingGumdrops == 0) then
return "沒有糖果了。查看屋內。"
end
local nextGumdrop = remainingGumdrops[1]
local colorDescription = nextGumdrop:GetAttribute("ColorDescription")
local locationDescription = nextGumdrop:GetAttribute("LocationDescription")
local message = #remainingGumdrops > 1
and "還有 " .. #remainingGumdrops .. " 顆糖果剩下。尋找 " .. colorDescription .. " 的糖果 " .. locationDescription .. "。"
or "還有一顆糖果。它是 " .. colorDescription .. " 的,並且位於 " .. locationDescription .. "。"
return message
end
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
print("玩家觸碰了音量。")
debounce = true
local remainingGumdrops = getRemainingGumdrops()
local message = getReindeerHint(remainingGumdrops)
local audioTextToSpeech = Workspace.HintVolume.AudioTextToSpeech
audioTextToSpeech.Text = message
audioTextToSpeech:Play()
audioTextToSpeech.Ended:Wait()
debounce = false
end)
此腳本首先獲取 Workspace 和 Players 服務,以便引用其子項和功能。對於每個玩家角色加載或重生進入體驗,該腳本會等待:
- 工作區中名為 HintVolume 的音量對象。
腳本的 getRemainingGumdrops() 函數返回 Gumdrops 文件夾中糖果 Part 對象的列表,並過濾掉玩家已收集的任何部件。剩餘的糖果根據每一顆糖果的 HintOrder 屬性按特定順序排序。
腳本的 getReindeerHint(remainingGumdrops) 函數接受該列表並返回一條字符串消息,描述玩家可以找到哪些剩餘的糖果,包括下一顆糖果的顏色和位置描述。但是,如果玩家在環境中收集了所有糖果,則字符串消息會描述他們下一步應該去哪裡。
當任何東西與音量碰撞時,Touched 事件處理函數獲取第一個祖先是 Model,如果遇到的 BasePart 是角色模型的子代,則應是角色。如果是,則函數將:
- 將防抖設置為 true。
- 獲取剩餘糖果的列表以及描述玩家需要尋找的下一顆糖果的字符串消息。
- 使用 AudioTextToSpeech 對象將該字符串轉換為音頻,播放音頻並等待音頻結束。
- 將防抖設置回 false。
將防抖從 false 設置為 true 然後再次設置為 false,以防止音頻在玩家不斷碰撞音量時重複觸發。若要獲得有關此防抖模式的更多信息,請參見 防抖 - 偵測碰撞。
測試體驗,當您的玩家角色觸摸圍繞馴鹿的音量時,聽到上下文提示。TTS音頻根據玩家角色在環境中已找到的糖果數量和顏色而改變。