using System; using System.Collections; using System.Collections.Generic; using Spine.Unity; using UnityEngine; using Spine; using UnityEngine.UI; using Cysharp.Threading.Tasks; // 特效播放器,预制体对象池管理,unity的预制体池使用有问题(1个特效1个池但复用又低)后续修改 // unity特效《预制体》是特效师在制作的时候生成的不同预制体,unity特效必须挂载设置播放时长脚本 EffectTime // spine特效是特效师制作的动画文件,可共同复用一个《预制体》 public class EffectPlayer : MonoBehaviour { [SerializeField] private int m_EffectID; public int effectId { get { return m_EffectID; } set { if (value != m_EffectID) { Stop(); isInit = false; m_EffectID = value; } } } public EffectConfig effectConfig; public float speedRate = 1f; [Header("是否循环播放spine特效")] public bool isPlaySpineLoop = false; [Header("是否在显示时播放")] public bool isPlayOnEnable = false; [Header("延迟播放(毫秒)")] public int playDelayTime = 0; public int playSpineAnimIndex = -1; //播放spine特效动画索引,代码控制 [Header("播放完毕立即回收")] public bool isReleaseImmediately = false; //界面特效一般不需要自我销毁,跟随界面或者父对象销毁就行 public Action onDestroy; [HideInInspector] public Canvas canvas = null; [HideInInspector] public GameObject effectTarget = null; protected EffectPenetrationBlocker blocker = null; protected bool isInit = false; public bool isPlaying { get; protected set; } protected List particleList = new List(); protected List animatorList = new List(); protected List rendererList = new List(); protected SkeletonGraphic spineComp; protected Spine.AnimationState spineAnimationState; public GameObjectPoolManager.GameObjectPool pool; public Action onComplete; protected virtual void OnEnable() { if (isPlayOnEnable) { Play(false); } else if (spineComp != null) { if (!isPlaying) { //隐藏,会有静态显示问题 spineComp.enabled = false; } } } protected void InitComponent(bool showLog = true) { if (effectId <= 0) { effectConfig = null; #if UNITY_EDITOR if (showLog) { Debug.LogError("EffectPlayer effectId is not set"); UnityEditor.Selection.activeGameObject = gameObject; UnityEditor.EditorGUIUtility.PingObject(gameObject); } #endif return; } effectConfig = EffectConfig.Get(effectId); if (null == effectConfig) { #if UNITY_EDITOR Debug.LogError("could not find effect config, effect id is " + effectId); UnityEditor.Selection.activeGameObject = gameObject; UnityEditor.EditorGUIUtility.PingObject(gameObject); #endif return; } Clear(); return; } protected virtual void Clear() { isInit = false; particleList.Clear(); animatorList.Clear(); rendererList.Clear(); if (spineComp != null) { spineComp.skeletonDataAsset = null; // spineComp.Initialize(false); spineComp.Clear(); } spineComp = null; } public virtual void Stop() { if (null != effectTarget) { // 如果使用了特效预制体池,则归还到池中 if (pool != null) { pool.Release(effectTarget); } effectTarget = null; } if (spineComp != null) { spineComp.enabled = false; } isPlaying = false; playSpineAnimIndex = -1; } protected void Release() { Stop(); Clear(); } public virtual void Play(bool showLog = true, bool closePMA = false) { isPlaying = true; if (!isInit) { InitComponent(showLog); //effeid 为0也初始化成功,避免重复处理,在变更effectid时会重新初始化 isInit = true; } else { //避免重复创建 if (!this.gameObject.activeSelf) { this.gameObject.SetActive(true); } //防范effeid 为0 if (effectConfig != null && effectConfig.isSpine != 0) { PlaySpineEffect(closePMA); } return; } if (effectConfig == null) return; if (EffectMgr.IsNotShowBySetting(effectId)) { return; } if (null != effectTarget) { if (pool != null) pool.Release(effectTarget); effectTarget = null; } if (!this.gameObject.activeSelf) { this.gameObject.SetActive(true); } // 加载spine特效资源 if (effectConfig.isSpine != 0) { PlaySpineEffect(closePMA); } else { PlayerEffect(); } SoundPlayer.Instance.PlayUIAudio(effectConfig.audio); } // protected virtual void PlaySpineEffect() // { // spineComp = gameObject.GetComponentInChildren(true); // spineComp.raycastTarget = false; // spineComp.Initialize(true); // spineAnimationState = spineComp.AnimationState; // spineAnimationState.Complete -= OnSpineAnimationComplete; // spineAnimationState.Complete += OnSpineAnimationComplete; // // 外层控制具体播放哪个动画 // spineComp.enabled = true; // return; // } protected void PlaySpineEffect(bool closePMA = false) { // 从特效预制体池获取特效 if (spineComp == null) { spineComp = gameObject.AddMissingComponent(); } if (spineComp.skeletonDataAsset == null || spineAnimationState == null) { //LoadAsset 已经有缓存SkeletonDataAsset spineComp.skeletonDataAsset = ResManager.Instance.LoadAsset("UIEffect/" + effectConfig.packageName, effectConfig.fxName); //为true时会有部分特效不显示 如主界面装备特效;改成伽马后不会出现BUG故注释代码 // spineComp.MeshGenerator.settings.pmaVertexColors = !closePMA; spineComp.raycastTarget = false; spineComp.Initialize(true); spineComp.timeScale = speedRate; // 检查动画是否有相加模式 // bool hasAdditiveBlend = CheckForAdditiveBlend(spineComp.Skeleton); // if (hasAdditiveBlend) // { // spineComp.material = ResManager.Instance.LoadAsset("UIEffect/" + effectConfig.packageName, effectConfig.fxName.Split('_')[0] + "_Material-Additive"); // } // else // { // spineComp.material = null; // } spineComp.material = ResManager.Instance.LoadAsset("Materials", "SkeletonGraphicDefault-Straight"); spineAnimationState = spineComp.AnimationState; spineAnimationState.Data.DefaultMix = 0f; spineAnimationState.Complete -= OnSpineAnimationComplete; spineAnimationState.Complete += OnSpineAnimationComplete; } spineComp.enabled = true; PlayerTheSpineAnim(); } // 播放指定动画 void PlayerTheSpineAnim() { spineComp.enabled = true; var skeletonData = spineComp.Skeleton.Data; if (skeletonData.Animations.Count > 0) { //按配置或者默认第一个 int defaultAnimIndex = Math.Max(0, effectConfig.animIndex.Length == 0 ? 0 : effectConfig.animIndex[0]); if (playSpineAnimIndex >= skeletonData.Animations.Count) { playSpineAnimIndex = -1; Debug.LogError("特效:" + effectConfig.id + " 索引超出播放默认动画。 error:" + playSpineAnimIndex); } string defaultAnimationName = skeletonData.Animations.Items[playSpineAnimIndex == -1 ? defaultAnimIndex : playSpineAnimIndex].Name; spineAnimationState.SetAnimation(0, defaultAnimationName, isPlaySpineLoop); } else { Debug.LogError("Spine 数据中没有找到任何动画!" + effectConfig.id); } } private bool CheckForAdditiveBlend(Spine.Skeleton skeleton) { // 遍历所有插槽,检查是否有相加模式 foreach (var slot in skeleton.Slots) { if (slot.Data.BlendMode == Spine.BlendMode.Additive) { return true; } } return false; } //unity特效 protected virtual void PlayerEffect() { var effectPrefab = ResManager.Instance.LoadAsset("UIEffect/" + effectConfig.packageName, effectConfig.fxName); if (effectPrefab == null) { Debug.LogError($"加载UI特效失败: {effectConfig.packageName}"); return; } if (effectConfig.isSpine == 0) { //unity特效 判断预制体是否挂载EffectTime var timeObj = effectPrefab.GetComponent(); if (null == timeObj) { Debug.LogError($"{effectPrefab.name}预制体没有挂载EffectTime"); return; } } // 从特效预制体池获取特效 pool = GameObjectPoolManager.Instance.RequestPool(effectPrefab); effectTarget = pool.Request(); // 设置父节点和位置 effectTarget.transform.SetParent(transform); effectTarget.transform.localPosition = Vector3.zero; effectTarget.transform.localScale = Vector3.one; effectTarget.transform.localRotation = Quaternion.identity; effectTarget.name = $"Effect_{effectConfig.fxName}"; //挂载组件后 开始收集 particleList.AddRange(gameObject.GetComponentsInChildren(true)); animatorList.AddRange(gameObject.GetComponentsInChildren(true)); rendererList.AddRange(gameObject.GetComponentsInChildren(true)); OnUnityAnimationComplete(); if (null == canvas) canvas = GetComponentInParent(); // 添加特效穿透阻挡器 blocker = effectTarget.AddMissingComponent(); // 如果没有canvas的话 正常是因为不在BattleWin下面的节点 意思就是当前没有显示 等到切回战斗的时候再通过BattleField.UpdateCanvas来更新 if (canvas != null) { blocker.SetParentCanvas(canvas); } } public async UniTask PlayAsync(bool showLog = true, bool closePMA = false) { await UniTask.Delay(playDelayTime); try { Play(showLog, closePMA); } catch (Exception e) { Debug.LogError(e); } } protected void OnDestroy() { if (onDestroy != null) { onDestroy.Invoke(this); onDestroy = null; } // 停止特效并归还到池中 Release(); if (spineAnimationState != null) { spineAnimationState.Complete -= OnSpineAnimationComplete; } } //单次播放完毕就会触发,即使是循环 protected virtual void OnSpineAnimationComplete(Spine.TrackEntry trackEntry) { if (!isPlaySpineLoop) { spineComp.enabled = false; isPlaying = false; onComplete?.Invoke(); // TODO 会报错暂时注释 // if (isReleaseImmediately) // { // Release(); // } } } private void OnUnityAnimationComplete() { var timeObj = effectTarget.GetComponent(); if (!timeObj.isLoop) { this.SetWait(timeObj.playTime); this.DoWaitRestart(); this.OnWaitCompelete(OnEffectComplete); } } private void OnEffectComplete(Component comp) { this.DoWaitStop(); if (isReleaseImmediately) Release(); } // 创建后的特效会自动隐藏 需要手动调用Play才能播放 public static EffectPlayer Create(int effectId, Transform parent, bool createNewChild = false) { // 直接创建特效播放器,不使用对象池 EffectPlayer effectPlayer = null; if (createNewChild) { GameObject newGo = new GameObject("EffectPlayer_" + effectId); newGo.transform.SetParent(parent, false); effectPlayer = newGo.AddComponent(); } else { effectPlayer = parent.AddMissingComponent(); } effectPlayer.effectId = effectId; effectPlayer.SetActive(true); return effectPlayer; } /// /// 设置游戏对象激活状态 // /// // public void SetActive(bool active) // { // if (gameObject != null) // { // gameObject.SetActive(active); // } // } public void Pause() { if (effectTarget == null) return; // Spine动画 // var spineGraphics = effectTarget.GetComponentsInChildren(true); // foreach (var sg in spineGraphics) if (spineComp != null) { spineComp.timeScale = 0f; } // Animator动画 foreach (var animator in animatorList) { animator.speed = 0f; } // 粒子特效 foreach (var ps in particleList) { ps.Pause(); } } public void Resume() { if (effectTarget == null) return; if (spineComp != null) { spineComp.timeScale = speedRate; } // Animator动画 foreach (var animator in animatorList) { animator.speed = speedRate; } // 粒子特效 foreach (var ps in particleList) { ps.Play(); } } public bool IsFinish() { if (effectTarget == null) return true; // Spine动画 if (!spineComp.AnimationState.GetCurrent(0).IsComplete) { return false; } // Animator动画 foreach (var animator in animatorList) { AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0); // 循环动画不考虑结束的问题 if (!stateInfo.loop && stateInfo.normalizedTime < 1f) { return false; } } // 粒子特效 foreach (var ps in particleList) { if (ps.IsAlive()) { return false; } } return true; } /// /// 设置遮罩(支持RectMask2D、Mask、SmoothMask等) /// public void SetMask(RectTransform maskArea = null) { if (effectTarget == null || blocker == null) return; // 优先使用传入的maskArea if (maskArea != null) { blocker.PerformMask(maskArea); return; } } }