Unity3d 基于UGUI和VideoPlayer 实现一个多功能视频播放器功能(含源码)

前言

随着Unity3d引擎在数字沙盘、智慧工厂、数字孪生等场景的广泛应用,视频已成为系统程序中展示时,不可或缺的一部分。在 Unity3d 中,我们可以通过强大的 VideoPlayer 组件和灵活的 UGUI 系统,将视频播放功能无缝集成到用户界面中,从而提升应用的用户体验。作为展示内容的媒体播放器,视频播放功能都需要一个清晰、易于交互的界面,所以本文就是以 VideoPlayer 组件为播放功能,通过UGUI搭建配套的操作界面,实现一个视频播放器功能。其功能包括:VideoClip播放、选择文件播放、播放/暂停、进度拖拽、快进/快退、速度调节、音量条件和视频画面尺寸自适应。
关注并私信 U3D视频播放器 免费获取源码(底部公众号)。

效果

选择文件播放:
在这里插入图片描述

快进:
在这里插入图片描述

进度拖拽:
在这里插入图片描述

横屏自动适配:
在这里插入图片描述

实现

实现关键组件包括:
1.VideoPlayer 组件:
VideoPlayer 是 Unity 中用于播放视频文件的核心组件。它能够播放本地视频文件或者通过网络流播放视频,并支持控制视频播放的各项参数,如播放、暂停、停止、音量控制、视频进度等。

2.RawImage 组件:
为了在 UI 中显示视频内容,我们可以使用 RawImage 组件。RawImage 是 Unity UGUI 中的一种 UI 组件,能够显示纹理,可以将视频流(VideoPlayer 播放的视频)输出到 RawImage 上进行显示。

3.Button、Slider 和 Text 组件:
为了实现播放、暂停、音量控制等功能,UGUI 提供了按钮(Button)、滑块(Slider)和文本(Text)组件,允许用户通过界面直接与视频进行交互。

按照步骤将实现过程拆分为UI搭建和编码实现。

UI搭建

播放器UI搭建的效果如下图:
在这里插入图片描述

使用 Button 控件来创建播放/暂停、快进快退按钮;使用 Slider 控件创建音量滑块、速度滑块和播放进度条实时更新视频的播放进度。使用 RawImage 控件显示视频内容,VideoPlayer 的视频输出会被设置为 RawImage.texture,从而将视频渲染到 UI 上。

编码

视频播放器的UI搭建好后,绑定对应的事件:

 #region 事件处理
    //点击暂停
    public void ClickPause()
    {
        LastInTime = Time.time;
        videoPlayer.Pause();
        PlayBtn.SetActive(true);
        PauseBtn.SetActive(false);
        CenterPlayBtn?.SetActive(true);
        ShowTip("暂停");
    }

    //点击播放 
    public void ClickPlay()
    {
        CenterPlayBtn?.SetActive(false);
        videoPlayer.Play();
        PlayBtn.SetActive(false);
        PauseBtn.SetActive(true);
        if (isIn)
        {
            isIn = false;
            CtrlTran.DOAnchorPos3D(OutPos, ScaleDura * 2);
        }
        ShowTip("播放");
    }


    //快进
    public void ClickForward() {
        double nowPos = videoPlayer.time + SpeedStep;
        videoPlayer.time = nowPos > VideoLength ? VideoLength : nowPos;
        LastInTime = Time.time;
        ShowTip("快进");
    }


    //快退
    public void ClickBackward() 
    {
        double nowPos = videoPlayer.time - SpeedStep;
        videoPlayer.time = nowPos < 0 ? 0 : nowPos;
        LastInTime = Time.time;
        ShowTip("快退");
    }

    public void MouseInCtrls()
    {
        LastInTime = Time.time;
        isOverCtrls = true;
    }
    public void MouseOutCtrls()
    {
        LastInTime = Time.time;
        isOverCtrls = false;
    }


    //音量滑动条变更
    public void OnVolumeChanged()
    {
        LastInTime = Time.time;
        // Set new audio output volume
        videoPlayer.SetDirectAudioVolume(0, VolSlider.value); // * 100

        ShowTip("播放音量:"+ (VolSlider.value * 100).ToString("F0"));
    }


    //进度滑动条变更
    public void OnTimeChanged(float val)
    {
        LastInTime = Time.time;
        videoPlayer.time = val * VideoLength;
        TimeSlider.value = val;


        ShowTip("播放时间:" + videoPlayer.time.ToString("F0"));
    }

    //速度重置
    public void ClickSpeed()
    {
        SpeedSlider.value = 0.2f;
        videoPlayer.playbackSpeed = 1;
        SpeedText.text = "速度×1.0";

        ShowTip("播放速度" + videoPlayer.playbackSpeed);
    }


    //速度滑动条拖拽
    public void OnSpeedChange(float val)
    {
        float speed = val / 0.2f;
        videoPlayer.playbackSpeed = speed;
        SpeedText.text = "速度×" + speed.ToString("F1");


        ShowTip("播放" + SpeedText.text);
    }

    #endregion 事件处理

