텍스트 음성 변환은 텍스트 문자열을 인공 음성을 사용하여 음성 소리로 변환하는 보조 기술의 한 형태입니다. 시각, 이동성 또는 인지 장애가 있는 플레이어를 위한 게임의 접근성을 향상시키는 것 외에도, 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는 플레이어가 비플레이어 캐릭터(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으로 설정하여 게임 내 다른 오디오 소스보다 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로 설정합니다.
기본 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 .. "개 있습니다. " .. 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) 함수는 getRemainingGumdrops()에서 반환된 목록을 사용하여 플레이어가 찾아야 할 남은 구름 사탕의 위치를 설명하는 문자열 메시지를 반환합니다. 그러나 플레이어가 환경 내의 모든 구름 사탕을 수집한 경우, 문자열 메시지는 환경 내의 모든 구름 사탕을 수집한 후 다음에 가야 할 곳을 설명합니다.
볼륨과 충돌하는 모든 것이 있을 때, Touched 이벤트 핸들러 함수는 Model인 첫 번째 조상을 가져오며, 이는 충돌한 BasePart가 캐릭터 모델의 자손인 경우 캐릭터여야 합니다. 그렇다면 함수는 다음을 수행합니다:
- 디바운스를 true로 설정합니다.
- 남은 구름 사탕 목록과 플레이어가 찾아야 할 다음 구름 사탕을 설명하는 문자열 메시지를 가져옵니다.
- 문자열을 AudioTextToSpeech 객체를 사용하여 오디오로 변환하고, 오디오를 재생하고 오디오가 끝날 때까지 기다립니다.
- 디바운스를 다시 false로 설정합니다.
상황 인식 TTS 오디오가 재생된 후 디바운스를 false에서 true로, 다시 false로 설정하는 것은 플레이어가 볼륨과 지속적으로 충돌할 때 오디오가 반복적으로 트리거되는 것을 방지하는 디바운스 패턴입니다. 이 디바운스 패턴에 대한 자세한 내용은 Debounce - Detect collisions를 참조하세요.
게임을 플레이 테스트하여 플레이어 캐릭터가 순록 주변의 볼륨에 닿을 때 상황에 맞는 힌트를 들으세요. TTS 오디오는 플레이어 캐릭터가 이미 환경에서 찾은 구름 사탕의 수와 색상에 따라 변경됩니다.