You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Unity中按住空格键平滑暂停/恢复分段音视频播放的求助

解决平滑暂停/恢复+三段音视频顺序播放的问题

嘿,你的需求很清晰,咱们先拆解一下问题:首先你之前的平滑变速代码没生效是因为错误的循环逻辑+瞬间完成变速,然后还要补上三段音视频自动顺序切换的逻辑。我给你一步步来解决:

先说说你现有代码的问题

你写的videoPause()函数有两个致命问题:

  • 循环里i从0开始,直接会触发除以0的错误,程序肯定跑不起来
  • 这个循环是在一帧内完成的,相当于瞬间把播放速度拉到0,根本看不到平滑过渡的效果

要实现平滑变速,必须用协程(Coroutine) 逐帧调整速度,让变化在一段时间内渐进完成。

完整解决方案代码

下面是整合了平滑暂停/恢复+三段音视频顺序播放的完整实现,我会附上详细注释:

第一步:定义变量

先在脚本里声明需要的组件和参数:

using UnityEngine;
using UnityEngine.Video;
using System.Collections;

public class SequentialVideoPlayer : MonoBehaviour
{
    [Header("三段音视频资源")]
    public VideoPlayer[] videoClips; // 拖拽三段视频到这里
    public AudioClip[] audioClips;   // 对应拖拽三段音频到这里

    [Header("平滑设置")]
    public float smoothTransitionTime = 0.3f; // 平滑变速的时长,可调整

    private AudioSource _audioSource;
    private int _currentClipIndex = 0; // 当前播放的音视频索引
    private bool _isPlaying = false;
    private bool _isTransitioning = false; // 防止重复触发平滑变速协程

    void Start()
    {
        _audioSource = GetComponent<AudioSource>();
        
        // 初始化:给每个视频绑定结束回调,准备好第一段资源
        foreach (var vp in videoClips)
        {
            vp.loopPointReached += OnVideoFinished;
            vp.Prepare();
        }
        _audioSource.clip = audioClips[_currentClipIndex];
    }
}

第二步:处理空格键输入

不用直接调用Play/Pause,而是触发平滑变速的协程:

void Update()
{
    // 按住空格键:触发平滑恢复播放
    if (Input.GetButton("Jump"))
    {
        if (!_isPlaying && !_isTransitioning)
        {
            StartCoroutine(SmoothResumePlayback());
        }
    }
    // 松开空格键:触发平滑暂停
    else
    {
        if (_isPlaying && !_isTransitioning)
        {
            StartCoroutine(SmoothPausePlayback());
        }
    }
}

第三步:平滑暂停的协程

IEnumerator SmoothPausePlayback()
{
    _isTransitioning = true;
    VideoPlayer currentVP = videoClips[_currentClipIndex];
    float startSpeed = currentVP.playbackSpeed;
    float elapsedTime = 0f;

    // 渐进式降低播放速度到0
    while (elapsedTime < smoothTransitionTime)
    {
        elapsedTime += Time.deltaTime;
        // 用线性插值计算当前速度
        float targetSpeed = Mathf.Lerp(startSpeed, 0f, elapsedTime / smoothTransitionTime);
        currentVP.playbackSpeed = targetSpeed;
        _audioSource.pitch = targetSpeed; // 音频pitch同步视频速度,保证音画同步
        yield return null; // 等待下一帧
    }

    // 完全暂停后,停止播放状态
    currentVP.Pause();
    _audioSource.Pause();
    _isPlaying = false;
    _isTransitioning = false;
}

第四步:平滑恢复的协程

IEnumerator SmoothResumePlayback()
{
    _isTransitioning = true;
    VideoPlayer currentVP = videoClips[_currentClipIndex];
    
    // 如果当前没在播放,先启动音视频
    if (!currentVP.isPlaying)
    {
        currentVP.Play();
        _audioSource.Play();
    }

    float startSpeed = currentVP.playbackSpeed;
    float elapsedTime = 0f;

    // 渐进式恢复播放速度到正常(1倍速)
    while (elapsedTime < smoothTransitionTime)
    {
        elapsedTime += Time.deltaTime;
        float targetSpeed = Mathf.Lerp(startSpeed, 1f, elapsedTime / smoothTransitionTime);
        currentVP.playbackSpeed = targetSpeed;
        _audioSource.pitch = targetSpeed;
        yield return null;
    }

    _isPlaying = true;
    _isTransitioning = false;
}

第五步:三段音视频自动切换

监听视频结束事件,自动切换到下一段:

void OnVideoFinished(VideoPlayer vp)
{
    // 如果不是最后一段,切换到下一段
    if (_currentClipIndex < videoClips.Length - 1)
    {
        // 先停止当前音视频
        vp.Stop();
        _audioSource.Stop();
        
        // 切换到下一段,准备资源
        _currentClipIndex++;
        VideoPlayer nextVP = videoClips[_currentClipIndex];
        nextVP.Prepare();
        _audioSource.clip = audioClips[_currentClipIndex];

        // 如果此时按住空格键,直接开始播放下一段
        if (Input.GetButton("Jump"))
        {
            StartCoroutine(SmoothResumePlayback());
        }
    }
    else
    {
        // 所有片段播放完毕,重置状态
        _isPlaying = false;
        // 可选:重置索引到第一段,方便重新播放
        // _currentClipIndex = 0;
        // videoClips[_currentClipIndex].Prepare();
        // _audioSource.clip = audioClips[_currentClipIndex];
    }
}

额外注意事项

  1. 音频变调问题:如果你的音频不能接受变调(比如人声),可以不用调整pitch,而是通过同步AudioSource.timeVideoPlayer.time来保证同步,但这种方式逻辑更复杂,小范围变速(比如0到1)用pitch完全没问题。
  2. 视频预加载:调用Prepare()方法可以让视频在切换前提前加载,避免硬切时的卡顿。
  3. 协程锁_isTransitioning变量是为了防止在平滑变速过程中重复触发协程,导致逻辑混乱。

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

火山引擎 最新活动