【Unity笔记】Unity音视频播放监听器封装笔记:VideoPlayer + AudioSource事件触发与编辑器扩展

关键点

  • Unity VideoPlayer 播放结束事件
  • Unity AudioSource 播放检测

Unity音视频播放监听器封装笔记:VideoPlayer + AudioSource事件触发与编辑器扩展

在 Unity 的多媒体开发中,我们经常需要监听 VideoPlayerAudioSource 的播放状态,以便在开始播放或播放结束时触发一系列操作,例如切换 UI、播放动画、调用某脚本的方法等。

为了提升开发效率与复用性,本文记录如何封装 可复用、可配置、可挂载 UnityEvent 的监听器组件 ,并通过 自定义 Inspector 实现良好的编辑器体验。


1. 监听 VideoPlayer 播放事件并触发脚本方法

Unity 的 VideoPlayer 提供了两个关键事件:

  • started:视频开始播放
  • loopPointReached:视频播放完成(非循环模式)

我们封装一个脚本 VideoPlayerEventListener.cs 来监听上述事件,并挂载 UnityEvent,供你在 Inspector 中拖拽执行目标方法(如 脚本A.Exec())。

VideoPlayerEventListener.cs

csharp 复制代码
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Video;

[RequireComponent(typeof(VideoPlayer))]
public class VideoPlayerEventListener : MonoBehaviour
{
    [Header("视频开始播放时触发")]
    public UnityEvent onVideoStarted;

    [Header("视频播放完成时触发")]
    public UnityEvent onVideoEnded;

    private VideoPlayer videoPlayer;
    private bool hasStarted = false;

    void Awake()
    {
        videoPlayer = GetComponent<VideoPlayer>();
    }

    void OnEnable()
    {
        videoPlayer.started += OnVideoStarted;
        videoPlayer.loopPointReached += OnVideoEnded;
    }

    void OnDisable()
    {
        videoPlayer.started -= OnVideoStarted;
        videoPlayer.loopPointReached -= OnVideoEnded;
    }

    private void OnVideoStarted(VideoPlayer vp)
    {
        if (!hasStarted)
        {
            hasStarted = true;
            onVideoStarted?.Invoke();
        }
    }

    private void OnVideoEnded(VideoPlayer vp)
    {
        hasStarted = false;
        onVideoEnded?.Invoke();
    }
}

2. 自定义 Inspector 提升编辑器体验

为了让 UnityEvent 在 Inspector 中更直观易用,我们还封装了一个自定义编辑器:

VideoPlayerEventListenerEditor.cs

csharp 复制代码
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(VideoPlayerEventListener))]
public class VideoPlayerEventListenerEditor : Editor
{
    SerializedProperty onVideoStarted;
    SerializedProperty onVideoEnded;

    void OnEnable()
    {
        onVideoStarted = serializedObject.FindProperty("onVideoStarted");
        onVideoEnded = serializedObject.FindProperty("onVideoEnded");
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();

        var videoPlayer = (target as VideoPlayerEventListener).GetComponent<UnityEngine.Video.VideoPlayer>();
        if (videoPlayer == null)
        {
            EditorGUILayout.HelpBox("缺少 VideoPlayer 组件。", MessageType.Error);
        }
        else
        {
            EditorGUILayout.HelpBox("监听 VideoPlayer 播放状态,并触发 UnityEvent。", MessageType.Info);

            EditorGUILayout.PropertyField(onVideoStarted, new GUIContent("🎬 视频开始播放"));
            EditorGUILayout.PropertyField(onVideoEnded, new GUIContent("🏁 视频播放结束"));
        }

        serializedObject.ApplyModifiedProperties();
    }
}

将此脚本放入 Editor 文件夹中即可自动生效。


3. 监听 AudioSource 音频播放状态

不同于 VideoPlayerAudioSource 并没有原生的播放完成事件。因此我们通过 Update() 方法持续检测播放状态,并提供播放进度(progress)供 UI 显示。

AudioSourceEventListener.cs

csharp 复制代码
using UnityEngine;
using UnityEngine.Events;

[RequireComponent(typeof(AudioSource))]
public class AudioSourceEventListener : MonoBehaviour
{
    [Header("音频开始播放时触发")]
    public UnityEvent onAudioStarted;

    [Header("音频播放完成时触发")]
    public UnityEvent onAudioEnded;

