Unity3D集成百度语音SDK:Android平台语音识别与唤醒实战指南

📅 2026/7/18 7:50:27 👁️ 阅读次数 📝 编程学习
Unity3D集成百度语音SDK:Android平台语音识别与唤醒实战指南

1. 项目概述与核心价值

最近在做一个需要语音交互的Unity3D项目,目标平台是Android。核心需求很简单:用户能通过语音指令控制游戏内的角色动作,或者进行语音输入。市面上语音方案不少,但综合考虑识别准确率、中文支持、离线能力、集成成本和开发周期,我最终选择了百度语音。这不仅仅是因为它提供了相对完善的Unity SDK和Android平台支持,更关键的是,它的“一句话识别”和“唤醒词”功能组合,能很好地满足实时交互和低功耗待机的场景。

你可能也遇到过类似需求:想给AR应用加个语音导航,或者做个语音控制的智能家居演示,又或者像我一样,做个需要语音指令的移动端游戏。从头训练模型不现实,用手机自带的语音助手又受限于系统权限和定制化程度。百度语音的Unity插件,相当于把一套成熟的语音技术封装好了,我们只需要关心业务逻辑怎么接。这次实战,我会把从零开始集成、调试到最终打包上线的完整过程,以及中间踩过的所有坑,都详细拆解一遍。无论你是Unity新手还是有一定经验的开发者,这篇指南都能帮你绕过弯路,快速实现功能。

2. 环境准备与SDK导入

集成任何第三方SDK,第一步永远是搭建好基础环境,这步没做好,后面全是坑。对于Unity3D集成百度语音Android SDK,你需要准备三个东西:Unity开发环境、Android构建环境、百度语音开发者账号和对应的SDK包。

2.1 开发环境配置清单

首先确保你的Unity版本是长期支持版,比如2021.3 LTS或2022.3 LTS。太老的版本可能遇到.NET兼容性问题,太新的版本则可能SDK还没适配。我用的Unity 2021.3.32f1,比较稳定。

Android环境是重头戏。你需要在Unity中安装Android Build Support模块。然后,重点来了:配置JDK、SDK和NDK。很多教程让你用Unity自带的,但我强烈建议手动配置。

  1. JDK:不要用最新的JDK 17或21,兼容性问题会让你头疼。使用JDK 8。你可以从Oracle官网下载历史版本,或者使用OpenJDK 8。安装后,在Unity的Edit -> Preferences -> External Tools中,将JDK路径指向你安装的JDK 8根目录。
  2. Android SDK:同样在External Tools里,取消勾选“Android SDK tools installed with Unity”,然后指定到你本地的Android SDK路径。如果你没有,可以通过Android Studio下载,或者单独下载命令行工具。确保SDK中安装了必要的API平台(比如API Level 31或32)和Build-Tools。
  3. NDK:百度语音SDK可能依赖特定NDK版本。Unity 2021.3通常配套NDK r21或r22。你可以在Unity Hub中安装对应版本的NDK,然后在External Tools中指定路径。如果集成后编译报错找不到C++库,十有八九是NDK版本不对。

注意:路径中不要有中文和空格!这是无数血泪教训换来的。最好把JDK、SDK、NDK都放在像D:\DevEnv\Android这样的纯英文路径下。

2.2 获取并导入百度语音SDK

接下来,前往百度AI开放平台,注册并创建应用,在语音技术板块找到“语音识别”和“语音唤醒”产品,根据指引开通。创建应用后,你会得到三个关键信息:AppIDAPI KeySecret Key。记好它们,后面初始化要用。

在资源下载页面,找到“Unity SDK”进行下载。通常百度提供的Unity插件包是一个.unitypackage文件。在Unity项目中,直接双击这个文件,会打开导入窗口。我建议不要全选导入,因为包里可能包含示例场景、文档、不同平台的库。对于Android项目,我们主要需要:

  • Plugins/Android文件夹下的.aar.jar库文件。
  • Plugins/下可能有的其他平台库(iOS等,可以暂时不导)。
  • Scripts/文件夹下的C#脚本。
  • Resources/StreamingAssets/下的配置文件或模型文件。

