Unity中使用UniTask实现双视频播放跳过功能的问题
问题
使用Unity 2022.3.36f1 + UniTask库,需要实现依次播放两个视频的功能:任一视频播放时触发“跳过”操作后,停止两个视频并直接执行后续代码。但目前在第一个视频播放时触发跳过后,第二个视频仍会自动播放。怀疑是外部布尔变量_isSkip被意外重置,或是UniTask.WaitUntil没有实时检查变量最新值,希望解决该问题。
当前代码如下:
private bool _isSkip; private void SkipVideos() { _isSkip = true; Media.VideoStop(); } private async UniTaskVoid StartMode() { // 该方法通过StartMode().Forget()调用 _isSkip = false; // video1已处于播放状态,时长7.1秒 // SkipVideos可在第一个或第二个await阶段被调用,需直接跳过两个视频 await UniTask.WhenAny(UniTask.Delay(7140), UniTask.WaitUntil(() =>_isSkip == true)); var delay = Media.VideoPlay("video2"); // 返回视频时长(毫秒) // 若在第一个delay阶段触发了SkipVideos,走到这里时_isSkip会变成false,原因是什么? await UniTask.WhenAny(UniTask.Delay(delay), UniTask.WaitUntil(() =>_isSkip == true)); // 两个视频播放完成或触发跳过之后,执行后续代码... }
最初尝试使用取消令牌,但后续代码无法正常执行。希望代码简洁清晰,若实现过于繁琐可能转回Unity协程。未来计划将Media.VideoPlay改为异步方法,但当前保持现有实现不变。
解决方案
问题核心是第一个await完成后,未检查_isSkip状态就直接执行了第二个视频的播放逻辑——哪怕第一个视频是被跳过结束的,代码依然会走到Media.VideoPlay("video2")这一步,导致第二个视频播放。
方案1:基于布尔变量的极简修改
在第一个await结束后,先判断_isSkip状态,若已触发跳过则直接跳过第二个视频的播放:
private bool _isSkip; private void SkipVideos() { _isSkip = true; Media.VideoStop(); } private async UniTaskVoid StartMode() { _isSkip = false; // 等待第一个视频结束或被跳过 await UniTask.WhenAny(UniTask.Delay(7140), UniTask.WaitUntil(() => _isSkip)); // 若已触发跳过,直接进入后续代码 if (!_isSkip) { var delay = Media.VideoPlay("video2"); await UniTask.WhenAny(UniTask.Delay(delay), UniTask.WaitUntil(() => _isSkip)); } // 跳过触发或两个视频播放完成后执行的后续代码 // ... }
关于UniTask.WaitUntil的疑问:它会在UniTask的更新周期内持续重新执行传入的委托,所以一定会检查_isSkip的最新值,问题不在这个API本身。
方案2:使用取消令牌(更符合UniTask最佳实践)
如果想改用取消令牌且保证后续代码执行,可以通过捕获取消异常实现:
private CancellationTokenSource _cts; private void SkipVideos() { _cts?.Cancel(); Media.VideoStop(); } private async UniTaskVoid StartMode() { using (_cts = new CancellationTokenSource()) { try { // 等待第一个视频,若被取消则抛出异常 await UniTask.Delay(7140, cancellationToken: _cts.Token); // 未被取消才播放第二个视频 var delay = Media.VideoPlay("video2"); await UniTask.Delay(delay, cancellationToken: _cts.Token); } catch (OperationCanceledException) { // 捕获取消异常,不做额外处理,直接进入后续代码 } // 无论是否被取消,都会执行的后续代码 // ... } }
这种方式不需要额外的_isSkip变量,逻辑更简洁,也避免了布尔变量可能带来的状态混乱问题。
内容的提问来源于stack exchange,提问作者Matt Kemp
相关产品推荐
相关产品推荐