    [Range(0f, 1f), Tooltip("当前播放进度 (仅查看)")]
    public float progress;

    private AudioSource audioSource;
    private bool hasStarted = false;
    private bool hasEnded = false;

    void Awake()
    {
        audioSource = GetComponent<AudioSource>();
    }

    void Update()
    {
        if (audioSource.clip == null)
            return;

        if (!hasStarted && audioSource.isPlaying)
        {
            hasStarted = true;
            hasEnded = false;
            onAudioStarted?.Invoke();
        }

        if (audioSource.isPlaying)
        {
            progress = audioSource.time / audioSource.clip.length;
        }

        if (hasStarted && !audioSource.isPlaying && !hasEnded && audioSource.time >= audioSource.clip.length)
        {
            hasEnded = true;
            onAudioEnded?.Invoke();
        }
    }
}

AudioSourceEventListenerEditor.cs

csharp 复制代码
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(AudioSourceEventListener))]
public class AudioSourceEventListenerEditor : Editor
{
    SerializedProperty onAudioStarted;
    SerializedProperty onAudioEnded;
    SerializedProperty progress;

    void OnEnable()
    {
        onAudioStarted = serializedObject.FindProperty("onAudioStarted");
        onAudioEnded = serializedObject.FindProperty("onAudioEnded");
        progress = serializedObject.FindProperty("progress");
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();

        EditorGUILayout.HelpBox("监听 AudioSource 播放状态,并触发 UnityEvent。", MessageType.Info);

        EditorGUILayout.PropertyField(onAudioStarted, new GUIContent("🎧 音频开始播放"));
        EditorGUILayout.PropertyField(onAudioEnded, new GUIContent("🏁 音频播放结束"));

        EditorGUILayout.Space();
        EditorGUILayout.LabelField("📊 播放进度", EditorStyles.boldLabel);
        EditorGUI.ProgressBar(EditorGUILayout.GetControlRect(), progress.floatValue, $"{(progress.floatValue * 100f):0.0}%");

        serializedObject.ApplyModifiedProperties();
    }
}

4. 使用示例

  1. 在一个 GameObject 上添加 VideoPlayerEventListenerAudioSourceEventListener
  2. 选择你需要监听的事件(开始/结束)。
  3. 点击 + 添加响应函数(如某个脚本的 Exec() 方法)。
  4. 运行时自动回调,不需要手动注册监听器。

5.总结与拓展建议

通过以上封装,我们实现了可复用的播放监听逻辑,统一用 UnityEvent 挂载,完全无需写代码也能实现播放回调,特别适合策划/UI 场景中使用。

📌 推荐进一步拓展:

  • 播放进度回调事件(支持 float 参数)。
  • 播放完自动播放下一个文件。
  • 可视化播放进度 UI(Slider/圆环等)。
  • 支持 AssetBundle 动态加载音视频资源。

6. 结语

音视频播放作为交互中重要的一环,其状态监听和回调封装将极大提升项目开发效率。希望这套封装组件可以帮助你打造更高效、模块化的多媒体播放系统。

如果你觉得有帮助,欢迎点赞、收藏并关注!

相关推荐
新知图书24 分钟前
音频特征工具Librosa包的使用
音视频·mamba
龙湾开发39 分钟前
轻量级高性能推理引擎MNN 学习笔记 02.MNN主要API
人工智能·笔记·学习·机器学习·mnn
HappyAcmen1 小时前
线代第二章矩阵第八节逆矩阵、解矩阵方程
笔记·学习·线性代数·矩阵
愚润求学3 小时前
【递归、搜索与回溯】专题一:递归(二)
c++·笔记·算法·leetcode
清水迎朝阳3 小时前
火山RTC 6 自定义视频
音视频·实时音视频·火山rtc·自定义视频
愚润求学4 小时前
【Linux】基础 IO(一)
linux·运维·服务器·开发语言·c++·笔记
Wallace Zhang4 小时前
STM32F103_LL库+寄存器学习笔记22 - 基础定时器TIM实现1ms周期回调
笔记·stm32·学习
大白的编程日记.4 小时前
【Linux学习笔记】理解一切皆文件实现原理和文件缓冲区
linux·笔记·学习
孞㐑¥4 小时前
Linux之进程控制
linux·开发语言·c++·经验分享·笔记
Flamesky4 小时前
Unity编辑器重新编译代码
unity·重新编译