完整代码如上,音量滑块(Slider 控件),通过 videoPlayer.SetDirectAudioVolume() 方法来调整音量;播放速度滑块(Slider 控件),通过videoPlayer.playbackSpeed 属性来调整播放速度;进度条(Slider 控件),实时更新视频的播放进度,并通过 videoPlayer.time 属性控制视频的当前播放时间;播放/暂停按钮(Button 控件),监听其点击事件,并通过 VideoPlayer 的 Play() 和 Pause() 方法控制视频的播放和暂停。其中TipText是提示的Text控件,状态变更时候会进行文字提示。

视频的控制按钮面板,对其进行了默认移出播放画面,鼠标点击移入,无操作一段时间后移出画面的处理。

视频画面高宽自适应:

Canvas canvas = transform.GetComponentInParent<Canvas>();
float WidRate = videoPlayer.width / (float)Screen.width;//Screen.width;
float HeightRate = videoPlayer.height / (float)Screen.height;//Screen.height;

//Debug.Log("VideoRt.sizeDelta:" + VideoRt.sizeDelta);
if (WidRate >= HeightRate)   //适配宽度
{
    float Width = (float)Screen.width / canvas.transform.localScale.x;
    float Height = (Screen.width / (float)videoPlayer.width) * videoPlayer.height / canvas.transform.localScale.y;

    VideoRt.sizeDelta = new Vector2(Width, Height);
    //Debug.Log("适配宽度  width:" + Width + "  height:" + Height);
}
else  //适配高度
{
    float Width = Screen.height / (float)videoPlayer.height * videoPlayer.width / canvas.transform.localScale.x;
    float Height = (float)Screen.height / canvas.transform.localScale.y;
    VideoRt.sizeDelta = new Vector2(Width, Height);
    //Debug.Log("适配高度 width:" + Width + "  height:" + Height);
}


// 先释放当前的 RenderTexture 资源
if (renderTexture != null)
{
    renderTexture.Release();

    // 设置新的宽高
    renderTexture.width = (int)videoPlayer.width;
    renderTexture.height = (int)videoPlayer.height;
}
else
    renderTexture = new RenderTexture((int)videoPlayer.width, (int)videoPlayer.height, 24);

renderTexture.Create();

videoPlayer.targetTexture = renderTexture;

fullImg.texture = renderTexture;

尺寸的适应采用高宽比的思路,如果宽度比 比 高度比高就适配宽度,反之则适配高度(这里比较拗口,详情参看代码)。因为renderTexture的尺寸需要动态变更,所以这里都是先进行释放了,然后再进行创建。

在播放过程中显示00:00:00/00:00:00的时间格式,设置函数如下:

//设置已播时长
void SetPlayerTime() //long playedTime
{
    TimeSpan time = TimeSpan.FromSeconds(videoPlayer.time);//TimeSpan.FromMilliseconds(playedTime);
    NowTimeText.text = string.Format("{0:D2}:{1:D2}:{2:D2}", time.Hours, time.Minutes, time.Seconds);

    if (isIn)
    {
        TimeSlider.value = VideoLength > 0 ? (float)(videoPlayer.time / VideoLength) : 0;
    }
}

//设置视频时长
void SetMediaLength(long mediaLength)
{
    VideoLength = mediaLength;

    //Debug.Log("SetMediaLength:" + mediaLength);
    var length = TimeSpan.FromSeconds(mediaLength);//TimeSpan.FromMilliseconds(mediaLength);

    AllTimeText.text = string.Format("{0:D2}:{1:D2}:{2:D2}", length.Hours, length.Minutes, length.Seconds);
}

使用方法

1.加入工程后将“VideoPlayer.prefab” 预设加入场景。

2.可以通过调用的接口的方式启动播放器:
传入VideoClip:

public void PlayClip(VideoClip clip, Transform tran = null)

clip是视频片段。

通过传入地址:

public void PlayVideoByUrl(string url, Transform tran = null)

url是地址。

tran 是动画的起始位置对象,可不传。传入后可以有触发位置移动到屏幕中间的动画效果:
在这里插入图片描述

案例中直接打开VideoClip的调用:

public VideoClip vc;
//直接播放片段
public void ClickPlayClip(GameObject sender) {
    VideoPlayerCtrl.instance?.PlayClip(vc, sender.transform);
}

案例中的选择视频的调用:

