协程导致统一崩溃
Coroutine causes crash in unity
我在我的脚本中添加了以下函数,它导致 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();
}
}
}
我还通过按钮 OnClick()
在另一个脚本中控制了 recordingButtonHasBeenPressed
变量。见:
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;
}
}
我不知道为什么它会崩溃。我不认为这是一个无限循环。
我还测试了 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 崩溃。
我该怎么办?怎么了?
这个
IEnumerator SetFrame()
{
yield return new WaitForSeconds(0.3f);
encoder.AddFrame(tex);
encoder.AddSamples(audioBuffer);
if (recordingButtonHasBeenPressed)
{
yield return StartCoroutine(SetFrame());
}
}
是一个递归调用,您再次 yield return
相同的例程(在内部再次 yield return
相同的例程等)所以它会等到所有嵌套子例程完成 => 所以在某个时候你会得到一个 Whosebug!
这绝对是一个封闭的永无止境的while循环
using (var audioBuffer = new NativeArray<float>(sampleFramesPerVideoFrame, Allocator.Temp))
{
do
{
encoder.AddFrame(tex);
encoder.AddSamples(audioBuffer);
} while (recordingButtonHasBeenPressed);
}
在循环中 recordingButtonHasBeenPressed
的值将 永远不会 改变并且 Unity/your 应用程序立即永远冻结!
你想要做的是像
这样的协程
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);
}
}
你甚至不需要主动阻止它。您需要做的就是启动它,然后为了中断它只需将 recordingButtonHasBeenPressed
设置为 false
。
事件驱动
现在一般来说,而不是使用 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;
}
我在我的脚本中添加了以下函数,它导致 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();
}
}
}
我还通过按钮 OnClick()
在另一个脚本中控制了 recordingButtonHasBeenPressed
变量。见:
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;
}
}
我不知道为什么它会崩溃。我不认为这是一个无限循环。
我还测试了 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 崩溃。
我该怎么办?怎么了?
这个
IEnumerator SetFrame()
{
yield return new WaitForSeconds(0.3f);
encoder.AddFrame(tex);
encoder.AddSamples(audioBuffer);
if (recordingButtonHasBeenPressed)
{
yield return StartCoroutine(SetFrame());
}
}
是一个递归调用,您再次 yield return
相同的例程(在内部再次 yield return
相同的例程等)所以它会等到所有嵌套子例程完成 => 所以在某个时候你会得到一个 Whosebug!
这绝对是一个封闭的永无止境的while循环
using (var audioBuffer = new NativeArray<float>(sampleFramesPerVideoFrame, Allocator.Temp))
{
do
{
encoder.AddFrame(tex);
encoder.AddSamples(audioBuffer);
} while (recordingButtonHasBeenPressed);
}
在循环中 recordingButtonHasBeenPressed
的值将 永远不会 改变并且 Unity/your 应用程序立即永远冻结!
你想要做的是像
这样的协程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);
}
}
你甚至不需要主动阻止它。您需要做的就是启动它,然后为了中断它只需将 recordingButtonHasBeenPressed
设置为 false
。
事件驱动
现在一般来说,而不是使用 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;
}