视频播放控制器全攻略:支持延迟播放、事件回调与多视频管理的完整实现
【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. 场景设置
- 导入视频:将 .mp4/.webm 文件拖入 Assets/Video 文件夹。
- 创建 RawImage:Hierarchy > UI > RawImage(渲染视频纹理)。
- 添加 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); // 清理事件
}
}
}
使用示例
- 场景配置:创建 RawImage,拖入 RenderTexture;配置 VideoEntry(ID=”Intro”, Clip=视频, Delay=3s)。
- 调用:
multiManager.PlayVideo("Intro")
;结束时触发 onVideoEnd(e.g., 加载下一个场景)。
性能优化与注意事项
- 性能:视频解码 GPU 开销高,限制分辨率(720p 移动,1080p PC);使用 VP8/WebM 格式压缩。
- 兼容:iOS 支持 H.264;WebGL 用 VideoClip 而非 URL。
- 事件清理:OnDestroy 中注销委托防泄漏。
- 扩展:集成 Addressables 异步加载视频。
通过此控制器,可实现高效视频管理。如果需要特定平台优化或 360 视频支持,请提供更多细节!