导入后,检查Project视图中的Plugins文件夹结构。一个典型的百度语音Unity插件Android部分,会包含BaiduSpeechSDK.aar这样的主库,以及一些依赖库。确保它们都在Plugins/Android目录下。

2.3 基础项目设置与权限配置

导入SDK后,需要修改Unity的Player Settings以适应Android平台和百度SDK的要求。

  1. 切换平台:在File -> Build Settings中,选择Android,点击“Switch Platform”。
  2. Package Name:在Player Settings -> Other Settings中,设置你的包名(如com.YourCompany.YourApp),确保其唯一性。
  3. Minimum API Level:根据你的目标用户设备情况设置,建议至少设置为API Level 24(Android 7.0)。
  4. Target API Level:设置为最新的稳定版(如API Level 33),但需要确保你的所有插件支持。
  5. 关键配置
    • Scripting Backend:选择IL2CPP。Mono在64位架构和性能上不如IL2CPP,且一些原生插件对IL2CPP支持更好。
    • Target Architectures:勾选ARM64。现在市面上绝大多数Android设备都是64位,只勾选ARMv7可能会在部分新设备上无法安装或运行。如果SDK也提供x86库(用于模拟器),可以一并勾选,但发布包时记得在打包设置里剔除以减少包体。
  6. 权限声明:语音功能必须获取麦克风权限。在Player Settings -> Android -> Manifest部分,如果你没有自定义的AndroidManifest.xml文件,Unity会生成一个默认的。我们需要修改或覆盖它。最简单的方法是:在Assets/Plugins/Android文件夹下创建一个名为AndroidManifest.xml的文件。内容至少包括:
    <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.YourCompany.YourApp"> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <!-- 如果需要在后台唤醒,可能需要WAKE_LOCK权限 --> <uses-permission android:name="android.permission.WAKE_LOCK" /> <application android:usesCleartextTraffic="true"> <!-- 你的Activity等配置 --> </application> </manifest>
    注意WRITE_EXTERNAL_STORAGEREAD_EXTERNAL_STORAGE权限在Android 10及以上版本有了作用域存储限制,我们通过maxSdkVersion限制只在低版本系统申请。usesCleartextTraffic设置为true,是因为SDK内部某些HTTP请求可能未加密,在Android 9以上默认禁止明文传输,设为true可避免网络请求失败。

3. 核心功能实现:语音识别与唤醒

环境搭好,SDK就位,现在进入核心环节:写代码。百度语音Unity SDK的核心类通常围绕SpeechRecognizer(识别器)和Wakeuper(唤醒器)展开。我们的目标是实现一个简单的管理器,封装初始化、开始识别、开始唤醒、处理回调等逻辑。

3.1 初始化语音服务

所有语音操作的前提是成功初始化SDK。初始化需要在合适的时机进行,比如游戏启动后、进入主菜单时。我习惯创建一个单例管理器BaiduSpeechManager来负责所有语音交互。

