using System;
using UnityEngine;
using UnityEngine.Video;
using UnityEngine.UI;
///
/// UI视频播放器组件 - 支持WebM格式透明视频播放
///
public class UIVideoPlayer : MonoBehaviour
{
[Header("组件引用")]
public VideoPlayer videoPlayer;
public RawImage videoImage;
[Header("调试信息")]
public const string directory = "Video";
public string videoName;
public bool isLoop = false;
[Header("资源管理")]
[Tooltip("播放完成后是否自动卸载资源(仅当非循环播放时有效)")]
public bool autoUnloadOnFinish = true;
[Header("回调事件")]
public Action onComplete;
public Action onPrepared;
private RenderTexture _renderTexture;
private VideoClip _loadedClip;
private bool _isPrepared;
private bool _isLoading;
private Action _onPrepareCallback;
#region Public API
///
/// 加载并播放视频(通过资源系统加载 VideoClip)
///
/// 视频文件名(不含扩展名)
/// 是否循环播放
/// 播放完成回调(仅在非循环播放时调用)
public async void LoadAndPlayVideo(string _videoName, bool _loop = false, Action _onComplete = null)
{
if (!ValidateComponents()) return;
if (_isLoading)
{
Debug.LogWarning($"UIVideoPlayer: Already loading video. Please wait.");
return;
}
videoName = _videoName;
isLoop = _loop;
onComplete = _onComplete;
_isPrepared = false;
_isLoading = true;
// 清理旧资源
ReleaseRenderTexture();
// 隐藏或设置空状态颜色
SetEmptyState();
// 通过 ResManager 加载 VideoClip 资源(支持 AB 打包)
_loadedClip = await ResManager.Instance.LoadAssetAsync(directory, videoName, false);
// 检查是否在加载过程中被取消
if (!_isLoading)
{
_loadedClip = null;
return;
}
_isLoading = false;
if (_loadedClip == null)
{
Debug.LogError($"UIVideoPlayer: Failed to load VideoClip: {directory}/{videoName}");
_isPrepared = false;
return;
}
// 创建并绑定 RenderTexture(按视频实际尺寸)
_renderTexture = new RenderTexture((int)_loadedClip.width, (int)_loadedClip.height, 0);
videoPlayer.targetTexture = _renderTexture;
videoImage.texture = _renderTexture;
videoImage.SetNativeSize();
// 显示视频
SetActiveState();
videoPlayer.source = VideoSource.VideoClip;
videoPlayer.clip = _loadedClip;
videoPlayer.isLooping = isLoop;
videoPlayer.prepareCompleted += OnVideoPrepared;
// 非循环播放时注册完成事件
if (!isLoop)
{
videoPlayer.loopPointReached += OnVideoFinished;
}
videoPlayer.Prepare();
}
///
/// 加载并播放指定视频
///
public void LoadAndPlay(string videoName, bool loop = false, Action onCompleteCallback = null)
{
LoadAndPlayVideo(videoName, loop, onCompleteCallback);
}
[ContextMenu("播放")]
public void Play()
{
if (!ValidateComponents()) return;
if (_isPrepared && !videoPlayer.isPlaying)
{
videoPlayer.Play();
}
}
[ContextMenu("暂停")]
public void Pause()
{
if (!ValidateComponents()) return;
if (videoPlayer.isPlaying)
{
videoPlayer.Pause();
}
}
[ContextMenu("恢复")]
public void Resume()
{
if (!ValidateComponents()) return;
if (_isPrepared && !videoPlayer.isPlaying)
{
videoPlayer.Play();
}
}
///
/// 预加载视频(不自动播放)
///
/// 视频文件名(不含扩展名)
/// 预加载完成回调,参数为是否成功
public async void Prepare(string _videoName, Action onPreparedOK)
{
if (!ValidateComponents())
{
onPreparedOK?.Invoke(false);
return;
}
if (_isLoading)
{
Debug.LogWarning($"UIVideoPlayer: Already loading video. Please wait.");
onPreparedOK?.Invoke(false);
return;
}
videoName = _videoName;
_onPrepareCallback = onPreparedOK;
_isPrepared = false;
_isLoading = true;
// 清理旧资源
ReleaseRenderTexture();
SetEmptyState();
// 通过 ResManager 加载 VideoClip 资源(支持 AB 打包)
_loadedClip = await ResManager.Instance.LoadAssetAsync(directory, videoName, false);
// 检查是否在加载过程中被取消
if (!_isLoading)
{
_loadedClip = null;
return;
}
_isLoading = false;
if (_loadedClip == null)
{
Debug.LogError($"UIVideoPlayer: Failed to load VideoClip: {directory}/{videoName}");
_isPrepared = false;
_onPrepareCallback?.Invoke(false);
_onPrepareCallback = null;
return;
}
// 创建并绑定 RenderTexture(按视频实际尺寸)
_renderTexture = new RenderTexture((int)_loadedClip.width, (int)_loadedClip.height, 0);
videoPlayer.targetTexture = _renderTexture;
videoImage.texture = _renderTexture;
videoImage.SetNativeSize();
SetActiveState();
videoPlayer.source = VideoSource.VideoClip;
videoPlayer.clip = _loadedClip;
videoPlayer.prepareCompleted += OnVideoPreparedForPreload;
videoPlayer.Prepare();
}
///
/// 通过 URL 加载并播放视频
///
/// 视频 URL 地址
/// 是否循环播放
/// 播放完成回调(仅在非循环播放时调用)
public void LoadAndPlayFromURL(string url, bool _loop = false, Action _onComplete = null)
{
if (!ValidateComponents()) return;
if (_isLoading)
{
Debug.LogWarning($"UIVideoPlayer: Already loading video. Please wait.");
return;
}
videoName = url;
isLoop = _loop;
onComplete = _onComplete;
_isPrepared = false;
_isLoading = true;
// 清理旧资源
ReleaseRenderTexture();
SetEmptyState();
// 根据 URL 视频创建默认尺寸的 RenderTexture(准备完成后会调整)
_renderTexture = new RenderTexture(1920, 1080, 0);
videoPlayer.targetTexture = _renderTexture;
videoImage.texture = _renderTexture;
SetActiveState();
videoPlayer.source = VideoSource.Url;
videoPlayer.url = url;
videoPlayer.isLooping = isLoop;
videoPlayer.prepareCompleted += OnVideoPreparedForURL;
// 非循环播放时注册完成事件
if (!isLoop)
{
videoPlayer.loopPointReached += OnVideoFinished;
}
videoPlayer.Prepare();
}
[ContextMenu("停止")]
public void Stop()
{
if (!ValidateComponents()) return;
videoPlayer.Stop();
}
[ContextMenu("重新播放")]
public void Restart()
{
if (!ValidateComponents()) return;
if (_isPrepared)
{
videoPlayer.Stop();
videoPlayer.Play();
}
}
///
/// 卸载视频资源
///
public void Unload()
{
if (videoPlayer == null) return;
// 取消所有事件订阅
videoPlayer.prepareCompleted -= OnVideoPrepared;
videoPlayer.prepareCompleted -= OnVideoPreparedForPreload;
videoPlayer.prepareCompleted -= OnVideoPreparedForURL;
videoPlayer.loopPointReached -= OnVideoFinished;
videoPlayer.Stop();
videoPlayer.clip = null;
videoPlayer.url = null;
videoPlayer.targetTexture = null;
if (videoImage != null)
{
videoImage.texture = null;
}
_loadedClip = null;
ReleaseRenderTexture();
// 恢复空状态
SetEmptyState();
_isPrepared = false;
_isLoading = false;
onComplete = null;
onPrepared = null;
}
#endregion
#region Properties
///
/// 视频是否已准备好
///
public bool IsPrepared => _isPrepared;
///
/// 视频是否正在播放
///
public bool IsPlaying => videoPlayer != null && videoPlayer.isPlaying;
///
/// 视频是否正在加载
///
public bool IsLoading => _isLoading;
///
/// 当前播放时间(秒)
///
public double CurrentTime => videoPlayer != null ? videoPlayer.time : 0;
///
/// 视频总时长(秒)
///
public double Duration => _loadedClip != null ? _loadedClip.length : 0;
#endregion
#region Private Methods
[ContextMenu("重新加载")]
private void Reload()
{
LoadAndPlayVideo(videoName, isLoop);
}
private void OnVideoPrepared(VideoPlayer vp)
{
vp.prepareCompleted -= OnVideoPrepared;
_isPrepared = true;
_isLoading = false;
onPrepared?.Invoke();
vp.Play();
}
private void OnVideoPreparedForPreload(VideoPlayer vp)
{
vp.prepareCompleted -= OnVideoPreparedForPreload;
_isPrepared = true;
_isLoading = false;
_onPrepareCallback?.Invoke(true);
_onPrepareCallback = null;
// 预加载完成后不自动播放
}
private void OnVideoPreparedForURL(VideoPlayer vp)
{
vp.prepareCompleted -= OnVideoPreparedForURL;
_isPrepared = true;
_isLoading = false;
// 调整 RenderTexture 尺寸为实际视频尺寸
if (_renderTexture != null && vp.texture != null)
{
var newRT = new RenderTexture((int)vp.width, (int)vp.height, 0);
ReleaseRenderTexture();
_renderTexture = newRT;
vp.targetTexture = _renderTexture;
videoImage.texture = _renderTexture;
videoImage.SetNativeSize();
}
onPrepared?.Invoke();
vp.Play();
}
private void OnVideoFinished(VideoPlayer vp)
{
onComplete?.Invoke();
// 根据设置自动清理资源
if (autoUnloadOnFinish)
{
Unload();
}
}
private void ReleaseRenderTexture()
{
if (_renderTexture != null)
{
_renderTexture.Release();
Destroy(_renderTexture);
_renderTexture = null;
}
}
///
/// 设置空状态(未加载视频时)
///
private void SetEmptyState()
{
if (videoImage != null)
{
videoImage.enabled = false;
}
}
///
/// 设置激活状态(视频已加载)
///
private void SetActiveState()
{
if (videoImage != null)
{
videoImage.enabled = true;
}
}
///
/// 验证必需组件
///
private bool ValidateComponents()
{
if (videoPlayer == null)
{
Debug.LogError("UIVideoPlayer: VideoPlayer component is not assigned.");
return false;
}
if (videoImage == null)
{
Debug.LogError("UIVideoPlayer: RawImage component is not assigned.");
return false;
}
return true;
}
#endregion
#region Unity Lifecycle
private void Awake()
{
// 初始化时设置为空状态
SetEmptyState();
}
private void OnDestroy()
{
Unload();
}
///
/// 取消预加载
///
public void CancelPrepare()
{
if (videoPlayer == null) return;
// 取消所有准备相关的事件订阅
videoPlayer.prepareCompleted -= OnVideoPrepared;
videoPlayer.prepareCompleted -= OnVideoPreparedForPreload;
videoPlayer.prepareCompleted -= OnVideoPreparedForURL;
// 停止 VideoPlayer(如果正在加载)
if (_isLoading)
{
videoPlayer.Stop();
}
// 清理资源
videoPlayer.clip = null;
videoPlayer.url = null;
videoPlayer.targetTexture = null;
if (videoImage != null)
{
videoImage.texture = null;
}
_loadedClip = null;
ReleaseRenderTexture();
SetEmptyState();
// 重置状态
_isPrepared = false;
_isLoading = false;
// 触发失败回调
_onPrepareCallback?.Invoke(false);
_onPrepareCallback = null;
}
#endregion
}