视频播放控制器全攻略:支持延迟播放、事件回调与多视频管理的完整实现

【Unity笔记】Unity视频播放控制器全攻略:支持延迟播放、事件回调与多视频管理的完整实现

引言

Unity 的 VideoPlayer 组件是处理视频播放的核心工具,支持本地/远程视频、RawImage/RenderTexture 渲染和事件监听。在游戏/AR/VR 项目中,常需实现延迟播放(e.g., 加载完成后播放)、事件回调(e.g., 播放结束触发逻辑)和多视频管理(e.g., 切换视频源、同步控制)。本攻略基于 Unity 2021.3+(推荐 2022.3 LTS 或 Unity 6),涵盖完整实现:从基本配置到高级管理器。使用协程实现延迟,VideoPlayer 原生事件处理回调,Dictionary 管理多视频。适用于过场动画、视频 UI 或交互媒体。时间复杂度 O(1),性能优化使用对象池和异步加载。

关键优势

  • 延迟播放:避免视频未准备就播放的黑屏。
  • 事件回调:支持准备完成/播放结束/循环/错误等。
  • 多视频管理:一个组件控制 N 个视频,支持暂停/切换/音量同步。
  • 兼容性:URP/HDRP 支持,移动端需 H.264 视频格式。

基本 VideoPlayer 配置

1. 场景设置

  1. 导入视频:将 .mp4/.webm 文件拖入 Assets/Video 文件夹。
  2. 创建 RawImage:Hierarchy > UI > RawImage(渲染视频纹理)。
  3. 添加 VideoPlayer:RawImage > Add Component > Video Player。
  • Source: Video Clip 或 URL
  • Render Mode: Render Texture(推荐,灵活输出)
  • Target Texture: 创建 RenderTexture(Assets > Create > Render Texture,调整 Size=1920×1080)

2. 基础播放脚本

using UnityEngine;
using UnityEngine.Video;

public class BasicVideoPlayer : MonoBehaviour
{
    public VideoPlayer videoPlayer;
    public RenderTexture renderTexture;  // 拖拽赋值

    void Start()
    {
        videoPlayer.targetTexture = renderTexture;
        videoPlayer.Play();  // 基础播放
    }
}

支持延迟播放的实现

3. 延迟播放原理

使用协程等待 VideoPlayer.isPrepared(准备完成事件),避免黑屏。延迟时间可参数化(e.g., 2s 后播放)。

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

public class DelayedVideoPlayer : MonoBehaviour
{
    public VideoPlayer videoPlayer;
    public float delaySeconds = 2f;  // 延迟秒数

    IEnumerator Start()
    {
        // 等待准备完成
        videoPlayer.Prepare();
        while (!videoPlayer.isPrepared)
        {
            yield return null;
        }

        // 延迟播放
        yield return new WaitForSeconds(delaySeconds);
        videoPlayer.Play();
    }

    // 扩展:动态延迟
    public void PlayWithDelay(float customDelay)
    {
        StartCoroutine(DelayedPlay(customDelay));
    }

    IEnumerator DelayedPlay(float delay)
    {
        yield return new WaitForSeconds(delay);
        videoPlayer.Play();
    }
}

支持事件回调的实现

4. VideoPlayer 事件详解

VideoPlayer 提供 6 个核心事件:

  • prepareCompleted:准备完成
  • loopPointReached:循环点到达(结束)
  • started:开始播放
  • frameReady:帧准备好
  • errorReceived:错误
  • clockResyncOccurred:时钟重同步

使用委托绑定回调,支持 UnityEvent 可视化。

using UnityEngine;
using UnityEngine.Video;
using UnityEngine.Events;

public class VideoEventHandler : MonoBehaviour
{
    public VideoPlayer videoPlayer;

    [Header("事件回调")]
    public UnityEvent onPrepareComplete;
    public UnityEvent onPlayStart;
    public UnityEvent onPlayEnd;
    public UnityEvent<string> onError;

    void OnEnable()
    {
        videoPlayer.prepareCompleted += OnPrepareCompleted;
        videoPlayer.started += OnStarted;
        videoPlayer.loopPointReached += OnLoopPointReached;
        videoPlayer.errorReceived += OnErrorReceived;
    }

    void OnDisable()
    {
        videoPlayer.prepareCompleted -= OnPrepareCompleted;
        videoPlayer.started -= OnStarted;
        videoPlayer.loopPointReached -= OnLoopPointReached;
        videoPlayer.errorReceived -= OnErrorReceived;
    }