//打开视频文件
public void ClickChooseBtn(GameObject sender)
{
    OpenFileName file = new OpenFileName();
    file.structSize = Marshal.SizeOf(file);
    file.filter = "文件(*.MP4;*.mp4;*.Mp4;*.wmv;*.rm;*.rmvb;*.avi;*.mkv;*.flv)\0*.mp4;*.mp4;*.Mp4;*.wmv;*.rm;*.rmvb;*.avi;*.mkv;*.flv";
    file.file = new string(new char[256]);
    file.maxFile = file.file.Length;
    file.fileTitle = new string(new char[64]);
    file.maxFileTitle = file.fileTitle.Length;
    file.initialDir = Application.streamingAssetsPath.Replace('/', '\\');//默认路径
    file.title = "选择文件";
    file.flags = 0x00080000 | 0x00001000 | 0x00000800 | 0x00000008;

    if (SelectFileDialog.GetSaveFileName(file))
    {
        VideoPlayerCtrl.instance?.PlayVideoByUrl(file.file, sender.transform);
    }

}

这里使用了comdlg32.dll,comdlg32.dll是Windows操作系统中的一个动态链接库(DLL)文件,主要用于提供多种标准对话框的实现功能。这些对话框包括文件打开/保存对话框、颜色选择器等,对于本视频播放器来说主要就是选择视频文件并打开播放。

Unity3d中的Comdlg32.dll引用:

using System.Runtime.InteropServices;

public class SelectFileDialog
{
    //系统函数  
    [DllImport("Comdlg32.dll", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = CharSet.Auto)]
    public static extern bool GetSaveFileName([In, Out] OpenFileName ofn);
    public static bool GetSFN([In, Out] OpenFileName ofn)
    {
        return GetSaveFileName(ofn);
    }
}

源码

https://download.csdn.net/download/qq_33789001/90178128

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

蓝牙协议——音乐启停控制

手机播放音乐 手机暂停音乐 耳机播放音乐 耳机暂停音乐

【EthIf-13】EthIfGeneral容器配置-01

1.EthIfGeneral类图结构 下面是EthIfGeneral配置参数的类图&#xff0c;比较重要的参数就是配置&#xff1a; 接收中断是否打开发送确认中断是否打开EthIf轮询周期 1.EthIfGeneral参数的含义

如何看待2024年诺贝尔物理学奖颁给了机器学习与神经网络?

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于2024年诺贝尔物理学奖颁给了机器学习与神…

有没有检测吸烟的软件 ai视频检测分析厂区抽烟报警#Python

在现代厂区管理中&#xff0c;安全与规范是重中之重&#xff0c;而吸烟行为的管控则是其中关键一环。传统的禁烟管理方式往往依赖人工巡逻&#xff0c;效率低且存在监管死角&#xff0c;难以满足当下复杂多变的厂区环境需求。此时&#xff0c;AI视频检测技术应运而生&#xff0…

VSCode搭建Java开发环境 2024保姆级安装教程(Java环境搭建+VSCode安装+运行测试+背景图设置)

名人说&#xff1a;一点浩然气&#xff0c;千里快哉风。—— 苏轼《水调歌头》 创作者&#xff1a;Code_流苏(CSDN) 目录 一、Java开发环境搭建二、VScode下载及安装三、VSCode配置Java环境四、运行测试五、背景图设置 很高兴你打开了这篇博客&#xff0c;更多详细的安装教程&…

二手车交易平台开发:安全与效率的双重挑战

3.1系统体系结构 系统的体系结构非常重要&#xff0c;往往决定了系统的质量和生命周期。针对不同的系统可以采用不同的系统体系结构。本系统为二手车交易平台系统&#xff0c;属于开放式的平台&#xff0c;所以在体系结构中采用B/s。B/s结构抛弃了固定客户端要求&#xff0c;采…

共享无人系统,从出行到生活全面覆盖

共享无人系统已经覆盖到我们生活中的方方面面&#xff0c;出行上&#xff0c;比如共享自行车小程序、共享自行车&#xff1b;生活中&#xff0c;比如说棋牌室、茶室。我们以棋牌室举例。 通过开发使用共享无人系统&#xff0c;可以极大地降低人力成本&#xff0c;共享无人棋牌室…

FPGA学习(基于小梅哥Xilinx FPGA)学习笔记

文章目录 一、整个工程的流程二、基于Vivado的FPGA开发流程实践&#xff08;二选一多路器&#xff09;什么是二选一多路器用verilog语言&#xff0c;Vivado软件进行该电路实现1、设计输入&#xff1a;Design Sources中的代码2、分析和综合&#xff1a;分析设计输入中是否有错误…

四相机设计实现全向视觉感知的开源空中机器人无人机

开源空中机器人 基于深度学习的OmniNxt全向视觉算法OAK-4p-New 全景硬件同步相机 机器人的纯视觉避障定位建图一直是个难题&#xff1a; 系统实现复杂 纯视觉稳定性不高 很难选到实用的视觉传感器 为此多数厂家还是采用激光雷达的定位方案。 OAK-4p-New 为了弥合这一差距…

