我们需要插件nobodywho,whisper,ui配置如下:两个text专门负责人说话文字,ai文字回答,一个滑动条slider代表说话倒计时,一个button负责录音,两个空物体挂载这两个对应的插件

代码如下

using UnityEngine;

using UnityEngine.UI;

using Whisper;

using Whisper.Utils;

using System.Collections;

using System.Diagnostics;

using System.Threading.Tasks;

using NobodyWho;

public class VoiceToText : MonoBehaviour

{

    [Header("核心组件")]

    public WhisperManager whisperManager;

    // 1. 将原来的 resultText 拆分为输入和输出两个 Text 组件

    public Text playerInputText;    // 专门显示玩家说的话(语音识别结果)

    public Text aiResponseText;     // 专门显示 AI 评委的回复

    [SerializeField] private Chat nobodyWhoChat;

    [Header("录音设置")]

    public float maxRecordTime = 30f;

    public Slider recordProgressSlider;

    public Button recordButton;

    [Header("打字机效果设置")]

    [Range(1f, 50f)]

    public float charsPerSecond = 20f;

    // 私有变量

    private MicrophoneRecord microphoneRecord;

    private bool isRecording;

    private float recordTimer;

    private Coroutine progressCoroutine;

    private Coroutine typewriterCoroutine;

    void Start()

    {

        microphoneRecord = GetComponent<MicrophoneRecord>();

        if (microphoneRecord == null)

            microphoneRecord = gameObject.AddComponent<MicrophoneRecord>();

        microphoneRecord.OnRecordStop += OnRecordStop;

        if (recordProgressSlider != null)

        {

            recordProgressSlider.minValue = 0f;

            recordProgressSlider.maxValue = maxRecordTime;

            recordProgressSlider.value = 0f;

        }

        if (recordButton != null)

            recordButton.onClick.AddListener(OnButtonPressed);

        if (nobodyWhoChat != null)

        {

            nobodyWhoChat.StartWorker();

        }

    }

    void Update()

    {

        if (Input.GetKeyDown(KeyCode.P))

            StartRecording();

        if (Input.GetKeyUp(KeyCode.P) && isRecording)

            StopRecording();

    }

    public void OnButtonPressed()

    {

        if (!isRecording) StartRecording();

        else StopRecording();

    }

    void StartRecording()

    {

        if (isRecording) return;

        microphoneRecord.StartRecord();

        isRecording = true;

        recordTimer = 0f;

        if (progressCoroutine != null) StopCoroutine(progressCoroutine);

        progressCoroutine = StartCoroutine(UpdateProgressBar());

        if (typewriterCoroutine != null)

        {

            StopCoroutine(typewriterCoroutine);

            typewriterCoroutine = null;

        }

        playerInputText.text = "🎤 录音中,请说话...";

        UnityEngine.Debug.Log("开始录音");

    }

    void StopRecording()

    {

        if (!isRecording) return;

        microphoneRecord.StopRecord();

        isRecording = false;

        if (progressCoroutine != null)

        {

            StopCoroutine(progressCoroutine);

            progressCoroutine = null;

        }

        if (recordProgressSlider != null)

            recordProgressSlider.value = 0f;

        playerInputText.text = "⏳ 识别中,请稍后...";

    }

    IEnumerator UpdateProgressBar()

    {

        while (isRecording && recordTimer < maxRecordTime)

        {

            recordTimer += Time.deltaTime;

            if (recordProgressSlider != null)

                recordProgressSlider.value = recordTimer;

            if (recordTimer >= maxRecordTime)

            {

                StopRecording();

                yield break;

            }

            yield return null;

        }

    }

    private async void OnRecordStop(AudioChunk recordedAudio)

    {

        var sw = new Stopwatch();

        sw.Start();

        var res = await whisperManager.GetTextAsync(

            recordedAudio.Data,

            recordedAudio.Frequency,

            recordedAudio.Channels

        );

        if (res == null || string.IsNullOrEmpty(res.Result))

        {

            playerInputText.text = "❌ 识别失败,请重试。";

            return;

        }

        string playerSpeech = res.Result;

        var time = sw.ElapsedMilliseconds;

        var rate = recordedAudio.Length / (time * 0.001f);

        UnityEngine.Debug.Log($"识别耗时: {time} ms, 实时率: {rate:F1}x");

        UnityEngine.Debug.Log($"玩家说: {playerSpeech}");

        // 2. 在玩家的输入框中显示打字机效果

        if (typewriterCoroutine != null) StopCoroutine(typewriterCoroutine);

        typewriterCoroutine = StartCoroutine(TypewriterEffect($"你说:{playerSpeech}", playerInputText));

        // 清空 AI 的回复框,准备接收新回复

        aiResponseText.text = "";

        await CallAIAsync(playerSpeech);

    }

    private async Task CallAIAsync(string playerMessage)

    {

        await Task.Delay(500);

        // 3. 将 "评委思考中..." 提示显示在 AI 回复框中

        aiResponseText.text = "评委思考中...";

        bool isFirstChunk = true;

        nobodyWhoChat.responseUpdated.RemoveAllListeners();

        nobodyWhoChat.responseFinished.RemoveAllListeners();

        nobodyWhoChat.responseUpdated.AddListener((token) =>

        {

            if (isFirstChunk)

            {

                // 第一次收到数据时,清除“思考中”提示,并加上“评委:”

                aiResponseText.text = "评委:";

                isFirstChunk = false;

            }

            // 4. 将 AI 的回复追加到 AI 回复框中

            aiResponseText.text += token;

        });

        nobodyWhoChat.responseFinished.AddListener((fullResponse) =>

        {

            UnityEngine.Debug.Log("AI评委已完成回复");

        });

        nobodyWhoChat.Say(playerMessage);

    }

    // 5. 修改打字机协程,让它支持指定要更新的 Text 组件

    IEnumerator TypewriterEffect(string fullText, Text targetText)

    {

        targetText.text = "";

        float delay = 1f / charsPerSecond;

        foreach (char c in fullText)

        {

            targetText.text += c;

            yield return new WaitForSeconds(delay);

        }

        typewriterCoroutine = null;

    }

    void OnDestroy()

    {

        if (nobodyWhoChat != null)

        {

            nobodyWhoChat.responseUpdated.RemoveAllListeners();

            nobodyWhoChat.responseFinished.RemoveAllListeners();

        }

    }

}

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