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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 20:55:07