⭐ Unity AVProVideo插件自带播放器 脚本重构 实现视频激活重置功能

news2025/6/2 4:43:57

一、功能概述

本笔记记录直接修改插件自带的场景播放其中 原始的 MediaPlayerUI 脚本,实现激活时自动重置播放器的功能。 我用的插件版本是 AVPro Video - Ultra Edition 2.7.3

修改后的脚本将具备以下特性:

  • 激活 GameObject 时自动重置播放位置到开头

  • 可配置是否在重置后自动开始播放

  • 可配置重置前的延迟时间

  • 视频播放结束后自动回到开头

  • 保留原始脚本所有功能

二、修改步骤

1. 添加新变量

在类变量声明区域添加以下变量:

[Header("Activation Behavior")]
[Tooltip("Reset playback to beginning when enabled")]
[SerializeField] bool _resetOnEnable = true;

[Tooltip("Automatically start playback after reset")]
[SerializeField] bool _playOnReset = true;

[Tooltip("Delay before resetting after enable (seconds)")]
[SerializeField] float _resetDelay = 0.1f;

private Coroutine _resetCoroutine;

2. 添加 ResetPlayer 方法

在 Start() 方法后添加:

/// <summary>
/// Resets the player to beginning and optionally starts playback
/// </summary>
public void ResetPlayer()
{
    if (_mediaPlayer == null || _mediaPlayer.Control == null)
    {
        Debug.LogWarning("MediaPlayer or Control is not available", this);
        return;
    }

    // Reset playback position
    _mediaPlayer.Control.Seek(0);
    
    // Reset audio state
    _audioVolume = 1f;
    _audioFade = 1f;
    _isAudioFadingUpToPlay = true;
    _audioFadeTime = 0f;
    ApplyAudioVolume();
    
    // Update UI
    UpdateVolumeSlider();
    
    // Start playback if configured to do so
    if (_playOnReset)
    {
        _mediaPlayer.Play();
        
        // Trigger play feedback if overlay manager exists
        if (_overlayManager)
        {
            _overlayManager.TriggerFeedback(OverlayManager.Feedback.Play);
        }
    }
    else
    {
        _mediaPlayer.Pause();
    }
    
    // Ensure controls are visible
    _controlsFade = 1f;
    if (_controlsGroup != null)
    {
        _controlsGroup.alpha = 1f;
        _controlsGroup.gameObject.SetActive(true);
    }
    
    // Update timeline slider
    if (_sliderTime != null)
    {
        _sliderTime.value = 0f;
    }
}

3. 修改/添加 OnEnable 方法

private void OnEnable()
{
    if (_resetOnEnable)
    {
        // Start reset coroutine with small delay to ensure everything is initialized
        if (_resetCoroutine != null)
        {
            StopCoroutine(_resetCoroutine);
        }
        _resetCoroutine = StartCoroutine(DelayedReset());
    }
}

4. 添加 OnDisable 方法

private void OnDisable()
{
    if (_resetCoroutine != null)
    {
        StopCoroutine(_resetCoroutine);
        _resetCoroutine = null;
    }
}

5. 添加 DelayedReset 协程

private IEnumerator DelayedReset()
{
    yield return new WaitForSeconds(_resetDelay);
    ResetPlayer();
}

6. 修改 Update 方法

// Check if video has finished playing
if (_mediaPlayer != null && _mediaPlayer.Control != null && 
    _mediaPlayer.Control.IsFinished())
{
    // Reset to beginning but don't auto-play
    _mediaPlayer.Control.Seek(0);
    _mediaPlayer.Pause();
    
    // Update timeline slider
    if (_sliderTime != null)
    {
        _sliderTime.value = 0f;
    }
}

7. 修改 Awake 方法(可选)

void Awake()
{
    #if UNITY_IOS
    Application.targetFrameRate = 60;
    #endif
    
    // 确保在第一次启用时也会重置
    if (_resetOnEnable && enabled && gameObject.activeInHierarchy)
    {
        StartCoroutine(DelayedReset());
    }
}

三、使用说明

1. Inspector 配置

修改后,脚本的 Inspector 面板将显示新的配置选项:

  • Reset On Enable:是否在激活时重置

  • Play On Reset:重置后是否自动播放

  • Reset Delay:重置前的延迟时间(秒)

2. 代码调用

可以通过代码调用 ResetPlayer() 方法手动重置播放器:

GetComponent<MediaPlayerUI>().ResetPlayer();

3. 注意事项

  • 修改后的脚本保留了所有原始功能

  • 重置操作包括:播放位置、音频状态、UI 控件状态

  • 视频播放结束后会自动回到开头并暂停

