| using System; | 
| using System.Collections; | 
| using System.Collections.Generic; | 
| using Spine.Unity; | 
| using UnityEngine; | 
| using Spine; | 
| using UnityEngine.UI; | 
| using System.Linq; | 
|   | 
| // 特效播放器,对象池管理:unity特效和spine特效都是做好的预制体 | 
| // unity特效预制体是特效师在制作的时候生成的,unity特效必须挂载设置播放时长脚本 EffectTime | 
| // spine特效是特效师制作的动画文件可直接加载用,制作成预制体可增加BoneFollower之类的进行逻辑处理 | 
| public class BattleEffectPlayer : MonoBehaviour | 
| { | 
|     [SerializeField] | 
|     private int m_EffectID; | 
|     public int effectId | 
|     { | 
|         get | 
|         { | 
|             return m_EffectID; | 
|         } | 
|         set | 
|         { | 
|             if (value != m_EffectID) | 
|             { | 
|                 isInit = false; | 
|                 m_EffectID = value; | 
|             } | 
|         } | 
|     } | 
|   | 
|     private Bone followedBone; | 
|   | 
|     public bool isBindBone = false; | 
|   | 
|     public bool isRedCamp = true; | 
|     public EffectConfig effectConfig; | 
|   | 
|     public float speedRate = 1.5f; | 
|   | 
|     private float m_Alpha = 1f; | 
|   | 
|     public float Alpha | 
|     { | 
|         get | 
|         { | 
|             return m_Alpha; | 
|         } | 
|         set | 
|         { | 
|             if (value == m_Alpha) | 
|                 return; | 
|   | 
|             m_Alpha = value; | 
|             OnAlphaChanged(); | 
|         } | 
|     } | 
|   | 
|     private void OnAlphaChanged() | 
|     { | 
|         if (spineComp != null) | 
|         { | 
|             var skeleton = spineComp.Skeleton; | 
|             skeleton.A = Alpha; | 
|             spineComp.LateUpdate(); | 
|         } | 
|   | 
|         for (int i = 0; i < rendererList.Count; i++) | 
|         { | 
|             var renderer = rendererList[i]; | 
|             if (renderer != null && renderer.material != null && renderer.material.HasProperty("_Color")) | 
|             { | 
|                 Color color = renderer.material.color; | 
|                 color.a = Alpha; | 
|                 renderer.material.color = color; | 
|             } | 
|         } | 
|     } | 
|   | 
|     [Header("播放完毕立即回收")] | 
|     public bool isReleaseImmediately = false;  //界面特效一般不需要自我销毁,跟随界面或者父对象销毁就行 | 
|   | 
|     public Action<BattleEffectPlayer> onDestroy; | 
|   | 
|     [HideInInspector] public GameObject effectTarget = null; | 
|   | 
|     protected RendererAdjuster blocker = null; | 
|   | 
|     protected bool isInit = false; | 
|   | 
|     protected List<ParticleSystem> particleList = new List<ParticleSystem>(); | 
|   | 
|     protected List<Animator> animatorList = new List<Animator>(); | 
|   | 
|     protected List<Renderer> rendererList = new List<Renderer>(); | 
|   | 
|     protected GameObject spineContainer; | 
|   | 
|     protected SkeletonAnimation spineComp; | 
|     protected Spine.AnimationState spineAnimationState; | 
|   | 
|     public GameObjectPoolManager.GameObjectPool pool; | 
|   | 
|     public Action onComplete; | 
|   | 
|     private bool isPlaying = false; | 
|   | 
|     private float timer = 0f; | 
|   | 
|     protected virtual void OnEnable() | 
|     { | 
|         // if (spineComp != null) | 
|         // { | 
|         //     //隐藏,会有静态显示问题 | 
|         //     spineComp.enabled = false; | 
|         // } | 
|   | 
|         ApplySortingOrder(); | 
|     } | 
|   | 
|   | 
|     protected void InitComponent(bool showLog = true) | 
|     { | 
|         Clear(); | 
|         if (effectId <= 0) | 
|         { | 
|             effectConfig = null; | 
| #if UNITY_EDITOR | 
|             if (showLog) | 
|             { | 
|                 Debug.LogError("BattleEffectPlayer 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; | 
|         } | 
|   | 
|         //  初始化spine组件 | 
|         if (effectConfig != null && effectConfig.isSpine != 0) | 
|         { | 
|             GameObject spineContainerPrefab = ResManager.Instance.LoadAsset<GameObject>("UIComp", "SpineAnimContainer"); | 
|             spineContainer = GameObject.Instantiate(spineContainerPrefab, transform, true); | 
|             spineContainer.transform.localPosition = Vector3.zero; | 
|         } | 
|         //  有特效可能带spine又带unity特效的情况 | 
|         spineComp = gameObject.GetComponentInChildren<SkeletonAnimation>(true); | 
|   | 
|         if (effectConfig.effectPos != null && effectConfig.effectPos.Length >= 2) | 
|         { | 
|             rectTrans.anchoredPosition += new Vector2((isRedCamp ? 1f : -1f) * effectConfig.effectPos[0], effectConfig.effectPos[1]); | 
|         } | 
|   | 
|         if (effectConfig.effectScale > 0f) | 
|         { | 
|             rectTrans.localScale *= effectConfig.effectScale; | 
|         } | 
|   | 
|         spineComp.loop = effectConfig.isLoop != 0; | 
|     } | 
|   | 
|     protected virtual void Clear() | 
|     { | 
|         particleList.Clear(); | 
|         animatorList.Clear(); | 
|         rendererList.Clear(); | 
|         spineComp = null; | 
|         spineAnimationState = null; | 
|         if (null != spineContainer) | 
|             GameObject.DestroyImmediate(spineContainer); | 
|         spineContainer = null; | 
|         pool = null; | 
|     } | 
|   | 
|     public virtual void Stop() | 
|     { | 
|         if (null != effectTarget) | 
|         { | 
|             // 如果使用了特效预制体池,则归还到池中 | 
|             if (pool != null) | 
|             { | 
|                 pool.Release(effectTarget); | 
|             } | 
|             effectTarget = null; | 
|         } | 
|   | 
|         isInit = false; | 
|   | 
|         Clear(); | 
|         onComplete?.Invoke(); | 
|     } | 
|   | 
|     public Func<bool> funcIsHeroFront; | 
|   | 
|     public void SetSortingOrder(Func<bool> _isHeroFrontCallback) | 
|     { | 
|         funcIsHeroFront = _isHeroFrontCallback; | 
|   | 
|         ApplySortingOrder(); | 
|     } | 
|   | 
|   | 
|     public void ApplySortingOrder() | 
|     { | 
|         if (null != blocker && effectConfig != null && funcIsHeroFront != null) | 
|         { | 
|             bool isEffectFront = effectConfig.frontBack == 1; | 
|   | 
|             bool isHeroFront = funcIsHeroFront(); | 
|   | 
|             int finalSortingOrder = isHeroFront ? | 
|                 (isEffectFront ? BattleConst.ActiveHeroActionSortingOrder : BattleConst.ActiveHeroBackSortingOrder) : (isEffectFront ? BattleConst.UnactiveHeroFrontSortingOrder : BattleConst.UnactiveHeroBackSortingOrder); | 
|   | 
|   | 
|             blocker.SetSortingOrder(finalSortingOrder); | 
|         } | 
|     } | 
|   | 
|     public void FollowBoneXY() | 
|     { | 
|         if (followedBone == null || !isBindBone) | 
|         { | 
|             return; | 
|         } | 
|   | 
|         Vector2 vector2 = Vector2.zero; | 
|   | 
|         if (effectConfig.effectPos != null && effectConfig.effectPos.Length >= 2) | 
|         { | 
|             vector2 = new Vector2((isRedCamp ? 1f : -1f) * effectConfig.effectPos[0], effectConfig.effectPos[1]); | 
|         } | 
|   | 
|         if (spineComp != null) | 
|         { | 
|             spineComp.transform.localPosition = new Vector3(followedBone.WorldX + vector2.x, followedBone.WorldY + vector2.y, 0); | 
|         } | 
|   | 
|         if (effectTarget != null) | 
|         { | 
|             effectTarget.transform.localPosition = new Vector3(followedBone.WorldX + vector2.x, followedBone.WorldY + vector2.y, 0); | 
|         } | 
|     } | 
|   | 
|     public virtual void Play(bool showLog = true) | 
|     { | 
|         if (!isInit) | 
|         { | 
|             InitComponent(showLog); | 
|             isInit = true; | 
|         } | 
|         else | 
|         { | 
|             //避免重复创建 | 
|             if (!this.gameObject.activeSelf) | 
|             { | 
|                 this.gameObject.SetActive(true); | 
|             } | 
|             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); | 
|         } | 
|   | 
|         isPlaying = true; | 
|         timer = 0f; | 
|   | 
|         PlayEffect(); | 
|     } | 
|   | 
|   | 
|   | 
|     public void Run() | 
|     { | 
|         if (null == effectConfig) | 
|         { | 
|             return; | 
|         } | 
|   | 
|         if (!isPlaying) | 
|         { | 
|             return; | 
|         } | 
|   | 
|         if (effectConfig.autoDestroy != 0) | 
|         { | 
|             timer += Time.deltaTime * speedRate; | 
|             if (timer >= effectConfig.destroyDelay) | 
|             { | 
|                 GameObject.DestroyImmediate(gameObject); | 
|             } | 
|         } | 
|     } | 
|   | 
|     protected virtual void PlayEffect() | 
|     { | 
|         if (null == effectConfig) | 
|         { | 
|             Debug.LogError("BattleEffectPlayer effectConfig is null, effect id is " + effectId); | 
|             return; | 
|         } | 
|   | 
|         //  如果delay小于等于0 那会立刻执行 | 
|         this.DelayTime(effectConfig.delayPlay / speedRate, () => | 
|         { | 
|             PlayEffectInternal(); | 
|         }); | 
|     } | 
|   | 
|     protected void PlayEffectInternal() | 
|     { | 
|         if (effectConfig.isSpine != 0) | 
|         { | 
|             PlaySpineEffect(); | 
|         } | 
|         else | 
|         { | 
|             PlayUnityEffect(); | 
|         } | 
|         OnAlphaChanged(); | 
|     } | 
|   | 
|     protected void PlaySpineEffect() | 
|     { | 
|         //  这里是纯spine的逻辑 | 
|   | 
|         if (spineComp == null) | 
|         { | 
|             Debug.LogError("BattleEffectPlayer spineComp is null, effect id is " + effectId); | 
|             return; | 
|         } | 
|   | 
|         SkeletonDataAsset skeletonDataAsset = ResManager.Instance.LoadAsset<SkeletonDataAsset>("UIEffect/" + effectConfig.packageName, effectConfig.fxName); | 
|         spineComp.skeletonDataAsset = skeletonDataAsset; | 
|         spineComp.Initialize(true); | 
|         spineComp.timeScale = speedRate; | 
|   | 
|         spineComp.skeleton.A = Alpha; | 
|   | 
|         spineAnimationState = spineComp.state; | 
|         spineAnimationState.Complete -= OnSpineAnimationComplete; | 
|         spineAnimationState.Complete += OnSpineAnimationComplete; | 
|   | 
|         // 添加特效穿透阻挡器 | 
|         blocker = spineComp.AddMissingComponent<RendererAdjuster>(); | 
|   | 
|         blocker.onSortingChanged = OnSortingChanged; | 
|   | 
|         ApplySortingOrder(); | 
|   | 
|         spineComp.enabled = true; | 
|   | 
|         spineComp.timeScale = speedRate; | 
|   | 
|         Spine.Animation animation = spineAnimationState.Data.SkeletonData.Animations.First(); | 
|         spineAnimationState.SetAnimation(0, animation, effectConfig.isLoop != 0); | 
|   | 
|         SoundPlayer.Instance.PlayUIAudio(effectConfig.audio); | 
|   | 
|     } | 
|   | 
|     private bool CheckForAdditiveBlend(Spine.Skeleton skeleton) | 
|     { | 
|         // 遍历所有插槽,检查是否有相加模式 | 
|         foreach (var slot in skeleton.Slots) | 
|         { | 
|             if (slot.Data.BlendMode == Spine.BlendMode.Additive) | 
|             { | 
|                 return true; | 
|             } | 
|         } | 
|         return false; | 
|     } | 
|   | 
|     protected void PlayUnityEffect() | 
|     { | 
|         //  这里是纯unity特效的逻辑 | 
|   | 
|         var effectPrefab = ResManager.Instance.LoadAsset<GameObject>("UIEffect/" + effectConfig.packageName, effectConfig.fxName); | 
|         if (effectPrefab == null) | 
|         { | 
|             Debug.LogError($"加载UI特效失败: {effectConfig.packageName}"); | 
|             return; | 
|         } | 
|   | 
|         if (spineComp != null) | 
|         { | 
|             spineComp.enabled = true; | 
|         } | 
|   | 
|         // 从特效预制体池获取特效 | 
|         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<ParticleSystem>(true)); | 
|         animatorList.AddRange(gameObject.GetComponentsInChildren<Animator>(true)); | 
|         rendererList.AddRange(gameObject.GetComponentsInChildren<Renderer>(true)); | 
|   | 
|         OnUnityAnimationComplete(); | 
|   | 
|   | 
|         // 添加特效穿透阻挡器 | 
|         blocker = effectTarget.AddMissingComponent<RendererAdjuster>(); | 
|   | 
|         blocker.onSortingChanged = OnSortingChanged; | 
|   | 
|         SoundPlayer.Instance.PlayUIAudio(effectConfig.audio); | 
|   | 
|     } | 
|   | 
|     void LateUpdate() | 
|     { | 
|         if (string.IsNullOrEmpty(sortingLayer)) | 
|         { | 
|             return; | 
|         } | 
|   | 
|         OnSortingChanged(sortingLayer, sortingOrder); | 
|     } | 
|   | 
|     public string sortingLayer; | 
|     public int sortingOrder; | 
|     public RectTransform rectTrans; | 
|   | 
|     protected void OnSortingChanged(string _sortingLayer, int _sortingOrder) | 
|     { | 
|         if (null == spineComp) | 
|             return; | 
|              | 
|         sortingLayer = _sortingLayer; | 
|         sortingOrder = _sortingOrder; | 
|         // 处理排序变化 | 
|         var renderers = spineComp.GetComponents<Renderer>(); | 
|         foreach (var renderer in renderers) | 
|         { | 
|             renderer.sortingLayerName = sortingLayer; | 
|             renderer.sortingOrder = sortingOrder; | 
|         } | 
|     } | 
|   | 
|     protected void OnDestroy() | 
|     { | 
|         if (onDestroy != null) | 
|         { | 
|             onDestroy.Invoke(this); | 
|             onDestroy = null; | 
|         } | 
|         // 停止特效并归还到池中  | 
|         Stop(); | 
|   | 
|         if (spineAnimationState != null) | 
|         { | 
|             spineAnimationState.Complete -= OnSpineAnimationComplete; | 
|         } | 
|     } | 
|   | 
|     //单次播放完毕就会触发,即使是循环 | 
|     protected virtual void OnSpineAnimationComplete(Spine.TrackEntry trackEntry) | 
|     { | 
|         if (isReleaseImmediately) | 
|         { | 
|             spineComp.enabled = false; | 
|             Stop(); | 
|         } | 
|     } | 
|   | 
|   | 
|     private void OnUnityAnimationComplete() | 
|     { | 
|         var timeObj = effectTarget.GetComponent<EffectTime>(); | 
|         if (!timeObj.isLoop) | 
|         { | 
|             this.SetWait(timeObj.playTime); | 
|             this.DoWaitRestart(); | 
|             this.OnWaitCompelete(OnEffectComplete); | 
|         } | 
|     } | 
|   | 
|     private void OnEffectComplete(Component comp) | 
|     { | 
|         this.DoWaitStop(); | 
|         if (isReleaseImmediately) | 
|             Stop(); | 
|     } | 
|   | 
|   | 
|     //  创建后的特效会自动隐藏 需要手动调用Play才能播放 | 
|     public static BattleEffectPlayer Create(int effectId, Transform parent, bool isRedCamp) | 
|     { | 
|         // 直接创建特效播放器,不使用对象池 | 
|         BattleEffectPlayer battleEffectPlayer = null; | 
|   | 
|         GameObject newGo = new GameObject("BattleEffectPlayer_" + effectId); | 
|         newGo.transform.SetParent(parent, false); | 
|         battleEffectPlayer = newGo.AddComponent<BattleEffectPlayer>(); | 
|         battleEffectPlayer.rectTrans = newGo.AddMissingComponent<RectTransform>(); | 
|          | 
|         battleEffectPlayer.effectId = effectId; | 
|   | 
|         // 设置阵营 | 
|         battleEffectPlayer.isRedCamp = isRedCamp; | 
|   | 
|         battleEffectPlayer.SetActive(true); | 
|         return battleEffectPlayer; | 
|     } | 
|   | 
|     /// <summary> | 
|     /// 设置游戏对象激活状态 | 
|     // /// </summary> | 
|     // public void SetActive(bool active) | 
|     // { | 
|     //     if (gameObject != null) | 
|     //     { | 
|     //         gameObject.SetActive(active); | 
|     //     } | 
|     // } | 
|   | 
|     public void Pause() | 
|     { | 
|         // if (effectTarget == null) return; | 
|   | 
|         // Spine动画 | 
|         // var spineGraphics = effectTarget.GetComponentsInChildren<SkeletonGraphic>(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; | 
|     } | 
|   | 
|     public void SetSpeedRatio(float ratio) | 
|     { | 
|         speedRate = ratio; | 
|         if (spineComp != null) | 
|         { | 
|             spineComp.timeScale = speedRate; | 
|         } | 
|   | 
|         // Animator动画 | 
|         foreach (var animator in animatorList) | 
|         { | 
|             animator.speed = speedRate; | 
|         } | 
|     } | 
|   | 
|     public void BindBone(SkeletonAnimation skeletonAnim, string v) | 
|     { | 
|         Bone bone = skeletonAnim.skeleton.FindBone(v); | 
|   | 
|         if (null == bone) | 
|         { | 
|              return; | 
|         } | 
|   | 
|         isBindBone = true; | 
|         followedBone = bone; | 
|   | 
|         BoneFollower boneFollower = gameObject.AddMissingComponent<BoneFollower>(); | 
|         boneFollower.boneName = v; | 
|         boneFollower.skeletonRenderer = skeletonAnim; | 
|          | 
|         boneFollower.followBoneRotation = false; | 
|         boneFollower.followXYPosition = true; | 
|         boneFollower.followZPosition = false; | 
|         boneFollower.followLocalScale = false; | 
|         boneFollower.followParentWorldScale = false; | 
|         boneFollower.followSkeletonFlip = false; | 
|   | 
|         boneFollower.Initialize(); | 
|         boneFollower.LateUpdate(); | 
|     } | 
| } |