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; } } } 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 onDestroy; [HideInInspector] public GameObject effectTarget = null; protected RendererAdjuster blocker = null; protected bool isInit = false; protected List particleList = new List(); protected List animatorList = new List(); protected List rendererList = new List(); 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("UIComp", "SpineAnimContainer"); spineContainer = GameObject.Instantiate(spineContainerPrefab, transform, true); spineContainer.transform.localPosition = Vector3.zero; } // 有特效可能带spine又带unity特效的情况 spineComp = gameObject.GetComponentInChildren(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 funcIsHeroFront; public void SetSortingOrder(Func _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 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("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(); blocker.onSortingChanged = OnSortingChanged; ApplySortingOrder(); spineComp.enabled = true; spineComp.timeScale = speedRate; spineAnimationState.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("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(true)); animatorList.AddRange(gameObject.GetComponentsInChildren(true)); rendererList.AddRange(gameObject.GetComponentsInChildren(true)); OnUnityAnimationComplete(); // 添加特效穿透阻挡器 blocker = effectTarget.AddMissingComponent(); 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(); 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(); 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.rectTrans = newGo.AddMissingComponent(); battleEffectPlayer.effectId = effectId; // 设置阵营 battleEffectPlayer.isRedCamp = isRedCamp; battleEffectPlayer.SetActive(true); return battleEffectPlayer; } /// /// 设置游戏对象激活状态 // /// // 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; } if (spineAnimationState != null) { spineAnimationState.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; } if (spineAnimationState != null) { spineAnimationState.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; BoneFollower boneFollower = gameObject.AddMissingComponent(); 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(); } }