四、实现原理

  1. 激活重置:通过 OnEnable 触发重置协程

  2. 延迟处理:使用 DelayedReset 协程确保组件完全初始化

  3. 完整重置ResetPlayer 方法处理所有重置逻辑

  4. 播放结束检测:在 Update 中检测播放结束状态

五、适用场景

  • 需要重复播放视频的场景

  • 视频播放器需要频繁激活/禁用的场景

  • 需要精确控制播放初始状态的场景

六、完整代码

// UnityEngine.UI was moved to a package in 2019.2.0
// Unfortunately no way to test for this across all Unity versions yet
// You can set up the asmdef to reference the new package, but the package doesn't 
// existing in Unity 2017 etc, and it throws an error due to missing reference
#define AVPRO_PACKAGE_UNITYUI
#if (UNITY_2019_2_OR_NEWER && AVPRO_PACKAGE_UNITYUI) || (!UNITY_2019_2_OR_NEWER)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using RenderHeads.Media.AVProVideo;
using RenderHeads.Media.AVProVideo.Demos.UI;

//-----------------------------------------------------------------------------
// Copyright 2018-2021 RenderHeads Ltd.  All rights reserved.
//-----------------------------------------------------------------------------

namespace RenderHeads.Media.AVProVideo.Demos
{
    public class MediaPlayerUI : MonoBehaviour
    {
        [SerializeField] MediaPlayer _mediaPlayer = null;

        [Header("Options")]

        [SerializeField] float _keyVolumeDelta = 0.05f;
        [SerializeField] float _jumpDeltaTime = 5f;
        [SerializeField] bool _showOptions = true;
        [SerializeField] bool _autoHide = true;
        [SerializeField] float _userInactiveDuration = 1.5f;
        [SerializeField] bool _useAudioFading = true;

        [Header("Keyboard Controls")]
        [SerializeField] bool _enableKeyboardControls = true;
        [SerializeField] KeyCode KeyVolumeUp = KeyCode.UpArrow;
        [SerializeField] KeyCode KeyVolumeDown = KeyCode.DownArrow;
        [SerializeField] KeyCode KeyTogglePlayPause = KeyCode.Space;
        [SerializeField] KeyCode KeyToggleMute = KeyCode.M;
        [SerializeField] KeyCode KeyJumpForward = KeyCode.RightArrow;
        [SerializeField] KeyCode KeyJumpBack = KeyCode.LeftArrow;

        [Header("Optional Components")]
        [SerializeField] OverlayManager _overlayManager = null;
        [SerializeField] MediaPlayer _thumbnailMediaPlayer = null;
        [SerializeField] RectTransform _timelineTip = null;

        [Header("UI Components")]
        [SerializeField] RectTransform _canvasTransform = null;
        //[SerializeField] Image image = null;
        [SerializeField] Slider _sliderTime = null;
        [SerializeField] EventTrigger _videoTouch = null;
        [SerializeField] CanvasGroup _controlsGroup = null;

        [Header("UI Components (Optional)")]
        [SerializeField] GameObject _liveItem = null;
        [SerializeField] Text _textMediaName = null;
        [SerializeField] Text _textTimeDuration = null;
        [SerializeField] Slider _sliderVolume = null;
        [SerializeField] Button _buttonPlayPause = null;
        [SerializeField] Button _buttonVolume = null;
        [SerializeField] Button _buttonSubtitles = null;
        [SerializeField] Button _buttonOptions = null;
        [SerializeField] Button _buttonTimeBack = null;
        [SerializeField] Button _buttonTimeForward = null;
        [SerializeField] RawImage _imageAudioSpectrum = null;
        [SerializeField] GameObject _optionsMenuRoot = null;
        [SerializeField] HorizontalSegmentsPrimitive _segmentsSeek = null;
        [SerializeField] HorizontalSegmentsPrimitive _segmentsBuffered = null;
        [SerializeField] HorizontalSegmentsPrimitive _segmentsProgress = null;

        private bool _wasPlayingBeforeTimelineDrag;
        private float _controlsFade = 1f;
        private Material _playPauseMaterial;
        private Material _volumeMaterial;
        private Material _subtitlesMaterial;
        private Material _optionsMaterial;
        private Material _audioSpectrumMaterial;
        private float[] _spectrumSamples = new float[128];
        private float[] _spectrumSamplesSmooth = new float[128];
        private float _maxValue = 1f;
        private float _audioVolume = 1f;

        private float _audioFade = 0f;
        private bool _isAudioFadingUpToPlay = true;
        private const float AudioFadeDuration = 0.25f;
        private float _audioFadeTime = 0f;

