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

【Code实战派】技术分享征文挑战赛 9w人浏览 46人参与

关键词: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 配置示例

在这里插入图片描述


五、使用步骤

  1. 在场景中创建一个 VideoPlayer GameObject
  2. 挂载 VideoSubtitleController
  3. namedVideoClips 中添加多个视频
  4. 为每个视频绑定开始和结束事件
  5. 在 UI 按钮中调用 PlayVideo("Video1")PlayVideoWithDelay("Video2", 3)

六、总结

通过本脚本,我们解决了:

  • 名称播放视频,避免索引错乱
  • 开始 & 结束事件回调,灵活绑定逻辑
  • 延迟播放,方便流程衔接

📌 建议
你可以将本脚本直接放入 Unity 工程,按上面步骤配置即可使用。如果需要跨场景播放,可以结合 DontDestroyOnLoad 保持控制器实例。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EQ-雪梨蛋花汤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值