    private void OnPrepareCompleted(VideoPlayer source)
    {
        onPrepareComplete?.Invoke();
        Debug.Log("视频准备完成");
    }

    private void OnStarted(VideoPlayer source)
    {
        onPlayStart?.Invoke();
        Debug.Log("视频开始播放");
    }

    private void OnLoopPointReached(VideoPlayer source)
    {
        onPlayEnd?.Invoke();
        Debug.Log("视频播放结束");
    }

    private void OnErrorReceived(VideoPlayer source, string message)
    {
        onError?.Invoke(message);
        Debug.LogError($"视频错误: {message}");
    }

    // 示例:结束时切换视频
    public void SwitchVideo(VideoClip newClip)
    {
        videoPlayer.clip = newClip;
        videoPlayer.Prepare();
    }
}

支持多视频管理的实现

5. 多视频管理器

使用 Dictionary 存储 VideoPlayer 实例,支持切换/暂停/音量同步。结合对象池优化性能。

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

public class MultiVideoManager : MonoBehaviour
{
    [System.Serializable]
    public class VideoEntry
    {
        public string videoID;
        public VideoClip clip;
        public RenderTexture renderTarget;
        public bool autoPlay = false;
        public float delay = 0f;
    }

    public List<VideoEntry> videoEntries = new List<VideoEntry>();
    public UnityEvent<string> onVideoEnd;  // 参数:videoID

    private Dictionary<string, VideoPlayer> videoPlayers = new Dictionary<string, VideoPlayer>();
    private Dictionary<string, GameObject> videoObjects = new Dictionary<string, GameObject>();

    void Start()
    {
        InitializeVideos();
    }

    private void InitializeVideos()
    {
        foreach (var entry in videoEntries)
        {
            // 创建视频对象
            GameObject videoObj = new GameObject(entry.videoID);
            videoObj.transform.SetParent(transform);

            VideoPlayer player = videoObj.AddComponent<VideoPlayer>();
            player.clip = entry.clip;
            player.targetTexture = entry.renderTarget;
            player.loopPointReached += (source) => OnVideoEnded(entry.videoID);

            videoPlayers[entry.videoID] = player;
            videoObjects[entry.videoID] = videoObj;

            if (entry.autoPlay)
            {
                StartCoroutine(PlayWithDelay(entry.videoID, entry.delay));
            }
        }
    }

    public void PlayVideo(string videoID)
    {
        if (videoPlayers.TryGetValue(videoID, out VideoPlayer player))
        {
            player.Play();
        }
    }

    public void PauseVideo(string videoID)
    {
        if (videoPlayers.TryGetValue(videoID, out VideoPlayer player))
        {
            player.Pause();
        }
    }

    public void StopAllVideos()
    {
        foreach (var player in videoPlayers.Values)
        {
            player.Stop();
        }
    }

    public void SetVolume(string videoID, float volume)
    {
        if (videoPlayers.TryGetValue(videoID, out VideoPlayer player))
        {
            player.SetDirectAudioVolume(0, volume);
        }
    }

    private IEnumerator PlayWithDelay(string videoID, float delay)
    {
        yield return new WaitForSeconds(delay);
        PlayVideo(videoID);
    }

    private void OnVideoEnded(string videoID)
    {
        onVideoEnd?.Invoke(videoID);
        Debug.Log($"视频 {videoID} 播放结束");
    }

    void OnDestroy()
    {
        foreach (var player in videoPlayers.Values)
        {
            player.loopPointReached -= (source) => OnVideoEnded(videoID);  // 清理事件
        }
    }
}

使用示例

  1. 场景配置:创建 RawImage,拖入 RenderTexture;配置 VideoEntry(ID=”Intro”, Clip=视频, Delay=3s)。
  2. 调用multiManager.PlayVideo("Intro");结束时触发 onVideoEnd(e.g., 加载下一个场景)。

性能优化与注意事项

  • 性能:视频解码 GPU 开销高,限制分辨率(720p 移动,1080p PC);使用 VP8/WebM 格式压缩。
  • 兼容:iOS 支持 H.264;WebGL 用 VideoClip 而非 URL。
  • 事件清理:OnDestroy 中注销委托防泄漏。
  • 扩展:集成 Addressables 异步加载视频。

通过此控制器,可实现高效视频管理。如果需要特定平台优化或 360 视频支持,请提供更多细节!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注