突破续航瓶颈:数字样机技术引领新能源汽车复合制动新方向

随着我国经济快速发展和人民生活水平不断提升&#xff0c;汽车保有量截至2023年9月底就已达到了3.3亿&#xff0c;同比增长6.32%。庞大的汽车保有量对我国的环境和能源都产生了巨大的压力&#xff0c;具备节能环保优势的新能源汽车对于有效解决环境恶化和能源危机问题具有重要意…

基于股票日频 K 线的自动因子挖掘实践

遗传算法最初由美国密歇根大学的 J.Holland 提出&#xff0c;是一种通过模拟自然界生物进化的过程来搜索最优解的算法&#xff0c;应用于量子计算、电子设计、游戏比赛等多种场景。 以大家熟知的 python gplearn 为例&#xff0c;它就是一款基于遗传算法开发的数据分析工具&am…

考前96天 学习巩固 计算机、数学、英语

2024年12月24日到2025年3月29日共有 96​ 天 一、计算机基础 回顾&#xff1a; 三大思维&#xff1a; 数学 推理/理论 物理 证实/实验 计算机 构造/计算 本质——》抽象/自动化 计算复杂性&#xff1a;空间复杂性、时间复杂性 计算机系统的组成&#xff1a; 1️⃣硬件…

Artec Space Spider助力剑桥研究团队解码古代社会合作【沪敖3D】

挑战&#xff1a;考古学家需要一种安全的方法来呈现新出土的陶瓷容器&#xff0c;对比文物形状。 解决方案&#xff1a;Artec Space Spider, Artec Studio 效果&#xff1a;本项目是REVERSEACTION项目的一部分&#xff0c;旨在研究无国家社会中复杂的古代技术。研究团队在考古地…

云原生服务网格Istio实战

基础介绍 1、Istio的定义 Istio 是一个开源服务网格&#xff0c;它透明地分层到现有的分布式应用程序上。 Istio 强大的特性提供了一种统一和更有效的方式来保护、连接和监视服务。 Istio 是实现负载平衡、服务到服务身份验证和监视的路径——只需要很少或不需要更改服务代码…

《Cocos Creator游戏实战》非固定摇杆实现原理

为什么要使用非固定摇杆 许多同学在开发摇杆功能时&#xff0c;会将摇杆固定在屏幕左下某一位置&#xff0c;不会让其随着大拇指触摸点改变&#xff0c;而且玩家只有按在了摇杆上才能移动人物&#xff08;触摸监听事件在摇杆精灵上)。然而&#xff0c;不同玩家的大拇指长度不同…

智能座舱进阶-应用框架层-Jetpack主要组件

Jetpack的分类 1. DataBinding&#xff1a;以声明方式将可观察数据绑定到界面元素&#xff0c;通常和ViewModel配合使用。 2. Lifecycle&#xff1a;用于管理Activity和Fragment的生命周期&#xff0c;可帮助开发者生成更易于维护的轻量级代码。 3. LiveData: 在底层数据库更…

登山第十六梯:深度恢复——解决机器人近视问题

文章目录 一 摘要 二 资源 三 内容 一 摘要 深度感知是基于 3D 视觉的机器人技术的一个重要问题。然而&#xff0c;现实世界的主动立体或 ToF 深度相机经常会产生嘈杂且深度不完整&#xff0c;从而成为机器人性能的瓶颈。在这项工作中&#xff0c;提出了 一个基于学习的立体…

【Jenkins】持久化

文章目录 持续集成CI持续部署CD部署部署到linux服务器 持续集成好处&#xff1a; 持续集成CI 持续集成&#xff08;Continuous integration&#xff0c;简称CI&#xff09;指的是频繁地&#xff08;一天多次&#xff09;将代码集成到主干。 持续集成的目的就是让产品可以快速…

小红书飞书素材库 | AI改写 | 无水印下载 | 多维表格 | 采集同步 | 影刀RPA

小红书飞书素材库 | AI改写 | 无水印下载 | 多维表格 | 采集同步 | 影刀RPA 模板准备 进入【小红书】素材采集库_荷逸模板&#xff0c;点击使用模板 创建文档应用 在开发者后台 - 飞书开放平台创建 企业自建应用 (需要账号有相应的权限, 如果没有权限向管理员申请) 获取 Ap…

layui动态拼接生成下拉框验证必填项失效问题

利用 jQuery 动态拼接下拉框时&#xff0c;lay-verify"required" 失效了&#xff0c;有以下几种原因。 1. <form></form>标签 加入 layui 类&#xff0c;class"layui-form" 。提交按钮上加自动提交&#xff0c;lay-submit ""; 。需…