关键词:Unity 视频播放、延迟播放、事件回调、多视频管理、VideoPlayer 教程
摘要:本教程详细讲解了如何在 Unity 中使用 VideoPlayer 实现一个功能完善的视频播放控制器,支持按视频名称播放、播放开始/结束事件回调、以及延迟播放等高级功能。
通过使用 NamedClip 数据结构和 Dictionary 映射,你可以轻松管理多个视频并在 Inspector 中绑定自定义逻辑,无需修改代码即可适应不同场景。
视频播放控制器全攻略:支持延迟播放、事件回调与多视频管理的完整实现
一、引言
在 Unity 项目中,视频播放已经成为许多项目的刚需。无论是游戏过场动画、教学演示、还是互动展览,视频内容都能极大提升用户的沉浸感。
Unity 提供了功能强大的 VideoPlayer
组件,但直接使用它进行多视频管理时,经常会遇到以下痛点:
-
需要手动指定 视频索引,一旦顺序改动,调用方就需要全局修改,极易出错。
-
难以在播放开始和播放结束时执行自定义逻辑,比如:
- 播放背景音乐
- 切换 UI 面板
- 自动播放下一个视频
-
缺乏延迟播放的机制,不便于在流程中穿插过渡动画或加载时间。
举个例子:
- 在一款解谜游戏中,解锁机关后会播放一段剧情动画来推进故事;
- 在博物馆互动大屏中,用户点击某个展品,会播放它的历史介绍视频;
- 在教学软件中,教师点击按钮即可播放不同章节的教学视频。
虽然 Unity 自带了功能强大的 VideoPlayer
组件,但如果直接使用它来管理多段视频,很快就会暴露出以下问题:
为了解决这些问题,可参考下面实现一个可扩展的 VideoSubtitleController 脚本,它支持:
- 按视频名称调用播放,不依赖索引顺序
- 视频开始与结束的 UnityEvent 回调
- 延迟播放(可在其它 UnityEvent 中直接触发)
- 清除
RenderTexture
画面,避免残影
接下来,我们将从需求分析开始,一步步实现这个脚本。
二、需求分析
1. 多视频管理问题
假设一个场景有 10 段视频,如果用数组索引来播放:
videoClips[3] // 播放第 4 个视频
一旦数组顺序发生变化(比如插入新视频),所有调用索引的地方都得改,维护成本极高。
改进方式:
- 用视频名称(string)作为唯一标识
- 通过字典 (
Dictionary<string, NamedClip>
) 查找视频资源
2. 播放回调的必要性
在视频播放开始或结束时,我们可能需要执行:
- 开始播放时:隐藏 Loading UI、启动计时器、播放音乐
- 结束播放时:切换场景、显示问卷、加载下一个视频
通过 UnityEvent,我们可以在 Inspector 中绑定事件,无需改代码即可定制逻辑。
3. 延迟播放的使用场景
延迟播放可用于:
- 播放前等待过渡动画结束
- 给用户留出操作时间
- 等待网络数据加载完成
三、脚本设计思路
我们将脚本分成几个核心模块:
模块 | 功能说明 |
---|---|
NamedClip | 存储视频的名字、视频文件、事件回调 |
clipDict | 用视频名字快速查找视频资源 |
PlayVideo | 播放指定名字的视频 |
PlayVideoWithDelay | 延迟播放指定视频 |
StopVideo | 停止播放并触发结束事件 |
ClearToTransparent | 清除 RenderTexture 画面 |
四、完整代码实现
1. 数据结构定义
[Serializable]
public class NamedClip
{
[Tooltip("视频的唯一标识名称")]
public string name;
[Tooltip("视频文件")]
public VideoClip clip;
[Tooltip("视频播放开始时的事件回调")]
public UnityEvent onVideoStarted;
[Tooltip("视频播放结束时的事件回调")]
public UnityEvent onVideoEnded;
}
2. 核心脚本实现
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Video;
[RequireComponent(typeof(VideoPlayer))]
public class VideoController : MonoBehaviour
{
[Header("视频资源配置")]
public NamedClip[] namedVideoClips;
private Dictionary<string, NamedClip> clipDict = new Dictionary<string, NamedClip>();
private VideoPlayer videoPlayer;
private NamedClip currentClip;
private void Awake()
{
videoPlayer = GetComponent<VideoPlayer>();
// 构建字典映射
foreach (var entry in namedVideoClips)
{
if (!string.IsNullOrEmpty(entry.name) && entry.clip != null)
{
clipDict[entry.name] = entry;
}
}
videoPlayer.loopPointReached += OnVideoFinished;
}
/// <summary>
/// 播放指定名称的视频
/// </summary>
public void PlayVideo(string clipName)
{
if (!clipDict.TryGetValue(clipName, out var namedClip))
{
Debug.LogWarning($"未找到视频片段:{clipName}");
return;
}
if (videoPlayer.isPlaying)
{
videoPlayer.Stop();
}
currentClip = namedClip;
videoPlayer.clip = currentClip.clip;
videoPlayer.Play();
currentClip.onVideoStarted?.Invoke();
}
/// <summary>
/// 延迟播放
/// </summary>
public void PlayVideoWithDelay(string clipName, float delaySeconds)
{
StartCoroutine(PlayVideoDelayedCoroutine(clipName, delaySeconds));
}
private IEnumerator PlayVideoDelayedCoroutine(string clipName, float delay)
{
yield return new WaitForSeconds(delay);
PlayVideo(clipName);
}
/// <summary>
/// 停止当前视频
/// </summary>
public void StopVideo()
{
if (videoPlayer != null && videoPlayer.isPlaying)
{
videoPlayer.Stop();
currentClip?.onVideoEnded?.Invoke();
}
}
/// <summary>
/// 视频播放结束回调
/// </summary>
private void OnVideoFinished(VideoPlayer vp)
{
currentClip?.onVideoEnded?.Invoke();
}
/// <summary>
/// 清除 RenderTexture 内容
/// </summary>
public void ClearToTransparent(RenderTexture rt)
{
if (rt == null) return;
RenderTexture currentRT = RenderTexture.active;
RenderTexture.active = rt;
GL.Clear(true, true, Color.clear);
RenderTexture.active = currentRT;
}
}
3. Unity 编辑器配置
Inspector 配置示例
五、使用步骤
- 在场景中创建一个
VideoPlayer
GameObject - 挂载
VideoSubtitleController
- 在
namedVideoClips
中添加多个视频 - 为每个视频绑定开始和结束事件
- 在 UI 按钮中调用
PlayVideo("Video1")
或PlayVideoWithDelay("Video2", 3)
六、总结
通过本脚本,我们解决了:
- 按名称播放视频,避免索引错乱
- 开始 & 结束事件回调,灵活绑定逻辑
- 延迟播放,方便流程衔接
📌 建议
你可以将本脚本直接放入 Unity 工程,按上面步骤配置即可使用。如果需要跨场景播放,可以结合 DontDestroyOnLoad
保持控制器实例。