**텍스트 음성 변환(TTS)**은 텍스트 문자열을 인공 음성을 사용하여 음성 소리로 변환하는 보조 기술의 한 형태입니다. 이는 시각, 이동성 또는 인지 장애가 있는 플레이어의 경험 접근성을 향상시키는 것 외에도 TTS를 사용하여 모든 가능한 서사 시나리오에 대한 오디오를 미리 녹음할 필요 없이 동적으로 음성을 생성할 수 있게 해줍니다.
Gingerbread House - Start .rbxl 파일을 시작점으로 하고 Gingerbread House - Text-to-Speech을 참조하여, 이 튜토리얼에서는 기본 및 상황 인식 TTS 오디오를 경험에 추가하는 방법을 보여줍니다. 여기에는 다음에 대한 지침이 포함됩니다:
- UI 상호작용 및 튜토리얼과 같이 절대 변하지 않는 일반 게임 플레이 시나리오에 대한 TTS 트리거하기
- 플레이어 행동, 환경 상태 또는 유연한 목표에 적응하도록 TTS 구성하기
과정 중 어느 시점에서든 막히게 된다면, Gingerbread House - Text-to-Speech를 참조하여 진행 상황을 비교할 수 있습니다.
오디오 객체
TTS 오디오를 생성하기 위해서는 이 튜토리얼 전반에 걸쳐 작업하게 될 오디오 객체를 이해하는 것이 중요합니다. TTS를 위한 주요 오디오 객체는 다음과 같이 다섯 가지입니다:
- AudioTextToSpeech 객체는 텍스트 문자열을 음성 소리로 변환합니다.
- AudioEmitter 객체는 3D 환경에서 오디오를 방출하는 가상 스피커입니다.
- AudioListener 객체는 3D 환경에서 오디오를 수신하는 가상 마이크입니다.
- AudioDeviceOutput 객체는 스피커나 헤드폰과 같은 실제 세계의 물리적 하드웨어 장치입니다.
- Wires는 오디오 스트림을 한 객체에서 다른 객체로 전달합니다.
이 모든 오디오 객체는 플레이어 행동에 따라 TTS 소리를 방출하기 위해 함께 작용합니다. 다음은 헤드셋을 착용한 플레이어가 노트북으로 경험을 플레이할 때의 3D 오디오 작동 방식의 예입니다:
- AudioTextToSpeech는 플레이어가 비 playable 캐릭터(NPC) 근처의 파트를 터치할 때마다 텍스트를 로드하고 음성으로 변환합니다.
- AudioEmitter는 NPC에서 3D 환경으로 TTS 오디오의 스트림을 방출합니다.
- 캐릭터의 자식 AudioListener 객체는 3D 환경 내에서 해당 소리를 듣고 이를 헤드셋으로 피드백합니다.
- AudioDeviceOutput 객체는 AudioListener에서 플레이어의 물리적 스피커 또는 현재의 경우 헤드폰으로 소리를 전달합니다.
다음 섹션에서는 이러한 객체를 자세히 설명하고 기본 및 상황 인식 오디오 재생 방법을 배웁니다. 향후 기술과 함께 이러한 객체를 검토함으로써 경험에서 플레이어에게 소리를 캡처하고 전달하는 방식을 보다 정확하게 예측할 수 있습니다.
기본 TTS
기본 TTS는 인공 음성이 플레이어나 환경의 맥락에 관계없이 텍스트 문자열을 읽는 가장 일반적인 형태의 텍스트 음성 변환입니다. 즉, 플레이어가 TTS 오디오를 트리거할 때마다 단어와 인공 음성이 단어를 읽는 방식은 플레이어의 상태, 행동 또는 환경 상태와 관계없이 일관되게 유지됩니다.
이 형태의 TTS는 주로 플레이어가 UI 메뉴, 튜토리얼 또는 상점 제안이나 적의 외침과 같은 일상적인 NPC 상호작용을 포함할 때 유용합니다. Roblox는 다음과 같은 여러 가지 음성을 제공하여 이러한 상호작용을 위해 실험할 수 있습니다:
| VoiceID | Voice Description | Audio Example |
|---|---|---|
| 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으로 설정합니다.
- 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로 설정합니다.
기본 TTS 오디오가 재생을 마친 후 false에서 true로 다시 false로 설정하는 것은 플레이어가 볼륨과 지속적으로 충돌하는 것을 방지하는 디바운스 패턴입니다. 디바운스 패턴에 대한 추가 정보는 Debounce - Detect collisions를 참조하십시오.
경험을 플레이하여 플레이어 캐릭터가 눈사람 주변의 볼륨에 닿을 때 지침 캐릭터 대화를 듣습니다.
이 오디오는 Text, VoiceID, Pitch, Speed 속성을 새 값으로 수정하여 더욱 실험할 수 있습니다. 생성된 음성은 각 시나리오에 대해 새로운 오디오 파일을 녹음하고 업로드할 필요 없이 완전히 달라집니다.
상황 인식 TTS
상황 인식 TTS는 인공 음성이 텍스트 문자열을 플레이어, 환경의 상태 또는 게임 플레이 상태와 연관지어 읽는 보다 발전된 형태의 텍스트 음성 변환입니다. 이는 플레이어가 TTS 오디오를 트리거할 때마다 단어와 인공 음성이 단어를 읽는 방식이 그에 따라 조정됨을 의미합니다.
이 형태의 TTS는 방향성 오디오 큐, 목표 상태 또는 독특한 NPC 상호작용과 같이 언제든지 변화하는 게임 플레이 시나리오에 유용합니다. 따라서 상황 인식 TTS가 정확하게 변환되기 위해서는 플레이어가 환경을 탐색하고 게임 플레이 목표를 완료함에 따라 상태를 추적할 수 있도록 게임 플레이 요소를 구성해야 합니다.
이 작업을 수행할 수 있는 방법은 많이 있지만, 샘플은 플레이어가 수집해야 할 각 구슬의 색상과 위치를 추적하기 위해 사용자 정의 속성을 사용합니다. 속성에 대한 더 많은 정보는 Properties and attributes를 참조하십시오.