        private readonly LazyShaderProperty _propMorph = new LazyShaderProperty("_Morph");
        private readonly LazyShaderProperty _propMute = new LazyShaderProperty("_Mute");
        private readonly LazyShaderProperty _propVolume = new LazyShaderProperty("_Volume");
        private readonly LazyShaderProperty _propSpectrum = new LazyShaderProperty("_Spectrum");
        private readonly LazyShaderProperty _propSpectrumRange = new LazyShaderProperty("_SpectrumRange");

        //cyqq
        [Header("Activation Behavior")]
        [Tooltip("Reset playback to beginning when enabled")]
        [SerializeField] bool _resetOnEnable = true;

        [Tooltip("Automatically start playback after reset")]
        [SerializeField] bool _playOnReset = true;

        [Tooltip("Delay before resetting after enable (seconds)")]
        [SerializeField] float _resetDelay = 0.1f;

        private Coroutine _resetCoroutine;

        void Awake()
        {
#if UNITY_IOS
			Application.targetFrameRate = 60;
#endif

            // 确保在第一次启用时也会重置
            if (_resetOnEnable && enabled &

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

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

相关文章

详解K8s API Server 如何处理请求的?

详解K8s API Server 如何处理请求的? Kubernetes(K8s)是一个强大的容器编排系统,而API Server(kube-apiserver) 是它的核心组件之一。 如果把 K8s 比作一个国家,API Server 就是政府机构,所有资源的创建、修改、删除都要经过它审批! 🎯 API Server 的作用 📌 A…

微调数据处理

1. 数据爬取 我们将爬取的1G文件都保存到all_m_files目录下 查看原始数据文件数量&#xff1a; find /root/all_m_files -type f | wc -l 2. 数据预处理 仅保留UTF-8 格式文件&#xff0c;且所有保留的代码文件长度必须大于20行 import os import pandas as pddef try_read…

解决开发者技能差距:AI 在提升效率与技能培养中的作用

企业在开发者人才方面正面临双重挑战。一方面&#xff0c;IDC 预测&#xff0c;到2025年&#xff0c;全球全职开发者将短缺400万人&#xff1b;另一方面&#xff0c;一些行业巨头已暂停开发者招聘&#xff0c;转而倚重人工智能&#xff08;AI&#xff09;来满足开发需求。这不禁…

XCTF-web-easyphp

解析 第一个条件&#xff08; k e y 1 &#xff09;&#xff1a; i s s e t ( key1&#xff09;&#xff1a;isset( key1&#xff09;&#xff1a;isset(a) && intval(KaTeX parse error: Expected EOF, got & at position 14: a) > 6000000 &̲& strl…

Transformer 通关秘籍11:Word2Vec 及工具的使用

将文字文本转换为词向量(word embedding&#xff09;的过程中&#xff0c;一个非常著名的算法模型应该就是 Word2Vec 了。 相信大家或多或少都听说过&#xff0c;本节就来简单介绍一下 Word2Vec 。 什么是 Word2Vec &#xff1f; Word2Vec 可以非常有效的创建词嵌入向量&…

【DAY34】GPU训练及类的call方法

内容来自浙大疏锦行python打卡训练营 浙大疏锦行 知识点&#xff1a; CPU性能的查看&#xff1a;看架构代际、核心数、线程数GPU性能的查看&#xff1a;看显存、看级别、看架构代际GPU训练的方法&#xff1a;数据和模型移动到GPU device上类的call方法&#xff1a;为什么定义前…

Flutte ListView 列表组件

目录 1、垂直列表 1.1 实现用户中心的垂直列表 2、垂直图文列表 2.1 动态配置列表 2.2 for循环生成一个动态列表 2.3 ListView.builder配置列表 列表布局是我们项目开发中最常用的一种布局方式。Flutter中我们可以通过ListView来定义列表项&#xff0c;支持垂直和水平方向展示…

muduo库的初步认识和基本使用,创建一个简单查询单词服务系统

小编在学习完muduo库之后&#xff0c;觉得对于初学者&#xff0c;muduo库还是有点不好理解&#xff0c;所以在此&#xff0c;小编来告诉大家muduo库的初步认识和基本使用&#xff0c;让初学者也可以更快的上手和使用muduo库。 Muduo由陈硕大佬开发&#xff0c;是⼀个基于 非阻塞…

【HTML/CSS面经】

HTML/CSS面经 HTML1. script标签中的async和defer的区别2. H5新特性&#xff08;1 标签语义化&#xff08;2 表单功能增强&#xff08;3 音频和视频标签&#xff08;4 canvas和svg绘画&#xff08;5 地理位置获取&#xff08;6 元素拖动API&#xff08;7 Web Worker&#xff08…

git查看commit属于那个tag

1. 快速确认commit原始分支及合入tag # git describe 213b4b3bbef2771f7a1b8166f6e6989442ca67c8 查看commit合入tag # git describe 213b4b3bbef2771f7a1b8166f6e6989442ca67c8 --all 查看commit原始分支 2.查看分支与master关系 # git show --all 0.5.67_0006 --stat 以缩…

mysql-mysql源码本地调试

前言 先进行mysql源码本地编译&#xff1a;mysql源码本地编译 1.本地调试 这里以macbook为例 1.使用vscode打开mysql源码 2.创建basedir目录、数据目录、配置文件目录、配置文件 cd /Users/test/ mkdir mysqldir //创建数据目录和配置目录 cd mysqldir mkdir conf data …

PCIe— Legacy PCI

Legacy Model ​​ 该器件通过将其引脚置位到控制器来生成中断。 在较旧的系统中&#xff0c;这个控制 器通常是Intel 8259 PIC&#xff0c;有15个IRQ输入和一个INTR输出。 然后&#xff0c;PIC将断 言INTR以通知CPU一个或多个中断正在挂起。 一旦CPU检测到INTR的断言…

PostgreSQL数据库配置SSL操作说明书

背景&#xff1a; 因为postgresql或者mysql目前通过docker安装&#xff0c;只需要输入主机IP、用户名、密码即可访问成功&#xff0c;这样其实是不安全的&#xff0c;可能会通过一些手段获取到用户名密码导致数据被窃取。而ES、kafka等也是通过用户名/密码方式连接&#xff0c;…

低碳理念在道路工程中的应用-预制路面

一、引子 在上一篇文章里&#xff0c;给大家介绍了预制基层的应用&#xff0c;有人提出&#xff0c;既然基层能够预制&#xff0c;那么&#xff0c;道路面层能不能预制呢&#xff0c;有没有相关的研究成果和应用实例呢&#xff1f;答案是肯定的&#xff0c;在本篇文章中&#x…

12-后端Web实战(登录认证)

在前面的课程中&#xff0c;我们已经实现了部门管理、员工管理的基本功能&#xff0c;但是大家会发现&#xff0c;我们并没有登录&#xff0c;就直接访问到了Tlias智能学习辅助系统的后台。 这是不安全的&#xff0c;所以我们今天的主题就是登录认证。最终要实现的效果是&#…

4.2.2 Spark SQL 默认数据源

在本实战概述中&#xff0c;我们探讨了如何在 Spark SQL 中使用 Parquet 格式作为默认数据源。首先&#xff0c;我们了解了 Parquet 文件的存储特性&#xff0c;包括其二进制存储方式和内嵌的 Schema 信息。接着&#xff0c;通过一系列命令&#xff0c;我们演示了如何在 HDFS 上…

234. Palindrome Linked List

目录 一、题目描述 方法一、使用栈 方法二、将链表全部结点值复制到数组&#xff0c;再用双指针法 方法三、递归法逆序遍历链表 方法四、快慢指针反转链表 一、题目描述 234. Palindrome Linked List 方法一、使用栈 需要遍历两次。时间复杂度O(n)&#xff0c;空间复杂度…

无人机报警器探测模块技术解析!

一、运行方式 1. 频谱监测与信号识别 全频段扫描&#xff1a;模块实时扫描900MHz、1.5GHz、2.4GHz、5.8GHz等无人机常用频段&#xff0c;覆盖遥控、图传及GPS导航信号。 多路分集技术&#xff1a;采用多传感器阵列&#xff0c;通过信号加权合并提升信噪比&#xff0c;…

Bonjour

Bonjour 是苹果的一套零配置网络协议&#xff0c;用于发现局域网内的其他设备并进行通信&#xff0c;比如发现打印机、手机、电视等。 一句话&#xff1a;发现局域网其他设备和让其他设备发现。 Bonjour 可以完成的工作 IP 获取名称解析搜索服务 实际应用场景示例&#xff0…

华为云Flexus+DeepSeek征文 | 基于Dify和DeepSeek-R1开发企业级AI Agent全流程指南

作者简介 我是摘星&#xff0c;一名专注于云计算和AI技术的开发者。本次通过华为云MaaS平台体验DeepSeek系列模型&#xff0c;将实际使用经验分享给大家&#xff0c;希望能帮助开发者快速掌握华为云AI服务的核心能力。 目录 1. 前言 2. 环境准备 2.1 华为云资源准备 2.1 实…