【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

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

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

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

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. 结语

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/76630.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WPF常用技巧汇总

主要用于记录工作中发现的一些问题和常见的解决方法。 此文会持续更新。 >abp new Evan.MyWpfApp -t wpf --old --framework .net8 1. 解决不同屏幕分辨率下的锯齿问题 UseLayoutRounding"True" <Grid UseLayoutRounding"True"><Border Mar…

分数线降低,25西电马克思主义学院(考研录取情况)

1、马克思主义学院各个方向 2、马克思主义学院近三年复试分数线对比 学长、学姐分析 由表可看出&#xff1a; 1、马克思主义理论25年相较于24年下降10分&#xff0c;为355分 3、25vs24推免/统招人数对比 学长、学姐分析 由表可看出&#xff1a; 1、 马克思主义学院25年共接…

【Linux网络】构建UDP服务器与字典翻译系统

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;博客仓库&#xff1a;https://gitee.com/JohnKingW/linux_test/tree/master/lesson &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &…

【项目管理】成本类计算 笔记

项目管理-相关文档&#xff0c;希望互相学习&#xff0c;共同进步 风123456789&#xff5e;-CSDN博客 &#xff08;一&#xff09;知识总览 项目管理知识域 知识点&#xff1a; &#xff08;项目管理概论、立项管理、十大知识域、配置与变更管理、绩效域&#xff09; 对应&…

div(HTML标准元素)和view(微信小程序专用组件)的主要区别体

div&#xff08;HTML标准元素&#xff09;和view&#xff08;微信小程序专用组件&#xff09;的主要区别体现在以下方面&#xff1a; 一、应用场景与开发框架 ‌适用平台不同‌ div是HTML/CSS开发中通用的块级元素&#xff0c;用于Web页面布局‌&#xff1b;view是微信小程序专…

【C++软件实战问题排查经验分享】UI界面卡顿 | CPU占用高 | GDI对象泄漏 | 线程堵塞 系列问题排查总结

目录 1、UI界面卡顿问题排查 2、软件CPU占用高问题排查 3、UI界面显示异常&#xff08;GDI对象泄漏导致窗口绘制异常&#xff09;问题排查 4、软件线程堵塞&#xff08;包含线程死锁&#xff09;问题排查 5、最后 C软件异常排查从入门到精通系列教程&#xff08;核心精品专…

管理杂谈——采石矶大捷的传奇与启示

南宋抗金史上&#xff0c;岳飞与岳家军的铁血传奇家喻户晓&#xff0c;但另一位力挽狂澜的“文官战神”却常被忽视——他从未掌兵&#xff0c;却在南宋存亡之际整合溃军&#xff0c;以少胜多&#xff0c;缔造采石矶大捷。此人正是虞允文。一介书生何以扭转乾坤&#xff1f;他的…

动态规划-零钱兑换

332.零钱兑换 给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1 。你可以认为每种硬币的数量是无…

SpringAI+DeepSeek大模型应用开发——4 对话机器人

目录​​​​​​​ ​​​​​​​​​​​​​​项目初始化 pom文件 配置模型 ChatClient 同步调用 流式调用 日志功能 对接前端 解决跨域 会话记忆功能 ChatMemory 添加会话记忆功能 会话历史 管理会话id 保存会话id 查询会话历史 完善会话记忆 定义可序列…

Java 关键字

本章列出了Java 语言的所有关键字和“类关键字的单词”。 “受限关键字”是指&#xff0c;它们旨在模块声明中是关键字&#xff0c;在其他情况下则是标识符。 “受限标识符”是指&#xff0c;除非用在某些特定位置&#xff0c;否则他们只是标识符。例如&#xff0c;var一般都…

AI重塑网络安全:机遇与威胁并存的“双刃剑”时代

一、引言 人工智能&#xff08;AI&#xff09;技术的迅猛发展&#xff0c;正在深刻改变网络安全行业的格局。从ChatGPT生成钓鱼邮件到AI驱动的漏洞挖掘&#xff0c;从零信任架构的普及到安全大模型的实战应用&#xff0c;AI既是攻击者的“新武器”&#xff0c;也是防御者的“新…

网络原理——UDP

1、 与TCP的关键区别 特性UDPTCP连接方式无连接面向连接可靠性不可靠可靠数据顺序不保证顺序保证顺序传输速度更快相对较慢头部开销8字节20-60字节流量控制无有拥塞控制无有适用场景实时应用、广播/多播可靠性要求高的应用 2、UDP 报文结构 报文结构大致可以分为首部和载荷&a…

STM32——新建工程并使用寄存器以及库函数进行点灯

本文是根据江协科技提供的教学视频所写&#xff0c;旨在便于日后复习&#xff0c;同时供学习嵌入式的朋友们参考&#xff0c;文中涉及到的所有资料也均来源于江协科技&#xff08;资料下载&#xff09;。 新建工程并使用寄存器以及库函数进行点灯操作 新建工程步骤1.建立工程2.…

Unocss 类名基操, tailwindcss 类名

这里只列出 unocss 的可实现类名&#xff0c;tailwindcss 可以拿去试试用 1. 父元素移入&#xff0c;子元素改样式 <!-- 必须是 group 类名 --> <div class"group"><div class"group-hover:color-red">Text</div> </div>2…

深度学习入门(一)

一、简介 深度学习是机器学习领域新兴且关键的研究方向。机器学习重点在于让计算机从数据中挖掘规律以预测未知&#xff0c;而深度学习借助构建多层神经网络&#xff0c;自动学习数据的复杂特征&#xff0c;从而实现更精准的模式识别&#xff0c;在图像、语音等众多领域广泛应…

element-plus中,Steps 步骤条组件的使用

目录 一.基本使用 1.代码 2.效果展示 3.代码解读 二.案例&#xff1a;修改用户的密码 1.期望效果 2.代码 3.展示效果 结语 一.基本使用 1.代码 从官网复制如下代码&#xff1a; <template><div><el-stepsstyle"max-width: 600px":space&quo…

jax 备忘录

https://zhuanlan.zhihu.com/p/532504225 https://docs.jax.dev/en/latest/index.html

NLTK 基础入门:用 Python 解锁自然语言处理

自然语言处理&#xff08;NLP&#xff09;是人工智能领域的重要分支&#xff0c;它让计算机能够理解、处理和生成人类语言。而 NLTK&#xff08;Natural Language Toolkit&#xff09; 作为 Python 生态中最经典的 NLP 库之一&#xff0c;提供了丰富的工具和资源&#xff0c;是…

ElementUI中checkbox v-model绑定值为布尔、字符串或数字类型

这篇博客介绍了在Vue.js中使用El-Checkbox组件时&#xff0c;如何设置和处理v-model的布尔值和类型转换。通过示例代码展示了如何设置true-label和false-label属性来改变选中状态的值&#xff0c;适用于需要特定类型&#xff08;如字符串或整数&#xff09;的场景。v-model不能…

JBoss 项目修复笔记:绕开 iframe 安全问题,JSF 与 Angular 最小代价共存方案

JBoss 项目修复笔记&#xff1a;绕开 iframe 安全问题&#xff0c;JSF 与 Angular 最小代价共存方案 本篇笔记衔接的内容为&#xff1a;JBoss WildFly 本地开发环境完全指南&#xff0c;里面简单的描述了一下怎么配置 docker&#xff0c;在本地启动一个可以运行的 JBoss 和 W…