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

Unity钢琴应用录制音符回放循环问题解决方案求助

解决Unity钢琴应用回放循环问题的方案

首先,咱们得先理清你遇到的核心问题:每次Update帧都会触发Playback方法,而你用Thread.Sleep的方式做延迟,不仅会卡死Unity主线程(导致整个游戏冻结),还会因为Update的高频调用,让Playback反复从头执行,陷入无限循环。

下面是一步步的修复方案,同时优化你的代码结构:

1. 替换ArrayList为泛型List

ArrayList是旧的非泛型集合,类型不安全,推荐用List<char>来存储音符:

// 替换原来的ArrayList notes;
public List<char> notes;

// Start里初始化
notes = new List<char>();

2. 用协程实现安全的回放延迟

Unity里绝对不要用System.Threading.Thread.Sleep,会让整个游戏卡住。我们用协程(Coroutine)处理播放延迟,既能按顺序播放音符,又不会阻塞主线程。

首先添加一个标记变量,防止重复触发回放:

public bool isPlayingBack = false; // 新增:标记是否正在回放

然后把Playback改成协程方法:

IEnumerator PlaybackCoroutine()
{
    isPlayingBack = true;
    // 遍历所有录制的音符
    foreach (char note in notes)
    {
        PlayNote(note);
        Debug.Log(note);
        // 等待1秒后播放下一个音符,用Unity的协程等待方法
        yield return new WaitForSeconds(1f);
    }
    // 所有音符播放完毕,重置状态
    playMode = false;
    isPlayingBack = false;
    Debug.Log("回放已完成");
}

3. 修改Update里的回放触发逻辑

原来的Update里只要playMode为true就调用Playback,现在我们要确保只有在playMode激活且没有在回放时,才启动协程:

void Update()
{
    // ... 你的录制和手动播放逻辑保持不变 ...

    // 修改回放触发逻辑
    if (playMode == true && !isPlayingBack)
    {
        StartCoroutine(PlaybackCoroutine());
    }
}

4. 修复PlayNote方法的逻辑问题

你原来的PlayNote里用了else if,这会导致如果前面的条件满足,后面的条件不会被检查(虽然这里每个音符是唯一的,但统一用switch更简洁稳妥):

void PlayNote(char note)
{
    GameObject targetSphere = null;
    AudioClip targetClip = null;
    string triggerName = "";

    switch (note)
    {
        case 'a':
            targetSphere = GameObject.Find("Sphere_A");
            targetClip = noteA;
            triggerName = "A";
            break;
        case 'b':
            targetSphere = GameObject.Find("Sphere_B");
            targetClip = noteB;
            triggerName = "B";
            break;
        case 'c':
            targetSphere = GameObject.Find("Sphere_C");
            targetClip = noteC;
            triggerName = "C";
            break;
        case 'd':
            targetSphere = GameObject.Find("Sphere_D");
            targetClip = noteD;
            triggerName = "D";
            break;
        case 'e':
            targetSphere = GameObject.Find("Sphere_E");
            targetClip = noteE;
            triggerName = "E";
            break;
        case 'f':
            targetSphere = GameObject.Find("Sphere_F");
            targetClip = noteF;
            triggerName = "F";
            break;
        case 'g':
            targetSphere = GameObject.Find("Sphere_G");
            targetClip = noteG;
            triggerName = "G";
            break;
    }

    if (targetSphere != null && targetClip != null)
    {
        targetSphere.GetComponent<AudioSource>().PlayOneShot(targetClip);
        targetSphere.GetComponent<Animator>().SetTrigger(triggerName);
        Debug.Log(note);
    }
}

5. 核心逻辑说明

  • 点击播放按钮时,playMode设为true,同时关闭recordMode。
  • Update检测到playMode为true且未在回放时,启动PlaybackCoroutine协程。
  • 协程逐个播放音符,每个音符播放后等待1秒,直到所有音符播放完毕。
  • 播放完成后,自动将playMode和isPlayingBack设为false,避免再次触发回放循环。

这样就彻底解决了回放循环问题,同时让代码结构更清晰,符合Unity的最佳实践。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:15:25