一、效果
全屏缩小画面、播放暂停、进度条拖拽、视频播放时长、倍速、音量等功能。 长时间不移动鼠标自动隐藏播放控制器、鼠标离开倍速、音量时隐藏倍速、音量ui
![](https://i-blog.csdnimg.cn/direct/064fb5bc2cf14bb283a42b0348afa0b0.gif)
二、脚本
cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Video;
public class VideoController : MonoBehaviour
{
[Header("视频进度时间")]
[SerializeField] Text sumTimeTxt; //总时间
[SerializeField] Text currentTimeTxt; //当前时间
[SerializeField] Slider scheduleSlider; //进度条
[Header("播放按钮")]
[SerializeField] Sprite playSprite; //播放
[SerializeField] Sprite pauseSprite; //暂停
[SerializeField] Button playBtn; //播放暂停
[SerializeField] GameObject playIcon; //播放图标
[Header("加载视频按钮")]
[SerializeField] Button lastBtn; //上一个
[SerializeField] Button nextBtn; //下一个
[Header("视频类型切换按钮")]
[SerializeField] Text videoTypeTxt; //视频类型文本
[SerializeField] Button videoTypeBtn; //视频类型按钮
[Header("音量按钮")]
// [SerializeField] Sprite volumCross; //静音
// [SerializeField] Sprite volumMiddle; //未静音
[SerializeField] Button volumBtn; //音量按钮
[SerializeField] Slider volumSlider; //音量滑动条
[SerializeField] CanvasGroup volumbg; //音量背景(渐隐)
[SerializeField] AudioSource audioSource; //音量控制器
[SerializeField] List<Transform> volumMaskList; //音量遮罩(ui检测用)
[Header("倍速")]
[SerializeField] Color32 selectColor=new Color32(0, 99, 242, 255);
[SerializeField] Dropdown speedDropdown; //倍速
[SerializeField] List<Transform> speedMaskList; //倍速遮罩(ui检测用)
[Header("全屏小屏控制器物体列表")]
[SerializeField] List<Transform> fullControllerObjList; //播放控制器物体(ui检测用)
[SerializeField] List<Transform> lessenControllerObjList; //播放控制器物体(ui检测用)
[Header("放大缩小按钮")]
[SerializeField] Button fullBtn; //放大按钮
[SerializeField] Button lessenBtn; //缩小按钮
[SerializeField] Button lessenPlayBtn; //小屏播放按钮
RenderTexture rt;
[Header("视频参数")]
[SerializeField] RawImage img; //视频画面
[SerializeField] VideoPlayer videoPlayer;
[Header("节点")]
[SerializeField] GameObject root;
[SerializeField] List<VideoClip> clipList;
[SerializeField] List<string> urlList;
//单例
public static VideoController instance;
private void Awake()
{
instance = this;
}
void Start()
{
// SwitchVideoType(); //默认设置视频类型
#region 委托添加
videoPlayer.prepareCompleted += PrepareCompleted; //准备完成
videoPlayer.loopPointReached += LoopPointReached; //播放结束
videoPlayer.started += Started; //Play后立即调用
videoPlayer.frameDropped += FrameDropped; //有丢帧发生时被执行
videoPlayer.errorReceived += ErrorReceived; //通过此回调报告 HTTP 连接问题等错误。
videoPlayer.seekCompleted += SeekCompleted; //查询帧操作完成时被执行
videoPlayer.frameReady += FrameReady; //新的一帧准备好时被执行
#endregion
//视频播放
playBtn.onClick.AddListener(PlayVideo);
lessenPlayBtn.onClick.AddListener(PlayVideo);
img.GetComponent<Button>().onClick.AddListener(PlayVideo); //视频画布可控制播放暂停
//上一个下一个视频切换
lastBtn.onClick.AddListener(() => { SwitchVideo(-1); });
nextBtn.onClick.AddListener(() => { SwitchVideo(1); });
//视频类型切换
videoTypeBtn.onClick.AddListener(SwitchVideoType);
//音量
volumBtn.onClick.AddListener(VolumeShowOrHide);
volumSlider.onValueChanged.AddListener(SetSliderVolume);
volumFade=new FadeOutData{canvasGroup=volumbg,onComplete = VolumOnComplete};
//倍速playSpeedDropdownOnclike
speedDropdown.onValueChanged.AddListener(PlayBackSpeed);
speedDropdown.value = 3;//默认设置为1倍速
//视频
lessenBtn.onClick.AddListener(VideoLessen);
fullBtn.onClick.AddListener(VideoFull);
}
void Update()
{
#region UI物体检测
//全屏状态下执行
if (isVideoFull)
{
//全屏模式下控制器显示隐藏检测
if (isVideoFull)
FullControllerShow();
//音量物体显示&& 鼠标未在音量物体上时
if (!ismute && !RayDetectionUI(Input.mousePosition, volumMaskList))
VolumeShowOrHide(); //音量物体隐藏
//倍速下拉框开启
if (speedDropdown.transform.Find("Dropdown List") )
{
if (!RayDetectionUI(Input.mousePosition, speedMaskList))
speedDropdown.Hide();//鼠标不在倍速mask上时 隐藏下拉列表
}
else if (isSpeedRaycast)
{
//倍速物体射线启用 && 倍速下拉框处于关闭时 设置Mask物体不可射线检测
isSpeedRaycast = false;
foreach (var item in speedMaskList)
item.GetComponent<Image>().raycastTarget = isSpeedRaycast;
}
}
#endregion
}
private void LateUpdate()
{
#region 视频总时间长度、视频已播放时长、滑动条设置
// 在播放时 && 进度条没有被按下
if (videoPlayer.isPlaying && !isSliderDrag)
{
SetTxtTimer((float)videoPlayer.time, currentTimeTxt); //视频时间显示
scheduleSlider.value = (float)(videoPlayer.time / (videoPlayer.frameCount / videoPlayer.frameRate)); //进度条
}
#endregion
//根据视频加载情况设置是否禁用滑动条
// scheduleSlider.interactable = videoPlayer.isPrepared;
}
#region 视频小屏和大屏播放
private bool isVideoFull;//当前视频是否是全屏 true全屏
//小屏
void VideoLessen()
{
isVideoFull = false;
//全屏控制器隐藏
foreach (var item in fullControllerObjList)
item.gameObject.SetActive(false);
//全屏控制器显示
foreach (var item in lessenControllerObjList)
item.gameObject.SetActive(true);
playIcon.SetActive(false);//小屏模式下隐藏
videoPlayer.isLooping=true;
//缩小屏幕
RectTransform rect = root.GetComponent<RectTransform>();
rect.anchorMax = rect.anchorMin = new Vector2(0, 1f);
rect.anchoredPosition = new Vector2(240f, -630f);
rect.sizeDelta = new Vector2(400f, 225f);
//屏幕缩小 kienct右下角画面显示、角度提示显示
KinectManager.Instance.computeColorMap = true;
// CalculatedAngle.instance.gameObject.SetActive(true);
}
//全屏
void VideoFull()
{
isVideoFull = true;
//全屏控制器显示
foreach (var item in fullControllerObjList)
item.gameObject.SetActive(true);
//全屏控制器隐藏
foreach (var item in lessenControllerObjList)
item.gameObject.SetActive(false);
playIcon.SetActive(isPlay);//全屏模式并且暂停状态下显示
videoPlayer.isLooping=false;
//放大屏幕
RectTransform rect = root.GetComponent<RectTransform>();
rect.anchorMin = new Vector2(0, 0);
rect.anchorMax = new Vector2(1, 1);
rect.anchoredPosition = rect.sizeDelta = new Vector2(0f, 0f);
//屏幕放大 kienct右下角画面隐藏、角度提示隐藏
KinectManager.Instance.computeColorMap = false;
// CalculatedAngle.instance.gameObject.SetActive(false);
}
#endregion
#region 全屏模式下 播放控制器显示隐藏
private Vector3 oldMousePos;
void FullControllerShow()
{
// 当全屏状态下当前鼠标位置与上次鼠标位置不同时显示播放控制器
if (Input.mousePosition!=oldMousePos)
{
oldMousePos = Input.mousePosition;
foreach (var VARIABLE in fullControllerObjList)
VARIABLE.gameObject.SetActive(true);
//取消invoke
if (IsInvoking("InvokeControllerHide"))
CancelInvoke("InvokeControllerHide");
//不在控制器ui下时
if (!RayDetectionUI(Input.mousePosition,fullControllerObjList))
Invoke("InvokeControllerHide", 3f);//开启3秒控制器隐藏
}
}
//全屏控制器3秒隐藏
void InvokeControllerHide()
{
foreach (var VARIABLE in fullControllerObjList)
VARIABLE.gameObject.SetActive(false);
}
#endregion
#region 修改指定文本时长
/// <summary>
/// 修改指定文本时长
/// </summary>
void SetTxtTimer(float time, Text timeTxt)
{
TimeData timeData = VideoTimeConversion(time);
//时:分:秒
// timeTxt.text = timeData == null
// ? "00:00:00"
// : $"{timeData.h.ToString("00")}:{timeData.m.ToString("00")}:{timeData.s.ToString("00")}";
//分:秒
int m = timeData.h * 60 + timeData.m;
timeTxt.text = timeData == null
? "00:00"
: $"{m.ToString("00")}:{timeData.s.ToString("00")}";
}
//时间数据
class TimeData
{
public int h;
public int m;
public int s;
}
/// <summary>
/// video时间换算
/// </summary>
/// <param name="totalTime">总时长</param>
/// <returns></returns>
TimeData VideoTimeConversion(float totalTime)
{
TimeData data = new TimeData();
/*1.VideoPlayer.time / 3600 通过时间获取(其中Time表示当前可用时间)*/
data.h = (int)totalTime / 3600;
data.m = (int)(totalTime - data.h * 3600) / 60;
data.s = (int)totalTime % 60;
#region 使用帧方式换算(暂不用)
/*2.VideoPlayer.frameCount / VideoPlayer.frameRate 通过帧获取(总帧数/1秒多少帧)*/
//int timer = (int)(videoPlayer.frameCount / videoPlayer.frameRate);
//int h = timer / (60 * 60);//时
//int m = (timer - (h * 3600)) / 60;//分
//int s = timer % 60;//秒
#endregion
return data;
}
#endregion
#region 视频委托
//视频准备完成
void PrepareCompleted(VideoPlayer _player)
{
// Debug.Log("视频准备完成" + _player.length);
//设置视频总时长
SetTxtTimer((float)_player.length, sumTimeTxt);
//设置RT
rt = CrteateRT((int)_player.clip.width, (int)_player.clip.height);
_player.targetTexture = rt;
img.texture = rt;
//播放视频
isPlay = true;
PlayVideo(); //播放视频
}
private bool isVideoEnd;
//视频播放结束
void LoopPointReached(VideoPlayer _player)
{
Debug.Log("视频播放结束");
_player.Stop();
isVideoEnd = true;
//视频播放按钮状态修改
isPlay = true;
lessenPlayBtn.gameObject.SetActive(true);
playBtn.image.sprite = playSprite;
}
//Play开始播放
void Started(VideoPlayer _player)
{
Debug.Log("Play开始播放");
}
//有丢帧发生时被执行
void FrameDropped(VideoPlayer _player)
{
Debug.Log("有丢帧发生时被执行");
}
//查询帧操作完成时被执行
void SeekCompleted(VideoPlayer _player)
{
Debug.Log("查询帧操作完成时被执行");
isSliderDrag = false;
//播放视频
isPlay = true;
PlayVideo();
}
//通过此回调报告 HTTP 连接问题等错误。
void ErrorReceived(VideoPlayer _player, string message)
{
Debug.Log("通过此回调报告 HTTP 连接问题等错误。" + message);
}
//新的一帧准备好时被执行
void FrameReady(VideoPlayer _player, long frameIdx)
{
Debug.Log("新的一帧准备好时被执行。" + frameIdx);
}
#endregion
#region 创建RenderTexture
public RenderTexture CrteateRT(int width, int hight)
{
RenderTexture rt = new RenderTexture(width, hight, 0, RenderTextureFormat.ARGB32);
rt.Create();
return rt;
}
#endregion
#region 滑动条控制
bool isSliderDrag;
//滑动条按下
public void ScheduleSliderDown()
{
Debug.Log("slider按下");
isSliderDrag = true;
}
private float DragTime; //滑动条拖动的时间
// 滑动条拖动
public void ScheduleSliderDrag()
{
isSliderDrag = true;
DragTime = scheduleSlider.value * (videoPlayer.frameCount / videoPlayer.frameRate);
SetTxtTimer(DragTime, currentTimeTxt); //修改视频已播放时长
}
//滑动条抬起
public void ScheduleSliderUP()
{
Debug.Log("slider抬起");
videoPlayer.time = DragTime; //修改播放进度
if (isVideoEnd)
{
isVideoEnd = false;
isSliderDrag = false;
isPlay = true;
PlayVideo();
}
}
#endregion
#region 播放控制
bool isPlay = true; //默认下次播放
private void PlayVideo()
{
Debug.Log($"调用Play方法:{isPlay}");
if (isPlay)
{
videoPlayer.Play(); //播放
lessenPlayBtn.gameObject.SetActive(false);
}
else
{
videoPlayer.Pause(); //暂停
lessenPlayBtn.gameObject.SetActive(true);
}
playIcon.SetActive(isVideoFull&&!isPlay);//全屏模式并且暂停状态下显示
playBtn.image.sprite = isPlay ? pauseSprite : playSprite;
isPlay = !isPlay;
}
#endregion
#region 视频类型切换
void SwitchVideoType()
{
currentVideoIndex = -1; //视频当前下标
switch (videoPlayer.source)
{
case VideoSource.VideoClip:
videoTypeTxt.text = "URL";
videoPlayer.source = VideoSource.Url;
break;
case VideoSource.Url:
videoTypeTxt.text = "Clip";
videoPlayer.source = VideoSource.VideoClip;
break;
}
SwitchVideo(1); //播放视频
}
#endregion
#region 上一个下一个视频
int currentVideoIndex;
void SwitchVideo(int index)
{
isPlay = true; //默认下一次播放
currentVideoIndex += index;
switch (videoPlayer.source)
{
case VideoSource.VideoClip:
if (currentVideoIndex > clipList.Count - 1)
currentVideoIndex = clipList.Count - 1;
if (currentVideoIndex < 0)
currentVideoIndex = 0;
videoPlayer.clip = clipList[currentVideoIndex];
break;
case VideoSource.Url:
if (currentVideoIndex > urlList.Count - 1)
currentVideoIndex = urlList.Count - 1;
if (currentVideoIndex < 0)
currentVideoIndex = 0;
videoPlayer.url = urlList[currentVideoIndex];
break;
}
videoPlayer.Prepare(); //视频预先加载准备
}
#endregion
#region 播放指定视频
public void PlayAssignedVideo(string videoName)
{
foreach (var clip in clipList)
{
if (clip.name==videoName)
{
videoPlayer.clip = clip;
videoPlayer.Prepare(); //视频预先加载准备
return;
}
}
Debug.Log($"{videoName}");
}
public void PlayAssignedVideo(VideoClip clip)
{
OpenPanel(); //开启面板
videoPlayer.clip = clip;
videoPlayer.Prepare(); //视频预先加载准备
}
#endregion
#region 音量控制
bool ismute = true;
FadeOutData volumFade;
IEnumerator volumeFadeOut; //当前渐隐协程
/// <summary>
/// 音量显示隐藏
/// </summary>
void VolumeShowOrHide()
{
ismute = !ismute;
//设置Mask物体的射线检测效果
foreach (var item in volumMaskList)
item.GetComponent<Image>().raycastTarget = !ismute;
//判断是否有音量协程在执行
if (volumFade.isFading) StopCoroutine(volumeFadeOut);
//渐隐
volumFade.fadeOutValue = ismute ? 0 : 1;
//设置当前执行的音量协程
volumeFadeOut = FadeOut(volumFade);
StartCoroutine(volumeFadeOut);
// 点击按钮静音并存储当前音量
// if (ismute)
// volumSlider.value = oldVolumeValue;//不静音 还原音量
// else
// {
// //静音
// oldVolumeValue = audioSource.volume; //记录原先音频数值
// volumSlider.value = 0;
// }
}
//音量渐隐回调
void VolumOnComplete()
{
// 渐隐结束 音量背景显示隐藏
volumbg.gameObject.SetActive(!ismute);
}
private float oldVolumeValue; //静音前的原始音频
//音量滑动条监听
void SetSliderVolume(float value)
{
// Debug.Log("滑动条value");
audioSource.volume = value; //设置音频音量
// 按钮静音
// ismute = value > 0;
// volumBtn.image.sprite = ismute ? volumMiddle:volumCross;//设置静音图片效果
}
#endregion
#region 倍速控制
private bool isSpeedRaycast;//倍速物体射线是否启用
//Dropdown下的挂载的Event Trigger调用
public void playSpeedDropdownOnclike()
{
isSpeedRaycast = true;
//设置Mask物体可以射线检测
foreach (var item in speedMaskList)
item.GetComponent<Image>().raycastTarget = isSpeedRaycast;
// 遍历所有选项
for (int i = 0; i < speedDropdown.options.Count; i++)
{
Text optionText = speedDropdown.transform.Find("Dropdown List/Viewport/Content").GetChild(i).GetComponentInChildren<Text>();
if (i == speedDropdown.value+1)
{
// 设置选中选项颜色
optionText.color = selectColor;
}
else
{
// 设置默认选项颜色
}
}
}
public void PlayBackSpeed(int value)
{
videoPlayer.playbackSpeed = float.Parse(speedDropdown.options[value].text.Split('x')[0]);
//Debug.Log(videoPlayer.playbackSpeed);
}
#endregion
#region 开启关闭视频
public void OpenPanel()
{
root.SetActive(true);
}
public void ClosePanel()
{
root.SetActive(false);
}
#endregion
#region UI渐隐
public class FadeOutData
{
public bool isFading; //是否正在渐隐
public float duration = 0.15f; //渐隐时间
public float fadeOutValue; //渐隐值
public CanvasGroup canvasGroup; //渐隐物体
public Action onComplete; //回调函数
}
/// <summary>
/// 音量渐隐
/// </summary>
private IEnumerator FadeOut(FadeOutData data)
{
//显示物体
data.canvasGroup.gameObject.SetActive(true);
data.isFading = true;
float elapsedTime = 0f;
// 设置当前的 alpha 值
float startAlpha = data.canvasGroup.alpha ;
// canvasGroup.alpha = startAlpha;
//N秒渐隐
while (elapsedTime < data.duration)
{
elapsedTime += Time.deltaTime;
// 计算新的 alpha 值
float newAlpha = Mathf.Lerp(startAlpha, data.fadeOutValue, elapsedTime / 1);
data.canvasGroup.alpha = newAlpha;
yield return null; // 等待下一帧
}
// 确保最终 alpha 值为 最终值
data.canvasGroup.alpha = data.fadeOutValue;
//结束渐隐
data.isFading = false;
//执行回调
data.onComplete?.Invoke();
}
#endregion
#region UI触发检测
/// <summary>
/// Ui触发检测
/// </summary>
/// <param name="position">点击屏幕坐标</param>
/// <param name="detectionObjs">识别物体</param>
/// <returns></returns>
private bool RayDetectionUI(Vector2 position, List<Transform> detectionObjs)
{
//识别物体为空直接返回false
if (detectionObjs == null)
{
Debug.Log("识别物体为空");
return false;
}
EventSystem eventSystem = EventSystem.current;
PointerEventData pointerEventData = new PointerEventData(eventSystem);
pointerEventData.position = position;
//射线检测ui
List<RaycastResult> uiRaycastResultCache = new List<RaycastResult>();
eventSystem.RaycastAll(pointerEventData, uiRaycastResultCache);
//检测鼠标是否在 指定UI上
foreach (var result in uiRaycastResultCache)
{
if (detectionObjs.IndexOf(result.gameObject.transform)!=-1)
return true;
}
return false;
}
#endregion
}