From 8d77144cfb756529507dc6927dcdcfd9f4a30750 Mon Sep 17 00:00:00 2001
From: yyl <yyl>
Date: 星期三, 25 二月 2026 14:48:37 +0800
Subject: [PATCH] 视频播放支持
---
Main/System/Video.meta | 8
Main/System/Video/VideoWin.cs | 20 +
Main/Utility/Extension.cs | 59 ++++
Main/System/Video/VideoWin.cs.meta | 11
Main/System/Hero/UIHeroController.cs | 20
Main/System/Video/VideoManager.cs.meta | 11
Main/System/Video/VideoManager.cs | 117 +++++++++
Main/ResModule/ResManager.cs | 18 +
Main/System/Video/UIVideoPlayer.cs | 505 ++++++++++++++++++++++++++++++++++++++
Main/System/Video/UIVideoPlayer.cs.meta | 11
10 files changed, 772 insertions(+), 8 deletions(-)
diff --git a/Main/ResModule/ResManager.cs b/Main/ResModule/ResManager.cs
index 48f61f6..b4c7112 100644
--- a/Main/ResModule/ResManager.cs
+++ b/Main/ResModule/ResManager.cs
@@ -6,6 +6,8 @@
using UnityEngine.Video;
using Spine.Unity;
using UnityEngine.UI;
+using Cysharp.Threading.Tasks;
+
@@ -251,6 +253,22 @@
LoadAssetAsyncInternal<T>(directory, name, callBack, needExt);
}
+ public async UniTask<T> LoadAssetAsync<T>(string directory, string name, bool needExt = true) where T : UnityEngine.Object
+ {
+ var tcs = new UniTaskCompletionSource<T>();
+ LoadAssetAsync<T>(directory, name, (isLoaded, asset) => {
+ if (isLoaded)
+ {
+ tcs.TrySetResult(asset as T);
+ }
+ else
+ {
+ tcs.TrySetException(new Exception($"Failed to load asset: {directory}/{name}"));
+ }
+ }, needExt);
+ return await tcs.Task;
+ }
+
private void LoadSpriteAsync<T>(string atlasName, string spriteName, Action<bool, UnityEngine.Object> callBack) where T : UnityEngine.Object
{
#if !UNITY_EDITOR
diff --git a/Main/System/Hero/UIHeroController.cs b/Main/System/Hero/UIHeroController.cs
index dd7ae75..7ae549e 100644
--- a/Main/System/Hero/UIHeroController.cs
+++ b/Main/System/Hero/UIHeroController.cs
@@ -1,5 +1,6 @@
using System;
+using Spine;
using Spine.Unity;
using UnityEngine;
using UnityEngine.UI;
@@ -10,7 +11,7 @@
private int skinID;
protected SkeletonGraphic skeletonGraphic;
- protected Spine.AnimationState spineAnimationState;
+ public Spine.AnimationState spineAnimationState;
private GameObject instanceGO;
public Action onComplete;
@@ -26,7 +27,7 @@
if (isLh)
{
var skinConfigTmp = HeroSkinConfig.Get(skinID);
- if (skinConfigTmp != null && skinConfigTmp.Tachie.Contains("SkeletonData"))
+ if (skinConfigTmp != null && skinConfigTmp.Tachie.IsSpine())
{
skeletonGraphic.enabled = true;
}
@@ -60,7 +61,7 @@
//绔嬬粯鐗规畩澶勭悊锛屾病鏈塻pine鍔ㄧ敾鐨勬敼鐢ㄥ浘鐗�
var lhImg = this.AddMissingComponent<RawImage>();
- if (!skinConfig.Tachie.Contains("SkeletonData"))
+ if (!skinConfig.Tachie.IsSpine())
{
//鍥剧墖鏇挎崲
lhImg.SetTexture2DPNG(skinConfig.Tachie);
@@ -150,7 +151,10 @@
spineAnimationState.Complete += OnAnimationComplete;
}
-
+ public bool HasAnimation(string motionName)
+ {
+ return skeletonGraphic != null && skeletonGraphic.Skeleton != null && skeletonGraphic.Skeleton.ContainsMotion(motionName);
+ }
protected void OnDestroy()
@@ -171,15 +175,15 @@
/// <param name="motionName">鍔ㄤ綔鍚�</param>
/// <param name="loop">寰幆</param>
/// <param name="replay">濡傛灉鐩稿悓鍔ㄤ綔鏄惁鍐嶆閲嶆挱锛屾瘮濡傝窇姝ラ噸鎾氨浼氳烦甯т笉椤烘粦</param>
- public virtual void PlayAnimation(string motionName, bool loop = false, bool replay = true)
+ public virtual TrackEntry PlayAnimation(string motionName, bool loop = false, bool replay = true)
{
- if (spineAnimationState == null) return;
+ if (spineAnimationState == null) return null;
if (GetCurrentAnimationName() == motionName && !replay)
- return;
+ return null;
// 鐩存帴浣跨敤 ToString() 鑰屼笉鏄皟鐢� GetAnimationName
- spineAnimationState.SetAnimation(0, motionName.ToString(), loop);
+ return spineAnimationState.SetAnimation(0, motionName.ToString(), loop);
}
// 鎾斁绗竴涓姩鐢伙紙浣滀负榛樿鍔ㄧ敾锛�
diff --git a/Main/System/Video.meta b/Main/System/Video.meta
new file mode 100644
index 0000000..bf20a4e
--- /dev/null
+++ b/Main/System/Video.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 9f8b60fd5cb8de143a61b1a3ea0fc728
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/System/Video/UIVideoPlayer.cs b/Main/System/Video/UIVideoPlayer.cs
new file mode 100644
index 0000000..1b005b2
--- /dev/null
+++ b/Main/System/Video/UIVideoPlayer.cs
@@ -0,0 +1,505 @@
+using System;
+using UnityEngine;
+using UnityEngine.Video;
+using UnityEngine.UI;
+
+/// <summary>
+/// UI瑙嗛鎾斁鍣ㄧ粍浠� - 鏀寔WebM鏍煎紡閫忔槑瑙嗛鎾斁
+/// </summary>
+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<bool> _onPrepareCallback;
+
+ #region Public API
+
+ /// <summary>
+ /// 鍔犺浇骞舵挱鏀捐棰戯紙閫氳繃璧勬簮绯荤粺鍔犺浇 VideoClip锛�
+ /// </summary>
+ /// <param name="_videoName">瑙嗛鏂囦欢鍚嶏紙涓嶅惈鎵╁睍鍚嶏級</param>
+ /// <param name="_loop">鏄惁寰幆鎾斁</param>
+ /// <param name="_onComplete">鎾斁瀹屾垚鍥炶皟锛堜粎鍦ㄩ潪寰幆鎾斁鏃惰皟鐢級</param>
+ 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<VideoClip>(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();
+ }
+
+ /// <summary>
+ /// 鍔犺浇骞舵挱鏀炬寚瀹氳棰�
+ /// </summary>
+ 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();
+ }
+ }
+
+ /// <summary>
+ /// 棰勫姞杞借棰戯紙涓嶈嚜鍔ㄦ挱鏀撅級
+ /// </summary>
+ /// <param name="_videoName">瑙嗛鏂囦欢鍚嶏紙涓嶅惈鎵╁睍鍚嶏級</param>
+ /// <param name="onPreparedOK">棰勫姞杞藉畬鎴愬洖璋冿紝鍙傛暟涓烘槸鍚︽垚鍔�</param>
+ public async void Prepare(string _videoName, Action<bool> 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<VideoClip>(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();
+ }
+
+ /// <summary>
+ /// 閫氳繃 URL 鍔犺浇骞舵挱鏀捐棰�
+ /// </summary>
+ /// <param name="url">瑙嗛 URL 鍦板潃</param>
+ /// <param name="_loop">鏄惁寰幆鎾斁</param>
+ /// <param name="_onComplete">鎾斁瀹屾垚鍥炶皟锛堜粎鍦ㄩ潪寰幆鎾斁鏃惰皟鐢級</param>
+ 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();
+ }
+ }
+
+ /// <summary>
+ /// 鍗歌浇瑙嗛璧勬簮
+ /// </summary>
+ 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
+
+ /// <summary>
+ /// 瑙嗛鏄惁宸插噯澶囧ソ
+ /// </summary>
+ public bool IsPrepared => _isPrepared;
+
+ /// <summary>
+ /// 瑙嗛鏄惁姝e湪鎾斁
+ /// </summary>
+ public bool IsPlaying => videoPlayer != null && videoPlayer.isPlaying;
+
+ /// <summary>
+ /// 瑙嗛鏄惁姝e湪鍔犺浇
+ /// </summary>
+ public bool IsLoading => _isLoading;
+
+ /// <summary>
+ /// 褰撳墠鎾斁鏃堕棿锛堢锛�
+ /// </summary>
+ public double CurrentTime => videoPlayer != null ? videoPlayer.time : 0;
+
+ /// <summary>
+ /// 瑙嗛鎬绘椂闀匡紙绉掞級
+ /// </summary>
+ 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;
+ }
+ }
+
+ /// <summary>
+ /// 璁剧疆绌虹姸鎬侊紙鏈姞杞借棰戞椂锛�
+ /// </summary>
+ private void SetEmptyState()
+ {
+ if (videoImage != null)
+ {
+ videoImage.enabled = false;
+ }
+ }
+
+ /// <summary>
+ /// 璁剧疆婵�娲荤姸鎬侊紙瑙嗛宸插姞杞斤級
+ /// </summary>
+ private void SetActiveState()
+ {
+ if (videoImage != null)
+ {
+ videoImage.enabled = true;
+ }
+ }
+
+ /// <summary>
+ /// 楠岃瘉蹇呴渶缁勪欢
+ /// </summary>
+ 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();
+ }
+
+ /// <summary>
+ /// 鍙栨秷棰勫姞杞�
+ /// </summary>
+ 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
+}
diff --git a/Main/System/Video/UIVideoPlayer.cs.meta b/Main/System/Video/UIVideoPlayer.cs.meta
new file mode 100644
index 0000000..a6bce43
--- /dev/null
+++ b/Main/System/Video/UIVideoPlayer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 780579551ee7fe442bc047278c4b96d6
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/System/Video/VideoManager.cs b/Main/System/Video/VideoManager.cs
new file mode 100644
index 0000000..18f7ac3
--- /dev/null
+++ b/Main/System/Video/VideoManager.cs
@@ -0,0 +1,117 @@
+
+
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+public class VideoManager : Singleton<VideoManager>
+{
+ private GameObjectPoolManager.GameObjectPool uiVideoPool;
+
+ private Dictionary<string, UIVideoPlayer> prepareDict = new Dictionary<string, UIVideoPlayer>();
+
+ private Dictionary<string, Action<bool, UIVideoPlayer>> prepareCallbacks = new Dictionary<string, Action<bool, UIVideoPlayer>>();
+
+ public void Init()
+ {
+ GameObject uiVideoPrefab = UILoader.LoadPrefab("UIVideoPlayer");
+ uiVideoPool = GameObjectPoolManager.Instance.GetPool(uiVideoPrefab);
+ }
+
+ public void Release()
+ {
+ if (uiVideoPool != null)
+ {
+ uiVideoPool.Clear();
+ uiVideoPool = null;
+ }
+ }
+
+ private UIVideoPlayer GetUIVideoPlayer()
+ {
+ GameObject videoObj = uiVideoPool.Request();
+ if (videoObj != null)
+ {
+ return videoObj.GetComponent<UIVideoPlayer>();
+ }
+ return null;
+ }
+
+ public UIVideoPlayer PrepareVideo(string videoName, Action<bool, UIVideoPlayer> onPrepared)
+ {
+ // 濡傛灉宸茬粡鍦ㄥ噯澶囦腑锛岀洿鎺ヨ繑鍥� 骞剁粰鍑烘彁绀� 宸茬粡鍦ㄥ姞杞藉噯澶囦腑
+ if (prepareDict.TryGetValue(videoName, out UIVideoPlayer existingPlayer))
+ {
+ Debug.LogError("Video " + videoName + " is already being prepared.");
+ return existingPlayer;
+ }
+
+
+ UIVideoPlayer uiVideoPlayer = GetUIVideoPlayer();
+ if (uiVideoPlayer != null)
+ {
+ string _videoName = videoName; // 閬垮厤闂寘闂
+ prepareDict.Add(_videoName, uiVideoPlayer);
+ prepareCallbacks.Add(_videoName, onPrepared);
+ uiVideoPlayer.Prepare(videoName, (success) =>
+ {
+ OnPrepareOK(videoName, success, uiVideoPlayer);
+ });
+ }
+ else
+ {
+ Debug.LogError("Failed to get UIVideoPlayer from pool.");
+ onPrepared?.Invoke(false, null);
+ }
+ return uiVideoPlayer;
+ }
+
+
+ public void CancelPrepare(UIVideoPlayer uIVideoPlayer)
+ {
+ if (uIVideoPlayer != null)
+ uIVideoPlayer.CancelPrepare();
+ {
+ uiVideoPool.Release(uIVideoPlayer.gameObject);
+ // 浠� prepareDict 绉婚櫎
+ string keyToRemove = null;
+ foreach (var kv in prepareDict)
+ {
+ if (kv.Value == uIVideoPlayer)
+ {
+ keyToRemove = kv.Key;
+ break;
+ }
+ }
+ if (keyToRemove != null)
+ {
+ prepareDict.Remove(keyToRemove);
+ prepareCallbacks.Remove(keyToRemove);
+ }
+ }
+ }
+
+ private void OnPrepareOK(string videoName, bool success, UIVideoPlayer uiVideoPlayer)
+ {
+ // 瀹夊叏绉婚櫎鍜屽洖璋�
+ if (prepareDict.ContainsKey(videoName))
+ {
+ prepareDict.Remove(videoName);
+ }
+ Action<bool, UIVideoPlayer> callback = null;
+ if (prepareCallbacks.TryGetValue(videoName, out callback))
+ {
+ callback?.Invoke(success, uiVideoPlayer);
+ prepareCallbacks.Remove(videoName);
+ }
+ }
+
+ public void ReleaseVideoPlayer(UIVideoPlayer uIVideoPlayer)
+ {
+ if (uIVideoPlayer != null)
+ {
+ CancelPrepare(uIVideoPlayer);
+ uiVideoPool.Release(uIVideoPlayer.gameObject);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Main/System/Video/VideoManager.cs.meta b/Main/System/Video/VideoManager.cs.meta
new file mode 100644
index 0000000..bee492c
--- /dev/null
+++ b/Main/System/Video/VideoManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 14d20be0871f2e24d96b79440816d7cb
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/System/Video/VideoWin.cs b/Main/System/Video/VideoWin.cs
new file mode 100644
index 0000000..290345a
--- /dev/null
+++ b/Main/System/Video/VideoWin.cs
@@ -0,0 +1,20 @@
+using System;
+using UnityEngine;
+using UnityEngine.Video;
+using UnityEngine.UI;
+
+public class VideoWin : UIBase
+{
+ public UIVideoPlayer videoPlayer;
+ protected override void OnOpen()
+ {
+ // 瑙嗛鍑嗗瀹屾垚鍚庝細鑷姩鎾斁
+ }
+
+ protected override void OnPreClose()
+ {
+ base.OnPreClose();
+ videoPlayer.Unload();
+ }
+
+}
diff --git a/Main/System/Video/VideoWin.cs.meta b/Main/System/Video/VideoWin.cs.meta
new file mode 100644
index 0000000..3494092
--- /dev/null
+++ b/Main/System/Video/VideoWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: cf2a5c5ccb4f22f4c9408bd3b3efed69
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/Utility/Extension.cs b/Main/Utility/Extension.cs
index b5d01c7..9725ea7 100644
--- a/Main/Utility/Extension.cs
+++ b/Main/Utility/Extension.cs
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
+using Spine;
+using Spine.Unity;
public static class Extension
{
@@ -74,4 +76,61 @@
}
}
}
+
+ public static bool IsSpine(this string _resName)
+ {
+ if (string.IsNullOrEmpty(_resName))
+ {
+ return false;
+ }
+
+ return _resName.Contains("SkeletonData");
+ }
+
+ public static bool IsVideo(this string _resName)
+ {
+ if (string.IsNullOrEmpty(_resName))
+ {
+ return false;
+ }
+
+ return _resName.EndsWith(".mp4");
+ }
+
+ public static bool ContainsMotion(this Spine.Skeleton skeleton, string motionName)
+ {
+ if (skeleton == null || string.IsNullOrEmpty(motionName))
+ {
+ return false;
+ }
+
+ for (int i = 0; i < skeleton.Data.Animations.Count; i++)
+ {
+ if (skeleton.Data.Animations.Items[i].Name.ToLower() == motionName.ToLower())
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static Spine.Animation GetSpineAnimation(this Spine.Skeleton skeleton, string motionName)
+ {
+ if (skeleton == null || string.IsNullOrEmpty(motionName))
+ {
+ return null;
+ }
+
+ for (int i = 0; i < skeleton.Data.Animations.Count; i++)
+ {
+ if (skeleton.Data.Animations.Items[i].Name.ToLower() == motionName.ToLower())
+ {
+ return skeleton.Data.Animations.Items[i];
+ }
+ }
+
+ return null;
+ }
+
}
\ No newline at end of file
--
Gitblit v1.8.0