You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity麦克风输入脚本冲突求助:跳跃与录音回放功能互斥

Unity麦克风脚本冲突解决:JumpFromMicrophone跳跃失效问题

问题描述

我开发了两个Unity脚本:

  • JumpFromMicrophone:通过麦克风音频输入控制玩家Y轴跳跃,同时自动沿Z轴前进;
  • RNP:实现麦克风录音并通过生成的AudioSource回放。

添加RNP脚本后,JumpFromMicrophone的Y轴跳跃功能失效,仅Z轴移动正常。尝试过将RNP挂载到其他GameObject、合并两个脚本均无效,相关原始脚本代码如下:

JumpFromMicrophone 原始脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class JumpFromMicrophone : MonoBehaviour
{
    public AudioSource source;
    public AudioLoudnessDetection detector;
    public Vector3 minPosition;
    public Vector3 maxPosition;
    public float speed = 2;

    public float loudnessSensibility = 100;
    public float threshold = 0.1f;

    public float smoothTime = 0.1f;
    private Vector3 velocity = Vector3.zero;

    void Start()
    {

    }

    void FixedUpdate()
    {
        float loudness = detector.GetLoudnessFromMicrophone() * loudnessSensibility;

        if (loudness < threshold)
            loudness = 0;
        else
            loudness = (loudness - threshold) / (1 - threshold);

        Vector3 targetPosition = Vector3.Lerp(minPosition, maxPosition, loudness);
        targetPosition.x = transform.localPosition.x;
        targetPosition.z = transform.localPosition.z;

        transform.localPosition = Vector3.SmoothDamp(transform.localPosition, targetPosition, ref velocity, smoothTime);

        transform.Translate(Vector3.forward * speed * Time.deltaTime);

    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "Obstacle")
        {
            SceneManager.LoadScene("Game");
        }
    }
}

RNP 原始脚本

public class RNP : MonoBehaviour
{
    public AudioClip recordedClip;
    public int maxRecordingTime = 10;

    void Start()
    {
        StartCoroutine(RecordAndPlayback());
    }

    IEnumerator RecordAndPlayback()
    {
        recordedClip = Microphone.Start(null, true, maxRecordingTime, 44100);
        yield return new WaitForSeconds(maxRecordingTime);
        Microphone.End(null);

        AudioSource audioSource = gameObject.AddComponent<AudioSource>();
        audioSource.clip = recordedClip;
        audioSource.loop = false;
        audioSource.Play();

        yield return new WaitWhile(() => audioSource.isPlaying);
        Destroy(audioSource);
    }
}

AudioLoudnessDetection 原始脚本

public class AudioLoudnessDetection : MonoBehaviour
{
    public int sampleWindow = 64;
    private AudioClip microphoneClip;

    void Start()
    {
        MicrophoneToAudioClip();
    }

    void FixedUpdate()
    {
        
    }

    public void MicrophoneToAudioClip()
    {
        string microphoneName = Microphone.devices[0];
        microphoneClip = Microphone.Start(microphoneName, true, 20, AudioSettings.outputSampleRate);
    }

    public float GetLoudnessFromMicrophone()
    {
        return GetLoudnessFromAudioClip(Microphone.GetPosition(Microphone.devices[0]), microphoneClip);
    }

    public float GetLoudnessFromAudioClip(int clipPosition,AudioClip clip)
    {
        int startPosition = clipPosition - sampleWindow;

        if (startPosition < 0)
            return 0;

        float[] waveData = new float[sampleWindow];
        clip.GetData(waveData, startPosition);

        float totalLoudness = 0;
        for (int i = 0; i < sampleWindow; i++)
        {
            totalLoudness += Mathf.Abs(waveData[i]);
        }
        return totalLoudness/ sampleWindow;
    }
}

问题原因

Unity的Microphone API同一时刻只能被一个AudioClip占用。AudioLoudnessDetection在Start阶段启动了麦克风录制,而RNP的协程中再次调用Microphone.Start(),后者会直接抢占麦克风资源,导致AudioLoudnessDetection中的microphoneClip失效,无法获取有效音频数据,最终GetLoudnessFromMicrophone()返回值始终为0,跳跃逻辑完全不触发。

解决方案

统一麦克风资源管理,让两个脚本共享同一个麦克风录制的AudioClip,避免重复抢占。具体修改如下:

1. 修改AudioLoudnessDetection脚本