using UnityEngine; using System; // 使用百度SDK的命名空间,这里用BaiduSpeech举例 // 假设百度SDK的类在Baidu.Speech命名空间下 // using Baidu.Speech; public class BaiduSpeechManager : MonoBehaviour { public static BaiduSpeechManager Instance { get; private set; } // 在百度AI平台创建应用后获得 public string appId = "你的AppID"; public string apiKey = "你的API Key"; public string secretKey = "你的Secret Key"; private SpeechRecognizer recognizer; private Wakeuper wakeuper; private bool isInitialized = false; // 定义一些事件,方便UI或其他脚本响应 public event Action<string> OnRecognizeResult; // 识别结果 public event Action<string> OnWakeupResult; // 唤醒结果 public event Action<string> OnError; // 错误信息 void Awake() { if (Instance == null) { Instance = this; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); } } void Start() { InitializeSpeechService(); } void InitializeSpeechService() { // 1. 初始化语音识别 RecognizerInitConfig recogConfig = new RecognizerInitConfig(); recogConfig.AppId = appId; recogConfig.ApiKey = apiKey; recogConfig.SecretKey = secretKey; // 设置识别模式:在线识别、离线识别、混合识别 recogConfig.RecognizeMode = RecognizeMode.ONLINE; // 先从在线开始调试 // 设置采样率、语言等 recogConfig.SampleRate = 16000; // 通常为16000 recogConfig.Language = "zh"; // 中文 recognizer = SpeechRecognizer.Create(recogConfig); recognizer.OnRecognizeResult += (result) => { Debug.Log($"识别结果: {result}"); OnRecognizeResult?.Invoke(result); }; recognizer.OnError += (errorCode, errorMessage) => { string err = $"识别错误 [{errorCode}]: {errorMessage}"; Debug.LogError(err); OnError?.Invoke(err); }; // 2. 初始化语音唤醒 WakeuperInitConfig wakeupConfig = new WakeuperInitConfig(); wakeupConfig.AppId = appId; wakeupConfig.ApiKey = apiKey; wakeupConfig.SecretKey = secretKey; // 唤醒词文件路径,这个文件通常需要放在StreamingAssets下,并在初始化时加载 wakeupConfig.WakeupWordFilePath = Application.streamingAssetsPath + "/你的唤醒词文件.bin"; wakeuper = Wakeuper.Create(wakeupConfig); wakeuper.OnWakeupResult += (result) => { Debug.Log($"唤醒结果: {result}"); OnWakeupResult?.Invoke(result); }; wakeuper.OnError += (errorCode, errorMessage) => { string err = $"唤醒错误 [{errorCode}]: {errorMessage}"; Debug.LogError(err); OnError?.Invoke(err); }; // 3. 检查必要的文件权限(Android运行时权限) #if UNITY_ANDROID && !UNITY_EDITOR CheckAndRequestPermissions(); #else isInitialized = true; Debug.Log("语音服务初始化完成(编辑器模式)"); #endif } void CheckAndRequestPermissions() { // 使用UnityEngine.Android.Permission类或第三方插件处理运行时权限 // 这里是一个简单示例,实际应用可能需要更复杂的逻辑 if (!UnityEngine.Android.Permission.HasUserAuthorizedPermission(UnityEngine.Android.Permission.Microphone)) { UnityEngine.Android.Permission.RequestUserPermission(UnityEngine.Android.Permission.Microphone); } // 等待权限授予,这里简化处理,实际应在权限回调后设置isInitialized isInitialized = true; Debug.Log("语音服务初始化完成(Android真机)"); } }

这段代码搭建了管理器的骨架。有几个关键点:

  • 单例模式:确保全局只有一个语音管理器,方便访问。
  • 初始化配置RecognizerInitConfigWakeuperInitConfig包含了连接百度服务所需的所有认证信息。RecognizeMode很重要,ONLINE依赖网络,识别率高;OFFLINE完全本地,速度快但资源占用大;MIX是混合模式。
  • 回调事件:SDK通过事件返回结果。我们将它们转发为C#的Action事件,这样UI脚本只需要监听这些事件即可,解耦做得更好。
  • Android权限:在真机上,麦克风权限必须在运行时动态申请。Unity提供了UnityEngine.Android.Permission类来处理。注意,在Unity编辑器中这些权限代码不会执行,所以用预处理指令#if隔开。

3.2 实现实时语音识别

初始化成功后,就可以开始“听”用户说话了。百度SDK的识别通常有两种模式:短语音识别(说完一段话自动结束)和长语音识别(需要手动控制结束)。对于游戏指令,短语音识别更常用。

我们在管理器中添加开始和停止识别的方法:

