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 }