暴露麦克风Clip供其他脚本访问,并统一采样率与RNP保持一致:

public class AudioLoudnessDetection : MonoBehaviour
{
    public int sampleWindow = 64;
    public AudioClip microphoneClip; // 改为public,共享麦克风Clip

    void Start()
    {
        MicrophoneToAudioClip();
    }

    public void MicrophoneToAudioClip()
    {
        string microphoneName = Microphone.devices[0];
        // 统一采样率为44100,和RNP保持一致
        microphoneClip = Microphone.Start(microphoneName, true, 20, 44100);
    }

    public float GetLoudnessFromMicrophone()
    {
        return GetLoudnessFromAudioClip(Microphone.GetPosition(Microphone.devices[0]), microphoneClip);
    }

    public float GetLoudnessFromAudioClip(int clipPosition, AudioClip clip)
    {
        int startPosition = clipPosition - sampleWindow;
        if (startPosition < 0)
            return 0;

        float[] waveData = new float[sampleWindow];
        clip.GetData(waveData, startPosition);

        float totalLoudness = 0;
        for (int i = 0; i < sampleWindow; i++)
        {
            totalLoudness += Mathf.Abs(waveData[i]);
        }
        return totalLoudness / sampleWindow;
    }
}

2. 修改RNP脚本

复用AudioLoudnessDetection的麦克风Clip,通过记录麦克风位置截取音频实现回放:

public class RNP : MonoBehaviour
{
    public AudioLoudnessDetection loudnessDetector; // 在Inspector中拖拽引用对应的组件
    public int maxRecordingTime = 10;

    void Start()
    {
        StartCoroutine(RecordAndPlayback());
    }

    IEnumerator RecordAndPlayback()
    {
        // 等待麦克风Clip初始化完成
        while (loudnessDetector.microphoneClip == null || !Microphone.IsRecording(Microphone.devices[0]))
        {
            yield return null;
        }

        // 记录录制起始位置
        int startRecordPosition = Microphone.GetPosition(Microphone.devices[0]);
        int sampleRate = loudnessDetector.microphoneClip.frequency;

        // 等待指定录制时长
        yield return new WaitForSeconds(maxRecordingTime);

        // 记录录制结束位置
        int endRecordPosition = Microphone.GetPosition(Microphone.devices[0]);

        // 计算录制的样本数,处理循环录制的边界情况
        int recordedSamples = endRecordPosition - startRecordPosition;
        if (recordedSamples < 0)
        {
            recordedSamples += loudnessDetector.microphoneClip.samples;
        }

        // 创建回放用的AudioClip
        AudioClip recordedClip = AudioClip.Create("RecordedClip", recordedSamples, loudnessDetector.microphoneClip.channels, sampleRate, false);
        
        // 从共享麦克风Clip中复制录制的数据
        float[] data = new float[recordedSamples];
        if (startRecordPosition + recordedSamples <= loudnessDetector.microphoneClip.samples)
        {
            loudnessDetector.microphoneClip.GetData(data, startRecordPosition);
        }
        else
        {
            // 处理跨Clip末尾的分段复制
            int firstPartLength = loudnessDetector.microphoneClip.samples - startRecordPosition;
            int secondPartLength = recordedSamples - firstPartLength;
            float[] firstPart = new float[firstPartLength];
            float[] secondPart = new float[secondPartLength];
            
            loudnessDetector.microphoneClip.GetData(firstPart, startRecordPosition);
            loudnessDetector.microphoneClip.GetData(secondPart, 0);
            
            System.Array.Copy(firstPart, data, firstPartLength);
            System.Array.Copy(secondPart, 0, data, firstPartLength, secondPartLength);
        }
        recordedClip.SetData(data, 0);

        // 播放录制的音频
        AudioSource audioSource = gameObject.AddComponent<AudioSource>();
        audioSource.clip = recordedClip;
        audioSource.loop = false;
        audioSource.Play();

        // 等待播放完成后清理资源
        yield return new WaitWhile(() => audioSource.isPlaying);
        Destroy(audioSource);
        Destroy(recordedClip);
    }
}

3. 配置说明

  • 在Inspector中,给RNP脚本的loudnessDetector字段拖拽引用场景中的AudioLoudnessDetection组件;
  • 确保JumpFromMicrophone脚本的detector字段也引用同一个AudioLoudnessDetection组件。

内容的提问来源于stack exchange,提问作者XiiRainz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 05:15:22