前言
TTS方法有很多,如果想跨平台那就需要外部插件,而如果仅仅是windows那就没必要用插件来占用空间了。
具体方法:
随便找个空物体,添加以下脚本,然后在需要播报语音的位置添加这行代码即可播报:
FlexibleTTS.Speak("确认定铲");
特点1:使用windows内置的tts功能,不用外部插件,节省空间。
特点2:不直接与api通讯,而是通过powershell来转接,原因是每台电脑的tts_api地址可能不同。
特点3:异步,不堵塞主线程
这是完整脚本:
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
public class FlexibleTTS : MonoBehaviour
{
private static Process _psProcess;
private static StreamWriter _inputWriter;
private static bool _isChineseSupported;
// 初始化长驻进程(建议在游戏启动时调用)
[RuntimeInitializeOnLoadMethod]
private static void Initialize()
{
StartPowerShellProcess();
}
private static void StartPowerShellProcess()
{
try {
var psi = new ProcessStartInfo
{
FileName = "powershell.exe",
Arguments = "-NoLogo -NoProfile -Command -",
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
StandardOutputEncoding = Encoding.UTF8,
StandardErrorEncoding = Encoding.UTF8
};
_psProcess = new Process { StartInfo = psi };
_psProcess.OutputDataReceived += (sender, e) => UnityEngine.Debug.Log($"PS Output: {e.Data}");
_psProcess.ErrorDataReceived += (sender, e) => UnityEngine.Debug.LogError($"PS Error: {e.Data}");
_psProcess.Start();
_inputWriter = _psProcess.StandardInput;
_psProcess.BeginOutputReadLine();
_psProcess.BeginErrorReadLine();
// 检测中文语音支持
_inputWriter.WriteLine(@"
Add-Type -AssemblyName System.Speech;
$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer;
$chineseVoice = $speak.GetInstalledVoices() |
Where-Object { $_.VoiceInfo.Culture.Name -eq 'zh-CN' } |
Select-Object -First 1;
if ($chineseVoice -ne $null) {
$speak.SelectVoice($chineseVoice.VoiceInfo.Name);
'CHINESE_SUPPORTED'
} else {
'CHINESE_NOT_SUPPORTED'
}
");
_inputWriter.Flush();
}
catch (System.Exception e) {
UnityEngine.Debug.LogError($"TTS Init Failed: {e.Message}");
}
}
// 动态配置语音引擎(可选)
public static void ConfigureVoice(string cultureName = "zh-CN", string voiceName = null)
{
if (_inputWriter == null || _psProcess.HasExited) return;
_inputWriter.WriteLine($@"
Add-Type -AssemblyName System.Speech;
$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer;
{(string.IsNullOrEmpty(voiceName) ?
$"$voice = $speak.GetInstalledVoices() | Where-Object {{ $_.VoiceInfo.Culture.Name -eq '{cultureName}' }} | Select-Object -First 1;" :
$"$voice = $speak.GetInstalledVoices() | Where-Object {{ $_.VoiceInfo.Name -like '*{voiceName}*' }} | Select-Object -First 1;")}
if ($voice -ne $null) {{
$speak.SelectVoice($voice.VoiceInfo.Name);
'VOICE_CONFIGURED'
}} else {{
'VOICE_NOT_FOUND'
}}
");
_inputWriter.Flush();
}
public static void Speak(string text)
{
if (_inputWriter == null || _psProcess.HasExited) {
StartPowerShellProcess();
return;
}
try {
// 将文本转换为Base64编码
byte[] textBytes = Encoding.UTF8.GetBytes(text);
string base64Text = Convert.ToBase64String(textBytes);
_inputWriter.WriteLine($@"
$bytes = [System.Convert]::FromBase64String('{base64Text}');
$decodedText = [System.Text.Encoding]::UTF8.GetString($bytes);
$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer;
$speak.Speak($decodedText);
");
_inputWriter.Flush();
}
catch (Exception e) {
UnityEngine.Debug.LogError($"TTS Error: {e.Message}");
}
}
// 异步语音合成(不阻塞主线程)
public static async Task SpeakAsync(string text)
{
await Task.Run(() => Speak(text));
}
// 清理资源
private void OnApplicationQuit()
{
if (_inputWriter != null) {
_inputWriter.WriteLine("exit");
_inputWriter.Close();
}
_psProcess?.Kill();
}
}