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
相关产品推荐
相关产品推荐