public class BaiduSpeechManager : MonoBehaviour { // ... 之前的代码 ... public void StartRecognizing() { if (!isInitialized || recognizer == null) { Debug.LogWarning("语音识别未初始化!"); return; } // 开始录音并识别 recognizer.Start(); Debug.Log("开始语音识别..."); } public void StopRecognizing() { if (recognizer != null && recognizer.IsRunning()) { recognizer.Stop(); Debug.Log("停止语音识别"); } } // 一个便捷方法:识别指定时长后自动停止 public void RecognizeForDuration(float durationInSeconds) { StartRecognizing(); StartCoroutine(StopAfterDelay(durationInSeconds)); } private System.Collections.IEnumerator StopAfterDelay(float delay) { yield return new WaitForSeconds(delay); StopRecognizing(); } }

看起来很简单,对吧?但这里有个大坑:音频录制与Unity的AudioListener冲突。Unity默认的音频系统可能会占用麦克风设备,导致SDK无法正常录音。解决方法是在开始识别前,暂停或关闭Unity的音频。可以在StartRecognizing开头加入:

// 暂停Unity音频,避免冲突 AudioListener.pause = true; // 或者,如果你知道是哪个AudioSource在录制,可以单独停止它

StopRecognizing或识别结束的回调中,再恢复:

AudioListener.pause = false;

另外,识别结果的回调OnRecognizeResult返回的result字符串,通常是JSON格式。你需要解析它来获取最终的识别文本。百度返回的JSON里,result字段是一个数组,包含多个可能的识别结果(按置信度排序)。通常取第一个。

recognizer.OnRecognizeResult += (resultJson) => { try { // 简单解析JSON,可以使用Unity自带的JsonUtility或第三方库如Newtonsoft.Json // 这里假设返回格式为 {"result":["识别出的文字"]} var resultObj = JsonUtility.FromJson<RecognitionResult>(resultJson); if (resultObj != null && resultObj.result != null && resultObj.result.Length > 0) { string text = resultObj.result[0]; Debug.Log($"最终识别文本: {text}"); OnRecognizeResult?.Invoke(text); } } catch (Exception e) { Debug.LogError($"解析识别结果失败: {e.Message}"); } }; [System.Serializable] public class RecognitionResult { public string[] result; }

3.3 集成语音唤醒功能

唤醒功能让设备在低功耗状态下“监听”特定的关键词(比如“小度小度”)。这对于需要随时待命的语音应用至关重要。百度语音唤醒需要两个东西:唤醒词和对应的声学模型文件(.bin文件)。通常你需要去百度AI平台,在唤醒词管理页面,自定义或选择预设的唤醒词,然后训练并下载对应的模型文件。

将下载的.bin文件放到Unity项目的Assets/StreamingAssets文件夹下。因为StreamingAssets在打包后会原封不动地包含在APK中,并且可以通过Application.streamingAssetsPath访问。在初始化Wakeuper时,指定这个路径。

唤醒的代码逻辑比识别更简单,因为它大部分时间在后台监听,只有唤醒成功时才触发一次回调。

public class BaiduSpeechManager : MonoBehaviour { // ... 之前的代码 ... public void StartWakeup() { if (!isInitialized || wakeuper == null) { Debug.LogWarning("语音唤醒未初始化!"); return; } wakeuper.Start(); Debug.Log("开始语音唤醒监听..."); } public void StopWakeup() { if (wakeuper != null && wakeuper.IsRunning()) { wakeuper.Stop(); Debug.Log("停止语音唤醒"); } } }

唤醒成功后,OnWakeupResult事件会被触发。这个结果通常也包含唤醒词的文本和置信度。之后,你可以手动启动语音识别,或者执行预设的唤醒后动作。

一个重要的实践:在移动设备上,为了省电,通常不会让唤醒功能一直以最高功耗运行。你可以结合Android的生命周期来管理。例如,在应用进入后台时停止唤醒,回到前台时再开启。或者在锁屏时使用更省电的“轻量唤醒”模式(如果SDK支持)。

4. Android平台打包与真机调试

代码在Unity编辑器里跑通了,不代表在真机上就能用。Android打包是检验集成的最终关卡。

4.1 关键打包设置复查

在点击Build之前,再次确认Player Settings

  • Other Settings:
    • Package Name: 确认无误。
    • Minimum API Level: 与你测试机匹配。
    • Target API Level: 建议与Minimum一致或更高,避免兼容性警告。
    • Scripting Backend:IL2CPP
    • Target Architectures:ARM64必须勾选,ARMv7可选。
    • Enable Multithreaded Rendering: 建议开启,提升性能。
  • Publishing Settings:
    • Keystore: 如果你要发布,需要自己的Keystore。调试可以用Unity默认的。
    • Custom Main Gradle Template/Custom Gradle Properties Template: 如果打包遇到Gradle相关错误(尤其是与AndroidX、SDK版本冲突),可能需要启用并修改这些模板文件。百度SDK如果比较老,可能会与Unity新的Gradle模板冲突。

4.2 处理常见的打包错误

  1. Dex文件数量超过64K限制:如果你的项目比较大,可能会报Cannot fit requested classes in a single dex file。解决方法:在Custom Main Gradle TemplateAssets/Plugins/Android/mainTemplate.gradle)中,在defaultConfig块内添加multiDexEnabled true。如果文件不存在,需要在Player Settings中勾选Custom Main Gradle Template来生成。
    android { defaultConfig { // ... 其他配置 ... multiDexEnabled true } }
  2. AndroidX / Support库冲突:报错信息中常出现Program type already present: android.support.v4之类。这是因为百度SDK可能使用了旧的Android Support库,而Unity或其它插件使用了AndroidX。解决方案是使用Jetifier。在Custom Gradle Properties TemplategradleTemplate.properties)文件中添加:
    android.useAndroidX=true android.enableJetifier=true
    同时,确保Player Settings中Publishing Settings下的Minify选项(如Proguard)配置正确,有时需要添加规则排除冲突的类。
  3. 找不到原生库(.so文件):如果报错java.lang.UnsatisfiedLinkError,说明.aar包里的原生库没有正确打包进去。检查Plugins/Android下的.aar文件是否完整。有时需要手动解压.aar,确认jni/文件夹下有所需架构(如arm64-v8a)的.so文件。如果缺失,可能需要联系SDK提供商或寻找其他版本。

4.3 真机调试与日志查看

打包出APK,安装到真机上,这是最激动人心也最容易崩溃的环节。

  • 连接设备:开启手机的USB调试模式,用数据线连接电脑。在Unity的Build Settings中,勾选Development BuildAutoconnect Profiler。点击Build And Run,Unity会自动安装并启动应用。
  • 查看日志:Unity编辑器的Console窗口在真机运行时看不到日志。你需要使用Android Logcat。在Unity编辑器中,打开Window -> Analysis -> Android Logcat。确保设备已连接,在Logcat窗口中选择你的设备,并过滤标签为Unity的日志。这里会输出你代码中的Debug.Log以及所有Unity和原生插件的错误信息。
  • 权限问题:如果应用启动后没声音或唤醒没反应,首先检查Logcat是否有权限被拒绝的日志。确保你的动态权限申请代码被正确触发,并且用户点击了“允许”。可以在应用启动后,主动调用一个按钮来触发权限申请,作为备用方案。
  • 网络问题:在线识别需要网络。确保手机网络通畅,并且没有防火墙阻止应用访问百度语音服务器。第一次初始化可能会从网络加载一些资源,如果超时,检查Logcat中的网络错误。

5. 性能优化与疑难问题排查

功能跑通只是第一步,要让体验流畅稳定,还需要优化和排错。

5.1 性能优化要点

  1. 内存与CPU占用:语音识别和唤醒是计算密集型任务。持续录音和音频处理会消耗CPU和内存。在不需要的时候(如玩家在观看过场动画时),务必调用StopRecognizing()StopWakeup()来释放资源。
  2. 电池消耗:持续唤醒是耗电大户。根据应用场景设计合理的唤醒策略。例如,只在特定界面开启唤醒,或者使用“按下说话”按钮来代替常时唤醒。
  3. 音频延迟:从说话到收到识别结果,会有一定延迟。优化方法:
    • 使用VAD(语音活动检测):百度SDK通常内置VAD,可以配置enableVAD为true,让它自动检测语音开始和结束,减少无效录音时间。
    • 选择合适的识别模式:对实时性要求高的指令,可以尝试离线识别,虽然词汇量有限,但延迟极低。
    • 优化网络:在线识别延迟受网络影响很大。
  4. 模型文件加载:离线识别或唤醒需要的模型文件(.dat, .bin)可能很大。避免在UI线程同步加载,可以在场景加载时异步加载到内存中。

5.2 常见问题与解决方案实录

下面是我在集成过程中遇到的一些典型问题及解决方法,整理成了速查表:

问题现象可能原因排查步骤与解决方案
初始化失败,错误码 3301授权失败,AppID/ApiKey/SecretKey 错误或网络问题。1. 检查三要素是否与百度AI平台创建的应用完全一致,注意大小写和空格。
2. 检查设备网络是否正常,能否ping通百度服务器。
3. 在百度AI平台检查该语音服务是否已“开通”。
4. 对于离线功能,检查是否在平台创建了对应的“离线授权”。
识别/唤醒没反应,无回调1. 权限未授予。
2. Unity音频冲突。
3. 初始化未完成就调用Start。
1. 检查Logcat,确认RECORD_AUDIO权限已授予。
2. 在开始识别前,尝试设置AudioListener.pause = true
3. 确保在OnInitializeSuccess回调或isInitialized为true后才调用Start方法。
真机上唤醒成功率极低1. 环境噪音大。
2. 麦克风距离远或遮挡。
3. 唤醒词模型不匹配或质量差。
4. 设备麦克风硬件差异。
1. 在安静环境下测试。
2. 让用户靠近麦克风说话。
3. 在百度平台重新训练唤醒词,尝试不同的发音和录制环境。
4. 在代码中调整唤醒的灵敏度阈值(如果SDK提供该参数)。
打包后崩溃,报错UnsatisfiedLinkError原生库(.so)未正确打包或架构不匹配。1. 检查APK包(可重命名为.zip解压),在lib/arm64-v8a等目录下查看是否有SDK的.so文件。
2. 在Player Settings中确认已勾选ARM64
3. 检查是否有多个插件包含了不同版本的同名.so文件,产生冲突。
在Android 12+设备上无法安装或运行新的Android权限策略或导出组件问题。1. 检查AndroidManifest.xml中所有<activity><service>等组件,如果设置了android:exported,确保其逻辑正确(通常主Activity需要exported=”true”)。
2. 确保没有请求过高的、不合理的权限。
识别结果总是为空或错误1. 采样率不匹配。
2. 音频格式问题。
3. 说话语言与设置不符。
1. 确认初始化时设置的SampleRate(如16000)与SDK要求一致。
2. 尝试说标准的普通话,避免方言和过快的语速进行测试。
3. 开启SDK的详细日志,查看音频数据是否正常发送。
应用切到后台后唤醒失效Android系统限制后台服务录音。1. 这是系统正常行为,为保护隐私和电量。可以考虑使用前台服务(需要常驻通知栏)来保持录音,但这会影响用户体验和审核。
2. 更合理的方案是:只在应用处于前台或特定界面时启用唤醒功能。

5.3 进阶:离线功能与自定义唤醒词

在线识别依赖网络,而离线识别在无网环境下是刚需。百度语音也支持离线识别,但需要下载离线引擎和对应的语言模型包(体积较大,几十到几百MB)。

  1. 集成离线引擎:在百度AI平台,找到离线语音识别,下载对应语言的离线识别SDK(通常是额外的aar/jar和模型文件)。按照文档将其集成到Plugins/Android目录。
  2. 初始化配置:在RecognizerInitConfig中,将RecognizeMode设置为OFFLINEMIX。并指定离线模型文件的路径(同样放在StreamingAssets下)。
  3. 模型管理:由于模型文件很大,需要考虑在应用内下载和更新的策略。可以在用户首次使用或Wi-Fi环境下提示下载。

自定义唤醒词的流程类似:

  1. 在百度AI开放平台的“语音唤醒”模块,创建自定义唤醒词。
  2. 按照提示,上传唤醒词的文本(如“嘿,我的游戏”)和若干条不同人、不同环境录制的该词语的音频文件,用于训练。
  3. 训练完成后,下载生成的.bin模型文件。
  4. 将这个文件放入项目的StreamingAssets,并在初始化Wakeuper时指定路径。

自定义唤醒词的识别率非常依赖训练数据的质量。尽量采集不同性别、年龄、口音、背景噪音环境下的录音,数量越多越好。

6. 实战案例:构建一个语音控制角色移动的Demo

理论说再多,不如一个实际案例。我们来构建一个简单的Demo场景:一个3D角色,通过语音指令“前进”、“后退”、“左转”、“右转”、“跳跃”来控制移动。

6.1 场景与UI搭建

  1. 创建一个新场景,放入一个胶囊体(Capsule)作为玩家角色,加上Character Controller组件。
  2. 创建一个空物体,挂载我们之前写的BaiduSpeechManager脚本。
  3. 创建UI:一个按钮显示“按住说话”,一个Text组件用于显示识别结果和状态。将按钮的OnClick事件绑定到管理器的StartRecognizing方法(按下开始,松开结束的逻辑更复杂,这里用点击简化)。

6.2 角色控制脚本

创建一个VoiceControlledPlayer脚本,挂载到胶囊体上。

using UnityEngine; public class VoiceControlledPlayer : MonoBehaviour { public float moveSpeed = 5f; public float rotateSpeed = 120f; public float jumpForce = 5f; public float gravity = -9.81f; private CharacterController controller; private Vector3 velocity; private bool isGrounded; void Start() { controller = GetComponent<CharacterController>(); // 订阅语音识别结果事件 if (BaiduSpeechManager.Instance != null) { BaiduSpeechManager.Instance.OnRecognizeResult += HandleVoiceCommand; } } void OnDestroy() { if (BaiduSpeechManager.Instance != null) { BaiduSpeechManager.Instance.OnRecognizeResult -= HandleVoiceCommand; } } void Update() { // 基础重力模拟 isGrounded = controller.isGrounded; if (isGrounded && velocity.y < 0) { velocity.y = -2f; // 轻微向下的力,确保贴地 } velocity.y += gravity * Time.deltaTime; controller.Move(velocity * Time.deltaTime); } void HandleVoiceCommand(string commandText) { Debug.Log($"收到指令: {commandText}"); commandText = commandText.Trim().ToLower(); // 转为小写并去除空格 if (commandText.Contains("前进") || commandText.Contains("向前")) { Vector3 move = transform.forward * moveSpeed * Time.deltaTime; controller.Move(move); } else if (commandText.Contains("后退") || commandText.Contains("向后")) { Vector3 move = -transform.forward * moveSpeed * Time.deltaTime; controller.Move(move); } else if (commandText.Contains("左转")) { transform.Rotate(0, -rotateSpeed * Time.deltaTime, 0); } else if (commandText.Contains("右转")) { transform.Rotate(0, rotateSpeed * Time.deltaTime, 0); } else if (commandText.Contains("跳跃")) { if (isGrounded) { velocity.y = Mathf.Sqrt(jumpForce * -2f * gravity); } } // 可以添加更多指令,如“攻击”、“蹲下”等 else { Debug.Log($"未识别的指令: {commandText}"); } } }

6.3 优化指令识别

上面的简单字符串匹配容错性很差。用户可能说“往前走”、“向前进”、“跳一下”。我们可以引入简单的模糊匹配或使用正则表达式

using System.Text.RegularExpressions; void HandleVoiceCommand(string commandText) { commandText = commandText.Trim().ToLower(); // 使用正则表达式匹配关键词 if (Regex.IsMatch(commandText, @"前(进|行|方|)")) { // 前进逻辑 } else if (Regex.IsMatch(commandText, @"后(退|撤|方)")) { // 后退逻辑 } else if (Regex.IsMatch(commandText, @"左(转|拐|边)")) { // 左转逻辑 } else if (Regex.IsMatch(commandText, @"右(转|拐|边)")) { // 右转逻辑 } else if (Regex.IsMatch(commandText, @"跳(跃|起来|一下)")) { // 跳跃逻辑 } }

对于更复杂的指令系统,可以考虑设计一个指令词库匹配度算法,或者使用意图识别(NLU)服务,但这超出了基础集成的范围。通过这个Demo,你已经掌握了将百度语音识别结果转化为游戏内操作的核心流程。剩下的就是结合你的具体游戏逻辑,设计更丰富的语音交互了。记住,在真机上测试时,环境噪音和麦克风距离会极大影响识别率,合理的UI反馈(比如显示“正在聆听...”和识别到的文字)和容错设计(允许玩家重复指令)非常重要。