hch
2025-08-05 6d9a990ce3eff793946d7b43e9b48e4f7045c20e
Main/Component/UI/Effect/EffectPlayer.cs
@@ -6,24 +6,43 @@
using Spine;
using UnityEngine.UI;
// 特效播放器,对象池管理:unity特效和spine特效都是做好的预制体
// unity特效预制体是特效师在制作的时候生成的,unity特效必须挂载设置播放时长脚本 EffectTime
// spine特效是特效师制作的动画文件可直接加载用,制作成预制体可增加BoneFollower之类的进行逻辑处理
// 非UI特效使用,UI特效UIEffectPlayer继承EffectPlayer,后续如果逻辑冲突大则不用继承
public class EffectPlayer : MonoBehaviour
{
    public int effectId;
    [SerializeField]
    private int m_EffectID;
    public int effectId
    {
        get
        {
            return m_EffectID;
        }
        set
        {
            if (value != m_EffectID)
            {
                isInit = false;
                m_EffectID = value;
            }
        }
    }
    public EffectConfig effectConfig;
    public Action<EffectPlayer> onDestroy;
    public Action onComplete;
    public float speedRate = 1f;
    [Header("是否在显示时播放")]
    public bool isPlayOnEnable = false;
    [Header("是否立即播放spine特效")]
    public bool isPlaySpineImmediately = false;
    [Header("是否循环播放spine特效")]
    public bool isPlaySpineLoop = false;
    [Header("播放完毕立即回收")]
    public bool isReleaseImmediately = true;  //界面特效一般不需要自我销毁,跟随界面或者父对象销毁就行
    public Action<EffectPlayer> onDestroy;
    [HideInInspector] public Canvas canvas = null;
@@ -42,150 +61,136 @@
    protected SkeletonGraphic spineComp;
    protected Spine.AnimationState spineAnimationState;
    protected void OnEnable()
    public GameObjectPoolManager.GameObjectPool pool;
    public Action onComplete;
    protected virtual void OnEnable()
    {
        if (isPlayOnEnable)
        if (spineComp != null)
        {
            Play();
            //隐藏,会有静态显示问题
            spineComp.enabled = false;
        }
    }
    protected bool InitCompnent()
    protected void InitComponent(bool showLog = true)
    {
        if (effectId <= 0)
        {
            effectConfig = null;
            // 根据逻辑需求,动态设置特效的情况
            // Debug.LogError("EffectPlayer effectId is not set");
            // #if UNITY_EDITOR
            //             UnityEditor.Selection.activeGameObject = gameObject;
            //             UnityEditor.EditorGUIUtility.PingObject(gameObject);
            // #endif
            return false;
#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)
        {
            Debug.LogError("could not find effect config, effect id is " + effectId);
#if UNITY_EDITOR
            Debug.LogError("could not find effect config, effect id is " + effectId);
            UnityEditor.Selection.activeGameObject = gameObject;
            UnityEditor.EditorGUIUtility.PingObject(gameObject);
#endif
            return false;
            return;
        }
        Clear();
        return;
    }
    protected virtual void Clear()
    {
        particleList.Clear();
        animatorList.Clear();
        rendererList.Clear();
        spineComp = null;
        //spine是加载文件,需要提前挂载脚本,unity特效是已经做好的预制体待加载后收集组件
        if (effectConfig.isSpine != 0)
        {
            spineComp = gameObject.AddMissingComponent<SkeletonGraphic>();
        }
        // else
        // {
        //     //  收集组件
        //     particleList.AddRange(gameObject.GetComponentsInChildren<ParticleSystem>(true));
        //     animatorList.AddRange(gameObject.GetComponentsInChildren<Animator>(true));
        // }
        // rendererList.AddRange(gameObject.GetComponentsInChildren<Renderer>(true));
        return true;
        pool = null;
    }
    public void Stop()
    public virtual void Stop()
    {
        if (null != effectTarget)
        {
            DestroyImmediate(effectTarget);
            // 如果使用了特效预制体池,则归还到池中
            if (pool != null)
            {
                pool.Release(effectTarget);
            }
            effectTarget = null;
        }
        isInit = false;
        particleList.Clear();
        animatorList.Clear();
        rendererList.Clear();
        spineComp = null;
        Clear();
        onComplete?.Invoke();
    }
    public void Play()
    {
        ReStart();
    }
    protected void ReStart()
    public virtual void Play(bool showLog = true)
    {
        if (!isInit)
        {
            if (InitCompnent())
            InitComponent(showLog);
            isInit = true;
        }
        else
        {
            //避免重复创建
            if (!this.gameObject.activeSelf)
            {
                isInit = true;
                this.gameObject.SetActive(true);
            }
            else
            {
                //根据逻辑需求,动态设置特效的情况
                return;
            }
            return;
        }
        if (EffectMgr.Instance.IsNotShowBySetting(effectId))
        if (EffectMgr.IsNotShowBySetting(effectId))
        {
            return;
        }
        if (null != effectTarget)
        {
            DestroyImmediate(effectTarget);
            if (pool != null)
                pool.Release(effectTarget);
            effectTarget = null;
        }
        //   YYL TODO
        //   在这里考虑用池的话可能走配置好一点 原本的是无论如何都走池 但是实际上有些特效并不需要
        // 加载spine特效资源
        if (effectConfig.isSpine != 0)
        if (!this.gameObject.activeSelf)
        {
            spineComp.skeletonDataAsset = ResManager.Instance.LoadAsset<SkeletonDataAsset>("UIEffect/" + effectConfig.packageName, effectConfig.fxName + "_SkeletonData");
            spineComp.raycastTarget = false;
            spineComp.Initialize(true);
            spineAnimationState = spineComp.AnimationState;
            spineAnimationState.Complete -= OnSpineAnimationComplete;
            spineAnimationState.Complete += OnSpineAnimationComplete;
            if (isPlaySpineImmediately)
            {
                //UI特效常用方式
                spineComp.enabled = true;
                // 播放第一个动画(作为默认动画)
                var skeletonData = spineComp.Skeleton.Data;
                if (skeletonData.Animations.Count > 0)
                {
                    string defaultAnimationName = skeletonData.Animations.Items[0].Name;
                    spineComp.AnimationState.SetAnimation(0, defaultAnimationName, isPlaySpineLoop);
                }
                else
                {
                    Debug.LogError("Spine 数据中没有找到任何动画!" + effectConfig.id);
                }
            }
            else
            {
                spineComp.enabled = false;
            }
            return;
            this.gameObject.SetActive(true);
        }
        //加载unity特效
        PlayerEffect(true);
        SoundPlayer.Instance.PlayUIAudio(effectConfig.audio);
    }
    protected virtual void PlaySpineEffect()
    {
        spineComp = gameObject.GetComponentInChildren<SkeletonGraphic>(true);
        spineComp.raycastTarget = false;
        spineComp.Initialize(true);
        spineAnimationState = spineComp.AnimationState;
        spineAnimationState.Complete -= OnSpineAnimationComplete;
        spineAnimationState.Complete += OnSpineAnimationComplete;
        // 外层控制具体播放哪个动画
        spineComp.enabled = true;
        return;
    }
    protected virtual void PlayerEffect(bool playSpine)
    {
        var effectPrefab = ResManager.Instance.LoadAsset<GameObject>("UIEffect/" + effectConfig.packageName, effectConfig.fxName);
        if (effectPrefab == null)
        {
@@ -193,20 +198,43 @@
            return;
        }
        // 实例化特效
        effectTarget = Instantiate(effectPrefab, transform);
        if (effectConfig.isSpine == 0)
        {
            //unity特效 判断预制体是否挂载EffectTime
            var timeObj = effectPrefab.GetComponent<EffectTime>();
            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<ParticleSystem>(true));
        animatorList.AddRange(gameObject.GetComponentsInChildren<Animator>(true));
        rendererList.AddRange(gameObject.GetComponentsInChildren<Renderer>(true));
        if (effectConfig.isSpine != 0 && playSpine)
        {
            //预制体
            PlaySpineEffect();
        }
        else
        {
            //挂载组件后 开始收集
            particleList.AddRange(gameObject.GetComponentsInChildren<ParticleSystem>(true));
            animatorList.AddRange(gameObject.GetComponentsInChildren<Animator>(true));
            rendererList.AddRange(gameObject.GetComponentsInChildren<Renderer>(true));
            OnUnityAnimationComplete();
        }
        //  思考一下在没有挂在节点的时候
        if (null == canvas)
            canvas = GetComponentInParent<Canvas>();
        // 添加特效穿透阻挡器
        blocker = effectTarget.AddMissingComponent<EffectPenetrationBlocker>();
@@ -215,12 +243,6 @@
        if (canvas != null)
        {
            blocker.SetParentCanvas(canvas);
        }
        // 自动销毁
        if (effectConfig.autoDestroy != 0)
        {
            Destroy(effectTarget, effectConfig.destroyDelay);
        }
    }
@@ -232,33 +254,51 @@
            onDestroy.Invoke(this);
            onDestroy = null;
        }
        // 停止特效并归还到池中
        Stop();
        if (spineAnimationState != null)
        {
            spineAnimationState.Complete -= OnSpineAnimationComplete;
        }
    }
    //单次播放完毕就会触发,即使是循环
    protected void OnSpineAnimationComplete(Spine.TrackEntry trackEntry)
    protected virtual void OnSpineAnimationComplete(Spine.TrackEntry trackEntry)
    {
        if (!isPlaySpineLoop)
        if (isReleaseImmediately)
        {
            if (onComplete == null)
            {
                if (effectConfig.isSpine != 0)
                {
                    spineComp.enabled = false;
                }
            }
            onComplete?.Invoke();
            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 EffectPlayer Create(int effectId, Transform parent, bool createNewChild = false)
    {
        // 直接创建特效播放器,不使用对象池
        EffectPlayer effectPlayer = null;
        if (createNewChild)
@@ -270,11 +310,23 @@
        else
        {
            effectPlayer = parent.AddMissingComponent<EffectPlayer>();
            effectPlayer.effectId = effectId;
        }
        effectPlayer.effectId = effectId;
        effectPlayer.SetActive(true);
        return effectPlayer;
    }
    /// <summary>
    /// 设置游戏对象激活状态
    // /// </summary>
    // public void SetActive(bool active)
    // {
    //     if (gameObject != null)
    //     {
    //         gameObject.SetActive(active);
    //     }
    // }
    public void Pause()
    {
@@ -372,4 +424,6 @@
            return;
        }
    }
}