샘플 Gingerbread House - Text-to-Speech 장소 파일에서 상황 인식 3D TTS 오디오를 재생하려면:
Explorer 창에서 Workspace ⟩ GumDrops로 이동합니다.
노란 구슬을 추적하기 위한 세 개의 사용자 정의 속성을 구성합니다.
- 노란 구슬을 선택한 후 Properties 창에서 Attributes 섹션으로 이동하여 더하기 아이콘을 클릭합니다. 팝업 대화 상자가 표시됩니다.
- Name 필드에 ColorDescription을 입력합니다.
- Type 드롭다운 메뉴에서 string을 선택합니다.
- Save 버튼을 클릭합니다.
- 새 ColorDescription 속성을 yellow로 설정합니다.
- 이 과정을 사용하여 다음 두 가지 값을 사용하여 추가 속성을 생성합니다.
Name Type Value HintOrder number 0 LocationDescription string 폭포 옆 초록 구슬을 추적하기 위한 세 개의 사용자 정의 속성을 구성합니다.
- Explorer 창에서 초록 구슬을 선택합니다.
- Properties 창에서 다음 값으로 세 개의 속성을 만듭니다.
Name Type Value ColorDescription string green HintOrder number 1 LocationDescription string 선반 위 빨간 구슬을 추적하기 위한 세 개의 사용자 정의 속성을 구성합니다.
- Explorer 창에서 빨간 구슬을 선택합니다.
- Properties 창에서 다음 값으로 세 개의 속성을 만듭니다.
Name Type Value ColorDescription string red HintOrder number 2 LocationDescription string 울타리 뒤 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 .. "개의 구슬이 남았습니다. " .. locationDescription .. "에 " .. colorDescription .. " 구슬을 찾아보세요."
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) 함수는 getRemainingGumdrops()로부터 그 목록을 받아 플레이어가 찾아야 할 나머지 구슬의 위치를 설명하는 문자열 메시지를 반환합니다. 그러나 플레이어가 환경 내 모든 구슬을 수집한 경우, 문자열 메시지는 남은 모든 구슬을 수집한 후 다음으로 가야 할 곳을 설명합니다.
어떤 것이 볼륨과 충돌할 때, Touched 이벤트 핸들러 함수는 첫 번째 조상 중 Model인 것을 찾아야 하며, 그것은 충돌한 BasePart가 캐릭터 모델의 후손일 경우 캐릭터여야 합니다. 그렇다면 함수는 다음과 같은 작업을 수행합니다:
- 디바운스를 true로 설정합니다.
- 남은 구슬 목록과 플레이어가 찾아야 할 다음 구슬에 대한 문자열 메시지를 가져옵니다.
- 문자열을 AudioTextToSpeech 객체를 사용하여 오디오로 변환하고, 오디오를 재생한 후 종료되기를 기다립니다.
- 디바운스를 다시 false로 설정합니다.
상황 인식 TTS 오디오가 재생을 마친 후 false에서 true로 다시 false로 설정하는 것은 플레이어가 볼륨과 지속적으로 충돌하는 것을 방지하는 디바운스 패턴입니다. 디바운스 패턴에 대한 추가 정보는 Debounce - Detect collisions를 참조하십시오.
플레이합니다. 경험을 테스트하여 플레이어 캐릭터가 순록 주변의 볼륨에 닿을 때 맥락적 힌트를 듣습니다. TTS 오디오는 플레이어 캐릭터가 환경에서 이미 찾은 구슬의 수와 색상에 따라 변경됩니다.