Coroutine导致unity崩溃

问题描述 投票:0回答:1

我在我的脚本中添加了以下函数,但它在unity中导致崩溃。

public void AddCurrentFrameToVideo()
{
    _addFrameFunctionHasBeenCalled = true;

    using (var encoder = new MediaEncoder(encodedFilePath, videoAttr, audioAttr))
    using (var audioBuffer = new NativeArray<float>(sampleFramesPerVideoFrame, Allocator.Temp))
    {

        IEnumerator SetFrame()
        {
            yield return new WaitForSeconds(0.3f);
            encoder.AddFrame(tex);
            encoder.AddSamples(audioBuffer);
            if (recordingButtonHasBeenPressed)
            {
                yield return StartCoroutine(SetFrame());
            }
            else
            {
                yield return null;
                yield break;
            }

        }

        IEnumerator mycoroutine;
        mycoroutine = SetFrame();

        if (recordingButtonHasBeenPressed)
        {
            StartCoroutine(mycoroutine);
        }
        else
        {
            StopCoroutine(mycoroutine);
        }

    }

}

我在Update函数的if语句中调用这个函数。

void Update()
{
    _currentsframe = Time.frameCount;

    if (recordingButtonHasBeenPressed)
    {
        if (!videoBasicFileHasBeenCreated)
        {
            CreateVideoBasicFile();
        }

        if (!_addFrameFunctionHasBeenCalled)
        {
            AddCurrentFrameToVideo();
        }

    }

}

我还控制了 recordingButtonHasBeenPressedOnClick().看到了吧:

public void RecordVideo_OnClick()
{
    if (videoIsRecording)
    {
        videoIsRecording = false;
        videoRecordButton.image.sprite = videoButtonIsNotRecordingSprite;

        _myRecorderSc.recordingButtonHasBeenPressed = false;
        _myRecorderSc.videoBasicFileHasBeenCreated = false;
    }
    else
    {
        videoRecordButton.image.sprite = videoButtonIsRecordingSprite;
        _myRecorderSc.recordingButtonHasBeenPressed = true;
        _myRecorderSc.videoBasicFileHasBeenCreated = false;
        videoIsRecording = true;
    }
}

我不知道为什么会让unity崩溃. 我不认为这是一个无限循环.此外,我还测试了一个。DO-While 循环,而不是使用 Croutine.看到了吧:

    using (var encoder = new MediaEncoder(encodedFilePath, videoAttr, audioAttr))
    using (var audioBuffer = new NativeArray<float>(sampleFramesPerVideoFrame, Allocator.Temp))
    {
        do
        {
                encoder.AddFrame(tex);
                encoder.AddSamples(audioBuffer);
        } while (recordingButtonHasBeenPressed);
    }

它也会导致unity崩溃.

我应该怎么办?它有什么问题?

c# unity3d crash coroutine
1个回答
2
投票

这个

    IEnumerator SetFrame()
    {
        yield return new WaitForSeconds(0.3f);
        encoder.AddFrame(tex);
        encoder.AddSamples(audioBuffer);
        if (recordingButtonHasBeenPressed)
        {
            yield return StartCoroutine(SetFrame());
        }
     }

是一个递归调用,其中你 yield return 再次执行相同的例程(这在内部 yield return的同一个例程等),所以它等待所有嵌套的子例程完成 => 所以在某些时候你会得到一个StackOverflow!


这绝对是一个封闭的永不结束的while循环

using (var audioBuffer = new NativeArray<float>(sampleFramesPerVideoFrame, Allocator.Temp))
{
    do
    {
            encoder.AddFrame(tex);
            encoder.AddSamples(audioBuffer);
    } while (recordingButtonHasBeenPressed);
}

循环中的 recordingButtonHasBeenPressed从来没有 而Unity你的应用会立即永远冻结。


你想做的是像Coroutine这样的程序。

IEnumerator SetFrame()
{
    // initially wait once
    yield return new WaitForSeconds(0.3f);

    // simply continue to execute the routine until the record shall be stopped
    while(recordingButtonHasBeenPressed)
    {
        encoder.AddFrame(tex);
        encoder.AddSamples(audioBuffer);

        // yield the next frames for 0.3 seconds before checking 
        // recordingButtonHasBeenPressed again
        yield return new WaitForSeconds(0.3f);
    }
}

那么你甚至不需要主动停止它。所有你需要做的就是启动它,然后为了中断它,只需设置为 recordingButtonHasBeenPressedfalse.


做到事件驱动

现在一般情况下,不使用 Update 和多个控制器标志 bool的方法一旦被调用,你就会立即重新设置,我宁愿让整个代码 事件驱动 并称为 曾经 当按钮被调用时。这将防止并发例程意外运行,并使整个事情更好地阅读和维护。

我不知道你的全部代码,但它可能看起来像这样

public void RecordVideo_OnClick()
{
    // invert the toggle flag
    videoIsRecording = !videoIsRecording;

    // depending on the new flag value chose the sprite
    videoRecordButton.image.sprite = videoIsRecording ? videoButtonIsRecordingSprite : videoButtonIsNotRecordingSprite;

    if (!videoIsRecording)
    {
        _myRecorderSc.StopRecording();
    }
    else
    {
        _myRecorderSc.StartRecoring();
    }
}

然后在录音机脚本中你只需要

public void StartRecording()
{
    if(!recording)
    {
        StartCoroutine(RecorderRoutine);
    }
}

public void StopRecording()
{
    recording = false;
}

// flag to interrupt running record
private bool recording;

private IEnumerator RecorderRoutine()
{
    // Just in case prevent concurrent routines
    if(recording) yield break;
    recording = true;

    // initialize your file
    CreateVideoBasicFile();    

    // initially wait once
    yield return new WaitForSeconds(0.3f);

    using (var encoder = new MediaEncoder(encodedFilePath, videoAttr, audioAttr))
    using (var audioBuffer = new NativeArray<float>(sampleFramesPerVideoFrame, Allocator.Temp))
    {
        // simply continue to execute the routine until the record shall be stopped
        while(recording)
        {
            encoder.AddFrame(tex);
            encoder.AddSamples(audioBuffer);

            // yield the next frames for 0.3 seconds before checking 
            // recordingButtonHasBeenPressed again
            yield return new WaitForSeconds(0.3f);
        }
    }

    recording = false;
}
© www.soinside.com 2019 - 2024. All rights reserved.